RT-Thread LwIP的使用

http://www.rt-thread.org/dokuwiki/doku.php?id=rt-thread_lwip%E8%AF%B4%E6%98%8E

在RT-Thread 1.1.x系列中默认使用lwIP 1.4.0作为TCP/IP协议栈,同时为了保持原有驱动的兼容性,
对lwIP 1.4初始移植做了调整,在驱动编写,初始化顺序上可以完整兼容lwIP 1.3.2的风格。

lwip 1.4 迁移指南

一、lwip,netif架构

  1.                     +-----------------------+
  2.                     |   driver interface    |
  3.                     |                       |
  4.                     |   struct eth_device   |
  5.                     | +-eth_rx_ready()      |
  6.                 +---->+-link_up()      |    |
  7.                 |   | +-link_down()    |    |
  8.                 |   +------------------|----+
  9. +-------------+ |   +------------------|----+    +-------------------+
  10. | driver      | |   |   ethernetif     |    |    | lwip 1.4          |
  11. |             | |   |                  V    |    |                   |
  12. | isr() --------+   |   rx_thread           |    | tcpip_thread      |
  13. | rx()  <-------------- netif->input  ------------>tcpip_input       |
  14. | tx()  <-------------- netif->output <----------- enetif_linkoutput |
  15. |             |     |                       |    |                   |
  16. +-------------+     +-----------------------+    +-------------------+

lwip 1.4的驱动架构和1.32相比并没有太大的变化,不过细节方面有所改动,各个模块之间功能划分更明确:

1、驱动部分
驱动的输入只有一个入口,就是isr isr接收到各种中断以后做相应的处理,包括接收到rx包,链路层通断
息等,然后调用ethernetif.h头文件中的各个函数来通知netif层。

驱动的另一个主要需要实现的部分是rx和tx这两个回调函数,它是提供给ethernetif来进行链路层数据收发使用。
rx函数一般会被netif中的rx线程调用,然后将数据包转发给tcpip层。
tx函数有可能被netif中的tx线程调用(lwip1.32的实现)或者直接被tcpip的enetif_linkoutput调用,来进行链路层
发送数据包使用。

2、ethernetif
ethernetif输入部分包含一个线程,用来接收rx消息,以及其他链路层状态消息,然后反馈给lwip。
输出部分由上层lwip调入netif→output函数,这里有可能作为消息传入tx线程(1.32实现)或者直接送给驱动的tx()。
相关参考 [http://lwip.wikia.com/wiki/Network_interfaces_management Network interfaces management]

3、LwIP1.4
lwip1.4 初始化流程和1.32的流程方面有所区别,具体参见下一节。

二、lwip,netif架构 初始化流程
初始化的过程包括两个独立的部分: 驱动初始化、 lwip和netif的初始化。
例如在mini2440平台上只需要分别执行连个函数即可:
rt_hw_dm9000_init();
lwip_enetif_init();
驱动的初始化可以放在启动第一个进程之前进行,而第二个函数需要在线程上下文中执行。

1、驱动初始化部分
a.创建eth_device的继承结构。
b.初始化eth_device中除了netif以外的所有域(netif结构会在ethernetif初始化结束以后填充)。
c.调用rt_device_register注册结构体。
d.注册中断函数。
完成这几件事情以后可能驱动部分就已经开始工作了,但是这时候lwip部分还完全没有初始化。
所以驱动部分还不能把消息传递给lwip/netif层。在这里因为eth_device的netif指针仍没有填充,
而这个指针是驱动通向netif的桥梁,所以虽然此时驱动在工作,例如可能接收中断已经打开了,
但是所有的包都会被丢弃,而不会传递到lwip去处理。

2、lwip和netif的初始化部分
lwip初始化需要遵循一定的流程,主函数是lwip_enetif_init。
在RTOS的环境下需要调用tcpip_init来初始化多线程的tcpip环境,初始化tcpip的内容就放在这个
函数的参数中tcpip_init_done_callback。之所以需要把这些函数都放在这个callback函数中的缘故
是很多函数都必须在tcpip线程中执行,以避免并发问题,例如netif_set_up之类的函数。
在tcpip_init_done_callback中初始化了ethernetif并注册到lwip中,以上这一部分一般来说应该是应
用相关的,这里只是提供一个公共的参考流程。
在初始化的最后,enetif_init函数中先将netif→linkoutput设置成了设备驱动对应的函数,在这一点
的时候实现了从netif到驱动的连接,然后又设置了dev→netif指针,于是从驱动到netif的连接也完成
了,到此为止lwip初始化完成。 有几点需要注意的:

a.在启动ethernetif的时候如果是启用了DHCP则应该调用dhcp_start,
   如果启用AUTOIP则是autoip_start,否则调用netif_set_up。
b.包括像netif_set_up,dhcp_start之类的函数,凡是在tcpip线程以外调用的时候都需要以msg发送
给tcpip线程执行的形式来执行以避免并行问题。例如,可以使用netifapi_dhcp_start替代dhcp_start,
用netifapi_netif_set_up替代netif_set_up。
但是在tcpip线程中不能调用这类函数,否则肯定会进入信号堵塞。

三、从lwip1.32升级
从lwip1.32升级到lwip1.4主要需要修改的地方有下面三个:

1、驱动修改
以dm9000的驱动为例,需要将eth_device_ready函数换成eth_rx_ready,这主要是一个语义上的改动。
同样,驱动还可以通过调用eth_linkup,eth_linkdown之类的函数来通知netif相应的状态消息。

  1. - eth_device_ready(&(dm9000_device.parent));
  2. + eth_rx_ready(&(dm9000_device.parent));

在驱动初始化函数中,原先的eth_device_init需要删除,然后添加初始化和注册设备驱动函数。

  1. - eth_device_init(&(dm9000_device.parent), "e0");
  2. + dm9000_device.parent.parent.type = RT_Device_Class_NetIf;
  3. + rt_device_register(&(dm9000_device.parent.parent), "eth0", RT_DEVICE_FLAG_RDWR);

最后,添加一个函数get_eth_dev,这个函数是在ethernetif最后被lwip初始化的时候获得设备指
针用的。对ethernetif来说必须有一个驱动与之对应,在逻辑上netif并不是驱动的一部分,而是
注册在lwip中的一部分,它必须由lwip来初始化,由lwip来使用。

  1. +struct eth_device * get_eth_dev(void)
  2. +{
  3. + return (struct eth_device *)&dm9000_device;
  4. +}

2、启动调用
目前的启动调用流程逻辑很清楚,按顺序调用下面两个函数即可。

  1. rt_hw_dm9000_init();  //初始化设备驱动
  2. lwip_enetif_init();   //初始化lwip,并间接初始化ethnetif,最后完成ethernetif和驱动的连接。

而原先的

  1. lwip_sys_init(void);
  2. eth_system_device_init();
  3. rt_hw_dm9000_init();
只有第三个函数保留,但功能上会有变化,其余两个函数不再使用。

四、lwip netconn 函数修改
lwip1.4修改了netconn调用接口,所有函数统一返回错误值,例如: lwip1.32中recv函数是

  1. struct netbuf *
  2. netconn_recv(struct netconn *conn)

到了lwip1.4中改为

  1. err_t
  2. netconn_recv(struct netconn *conn, struct netbuf **new_buf)
原先以返回值得到的netbuf改成通过参数返回,其他函数大同小异,可以此类推。

你可能感兴趣的:(RT-Thread)