Uboot的网络

您所在的位置:网站首页 ubootcmd Uboot的网络

Uboot的网络

#Uboot的网络| 来源: 网络整理| 查看: 265

7. net

uboot 支持 tcp/ip 网络协议,但是作为一个 bootloader 它并没有把协议栈作为一个后台线程长时间运行,而是在使用到网络功能时才会初始化协议栈、使用网络功能。

7.1. 协议栈主循环

net_loop(net/net.c) 是网络协议栈的主循环,所有的网络操作最终都会进入这里。

int net_loop(enum proto_t protocol) { ... if (eth_is_on_demand_init() || protocol != NETCONS) { eth_halt(); eth_set_current(); ret = eth_init(); if (ret < 0) { eth_halt(); return ret; } } else { eth_init_state_only(); } restart: ... switch (net_check_prereq(protocol)) { case 1: /* network not configured */ eth_halt(); return -ENODEV; case 2: /* network device not configured */ break; case 0: net_dev_exists = 1; net_boot_file_size = 0; switch (protocol) { case TFTPGET: #ifdef CONFIG_CMD_TFTPPUT case TFTPPUT: #endif /* always use ARP to get server ethernet address */ tftp_start(protocol); break; #ifdef CONFIG_CMD_TFTPSRV case TFTPSRV: tftp_start_server(); break; #endif #if defined(CONFIG_CMD_DHCP) case DHCP: bootp_reset(); net_ip.s_addr = 0; dhcp_request(); /* Basically same as BOOTP */ break; #endif case BOOTP: bootp_reset(); net_ip.s_addr = 0; bootp_request(); break; #if defined(CONFIG_CMD_RARP) case RARP: rarp_try = 0; net_ip.s_addr = 0; rarp_request(); break; #endif #if defined(CONFIG_CMD_PING) case PING: ping_start(); break; #endif #if defined(CONFIG_CMD_NFS) case NFS: nfs_start(); break; #endif #if defined(CONFIG_CMD_CDP) case CDP: cdp_start(); break; #endif #if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD) case NETCONS: nc_start(); break; #endif #if defined(CONFIG_CMD_SNTP) case SNTP: sntp_start(); break; #endif #if defined(CONFIG_CMD_DNS) case DNS: dns_start(); break; #endif #if defined(CONFIG_CMD_LINK_LOCAL) case LINKLOCAL: link_local_start(); break; #endif default: break; } break; } #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) #if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \ defined(CONFIG_STATUS_LED) && \ defined(STATUS_LED_RED) /* * Echo the inverted link state to the fault LED. */ if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR)) status_led_set(STATUS_LED_RED, STATUS_LED_OFF); else status_led_set(STATUS_LED_RED, STATUS_LED_ON); #endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */ #endif /* CONFIG_MII, ... */ #ifdef CONFIG_USB_KEYBOARD net_busy_flag = 1; #endif /* * Main packet reception loop. Loop receiving packets until * someone sets `net_state' to a state that terminates. */ for (;;) { WATCHDOG_RESET(); #ifdef CONFIG_SHOW_ACTIVITY show_activity(1); #endif if (arp_timeout_check() > 0) time_start = get_timer(0); /* * Check the ethernet for a new packet. The ethernet * receive routine will process it. * Most drivers return the most recent packet size, but not * errors that may have happened. */ eth_rx(); /* * Abort if ctrl-c was pressed. */ if (ctrlc()) { /* cancel any ARP that may not have completed */ net_arp_wait_packet_ip.s_addr = 0; net_cleanup_loop(); eth_halt(); /* Invalidate the last protocol */ eth_set_last_protocol(BOOTP); puts("\nAbort\n"); /* include a debug print as well incase the debug messages are directed to stderr */ debug_cond(DEBUG_INT_STATE, "--- net_loop Abort!\n"); ret = -EINTR; goto done; } /* * Check for a timeout, and run the timeout handler * if we have one. */ if (time_handler && ((get_timer(0) - time_start) > time_delta)) { thand_f *x; #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) #if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \ defined(CONFIG_STATUS_LED) && \ defined(STATUS_LED_RED) /* * Echo the inverted link state to the fault LED. */ if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR)) status_led_set(STATUS_LED_RED, STATUS_LED_OFF); else status_led_set(STATUS_LED_RED, STATUS_LED_ON); #endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */ #endif /* CONFIG_MII, ... */ debug_cond(DEBUG_INT_STATE, "--- net_loop timeout\n"); x = time_handler; time_handler = (thand_f *)0; (*x)(); } if (net_state == NETLOOP_FAIL) ret = net_start_again(); switch (net_state) { case NETLOOP_RESTART: net_restarted = 1; goto restart; case NETLOOP_SUCCESS: net_cleanup_loop(); if (net_boot_file_size > 0) { printf("Bytes transferred = %d (%x hex)\n", net_boot_file_size, net_boot_file_size); setenv_hex("filesize", net_boot_file_size); setenv_hex("fileaddr", load_addr); } if (protocol != NETCONS) eth_halt(); else eth_halt_state_only(); eth_set_last_protocol(protocol); ret = net_boot_file_size; debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n"); goto done; case NETLOOP_FAIL: net_cleanup_loop(); /* Invalidate the last protocol */ eth_set_last_protocol(BOOTP); debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n"); goto done; case NETLOOP_CONTINUE: continue; } } done: #ifdef CONFIG_USB_KEYBOARD net_busy_flag = 0; #endif #ifdef CONFIG_CMD_TFTPPUT /* Clear out the handlers */ net_set_udp_handler(NULL); net_set_icmp_handler(NULL); #endif return ret; }

从上面的代码可以看出 uboot 网络处里流程和一般的协议栈类似(如 lwip 所有的操作都是放在一个循环,在一个线程中运行)。

流程可以分解为 3 步:

初始化协议栈 net_init(); net_init_loop(); 区分不同的子协议(如 ping 、 ICMP 、 arp 等) case DHCP: ... case PING: ... case DNS: ... ... 关闭协议栈和网络设备 ... eth_halt(); ... net_cleanup_loop(); ... 7.2. 协议栈和驱动

协议栈收发数据都是通过函数 eth_send() 和 eth_rx() 完成的。

发包:

int eth_send(void *packet, int length) { ... current = eth_get_dev(); ... ret = eth_get_ops(current)->send(current, packet, length); if (ret < 0) { /* We cannot completely return the error at present */ debug("%s: send() returned error %d\n", __func__, ret); } return ret; }

收包:

int eth_rx(void) { ... current = eth_get_dev(); ... for (i = 0; i < 32; i++) { ret = eth_get_ops(current)->recv(current, flags, &packet); flags = 0; if (ret > 0) net_process_received_packet(packet, ret); if (ret >= 0 && eth_get_ops(current)->free_pkt) eth_get_ops(current)->free_pkt(current, packet, ret); if (ret udp_src; struct in_addr src_ip; int eth_hdr_size; switch (icmph->type) { case ICMP_ECHO_REPLY: src_ip = net_read_ip((void *)&ip->ip_src); if (src_ip.s_addr == net_ping_ip.s_addr) net_set_state(NETLOOP_SUCCESS); return; ... /* default: return;*/ } }

通过函数 net_set_state() 告知 net_loop() ping 操作成功,否则 ping 失败,即网络有问题。

7.4. tftp

tftp 分两部分:向服务器发送请求和从服务器接收数据,所以在 net_loop() 中 tftp 协议有两组判断条件:

int net_loop(enum proto_t protocol) { ... case TFTPGET: #ifdef CONFIG_CMD_TFTPPUT case TFTPPUT: #endif /* always use ARP to get server ethernet address */ tftp_start(protocol); break; ... }

发送请求(TFTPGET 和 TFTPPUT),向对端发送 tftp 请求,比如 GET 和 PUT ,调用 tftp_start() 发送请求报文:

void tftp_start(enum proto_t protocol) { ... net_set_udp_handler(tftp_handler); ... tftp_send(); ... }

其中 net_set_udp_handler() 会注册 tftp_handler() 到钩子函数 static rxhand_f *udp_packet_handler;,而 tftp_handler() 会调用 tftp_complete() 检查接收文件是否结束。

接下来 net_loop() 会循环调用 eth_rx() 接收数据包,直到所有数据都收完(钩子函数 udp_packet_handler 会不断的检查已收到的文件长度和实际文件长度是否一直)。

7.5. 小结

uboot 的网络协议栈可以说是麻雀虽小肝胆俱全,网络协议栈所需要的功能它基本都实现了,比如 ping 、 tftp 、tftp server 、DHCP 、 bootp 、 arp/rarp 、DNS 等,它主要侧重于功能的实现,对实时性、并发性等要求不高,所以都是由用户输入命令主动发起操作,并且是单线程操作。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3