Lwip的移植主要是三个接口部分:
1、与CPU或编译器相关接口(include中cc.h文件)
2、与操作系统的接口;
3、与底层网卡驱动的接口。
一、与CPU或编译器相关接口
与CPU或编译器相关接口(include中cc.h文件),主要包括数据长度,字的高低位顺序,编译器对c语言中struct结构字节对齐问题(c语言中struct结构是四字节对对齐的,但lwip中是根据struct结构中不同数据的长度来读取数据的)
例如nios平台下的cc.h文件:
/* FPAG-NIOS platform*/
#ifndef __ARCH_CC_H__
#define __ARCH_CC_H__
#include"errno.h" //定义错误编码
#include "alt_types.h" //定义NIOS数据类型
#include "lwipopts.h" //
#define BYTE_ORDERLITTLE_ENDIAN //字的高低位顺序
//数据长度
typedef alt_u8 u8_t; //一个无符号字节类型, 8bits 0~~255
typedef alt_8 s8_t; //一个有符号字节类型, 8 bits -127~~-127
typedef alt_u16 u16_t; //一个无符号字(=2个字节)类型,16 bits
typedef alt_16 s16_t; //一个有符号字(=2个字节)类型,16 bits
typedef alt_u32 u32_t; //一个无符号字节类型, 32 bits
typedef alt_32 s32_t; //一个有符号字节类型, 32 bits
typedef u32_t mem_ptr_t; //
#include
/* Plaform specific diagnostic output 与平台相关的调试输出 */
#define LWIP_PLATFORM_DIAG(x) {printf x;}
#define LWIP_PLATFORM_ASSERT(x) {printf("Assertion\"%s\" failed at line %d in %s\n", \
x, __LINE__, __FILE__); while(1);}
//编译器对c语言中struct结构字节对齐
#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
#endif /* __ARCH_CC_H__ */
与操作系统的接口,主要分为四个部分
1、sys_sem_t信号量: 在sys_arch中应实现如下sys_sem_t结构体和处理函数
struct sys_sem_t
sys_sem_new() //创建一个信号量结构
sys_sem_free() //释放一个信号量结构
sys_sem_signal() //发送信号量
sys_arch_sem_wait() //请求信号量
UCOS中已经实现了信号量OS_EVENT的操作,且功能和LWIP上面几个函数的目标功能是完全一样的,所以只要把UCOS的相关函数包装成上面的函数就可以直接使用了。
2、sys_mbox_t消息:LwIP使用消息队列来缓冲、传递数据报文,在sys_arch中应实现如下sys_sem_t结构体和处理函数
struct sys_mbox_t
sys_mbox_new() //创建一个消息队列
sys_mbox_free() //释放一个消息队列
sys_mbox_post() //向消息队列发送消息
sys_arch_fetch() //从消息队列中获取消息
由于UCOS中没有对消息队列中的消息进行管理,所以得另外定义一个消息队列结构:
typeedef struct{
OS_EVENT*pQ; //队列指针
Void* pvQEntries[MAX_QUEUE_ENTRIES] //队列中的消息
}sys_mbox_t
利用UC-OS的OSQ操作完成对队列的管理,利用UC-OS的内存管理模块完成队列的创建、使用、删除回收操作。
3、 sys_arch_timeout() 函数:LwIP中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为:每个线程都对应一个sys_timeout结 构体队列,包括这个线程的timeout时间长度,以及超时后应调用的timeout函数,该函数会释放连接和回收资源的工作。如果一个线程对应的sys_timeout为空(NULL),说明该线程对连接做永久的等待。
timeout结构体已经由LwIP在sys.h中定义好了,而 且对结构体队列的数据操作也由LwIP负责,我们所要实现的是如下函数:struct sys_timeouts* sys_arch_timouts(void),这个函数的功能是返回目前正处于运行态的线程所对应的timeout队列指针。timeout队列属于线 程的属性,因此是与OS相关的函数,只用由用户实现。例如nios中UCOS平台下的sys_arch_timeouts(void)函数:
struct sys_timeouts *
sys_arch_timeouts(void)
{
INT8S currPrio;
currPrio = (OSPrioCur-LWIP_START_PRIO);
if((currPrio < 0)|| (currPrio >= LWIP_MAX_TASKS))
{
/* Error! */
printf("sys_arch_timeouts: Prio not found\n");
while(1);
}
return &timeoutlist[currPrio];
}
4、 sys_thread_new创建新线程函数:LwIP可以是单线程运行,即只有一个TCPIP线程(tcpip_thread),负责处理所有的 TCP/UDP连接,各种网络程序都通过TCPIP线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。这时需要用户实现创建新线程的函数:void sys_thread_new(void(*thread)(void* arg),void* arg);
在UC-OS中,没有线程的概念,只有任务(task)。它已经提供了创建新任务的系统API调用OSTaskCreat,因此,只要把 OSTaskCreat封装一下,就可以实现sys_thread_new。需要注意的是,LwIP中的thread并没有UC-OS中优先级的概念,实现时,要由用户事先为LwIP中创建的线程分配好优先级。
例如nios中UCOS平台下的sys_thread_new函数:
sys_thread_t
sys_thread_new(void (* function)(void *arg), void *arg, intprio)
{
INT8U bTemp;
OS_STK * stack;
if(sys_thread_no >=LWIP_MAX_TASKS)
{
printf("sys_thread_new: Max Sys. Tasks reached\n");
}
stack =sys_stack[sys_thread_no];
#if 0
if(bTemp = OSTaskCreate( function, arg,(void *)&sys_stack[sys_thread_no][LWIP_STACK_SIZE - 1],LWIP_START_PRIO+sys_thread_no ))
{
printf("sys_thread_new: Task creation error [%d]\n",bTemp);
while(1);
}
#else
if((bTemp = OSTaskCreateExt( function,arg, stack + LWIP_STACK_SIZE, LWIP_START_PRIO+prio, LWIP_START_PRIO+prio,stack, LWIP_STACK_SIZE, NULL, 0 )))
{
printf("sys_thread_new: Task creation error [%d]\n",bTemp);
while(1);
}
#endif
++sys_thread_no; /* next task createdwill be one lower to this one */
return sys_thread_no;
}
nios平台下的整个sys_arch.h文件:
*********************************************************************************************************
* UCOS-II Port
*
* Target : Anyprocessor
* Put together by : Michael Anburaj
* URL : http://geocities.com/michaelanburaj/ Email : [email protected]
*
*********************************************************************************************************
*/
#ifndef __SYS_ARCH__H__
#define __SYS_ARCH__H__
#include "os_cpu.h"
#include "os_cfg.h"
#ifndef OS_VERSION
#include "ucos_ii.h"
#endif /* OS_VERSION */
#include "system.h"
#define LWIP_MAX_TASKS OS_LOWEST_PRIO /* Number of LwIPtasks */
#define LWIP_START_PRIO0 /* Defined the lowestLwIP task priority */
#ifndef LWIP_STACK_SIZE
#define LWIP_STACK_SIZE 2048
#endif /* LWIP_STACK_SIZE */
/*#define LWIP_STACK_SIZE2048 Stack size for LwIP tasks */
/* Note:
Task priorities, LWIP_START_PRIO through(LWIP_START_PRIO+LWIP_MAX_TASKS-1) must be reserved
for LwIP and must not used by otherapplications outside. */
#define LWIP_Q_SIZE10 /* LwIP queue size */
#define LWIP_MAX_QS50 /* Max. LwIP queues */
#define SYS_MBOX_NULL NULL
#define SYS_SEM_NULL NULL
typedef struct {
OS_EVENT* pQ;
void* pvQEntries[LWIP_Q_SIZE];
} TQ_DESCR, *PQ_DESCR;
typedef OS_EVENT* sys_sem_t;
typedef PQ_DESCR sys_mbox_t;
typedef INT8U sys_thread_t;
#endif /* __SYS_ARCH__H__ */
/* 相关函数原型说明 */
/*------------------- 信号量相关函数-------------------*/
sys_sem_t sys_sem_new(u8_tcount); //建立并返回一个新的信号量
void sys_sem_signal(sys_sem_tsem); //释放一个信号量
void sys_sem_free(sys_sem_tsem); //删除一个信号量 (注意:此函数调用的OSSemDel( )函数UCOS不提供)
u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout); //等待由信号sem并阻塞线程,timeout为等待超时
sys_mbox_tsys_mbox_new(void);
void sys_mbox_free(sys_mbox_tmbox);
void sys_mbox_post(sys_mbox_t mbox, void *msg);
u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg,u32_t timeout);
sys_thread_t sys_thread_new(void (* function)(void *arg),void *arg, int prio);
void sys_init(void); //建立邮箱及其链表
struct sys_timeouts * sys_arch_timeouts(void);
Lib_arch中库函数的实现:
LwIP中用到了8个外部函数,这些函数与用户用到的编译器或系统CPU有关:
u16_t htons(u16_tn); //16位数据高低字节交换;
u16_t ntohs(u16_tn); //
u32_t htons(u32_tn); //32位数据高低字节交换;
u32_t ntohs(u32_tn); //
int strlen(const char*str ); //返回字符串长度
int strncmp(constchar* str1, const char* str2, int len); //字符串比较
void bcopy(const void*src,void* dest, int len); //内存数据块之间的相互拷贝
void bzero(void*data,int len); //内存块中指定长度的数据清零
网卡驱动程序
LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c文件即为驱动的模版,用户为自己的网络设备实现驱动时应参照此模块。在 LwIP中可以有多个网络接口,每个网络接口都对应了一个struct netif。这个netif包含了相应网络接口的属性、收发函数。LwIP通过调用netif的方法netif->input()及 netif->output()进行以太网packet的收发等操作。在驱动中主要做的就是,实现网络接口的收发、初始化以及中断处理函数。驱动程序工作在IP协议模型的网络接口层,它提供给上层(IP层)的接口函数如下:
void ethernetif_init(struct netif* netif) //网卡初始化函数
void ethernetif_input(struct netif*netif) //网卡接收函数,从网络接口接收以太网数据包,并把其中的IP报文向IP层发送,在中断//方式下向网卡ISR调用
ett_t ethernetif_output(struct netif* netif,struct pbuf*p,struct ip_addr* ipaddr) //网卡发送函数,给IP层传过来的IP报文加上以太网//包头,并通过网络接口发送
void ethernetif_isr(void) //网卡中断处理函数ISR