LwIP协栈在uCOS II下的实现
1、述: LwIP协栈在设时就考虑到了将来的移植问题,因把所有与硻、OS、编译器相关的部份独立出来,放在ucosii&LwIPsource etlwiparch盽下。因wIP在uCOS II上的实现就是俔这个盽下的文件,其它的文件一舸应俔。下面分几部份分明相应文件的实现原理和过程?、与CPU或编译器相关的include文件:ucosii&LwIPsource etlwiparchucosIIincludearch盽下cc.h、cpu.h、perf.h东一些与CPU或编译器相关的定义,如数捕度,字的高低位顺序等。这应与用户实?micro;C/OS II时定义的数据长度等参数是一致的?define BYTE_ORDER LITTLE_ENDIAN //C33209默为小竭储系?/数据
型长度的定义typedef unsigned char u8_t;typedef signed char s8_t;typedef unsigned short u16_t;typedef signed short s16_t;typedef unsigned int u32_t;typedef signed int s32_t;此还有一点:一舃况下C诨的结构体struct?字节对齐的,但是在理数捌的时候,LwIP使用的是通过结构体中不同数据的长度来读取相应的数捚,所以,一定在定义struct的时候使用_packed关键字,让编译器放弃struct的字节齐。LwIP也考虑到了这个,所以,在它的结构体定义东几个PACKED_FIELD_xxx宏,默的时候这几个宏都昩的,叻在移植的时候添加不同的编译器所对应的_packed关键字。比如在Skyeye(C33209)上对应gcc编译器的定义?define PACK_STRUCT_FIELD(x) x __attribute__((packed))#define PACK_STRUCT_STRUCT __attribute__((packed))#define PACK_STRUCT_BEGIN#define PACK_STRUCT_END3、sys_arch操作系统相关部份? sys_arch.[ch]业内昸OS相关的一些结构和函数,主要可以分为四七份:(1) sys_sem_t 信号量LwIP东要使用信号量通信,所以在sys_arch为实现信号量结构体和理函数:struct sys_sem_t sys_sem_new() //创建一丿号量结构 sys_ sem _free
) //释放一丿号量结构 sys_ sem _signal() //发送信号量 sys_ arch_sem _wait() //请求信号量由?micro;C/OSII已经实现了信号量OS_EVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把µC/OSII的函数重新包装成上面的函数,就可以直接使用了?2) sys_mbox_t 消息LwIP使用消息队列来缓冲、传递数捊文,因要在sys_arch丮现消恘列结构sys_mbox_t,以及相应的操作函数:sys_mbox_new() //创建一丶恘列s
ys_mbox_free() //释放一丶恘列sys_mbox_post() //向消恘列发送消息sys_arch_mbox_fetch() //从消恘列中获取消息µC/OSII同样实现了消恘列结构OSQ及其操作,但?micro;C/OS-II没有对消恘列中的消恿行理,因不能直接使用,必须在µC/OS-II的基础上重新实现。为了实现消息的理,我们定义了以下结构:typedef struct { OS_EVENT* pQ; void* pvQEntries[MAX_QUEUE_ENTRIES];} sys_mbox_t
;在以上结构丼包括OS_EVENT类型的队列指针(pQ)和队列内的消息(pvQEntries)两部分,队列机的理利?micro;C/OS-II臷的OSQ操作完成,然后使?micro;C/OS-II业内存管理模块实现对消恚创建、使用、删除回收,两部分综合起来形成了LwIP的消恘列功能?3) sys_arch_timeout 函数LwIP丯丸外界网络连接的线程都有自己的timeout属性,即等待超时时间。这丱性表现为每个线程都应一个sys_timeout结构体队列,包括这个线程的timeout时间长度,以及超时后应调用的timeout函数,函数会做一些释放连接,回收资源的工作。果一为程应的sys_timeout为空(NULL),说明该线程连接做永久的等待。timeout结构体已经由LwIP臷在sys.h丮义好了,而且对结构体队列的数捓作也由LwIP负责,我仉要实现的下函数:struct sys_timeouts * sys_arch_timeouts(void)这个函数的功能是返回盉正于运行态的线程所对应的timeout队列指针。timeout队列属于线程的属性,因是OS相关的函数,參由用户实现?4) sys_thread_new 创建新线程LwIP叻昍线程运,即叜一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以线程运,以提高效率,降低编程杂度。这时就需要用户实现创建新线程的函数:void sys_thread_new(void (* thread)(void *arg), void *arg);?micro;C/OS II丼没有线程(thread)的概念,只有任务(Task)。它已经提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封一下,就可以实现sys_thread_new。需要注意的是LwIP业thread并没?micro;C/OS II丼先级的念,实现时由用户事先为LwIP丈建的线程分配好优先级?、lib_arch为函数的实现: LwIP协栈中用到?部函数,这些函数通常与用户使用的系统或编译器有关,因此留给用户自己实现。下:u16_t htons(u16_t n); //16位数捫低字节交16_t ntohs(u16_t n);u32_t htonl(u32_t n); //32位数捤小头对调u32_t ntohl(u32_t n);int strlen(const char *str); //返回字串长nt strncmp(const char *str1, const char *str2, int len); //字串比较void bcopy(const void *src, void *dest,
int len); //内存数据块之间的互相拷贝void bzero(void *data, int n); //内存丌定长度的数据块清? 前四万数通常由用户自己实现。在我的系统丼由于使用了gcc编译噼gcc的lib库里已经有了两个字串操作函数。若用户的编译器的库串有这些函数,需要自己编写?、网络备驱动程序: 在我的系统中使用的网络芯片为RealTek?019as芉,这是ISA 10BASE-T的以夽芉,与Ne2k兼。所以目前实现的网络设驱动昒对Ne2k的,其它类型的网络芯片驱动可以在LwIP的网站上找到。LwIP的网络驱动有一定的模型,ucosii&LwIPsource etlwiparchucosII etif 业ne2kif.c文件即为驱动的模板,用户为自己的网络设实现驱动时应参照此模板? 在LwIP丏以有多个网络接口,每丽络接口都对应了一个struct netif,这个n
e2kif包含了相应网络接口的属性、收发函数。LwIP调用ne2kif的方法netif->input()及netif->output()进以太网packet的收、发等操作。在驱动丸要做的,就是实现网络接口的收、发、初始化以及世处理函数。驱动程序工作在IP协模型的网络接口层,它提供给上层(IP层)的接口函数下://网卡初化函数void low_level_init (struct netif *netif)//网卡接收函数,从网络接口接收以太网数捌并把其中的IP报文向IP层发?/在中斖式下由网SR调用void ne2k_recv_packet (struct netif *netif)//网卡发送函数,给IP层传过来的IP报文加上以太网包头并通过网络接口发送err_t ne2k_send_packet (struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)//网卡世处理函数ISRvoid ne2k_isr (void); 以上的函数都叻分为协栈本躚处理和网络接口硻的操作两部份,但硻操作上层屏蔽的,具体参RTL8019as、DM9008等Ne2k网络芉的数捉册。驱动程序可以到LwIP的网站下载?、应用实例的建立和测? 做完上面的移植修改工作以后,就可以在uCOS II丈始化LwIP,并创建TCP或UDP任务进测试了。这部份完全是C诨的实现,因这部份在ez80和ARM7上基會昸样的。值得注意的是LwIP的初始化必须在uCOS II完全吊之后也就昜任务丿行,因为它的初化用到了信号量等OS相关的操作。关锃份的代码和明下:void start_kernel(void){ int LineNo10 = 0; int LineNo11 = 1; int LineNo12 = 2; int LineNo13 = 3; int LineNo14 = 4; OSInit(); OSTaskCreate(lwip_init_task, &LineNo10, &lwip_init_stk[TASK_STK_SIZE-1], 0); OSTaskCreate(usr_task,&LineNo14,&usr_stk[TASK_STK_SIZE-1],20); vRTCStart(); OSStart(); /* NEVER EXECUTED */ while(1);}主程序中创建了lwip_init_task初化LwIP任务(优先级0)和usr_task用户任务(优先级20)。lwip_init_task任务丙了初始化硻时钟和LwIP之,还创建了tcpip_thread(优先级5)和tcpecho_thread(优先级6)。实际上tcpip_thread才是LwIP的主线程,线程的Berkley API也是基于这个线程实现的,即上面的tcpecho_thread线程也依靠tcpip_thread线程来与外界通信,这样做的好处是编程简单,结构清晰。实用Berkley API实现的tcpecho_thread昸个TCP echo服务噼监听7号口,程序框架如下:void tcpecho_thread(void *arg){conn = netconn_new(NETCONN_TCP); //创建新的连接标识netconn_bind(conn, NULL, 7); //绑定?号etconn_listen(conn); //开始监? while(1){ newconn = netconn_accept(conn); //接收外部到来的连? buf = netconn_recv(newconn) //获取数据 ……. //处理数据 netconn_write(newconn, data, len, NETCONN_COPY); //发送数? netconn_delete(newconn); //释放连接} } 编译运后,用ping ip地址命令叻得到ICMP reply响应。用telnet ip地址 7(登?号口)命令叻看到echo server的回显效果。明ARP、ICMP、IP、TCP协都已正确运