RT-Thread的TCP/IP协议栈讨论之二:LwIP 1.3.2初始化序列

140 views
Skip to first unread message

bernard

unread,
Aug 8, 2011, 10:12:02 PM8/8/11
to rt-threa...@googlegroups.com, itspy wei
最近mbbill在svn上完成了lwip
1.4.0的移植、更新、完善。相应的也发现了原来(1.3.2等版本)移植的一些问题,例如以太网报文接收到时,需要先行处理ARP。

原来的做法是ethernet interface中主动调用etherarp_input来处理。而lwip正确的做法则是,当一个interface的参数包含ARP选项时,lwip自动调用etherarp_input来进行处理,并不需要ethernet
interface这层来关注这个事情。

另外,ethernet interface支持link
change的功能,这样在网线拔插时,能够通知到上层lwip协议栈。但是当启用link change后,当一个interface link
up时,会通知给lwip协议栈。如果这时这个interface配置了DHCP功能,LwIP将自动进行DHCP请求以获得IP地址。按照以前的初始化序列,DHCP功能是在LwIP初始化时进行。这样如果是link
change在进行DHCP时触发了link up,两个DHCP请求将产生冲突,导致系统当机。

这块目前看来还是与LwIP初始化序列相关。那么现在1.3.2版本的lwip是否也应该改成与1.4.0类似的?如果这样改,是否也会牵涉到修改以太网驱动,是否有办法尽量不修改?mbbill和itspy多看看这个问题,谢谢。

NOTE: 这些问题也是在为客户做支持,做定制时发现的,既然发现了,就希望能够改正它,并回馈到开源社区。


Best Regards,
Bernard Xiong

bernard

unread,
Aug 9, 2011, 5:25:33 AM8/9/11
to rt-threa...@googlegroups.com, itspy wei
目前lwip 1.3.2的初始序列是这样的:
eth_system_device_init() --> 初始化etx/erx线程(这两个线程分别处理以太网的数据收发);
rt_hw_dm9000_init() --> 向系统中注册设备;
lwip_sys_init() --> 初始化lwip协议栈。
在lwip_sys_init()中,如果一个interface配置成DHCP方式,将有一个while
(netif_default->ip_addr.addr == 0)的DHCP请求发出。

mbbill, 1.4.0的初始化序列是什么样的?

MingBai

unread,
Aug 9, 2011, 7:03:57 AM8/9/11
to rt-threa...@googlegroups.com

�� 2011/8/9 17:25, bernard �:
> Ŀǰlwip 1.3.2�ij�ʼ����������ģ�
> eth_system_device_init() --> ��ʼ��etx/erx�̣߳��������̷ֱ߳�����̫�������շ�����
> rt_hw_dm9000_init() --> ��ϵͳ��ע���豸��
������������漸�д��룬����һЩ���⣺

dm9000_device.parent.eth_rx = rt_dm9000_rx;
dm9000_device.parent.eth_tx = rt_dm9000_tx;
rt_hw_dm9000_init() -> eth_device_init
1 ��eth_device_init�л����rt_device_register��ע��device�Ĺ�����Ӧ����
netif�У�Ӧ���� dm9000_init���������޸�����һ�㡣
2 ��������л����ʼ��netif�IJ��ֲ���netif��ȷ�ij�ʼ��ʱ��������
netif_addע��netif��ʱ��ͨ��ص����������С�
3 ������������netif_add��ע��netif��ʱ��lwip���?û�г�ʼ������Ҳ����
����ġ�
4 ��rt_hw_dm9000_init ִ������Ժ�eth���netif��Ϳ�ʼ�����ˡ�����������
lwip��ʼ��֮ǰ�����߰�ض������tx�ж�Ҳ�ǻ�������ġ�

lwip_sys_init() --> ��ʼ��lwipЭ��ջ��
> ��lwip_sys_init()�У����һ��interface���ó�DHCP��ʽ������һ��while
> (netif_default->ip_addr.addr == 0)��DHCP���󷢳���
��lwip_sys_init()��Ҳ��һЩ����
5 �ȵ�����tcpip_init()��ʼ��lwip��Ȼ�����netif_set_addr��netif_set_up��
���Dz��Եġ�netif_xxx�� Щ������ȷ�ĵ���λ��Ӧ������tcpip�̷߳��𣬿���
�ûص�������tcpip_init()����������ͨ��msgapi����tcpip�߳� ������ֹ����
����
6 netif_set_up��dhcp_start�ǵȼۺ�����ʵ����һ������autoip_start������
�������ǵȼ۵ģ�����һ��netif����� Ҫ����������֮һ���� �������lwip��
wiki����˵����

> mbbill, 1.4.0�ij�ʼ��������ʲô��ģ�
>
������Կ�����һ�������Ե����⣬�������ʼ�����ֺ�lwip��ʼ�����ֻ���һ
�𣬵��?�Ǻ�������ʱ��������Ҳ�������⣬����ǰЩʱ����� �����ϵ���
���һ���߳�����֮ǰ�ͳ�ʼ��lwip�����⡣

1.4��������ô���ģ�
�Ӵ�ķ��濴����ʼ���������֣�Ҳ�������ϵ��Ժ������������
rt_hw_dm9000_init();
lwip_enetif_init();
����������ֱ������ʼ����lwip/netif��ʼ�������ǻ���û�н�����ϣ�˳��
Ҳ���Եߵ�������һ�㽨�����ʼ����ǰ�档
Ȼ��ӵ���ʱ��������˵�����ʼ�����֣��������rt_hw_dm9000_init();����
���ﶼ�У����� lwip_enetif_init()��������߳���������ִ�С�
��С�ķ��濴�����ȵ�һ������rt_hw_dm9000_init��������þ��dz�ʼ���豸��
Ȼ����ϵͳע���豸����ʱ���жϿɿ��ɲ���������� �ϴ��ˣ���ô����rx��
�Ķ��ᶪ���ύ��lwip�㣬�������������3
Ȼ��ڶ�������lwip_enetif_init
��ҿ��Կ�һ��1.4�����ʵ�֣���src/netif/ethernetif.c��󲿷֣��Ҿ��÷�
��������ʵҲ�Dz�̫��ȷ�ģ���Ϊ�ⲿ�ֳ�ʼ ��Ӧ���Ǻ�Ӧ����أ�������õ�
λ��Ӧ����bsp����������Ҫ������ο����ã�
���������ǵ�����һ��tcpip_init��Ȼ��ȴ��ʼ����ɣ���Ҫ������
tcpip_init_done_callback��������Ϊʲô Ҫ����Ϊ�˱������������2��5
Ȼ����callback�������netif_add����ʱ����ͨ�����callback����ʼ��netif��
��Ϊnetif_add������ callback����һ�����ʵ�ʱ����ã��������dz�ʼ��
netif������ʱ��
�������ʼ��netif����󣬰�device��netif�����ӣ���ͨlwip����㡣��ʱ��
��Ϊlwip����Ҳ�Ѿ���ʼ����ɣ����Դ���һ �㿪ʼ���¶�����һ�����ˣ���
�����������4


��˵�Ŀ��ܿ������Ƚ��ƣ��������ͺ������ˣ�������Ҫ��ԭ�Ȼ�Ҫ��һЩ��

bernard

unread,
Aug 9, 2011, 11:31:31 AM8/9/11
to rt-threa...@googlegroups.com
那么我们是否可以这样做,
eth_system_device_init函数保留,但是置空;
然后执行rt_hw_dm9000_init();
然后执行lwip_sys_init,但是lwip_sys_init函数的实现改成执行lwip_enetif_init();类似的操作。

然后是驱动,驱动需要做哪些修改?目前针对于lwip 1.4.0的dm9000驱动还没有,能先用附件发一个上来吗

在 2011年8月9日 下午7:03,MingBai <mbb...@gmail.com> 写道:


>
>
> 于 2011/8/9 17:25, bernard 写道:
>> 目前lwip 1.3.2的初始序列是这样的:
>> eth_system_device_init() --> 初始化etx/erx线程(这两个线程分别处理以太网的数据收发);
>> rt_hw_dm9000_init() --> 向系统中注册设备;

> 这个函数有下面几行代码,会有一些问题:


>
> dm9000_device.parent.eth_rx = rt_dm9000_rx;
> dm9000_device.parent.eth_tx = rt_dm9000_tx;
> rt_hw_dm9000_init() -> eth_device_init

> 1 在eth_device_init中会调用rt_device_register,注册device的工作不应该在
> netif中,应该在 dm9000_init里面做,修改了这一点。
> 2 这个函数中还会初始化netif的部分参数,netif正确的初始化时机是在用
> netif_add注册netif的时候通过回调函数来进行。
> 3 在这个函数调用netif_add来注册netif的时候lwip本身还没有初始化,这也是有
> 问题的。
> 4 在rt_hw_dm9000_init 执行完毕以后eth层和netif层就开始工作了。接下来,在
> lwip初始化之前,或者半截儿如果来tx中断也是会有问题的。


>
> lwip_sys_init() --> 初始化lwip协议栈。
>> 在lwip_sys_init()中,如果一个interface配置成DHCP方式,将有一个while
>> (netif_default->ip_addr.addr == 0)的DHCP请求发出。

> 在lwip_sys_init()中也有一些问题
> 5 先调用了tcpip_init()初始化lwip,然后调用netif_set_addr和netif_set_up,
> 这是不对的。netif_xxx这 些函数正确的调用位置应该是由tcpip线程发起,可以
> 用回调函数传入tcpip_init()来做,或者通过msgapi发给tcpip线程 做,防止并发
> 错误。
> 6 netif_set_up和dhcp_start是等价函数,其实还有一个函数autoip_start,这三
> 个函数是等价的,启动一个netif根据需 要来调用其中之一即可 (这个在lwip的
> wiki上有说明)
>
>> mbbill, 1.4.0的初始化序列是什么样的?
>>
> 上面可以看到有一个很明显的问题,就是驱动初始化部分和lwip初始化部分混在一
> 起,调理不是很清晰,有时候用起来也会有问题,比如前些时间出现 的在上电以
> 后第一个线程启动之前就初始化lwip的问题。
>
> 1.4里面是这么做的:
> 从大的方面看,初始化分两部分,也就是在上电以后调用两条函数:
> rt_hw_dm9000_init();
> lwip_enetif_init();
> 这两条函数分别做驱动初始化和lwip/netif初始化,他们互相没有紧密耦合,顺序
> 也可以颠倒,不过一般建议驱动初始化放前面。
> 然后从调用时机上面来说,驱动初始化部分,这里就是rt_hw_dm9000_init();放在
> 哪里都行,但是 lwip_enetif_init()则必须在线程上下文中执行。
> 从小的方面看,首先第一个函数rt_hw_dm9000_init,它的作用就是初始化设备,
> 然后向系统注册设备,这时候中断可开可不开。如果中 断打开了,那么所有rx报
> 文都会丢弃,不会交给lwip层,避免上面的问题3
> 然后第二个函数lwip_enetif_init
> 大家可以看一下1.4里面的实现,在src/netif/ethernetif.c最后部分(我觉得放
> 在这里其实也是不太正确的,因为这部分初始 化应该是和应用相关,所以最好的
> 位置应该是bsp,放这里主要是起个参考作用)
> 这个函数就是调用了一下tcpip_init,然后等待初始化完成,主要工作在
> tcpip_init_done_callback里面做。为什么 要这样?为了避免上面的问题2和5
> 然后在callback里面调用netif_add,这时候再通过它的callback来初始化netif,
> 因为netif_add本身会把 callback放在一个合适的时机调用,所以这是初始化
> netif最合理的时候。
> 在这里初始化netif的最后,把device和netif做连接,打通lwip和驱动层。这时候
> 因为lwip本身也已经初始化完成,所以从这一 点开始上下都可以一起工作了,避
> 免上面的问题4
>
>
> 我说的可能看起来比较绕,不过看代码就很明白了,代码量要比原先还要少一些。
>

MingBai

unread,
Aug 9, 2011, 11:38:18 AM8/9/11
to rt-threa...@googlegroups.com
����޸���Ҫ������rt_hw_dm9000_init��ɾ��eth_device_init��Ȼ�����
rt_device_register
�����������޸ĵ�dm9000.c

�� 2011/8/9 23:31, bernard �:
> ��ô�����Ƿ������������
> eth_system_device_init�������������ÿգ�
> Ȼ��ִ��rt_hw_dm9000_init()��
> Ȼ��ִ��lwip_sys_init������lwip_sys_init�����ʵ�ָij�ִ��lwip_enetif_init();���ƵIJ�����
>
> Ȼ����������Ҫ����Щ�޸ģ�Ŀǰ�����lwip 1.4.0��dm9000��û�У������ø�����һ��������
>
> �� 2011��8��9�� ����7:03��MingBai <mbb...@gmail.com> ���


>>
>> �� 2011/8/9 17:25, bernard �:
>>> Ŀǰlwip 1.3.2�ij�ʼ����������ģ�
>>> eth_system_device_init() --> ��ʼ��etx/erx�̣߳��������̷ֱ߳�����̫�������շ�����
>>> rt_hw_dm9000_init() --> ��ϵͳ��ע���豸��
>> ������������漸�д��룬����һЩ���⣺
>>

>> dm9000_device.parent.eth_rx = rt_dm9000_rx;
>> dm9000_device.parent.eth_tx = rt_dm9000_tx;
>> rt_hw_dm9000_init() -> eth_device_init

dm9000.c

bernard

unread,
Aug 9, 2011, 11:46:39 AM8/9/11
to rt-threa...@googlegroups.com
那么把eth_device_init直接实现成rt_device_register,是否就可以完全兼容了?

另外一个问题,lwip 1.4.0的get_eth_dev()是在netif_add中使用,这样实现好像非常别扭,如果有两个或两个以上的网络接口怎么办?


在 2011年8月9日 下午11:38,MingBai <mbb...@gmail.com> 写道:
> 驱动的修改主要就是在rt_hw_dm9000_init中删除eth_device_init,然后添加
> rt_device_register
> 附件里是我修改的dm9000.c


>
> 于 2011/8/9 23:31, bernard 写道:
>> 那么我们是否可以这样做,
>> eth_system_device_init函数保留,但是置空;
>> 然后执行rt_hw_dm9000_init();
>> 然后执行lwip_sys_init,但是lwip_sys_init函数的实现改成执行lwip_enetif_init();类似的操作。
>>
>> 然后是驱动,驱动需要做哪些修改?目前针对于lwip 1.4.0的dm9000驱动还没有,能先用附件发一个上来吗
>>
>> 在 2011年8月9日 下午7:03,MingBai <mbb...@gmail.com> 写道:
>>>
>>> 于 2011/8/9 17:25, bernard 写道:
>>>> 目前lwip 1.3.2的初始序列是这样的:
>>>> eth_system_device_init() --> 初始化etx/erx线程(这两个线程分别处理以太网的数据收发);
>>>> rt_hw_dm9000_init() --> 向系统中注册设备;
>>> 这个函数有下面几行代码,会有一些问题:
>>>

>>> dm9000_device.parent.eth_rx = rt_dm9000_rx;
>>> dm9000_device.parent.eth_tx = rt_dm9000_tx;
>>> rt_hw_dm9000_init() -> eth_device_init

bernard

unread,
Aug 9, 2011, 7:16:17 PM8/9/11
to rt-threa...@googlegroups.com
发现原来当前的lwip 1.4.0移植是不能够支持多interface的,它只支持一个interface,也就是
static struct netif ethernetif;

Ming Bai

unread,
Aug 9, 2011, 9:19:07 PM8/9/11
to rt-threa...@googlegroups.com
多interface是考虑到的,这个static的netif只是个参考,在这行之前的注释里面提到过。从这一行开始的代码按理说都不应该属于ethernetif.c,我觉得这部分内容应该是平台或者应用相关的,最好放在bsp里面。
而且现在添加多个interface比以前要方便:
1 添加一个static的netif
2 在 tcpip_init_done_callback里面添加一行
netif_add(&ethernetif, &ipaddr, &netmask, &gw,
get_eth_xxx_dev(), enetif_init, tcpip_input);
就可以了。

2011/8/10 bernard <bernar...@gmail.com>:

bernard

unread,
Aug 10, 2011, 2:25:10 AM8/10/11
to rt-threa...@googlegroups.com
感觉可以考虑采用这样的方式:
1. driver_init,把驱动注册到eth中,同时也注册到rtt device中;
2. eth_systm_init,把ethernet层面的线程进行初始化,邮箱初始化;
目前1.4.0中:
这块是enetif_init函数,它会在add一个ethernet
interface时被调用;也就是说,如果有多个interface,那么它将会被调用多次,并创建出多个erx线程。
3. lwip_system_init
类似目前的lwip_enetif_init,并把tcpip_init_done_callback函数放到了tcp线程(lwip主线程)中执行。

在tcpip_init_done_callback函数中,可以对rtt的eth device做一个遍历,
* 默认interface设置RT_LWIP_IPADDR地址;
* 非默认interface设置为ANY_IPADDR;(推迟到用户层面去设置IP地址)
* 执行netif_add,添加到lwip中;其中get_eth_dev()函数取消,可以用rtt的一些扩展来获得这个结构体;
* 如果是DHCP方式,发送DHCP请求;
* 执行netif_set_link_up

bernard

unread,
Aug 10, 2011, 7:39:50 PM8/10/11
to rt-threa...@googlegroups.com
针对于1.3.2版本修改了一个补丁出来,驱动不需要进行修改,在application中需要修改下:
原来的初始化序列是
{
extern void lwip_sys_init(void);
eth_system_device_init();

/* register ethernetif device */
rt_hw_dm9000_init();

/* re-init device driver */
rt_device_init_all();

/* init lwip system */
lwip_sys_init();
rt_kprintf("TCP/IP initialized!\n");
}
新的则是:
{
extern void lwip_sys_init(void);
eth_system_device_init();

/* register ethernetif device */
rt_hw_dm9000_init();

/* init lwip system */
lwip_system_init();
rt_kprintf("TCP/IP initialized!\n");
}

几点说明:
* 针对于多网口,只会设置第一个接口为默认接口,并把相应的地址设置上。其他接口默认都是0.0.0.0,然后由上层应用再行设置IP地址;
* 驱动的初始化放到lwip初始化完毕之后,这样中断开启也会在后面,不会影响到上层。(如果在前面调用了初始化关系也不大,初始化不会重复进行)
* 后面还可做一个lwip_sys_init到lwip_system_init兼容调用。先看看效果吧。

后续还需要测试DHCP情况,速度情况,应该能够做成完全和目前0.4.0 beta2完全兼容。

lwip_1.3.2.diff

bernard

unread,
Aug 11, 2011, 1:14:06 AM8/11/11
to rt-threa...@googlegroups.com
1.3.2修改前后速度对比(mini2440 + DM9000测试得到)

修改前:
NETIO - Network Throughput Benchmark, Version 1.31
(C) 1997-2010 Kai Uwe Rommel

TCP connection established.
Packet size 1k bytes: 1041.50 KByte/s Tx, 4472 Byte/s Rx.
Packet size 2k bytes: 1044.00 KByte/s Tx, 1003.18 KByte/s Rx.
Packet size 4k bytes: 1032.50 KByte/s Tx, 1957.45 KByte/s Rx.
Packet size 8k bytes: 1045.33 KByte/s Tx, 2006.57 KByte/s Rx.
Packet size 16k bytes: 1045.16 KByte/s Tx, 2010.90 KByte/s Rx.
Packet size 32k bytes: 1049.10 KByte/s Tx, 2011.77 KByte/s Rx.
Done.

修改后:
NETIO - Network Throughput Benchmark, Version 1.31
(C) 1997-2010 Kai Uwe Rommel

TCP connection established.
Packet size 1k bytes: 1031.50 KByte/s Tx, 5172 Byte/s Rx.
Packet size 2k bytes: 1044.50 KByte/s Tx, 1004.26 KByte/s Rx.
Packet size 4k bytes: 1042.84 KByte/s Tx, 1523.91 KByte/s Rx.
Packet size 8k bytes: 1045.16 KByte/s Tx, 2000.28 KByte/s Rx.
Packet size 16k bytes: 1034.16 KByte/s Tx, 1856.02 KByte/s Rx.
Packet size 32k bytes: 1025.96 KByte/s Tx, 1742.60 KByte/s Rx.
Done.

bernard

unread,
Aug 12, 2011, 9:11:49 AM8/12/11
to rt-threa...@googlegroups.com
另外一个问题,
netif_set_link_up
函数真不适合于在ISR中使用,0.4.0 beta2版本直接出ASSERT。

netif_set_link_up函数应该在lwip线程上下文中调用比较好。

bernard

unread,
Aug 23, 2011, 4:54:00 AM8/23/11
to rt-threa...@googlegroups.com
嗯,这个帖可以结了,目前lwip 1.3.2的初始化算是完美了,能够很好的支持link change功能,以及DHCP功能等:
- 设备连着网线向DHCP server发送请求,获得IP地址;
- 断开设备;
- 把设备的网线换到另外一个网络,DHCP请求发出,获得相应子网的IP地址,而不是使用老的IP地址。

采用新的构架方式,网络速度也有所提升,相关的修改会反映到0.4.0 rc1版本发布中。

Ming Bai

unread,
Aug 23, 2011, 4:55:28 AM8/23/11
to rt-threa...@googlegroups.com

2011/8/23 bernard <bernar...@gmail.com>:

Reply all
Reply to author
Forward
0 new messages