uboot中的协议栈相对来说比较简单,有以下几个特点:
传输层只支持UDP协议;
- 目前只支持ICMP、TFTP、NFS、DNS、DHCP、CDP、SNTP等几种协议;
- 网卡采用poll接收数据包,而不是中断方式;
- 数据包的发送和接收操作是串行操作,不支持并发。
1. 网络协议栈架构
下面是uboot网络协议栈的函数调用流程:
2. 通过DNS命令来解析一个数据包的收发流程
uboot中,所有的命令都用宏U_BOOT_CMD来定义, dns命令的定义如下:
- 426 U_BOOT_CMD(
- 427 dns, 3, 1, do_dns,
- 428 "lookup the IP of a hostname",
- 429 "hostname [envvar]"
- 430 );
当我们在uboot的命令终端输入命令dns后,命令解析函数就会调用dns执行函数do_dns()
- 389 int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
- 390 {
- ……
- 406 if (strlen(argv[1]) >= 255) {
- 407 printf("dns error: hostname too long\n");
- 408 return 1;
- 409 }
- 410
- 411 NetDNSResolve = argv[1];
- 412
- 413 if (argc == 3)
- 414 NetDNSenvvar = argv[2];
- 415 else
- 416 NetDNSenvvar = NULL;
- 417
- 418 if (NetLoop(DNS) < 0) {
- 419 printf("dns lookup of %s failed, check setup\n", argv[1]);
- 420 return 1;
- 421 }
- 422
- 423 return 0;
- 424 }
406行 判断参数字符串长度,大于255非法 411行 参数1必须是要解析的主机,存储在NetDNSResolve 中 413~416行 保存dns命令的环境参数,该参数可以没有 418行 进入网络协议处理函数入口NetLoop(),并将对应的协议DNS传递给该函数
NetLoop()代码比较长,我们只分析其中比较重要的几段代码
- 316
- 317
- 320
- 321 int NetLoop(enum proto_t protocol)
- 322 {
- 323 bd_t *bd = gd->bd;
- 324 int ret = -1;
- …………
- 352 NetInitLoop();
- …………
- 367 switch (protocol) {
- 368 case TFTPGET:
- 369 #ifdef CONFIG_CMD_TFTPPUT
- 370 case TFTPPUT:
- 371 #endif
- 372
- 373 TftpStart(protocol);
- 374 break;
- …………
- 426 #if defined(CONFIG_CMD_DNS)
- 427 case DNS:
- 428 DnsStart();
- 429 break;
- 430 #endif
- 438 }
- …………
- 461 for (;;) {
- 462 WATCHDOG_RESET();
- 463 #ifdef CONFIG_SHOW_ACTIVITY
- 464 show_activity(1);
- 465 #endif
- 466
- 470 eth_rx();
- 471
- 472
- 475 if (ctrlc()) {
- 476
- 477 NetArpWaitPacketIP = 0;
- 478
- 479 net_cleanup_loop();
- 480 eth_halt();
- 481
- 482 eth_set_last_protocol(BOOTP);
- 483
- 484 puts("\nAbort\n");
- 485
- 487 debug_cond(DEBUG_INT_STATE, "--- NetLoop Abort!\n");
- 488 goto done;
- 489 }
- …………
- 522 switch (net_state) {
- 523
- 524 case NETLOOP_RESTART:
- 525 NetRestarted = 1;
- 526 goto restart;
- 527
- 528 case NETLOOP_SUCCESS:
- 529 net_cleanup_loop();
- 530 if (NetBootFileXferSize > 0) {
- 531 char buf[20];
- 532 printf("Bytes transferred = %ld (%lx hex)\n",
- 533 NetBootFileXferSize,
- 534 NetBootFileXferSize);
- 535 sprintf(buf, "%lX", NetBootFileXferSize);
- 536 setenv("filesize", buf);
- 537
- 538 sprintf(buf, "%lX", (unsigned long)load_addr);
- 539 setenv("fileaddr", buf);
- 540 }
- 541 if (protocol != NETCONS)
- 542 eth_halt();
- 543 else
- 544 eth_halt_state_only();
- 545
- 546 eth_set_last_protocol(protocol);
- 547
- 548 ret = NetBootFileXferSize;
- 549 debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!\n");
- 550 goto done;
- 551
- 552 case NETLOOP_FAIL:
- 553 net_cleanup_loop();
- 554
- 555 eth_set_last_protocol(BOOTP);
- 556 debug_cond(DEBUG_INT_STATE, "--- NetLoop Fail!\n");
- 557 goto done;
- 558
- 559 case NETLOOP_CONTINUE:
- 560 continue;
- 561 }
- 562 }
- 563
- 564 done:
- 565 #ifdef CONFIG_CMD_TFTPPUT
- 566
- 567 net_set_udp_handler(NULL);
- 568 net_set_icmp_handler(NULL);
- 569 #endif
- 570 return ret;
- 571 }
函数参数为DNS 352行 初始化网络信息,读取ipaddr、gatewayip、netmask、serverip、dnsip等环境变量的值并复制到对应的全局变量中
- static void NetInitLoop(void)
- {
- static int env_changed_id;
- int env_id = get_env_id();
-
-
- if (env_changed_id != env_id) {
- NetOurIP = getenv_IPaddr("ipaddr");
- NetOurGatewayIP = getenv_IPaddr("gatewayip");
- NetOurSubnetMask = getenv_IPaddr("netmask");
- NetServerIP = getenv_IPaddr("serverip");
- NetOurNativeVLAN = getenv_VLAN("nvlan");
- NetOurVLAN = getenv_VLAN("vlan");
- #if defined(CONFIG_CMD_DNS)
- NetOurDNSIP = getenv_IPaddr("dnsip");
- #endif
- env_changed_id = env_id;
- }
- memcpy(NetOurEther, eth_get_dev()->enetaddr, 6);
-
- return;
- }
367行 对传入的参数做switch操作,不同的协议进入到不同的处理流程 428行 执行DnsStart(),
- 197 void
- 198 DnsStart(void)
- 199 {
- 200 debug("%s\n", __func__);
- 201
- 202 NetSetTimeout(DNS_TIMEOUT, DnsTimeout);
- 203 net_set_udp_handler(DnsHandler);
- 204
- 205 DnsSend();
- 206 }
203行 函数net_set_udp_handler()主要将dns协议的回调函数DnsHandler()注册到udp协议的回调指针udp_packet_handler,
- void net_set_udp_handler(rxhand_f *f)
- {
- debug_cond(DEBUG_INT_STATE, "--- NetLoop UDP handler set (%p)\n", f);
- if (f == NULL)
- udp_packet_handler = dummy_handler;//注册到udp协议回调函数指针
- else
- udp_packet_handler = f;
- }
DnsStart()最终会调用函数DnsSend()发送dns协议数据包,该函数是根据dns协议填充udp数据包
- 37 static void
- 38 DnsSend(void)
- 39 {
- 40 struct header *header;
- 41 int n, name_len;
- 42 uchar *p, *pkt;
- 43 const char *s;
- 44 const char *name;
- 45 enum dns_query_type qtype = DNS_A_RECORD;
- 46
- 47 name = NetDNSResolve;
- 48 pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE);
- 49
- 50
- 51 header = (struct header *) pkt;
- 52 header->tid = 1;
- 53 header->flags = htons(0x100);
- 54 header->nqueries = htons(1);
- 55 header->nanswers = 0;
- 56 header->nauth = 0;
- 57 header->nother = 0;
- 58
- 59
- 60 name_len = strlen(name);
- 61 p = (uchar *) &header->data;
- 62
- 63 do {
- 64 s = strchr(name, '.');
- 65 if (!s)
- 66 s = name + name_len;
- 67
- 68 n = s - name;
- 69 *p++ = n;
- 70 memcpy(p, name, n);
- 71 p += n;
- 72
- 73 if (*s == '.')
- 74 n++;
- 75
- 76 name += n;
- 77 name_len -= n;
- 78 } while (*s != '\0');
- 79
- 80 *p++ = 0;
- 81 *p++ = 0;
- 82 *p++ = (unsigned char) qtype;
- 83
- 84 *p++ = 0;
- 85 *p++ = 1;
- 86
- 87 n = p - pkt;
- 88 debug("Packet size %d\n", n);
- 89
- 90 DnsOurPort = random_port();
- 91
- 92 NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT,
- 93 DnsOurPort, n);
- 94 debug("DNS packet sent\n");
- 95 }
51~57行 根据dns协议填充dns协议头,数据帧首地址为NetTxPacket,此处通过指针pkt和p来填充dns数据帧 60~85行 根据协议格式要求填充要解析的host名字到数据包 87行 计算数据包长度 90行 产生一个随机的端口号 92~93行 调用udp协议的发送函数NetSendUDPPacket(),参数依次是:以太头信息,DNS服务器 ip地址,DNS服务器端口号,我们的dns服务端口号,数据包长度
- 688 int NetSendUDPPacket(uchar *ether, IPaddr_t dest, int dport, int sport,
- 689 int payload_len)
- 690 {
- 691 uchar *pkt;
- 692 int eth_hdr_size;
- 693 int pkt_hdr_size;
- 694
- 695
- 696 assert(NetTxPacket != NULL);
- 697 if (NetTxPacket == NULL)
- 698 return -1;
- 699
- 700
- 701 if (dest == 0)
- 702 dest = 0xFFFFFFFF;
- 703
- 704
- 705 if (dest == 0xFFFFFFFF)
- 706 ether = NetBcastAddr;
- 707
- 708 pkt = (uchar *)NetTxPacket;
- 709
- 710 eth_hdr_size = NetSetEther(pkt, ether, PROT_IP);
- 711 pkt += eth_hdr_size;
- 712 net_set_udp_header(pkt, dest, dport, sport, payload_len);
- 713 pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
- 714
- 715
- 716 if (memcmp(ether, NetEtherNullAddr, 6) == 0) {
- 717 debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &dest);
- 718
- 719
- 720 NetArpWaitPacketIP = dest;
- 721 NetArpWaitPacketMAC = ether;
- 722
- 723
- 724 NetArpWaitTxPacketSize = pkt_hdr_size + payload_len;
- 725
- 726
- 727 NetArpWaitTry = 1;
- 728 NetArpWaitTimerStart = get_timer(0);
- 729 ArpRequest();
- 730 return 1;
- 731 } else {
- 732 debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n",
- 733 &dest, ether);
- 734 NetSendPacket(NetTxPacket, pkt_hdr_size + payload_len);
- 735 return 0;
- 736 }
- 737 }
696~706行 参数检查 710行 设置以太头 713行 设置udp协议头 716~730行 如果没有目的MAC地址,就要先发送ARP请求 734行 调用函数NetSendPacket(),参数分别是:要发送数据帧的首地址,数据包长度
- 529
- 530 static inline void NetSendPacket(uchar *pkt, int len)
- 531 {
- 532 (void) eth_send(pkt, len);
- 533 }
532行 调用我们注册的函数dm9000_send() 该函数已经分析过,根据流程图,回到函数NetLoop()
461~562行 循环接收网络数据包 470行 调用网卡驱动接收函数eth_rx()
- int eth_rx(void)
- {
- if (!eth_current)
- return -1;
-
- return eth_current->recv(eth_current);
- }
eth_current->recv(eth_current)函数就是我们注册的网卡的接收函数dm9000_rx(),该函数我们上一章已经分析过,最终通过调用函数NetReceive(),将数据帧上传到协议栈
- 943 void
- 944 NetReceive(uchar *inpkt, int len)
- 945 {
- 946 struct ethernet_hdr *et;
- 947 struct ip_udp_hdr *ip;
- 948 IPaddr_t dst_ip;
- 949 IPaddr_t src_ip;
- 950 int eth_proto;
- ……
- 957
- 958 NetRxPacket = inpkt;
- 959 NetRxPacketLen = len;
- 960 et = (struct ethernet_hdr *)inpkt;
- 961
- 962
- 963 if (len < ETHER_HDR_SIZE)
- 964 return;
- 965
- ……
- 984
- 985 eth_proto = ntohs(et->et_protlen);
- 986
- 987 if (eth_proto < 1514) {
- 988 struct e802_hdr *et802 = (struct e802_hdr *)et;
- ……
- 997
- 998 } else if (eth_proto != PROT_VLAN) {
- 999 ip = (struct ip_udp_hdr *)(inpkt + ETHER_HDR_SIZE);
- 1000 len -= ETHER_HDR_SIZE;
- 1001
- 1002 } else {
- ……
- 1026 }
- 1027
- ……
- 1045 switch (eth_proto) {
- ……
- 1056 case PROT_IP:
- 1057 debug_cond(DEBUG_NET_PKT, "Got IP\n");
- 1058
- 1059 if (len < IP_UDP_HDR_SIZE) {
- 1060 debug("len bad %d < %lu\n", len,
- 1061 (ulong)IP_UDP_HDR_SIZE);
- 1062 return;
- 1063 }
- 1064
- 1065 if (len < ntohs(ip->ip_len)) {
- 1066 debug("len bad %d < %d\n", len, ntohs(ip->ip_len));
- 1067 return;
- 1068 }
- 1069 len = ntohs(ip->ip_len);
- 1070 debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
- 1071 len, ip->ip_hl_v & 0xff);
- 1072
- 1073
- 1074 if ((ip->ip_hl_v & 0xf0) != 0x40)
- 1075 return;
- 1076
- 1077 if ((ip->ip_hl_v & 0x0f) > 0x05)
- 1078 return;
- 1079
- 1080 if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE / 2)) {
- 1081 debug("checksum bad\n");
- 1082 return;
- 1083 }
- 1084
- 1085 dst_ip = NetReadIP(&ip->ip_dst);
-
- 1092
- 1093 src_ip = NetReadIP(&ip->ip_src);
-
-
- 1184
- 1187 (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE,
- 1188 ntohs(ip->udp_dst),
- 1189 src_ip,
- 1190 ntohs(ip->udp_src),
- 1191 ntohs(ip->udp_len) - UDP_HDR_SIZE);
- 1192 break;
- 1193 }
- 1194 }
参数inpkt:指向接收到的以太数据包头 len:接收到的数据包的长度 960行 变量NetRxPacket指向接收的数据头,以太数据包包头比定位以太协议头 985行 从以太协议头提取出协议字段,该字段表示后面是否是ip协议 999行 解析出ip协议头 1045行 根据以太头协议进行switch操作 1059~1083行 对协议头进行合法性检查 1085行 读取出目的ip地址 1093行 读取出源ip地址, 1187行 ip协议头解析成功,调用udp协议回调函数udp_packet_handler(),该函数在之前的DnStart()注册了DnsHandler
- 104 static void
- 105 DnsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len)
- 106 {
- 193
- 194 net_set_state(NETLOOP_SUCCESS);
- 195 }
该函数用于解析DNS协议,在此不再详解 解析成功后194行,会设置当前执行状态为NETLOOP_SUCCESS, 代码回到函数NetLoop()470行 475行 判断是否按下ctrl+c快捷键,并作出操作 522~562行 对执行结果进行处理,计入统计信息 564行 如果net_state为NETLOOP_SUCCESS、NETLOOP_FAIL最终都会进入done,从而置空udp回调函数 如果net_state为NETLOOP_CONTINUE,表明仍然有后续数据包要接收,则回到461行,继续下一个数据包的接收
至此DNS协议的处理流程分析完毕,大家可以根据这个流程自行分析其他几个协议的处理流程。
容易遇到的问题
有的时候能读取到 DM9000A 的 ID,连续操作就能读取到 DM9000A 的 ID,但间隔一会操作就读取不到 DM9000A 的 ID,通过调试,在 dm9000_reset 函数中加一句延时操作,就可以正常读取 DM9000A 的 ID 了。
- 277 do {
- 278 DM9000_DBG("resetting the DM9000, 2nd reset\n");
- 279 udelay(25);
- 280 } while (DM9000_ior(DM9000_NCR) & 1);
- 281 udelay(150);
- 282
- 283 if ((DM9000_ior(DM9000_PIDL) != 0x0) ||
- 284 (DM9000_ior(DM9000_PIDH) != 0x90))
- 285 printf("ERROR: resetting DM9000 -> not responding\n");