由于写了《RTEMS 4.9.5:QEMU MINI2440 BSP 中的网络驱动开发》(上中下)三篇文章,并参与了大牛的Rick Leaf的项目emboslab。很多朋友写信给我,包括很多国外的朋友。他们在移植 RTEMS 4.10.2 到 Mini2440 上遇到了不少困难。我在帮助他们的过程中,也受到了一些启发。把相关内容写在这里,供大家批评指正。
 
RTEMS 是个复杂比较完善的操作系统,所以移植调试都是一件不怎么太轻松的活。需要很多操作系统和处理器底层的知识。好在同类型的CPU大家都差不多。所以,处理器核心移植都是不需要太操心的,而那部分是最复杂也是最难调的部分。所幸大牛帮我们做好了。我们只用就好。外设的移植,最复杂的莫过于以太网等大型的协议栈的底层驱动开发了。今天我就聊聊以太网底层开发的一些要点。这篇文章不仅仅限于 RTEMS,对其他的操作系统也是有用的。:)
 

以太网的调试相对来讲是较为复杂的。以太网的特点是:

首先我们面对的东西是个复杂而庞大的系统:

  1. 以太网的帧格式多,Ethernet II、Novell Ethernet、IEEE802.3/802.2、Ethernet SNAP;
  2. 协议种类多,IP、ICMP、IGMP、GGP、EGP、UDP、TCP;
  3. 以太网类型多,Netware, XNS, IP, ARP, RARP, IP(Wines),DRP, LAT, LAVC。
  • 整个协议栈是个围绕数据流加工的结构。发送数据的时候,按照不同的应用,TCP连接的数据包可能被分包。由应用层->传输层->网际层->网络接口,把数据发送走。而接收数据走个反向的过程。数据从高层流向底层的时候层层打包,而底层数据流向高层的时候层层剥茧,到了最后一层只剩下需要的数据。数据处理过程较为复杂,要针对问题具体分析。
  • 协议栈是动态的。我们平时的时候用一个静态的协议报文去描述通讯过程,但那不是协议栈正真的处理过程。既然有通讯,就有可能有出错,出错就要有错误恢复机制。错误恢复机制不是固定在某一个地方出现的。大家都知道,OSI七层,TCP/IP分为4层,层层都有容错机制。协议报文也不是在任何时候都有出现。比如ARP协议,当本机有对方的物理地址时,就不会发送ARP协议解析地址。当机器启动的时候,一般都会发送一条ARP协议用于广播自己的地址。当需要具体的协议时,时机不容易掌握。
  • 协议栈里有大量的内存申请和释放。在这个过程中,没有处理好,很容易产生碎片和泄漏。这会造成协议栈不能长时间运行。
  • 完全功能的协议栈需要获取CPU时间运行。不能仅仅从接受数据的时候,或者发送数据的任务获得执行时间,还需要额外的运行时间处理诸如保活定时器、错误和网际层协议等一列的事务。如果在多任务环境下,必然有多个任务,多个任务必然有同步的问题。处理不好,容易诱发死机、代码不能重入等等一系列的问题。


好在,我们不是做协议栈开发,只是做成熟的协议栈移植而已,以上几个问题中真正要我们涉及的大概只有30%。但没有这方面知识是远远不够的,是无法完成协议栈的调试。针对以太网的特点,要设计好调试环境。调试的原则是:设计自己的调试环境,使得每一次的结果具有可再现性;应尽可能排除干扰因素,使输入(或触发条件)和结果产生对应。做到这两条很不容易,但这也是调试的魅力所在。


  • 面对庞大纷乱的协议,我们不能把刚开发出来的系统直接暴露在Intelnet上或者一个复杂拓扑的网络上。否则,什么样的数据包都可能发现。为了简化调试环境,一般刚开发的系统和PC机之间直接通过直连线连接。不通过任何路由、交换机。直连线即两侧水晶头一个是(T568A), 一个是(T568B)的线序。但现在网络的物理层都支持AutoMix,即普通的线和直连线都可以连接,不用担心有任何问题。可直接通讯。这里要注意一下,如果PC机器或者系统是通过DHCP获得IP地址,要配置一下PC机,弄一个DHCP服务器,用于这个临时的小系统的IP地址分配。如果不是通过DHCP获得IP,要把PC机和系统的IP地址配置成同一个段的IP地址。这一步用于简化调试环境的复杂性。
  • 准备好一个好用的抓包工具。这个工具就太多了,Linux下的如tcpdump,windows下的WireShark,sniff, iptools等等。根据系统选择自己想要的。使用抓包工具,用于验证系统发出的包是否是正确的。
  • 有些抓包工具具有发送数据的功能,这个功能很强大,可以重复发送一个指定的帧数据;不用等到一个特殊的条件去触发,增加效率。但软件有时候不是万能的,这里推荐使用 winpcap(linux下和windows下都可以),自己用C写个工具,想怎么发就怎么发,想发几次就几次。用以触发系统的问题,建立重复的调试环境,可反复发送一个特定的包,即使在通常情况下不易出现的包。
  • 如果调试的系统是嵌入式系统,稍微麻烦点,需要仿真器和终端用于观察整个程序的动态行为。光靠终端也是可以的,利用printf夹杀的办法,很好用,但具体到一些特殊的问题,有仿真器的辅助会事半功倍。仿真器调试网络的应用程序,不能作为主力。(为什么大家可以在实践中多多体会。)
  • 有一本详细的书,介绍各种协议的帧格式,各种报文的组成。当然抓包工具能做到,但是最好能显示二进制数据,自己来看。软件有时候比人差一些。当然什么都比不上google强大。 :)
  • 确认自己了解所调试系统的细节,比如说仔细阅读以太网MAC和PHY的器件手册(datasheet);了解所配合的协议栈的大致工作机制,以及数据流转的方式。

好了,说到这里,准备活动是做好了。

板子上电以后:

1.首先确认的是各自的IP地址是否正确。PC 机查看比较简单。嵌入式系统那边可能要用printf+终端的方法,在合适的地方打印出来。如果就是调试DHCP服务器,在PC机上准备好抓包工具;看看嵌入式单板发出的包。


2.如果嵌入式单板获得IP地址,那就调用ping命令,ping一下嵌入式系统,看看是否正常。如果正常,恭喜,可以初步的说明协议栈没有问题了。在试试超过一帧大小的包,linux下用命令 ping xxx.xxx.xxx.xxx -s 3000 (大包)。如果协议栈支持,一般都是没问题的。


3.如果ping不通,大小包都ping不通。首先从驱动的中断开始检查,在中断中放一个printf函数。打印是否进入中断。这里注意一下,并不是所有的以太网都是来一次包,进一次中断。很多复杂的系统,是可以把一个时间段的接受中断合并成一个中断。总之,有包进入,等一段时间进入中断就可以了。需要具体问题具体分析。中断正常的话,就需要检查,收包的函数。看看接受到的数据是否正确,这个也简单,用printf把接受到的包直接打印出来就好。


打印出来的包需要和系统发送的包做一个对比。看看是否一致,如果不一致,确认是硬件配置的问题还是什么问题。如果协议栈是个成熟的协议栈,确定没有问题,把正确的数据送到协议栈,这部分工作就算检查完毕了。再检查发送到驱动的数据。这里需要检查两个内容:

a).发送中断不要频繁开启关闭,驱动初始化开一次中断,驱动废弃不用时关闭发送中断就好。但以太网并不是时时刻刻都有数据发送,所以,想发送中断不起作用,简单的把发送引脚不使能就好。频繁地单独关闭设备的中断可能会引起硬件行为的不正常。

b).发送一般由信号量或事件触发。要反复确认生产者和消费者都是正常的。

发送到硬件前,需要把输出的数据打印出来,用printf打印。看看是否正常。数据正常,送到指定的硬件位置后,如果不能正常的送达目的地。则是硬件问题,要确认MAC和PHY的寄存器是否工作正常。

如此反复,即可把一些基本问题全排除了。让协议栈正常工作。接着就是确认相关的缓冲区被正确的释放和申请,没有泄漏。

再下来,就是一些实时性和可靠性的问题。这个水太深,就不做讨论了。

如果调试的是协议栈本身,那就更复杂了。但思路相同。一层一层的确认。下次我们就看看RTEMS 4.10.2究竟是怎么具体操作这些问题的。