🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
经过上面的移植,我们的底层与操作系统接口都基本移植完毕,想要让LwIP在操作系统中能跑起来,还需要最后一步,将协议栈进行初始化,前面我们也说了,内核在操作系统中是作为一个线程独立存在的,在初始化的时候,我们不仅要挂载网卡,也要创建tcpip\_thread()线程,当然,这个线程LwIP会在初始的时候自动创建,而挂载网卡的内容与无操作系统是一样的,就无需过多修改,协议栈初始化的源码具体见代码清单 8‑7。 ``` 1 /*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */ 2 #define IP_ADDR0 192 3 #define IP_ADDR1 168 4 #define IP_ADDR2 1 5 #define IP_ADDR3 122 6 7 /*NETMASK*/ 8 #define NETMASK_ADDR0 255 9 #define NETMASK_ADDR1 255 10 #define NETMASK_ADDR2 255 11 #define NETMASK_ADDR3 0 12 13 /*Gateway Address*/ 14 #define GW_ADDR0 192 15 #define GW_ADDR1 168 16 #define GW_ADDR2 1 17 #define GW_ADDR3 1 18 /* USER CODE END 0 */ 19 20 struct netif gnetif; 21 ip4_addr_t ipaddr; 22 ip4_addr_t netmask; 23 ip4_addr_t gw; 24 uint8_t IP_ADDRESS[4]; 25 uint8_t NETMASK_ADDRESS[4]; 26 uint8_t GATEWAY_ADDRESS[4]; 27 28 void LwIP_Init(void) 29 { 30 31 tcpip_init(NULL, NULL); 32 33 /* IP addresses initialization */ 34 /* USER CODE BEGIN 0 */ 35 #ifdef USE_DHCP 36 ip_addr_set_zero_ip4(&ipaddr); 37 ip_addr_set_zero_ip4(&netmask); 38 ip_addr_set_zero_ip4(&gw); 39 #else 40 IP4_ADDR(&ipaddr,IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3); 41 IP4_ADDR(&netmask,NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3); 42 IP4_ADDR(&gw,GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3); 43 #endif /* USE_DHCP */ 44 /* USER CODE END 0 */ 45 /* Initilialize the LwIP stack without RTOS */ 46 /* add the network interface (IPv4/IPv6) without RTOS */ 47 netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, 48 &ethernetif_init, &tcpip_input); 49 50 /* Registers the default network interface */ 51 netif_set_default(&gnetif); 52 53 if (netif_is_link_up(&gnetif)) 54 { 55 /* When the netif is fully configured this function must be called */ 56 netif_set_up(&gnetif); 57 } 58 else 59 { 60 /* When the netif link is down this function must be called */ 61 netif_set_down(&gnetif); 62 } 63 64 } ``` 在tcpip\_init()函数中,LwIP会调用lwip\_init()将内核进行初始化,并且创建一个tcpip\_mbox邮箱,邮箱的大小是TCPIP\_MBOX\_SIZE,用于接收从底层或者上层传递过来的消息,并且最重要的是创建一个tcpip\_thread线程,这就是LwIP在操作系统中作为一个独立的线程运行,所有处理的数据都要这个线程去处理,这个线程我们会在后续讲解。lwip\_init()函数的源码具体见代码清单 8‑8: ``` 1 void 2 tcpip_init(tcpip_init_done_fn initfunc, void *arg) 3 { 4 lwip_init(); 5 6 tcpip_init_done = initfunc; 7 tcpip_init_done_arg = arg; 8 if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) 9 { 10 LWIP_ASSERT("failed to create tcpip_thread mbox", 0); 11 } 12 #if LWIP_TCPIP_CORE_LOCKING 13 if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) 14 { 15 LWIP_ASSERT("failed to create lock_tcpip_core", 0); 16 } 17 #endif /* LWIP_TCPIP_CORE_LOCKING */ 18 19 sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, 20 TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); 21 } ``` 对于代码清单 8‑7中netif\_add()函数,与裸机移植的时候有一点细微的差别,它最后一个参数就是tcpip\_input,是一个函数,而不是原来的ethernet\_input(),因为tcpip\_input()函数会将网卡收到的数据包打包成为一个消息,发送到tcpip\_mbox邮箱中,传递给tcpip\_thread线程去处理,不过本质上也是调用ethernet\_input()函数去递交这个数据包,只不过是绕了一大圈,因此,想要LwIP能正常运行,消息机制是不可或缺的,关于这个运作机制我们在后文详细讲解。