uIP无操作系统(裸机)移植

 本文转自music_fong博客:http://blog.csdn.net/music_fong/article/details/7191773

以前自己写了一个TCP/IP的协议栈,但是需要的48K的RAM。后来随着产品功能的增加,RAM资源就越来越不够用了。在一个偶然的机会下,发现了一个开源的协议栈--uIP。于是在网上找了一些资料。

  原来现在比较流行的开源的小型协议栈有两个--lwIP和uIP。

  lwIP主页:http://savannah.nongnu.org/projects/lwip/

       uIP主页:http://www.sics.se/~adam/old-uip/

    uIP文档:http://www.sics.se/~adam/download/?f=uip-1.0-refman.pdf

  其中lwIP实现的功能比较多,所以其会较复杂;uIP主要实现了TCP和UDP以及ARP的等简单功能。并且两者都可用于裸机或有操作系统。由于我只需要简单的功能,所以选择了简单的uIP。

  下面概述一下uIP及裸机的移植方法,不足之处,请大家多多指正。

                                  uIP概述

  uIP的结构不是层次式的,其整个处理过程单独为一个进程,即一个函数,下面就大概分析这个函数。

一、主循环控制
  在主循环中,需要处理两个事情。
 1.    检测是否有数据到来,当有数据到来时调用uip_input()函数。
    2. 检测网络定时器是否超时,如果超时,则调用uip_periodic()函数。

二、体系结构相关功能定义
    1. 检验计算,可以手动修改的两个函数是uip_ipchksum()和uip_tcpchksum()。
    2. uip不使用32-bit的变量,所以要手动实现uip_add32()这个函数。
  (这里移植的时候可以不重写这些函数,按照其默认的算法也可以)

三、内存管理
    1. 整个协议栈使用一个全局的buffer(变量uip_buf)装载收到的数据包,所以有数据到来时,必须立刻处理该数据,或者将数据从buffer中考出来;只有buffer中的数据处理了,才会接受下一包数据。
    2. 当程序正在处理数据包时,这时如果有数据到来,则需要网卡的驱动程序去把数据按顺序缓存
    有一些网卡可以缓存好几包的数据(即网卡或驱动程序把数据包缓存起来),否则丢掉数据或
    降低性能。(但这一般只会在多个TCP连接时才会发生)
    3. 发送的数据的头部会使用全局buffer,同时uIP没有重传缓冲,即没有把已发送的数据排队,
       所以当需要重传数据时,需要应用程序再生成重传数据。
    4. 总共需要的内存可以按应用程序的发在来配置。


                                                                                                                                移植

一、下载源代码。

  从http://www.sics.se/~adam/old-uip/download.html下载源代码。解压后,uip的核心代码都在uip文件夹中,所以我们只要把该目录放到自己的工程里面就行了。同时去掉

       clock.h timer.c(因为我不使用这种定时方法,我只是在定时器里自加来做超时)

       psock.c psock.h(因为我是裸机,所以不需用到这个功能)

       uip-split.c uip-split.h(因为我的程序中不需要分包,所以也没有这个功能)

二、配置

  配置文件是uipopt.h和uip-conf.h文件,其中uip-conf.h在解压后的unix文件夹下,并且include在uipopt.h文件中,具体配置参考其文档。

  我修改了以下地方

  //#define UIP_CONF_BYTE_ORDER      UIP_LITTLE_ENDIAN  // 注释这行,如果该配置,则默认配置为LITTLE_ENDIAN(小端系统)

  #define UIP_CONF_BUFFER_SIZE     1500    //定义了uip_buf的大小

       #include "client.h"                                                              //include了我的客户端程序的头文件

       

  在uipopt.h中

  #define UIP_APPCALL HandleClientApp   //“HandleClientApp”是我的客户端处理函数。因为其收到数据或定时器到期,都会回调该函数。

三、结构和变量的定义

  在我的"client.h"文件中定义了

  struct client_state {
  uint8 state;
  };
  typedef struct client_state uip_tcp_appstate_t;   // 这里定义了uip_tcp_appstate_t 结构,这个结构在uip_conn结构里引用,其作用是记录应用程序的状态。

四、初始化

  uip_ipaddr_t ipaddr;
struct uip_eth_addr temp_eth_addr;  

  uip_init();
uip_arp_init();
// 设置主机的ip地址
uip_ipaddr(ipaddr, 192, 168, 1,228);
     uip_sethostaddr(ipaddr);


// 设置子网掩码
     uip_ipaddr(ipaddr, 255, 255, 255,0);
     uip_setnetmask(ipaddr);
    
     // 设置网关
     uip_ipaddr(ipaddr, 192, 168, 1,1);
     uip_setdraddr(ipaddr);
    
     // 设置mac
     temp_eth_addr.addr[0] = SourceMAC[0];
     temp_eth_addr.addr[1] = SourceMAC[1];
     temp_eth_addr.addr[2] = SourceMAC[2];
     temp_eth_addr.addr[3] = SourceMAC[3];
     temp_eth_addr.addr[4] = SourceMAC[4];
     temp_eth_addr.addr[5] = SourceMAC[5];
     uip_setethaddr(temp_eth_addr);


五、与硬件相关部分(即把数据从网卡读出来或把数据发到网卡)

uint8 temp;
     uint8 *inbuf;
//#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
struct uip_eth_hdr *pfram;

     //检测是否有数据到达
     QueryRTL8019();    // 如果有数据到来,就置位EVENT_WORD
    
     // 以太网接收数据   
    if(EVENT_WORD == VALID_FLAG) {
    pfram = (struct uip_eth_hdr *)&uip_buf[0];
        EVENT_WORD = 0; 
        uip_len = RTLReceivePacket();                           // 这个函数的作用是吧网卡收到的数据拷贝到uip_buf中,并返回数据长度
if(uip_len > 0) {
if(pfram->type == HTONS(UIP_ETHTYPE_IP)) {
uip_arp_ipin();
uip_input();
if(uip_len > 0) {
uip_arp_out();
Eth_Send();      //这个函数发送网络数据
}
} else if(pfram->type == HTONS(UIP_ETHTYPE_ARP)) {
uip_arp_arpin();
if(uip_len > 0) {
Eth_Send();
}
}
}
}

if(ethernet_timer > 5) {                                    //ethernet_timer在100ms定时器中自加ethernet_timer++
ethernet_timer = 0;
for(temp = 0; temp < UIP_CONNS; temp++) {
uip_periodic(temp);


                //
                // If the above function invocation resulted in data that
                // should be sent out on the network, the global variable
                // uip_len is set to a value > 0.
                //
if(uip_len > 0) {
uip_arp_out();
Eth_Send();

}
}


#if UIP_UDP
for(temp = 0; temp < UIP_UDP_CONNS; temp++) {
uip_udp_periodic(temp);


                //
                // If the above function invocation resulted in data that
                // should be sent out on the network, the global variable
                // uip_len is set to a value > 0.
                //
if(uip_len > 0) {
uip_arp_out();
Eth_Send();

}
}
#endif
}

//
// Process ARP Timer here.
//
if(arp_timer > 100)                                                     //arp_timer也在100ms定时器中自加
{
arp_timer = 0;
uip_arp_timer();
}


       // 发送函数

     void Eth_Send(void)
{
memcpy(EthernetTxData, uip_buf, UIP_LLH_LEN);                                       // 把uip_buf中的帧的头部靠到EthernetTxData中
    

        // 拷贝uip_buf中的剩下部分
if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
memcpy(&EthernetTxData[UIP_LLH_LEN], &uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN);
} else {
memcpy(&EthernetTxData[UIP_LLH_LEN], &uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);
memcpy(&EthernetTxData[UIP_LLH_LEN+UIP_TCPIP_HLEN], uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN);
}

        // 把数据发到网卡
RTLSendPacket(EthernetTxData, uip_len);
uip_len = 0;
}


六、于应用程序相关部分

  void HandleClientApp(void)        // 配置中的函数
{

if (uip_connected()) {
//uip_send("Start\n",6);
ClientLinkStatus = CLIENT_WORK;
return;
}

if (ClientLinkStatus == CLIENT_CLOSING) {
uip_close();
ClientLinkStatus = CLIENT_UNLINK;
return;
}

if (uip_aborted() || uip_closed()) {
ClientLinkStatus = CLIENT_UNLINK;
uip_abort();
return;
}

if (uip_timedout()) {
uip_abort();
ClientLinkStatus = CLIENT_UNLINK;
return;
}

if(uip_acked()) {
ClientAcked();
}

if(uip_newdata()) {
ClientNewData();        // 这个是收到数据的处理函数
}

if(uip_rexmit() ||
uip_newdata() ||
uip_acked()   ||
uip_poll()) {
ClientSendData();        // 这个函数的作用是把缓存中的数据靠到uip_buf中

}


//我在应用程序中会调用uint8 ClientSend(uint8 *pdata, uint16 len)函数,把要发的数据缓冲在ClientTxBuf 这个循环缓存中,然后在这里实际拷贝到uip_buf中发送

void ClientSendData(void)
{
uint16 len;
uint16 left;

uint8  *psend;

       // 缓存中有数据

if (ClientTxSize>0) {
len = ClientTxSize > uip_mss() ? uip_mss():ClientTxSize;
if ((pTxBuf+len) > (ClientTxBuf+2048)) {
memcpy(ClientTxTempBuf, pTxBuf, 2048 - (pTxBuf-ClientTxBuf));
left = 2048-(pTxBuf-ClientTxBuf);
memcpy(ClientTxTempBuf+left, ClientTxBuf, len-left);
uip_send(ClientTxTempBuf, len);
} else {
uip_send(pTxBuf, len);
}
}
}

七、总结

  整个移植主要处理三方面的事情,一是跟应用程序的接口,即应用程序怎么知道收到数据(在HandleClientApp里面检测uip_newdata()是否收到信数据),以及怎么发数据;二是跟硬件的接口,即怎么知道网口收到了数据和怎么发数据(我是通过循环检测处理的);三是定时问题,因为整个协议栈的定时都是通过uip_periodic(uint8 conn_id)处理的。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值