分享

一步步移植uCOS-II and LwIP (二)

 立志德美 2019-07-17

2、ethernetif.c 函数接口编写

ethernetif.c文件主要涉及5个接口函数的编写:

static void low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif, 
                                struct pbuf *p);
static struct pbuf * low_level_input(struct netif *netif);
err_t ethernetif_input(struct netif *netif);
err_t ethernetif_init(struct netif *netif);

(1) low_level_init(struct netif *netif)

从函数的声明可以看出,我们的主要工作是对struct netif定义的netif初始化,查阅LwIP源文件找到struct netif结构体的定义(删除条件编译后的基本定义):

struct netif {
  /** pointer to next in linked list */
  struct netif *next;

  /** IP address configuration in network byte order */
  ip_addr_t ip_addr;
  ip_addr_t netmask;
  ip_addr_t gw;

  /** This function is called by the network device driver
   *  to pass a packet up the TCP/IP stack. */
  netif_input_fn input;
  /** This function is called by the IP module when it wants
   *  to send a packet on the interface. This function typically
   *  first resolves the hardware address, then sends the packet. */
  netif_output_fn output;
  /** This function is called by the ARP module when it wants
   *  to send a packet on the interface. This function outputs
   *  the pbuf as-is on the link medium. */
  netif_linkoutput_fn linkoutput;
  /** This field can be set by the device driver and could point
   *  to state information for the device. */
  void *state;
  /** maximum transfer unit (in bytes) */
  u16_t mtu;
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** flags (see NETIF_FLAG_ above) */
  u8_t flags;
  /** descriptive abbreviation */
  char name[2];
  /** number of this interface */
  u8_t num;
};

其中

typedef err_t (*netif_input_fn)(struct pbuf *p, 
                            struct netif *inp); 

typedef err_t (*netif_output_fn)(struct netif *netif,                struct pbuf *p, ip_addr_t *ipaddr);

typedef err_t (*netif_linkoutput_fn)(struct netif *netif,   
                struct pbuf *p);

在这里我们将DM9000底层的MAC地址及初始化写入该函数内,其代码如下:

static void low_level_init(struct netif *netif)
{ 
  /* set MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
  netif->hwaddr[0] = MAC_ADDR0;
  netif->hwaddr[1] = MAC_ADDR1;
  netif->hwaddr[2] = MAC_ADDR2;
  netif->hwaddr[3] = MAC_ADDR3;
  netif->hwaddr[4] = MAC_ADDR4;
  netif->hwaddr[5] = MAC_ADDR5;

  /* maximum transfer unit */
  netif->mtu = 1500;

  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

  /* Do whatever else is needed to initialize interface. */  
    DM9000_Init();
}

(2) struct pbuf * low_level_input(struct netif netif)

该函数主要是用来实现将网卡中接收到的数据转换为LwIP定义的struct pbuf 类型的数据包,便于协议栈数据处理。我们先来看看结构体struct pbuf

struct pbuf {
  /** next pbuf in singly linked pbuf chain */
  struct pbuf *next;

  /** pointer to the actual data in the buffer */
  void *payload;

  /**
   * total length of this buffer and all next buffers in chain
   * belonging to the same packet.
   *
   * For non-queue packet chains this is the invariant:
   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
   */
  u16_t tot_len;

  /** length of this buffer */
  u16_t len;

  /** pbuf_type as u8_t instead of enum to save space */
  u8_t /*pbuf_type*/ type;

  /** misc flags */
  u8_t flags;

  /**
   * the reference count always equals the number of pointers
   * that refer to this pbuf. This can be pointers from an application,
   * the stack itself, or pbuf->next pointers from a chain.
   */
  u16_t ref;
};

这里我们要做的工作就是将网卡中数据存入payload 中,并将网络数据的相关信息写入struct pbuf 链表中。先将low_level_input() 的实现代码列出:

static struct pbuf *
low_level_input(struct netif *netif)
{
  // cm3 cpu is little endian mode,so the first byte copied(low address)is low byte in 16bit mode
    // data copied from address 0x0c00 in dm9000 sram
    // there are four bytes before the mac frame in the packet
    // 1 byte:0x00 or 0x01 indicate there are or no packets coming;
    // 2 byte:related RSR status;
    // 3 byte:the mac frame length's low byte;
    // 4 byte:the mac frame length's high byte;
    // mac header: 14bytes

    struct pbuf *p, *q;
    uint16_t packet_length; // packet length
    uint8_t frameheader[18]; // 14 mac header + 4 byte
    uint8_t *payload;
    uint16_t len;
    uint16_t Rxstatus;

    if(DM9000_CheckNetPacket())  // check packet is coming
    {
        DM9000->REG = DM9000_MRCMD;
        DM9000_CopyIn(18, frameheader); //copy 18 byte header
        Rxstatus = ((uint16_t)frameheader[1])<<8|frameheader[0];
        packet_length = ((uint16_t)frameheader[3])<<8|frameheader[2];
        // check frame is a IP packet(0x0800) or ARP packet(0x0806)
        // check packet length is larger than 1536 bytes
        if((frameheader[3]>0x06)||(frameheader[16] != 0x08)||((frameheader[17] != 0x00)&&(frameheader[17] != 0x06)))
        {
            DM9000_Discard(packet_length);
            return NULL;
        }

        p = pbuf_alloc(PBUF_RAW, packet_length, PBUF_POOL); //allocate a pbuf

        if(p != NULL)
        {
            memcpy(p->payload,frameheader+4,14);  
            // copy mac frame header
            for(q=p;q!=NULL;q=q->next)
            {
                len = q->len;
                payload= (uint8_t *)(q->payload);
                if(q == p)
                {
                    payload= (uint8_t *)(q->payload)+14;
                    len = q->len - 14;
                }

                DM9000_CopyIn(len, payload);
            }
        }
        else // allocate a pbuf failed
        {
            printf("input packet allocate pbuf failed\n");
            DM9000_Discard(packet_length-14);
        }
    }
    else
    {
        p = NULL;
    }

  return p;  
}

该函数工作流程首先是甄别网口是否接收到有效的数据包,然后利用pbuf_alloc()向内存池申请内存,并复制读取DM9000的SRAM接收到的数据。这里介绍一下以太网数据的帧格式:
MAC Frame
D-MAC为目标机的MAC地址,S-MAC为源主机的MAC地址,类型决定数据是IP数据包(0x0800)还是ARP数据包(0x0806),CRC为循环冗余校验。根据DM9000的数据手册,我们看到DM9000接收到的数据一般在以太网数据帧头前额外添加4个字节,这4个字节的的含义已在程序中注释。程序利用函数DM9000_CheckNetPacket()检验DM9000接收的首字节是否为0x01,并复制收到的前18个字节数据(DM9000 add 4 bytes+MAC Frame 14 bytes)到frameheader[]数组中,以判别接收的数据包长度和类型是否合法。若合法,利用DM9000_CopyIn(len, payload)复制数据到新申请的pbuf中。DM9000_CopyIn()主要功能是复制DM9000 接收缓存SRAM中的数据到单片机内存中,其程序如下:

#define WORDMODE 1
void DM9000_CopyIn(uint16_t length, uint8_t* databuf) 
 // copy  length of data from dm9000 sram to databuf
{
    uint16_t count;  // 16bit - packet words
    uint16_t *dataread;
    // WORDMODE decide by microcontroller 8-bit or 16-bit
    count = WORDMODE?length>>1:length;
    dataread = (uint16_t *)databuf;

    #if WORDMODE
        dataread = (uint16_t *)databuf;
        while(count--)
            *dataread++ = DM9000->DATA;
        // length is odd
        if(length&0x01)
            *((uint8_t *)dataread) = (uint8_t)DM9000->DATA;
    #else
        while(count--)
            *databuf++ = (uint8_t)DM9000->DATA;
    #endif
}

需要注意到是DM9000->REG = DM9000_MRCMD; 对DM9000寄存器MRCMD的访问,其地址递增一个字节还是半字长是由DM9000的工作位长模式决定的。另外,在复制SRAM中的数据时,需要舍弃DM9000添加的前4个字节。程序中还有一函数个DM9000_Discard(packet_length); 该函数的作用是丢弃不合法 数据,实现MRCMD地址的移动,其源代码也附上如下:

void DM9000_Discard(uint16_t length)
{
    uint16_t count;
    uint16_t temp;
    uint8_t btemp;

    count = WORDMODE?length>>1:length;
    #if WORDMODE
        while(count--)
            temp = DM9000->DATA ;
        if(length&0x01)
            btemp = DM9000->DATA;
    #else
        while(count--)
            btemp = DM9000->DATA;
    #endif
}

(3)low_level_output(struct netif *netif, struct pbuf *p)

该函数的主要功能是向某一网口发送数据,其难点在于如何将链表struct pbuf p中的数据组织成一串数据向网卡发送。由于DM9000的工作在16-bit模式,当链表pbuf中的某一个pbuf长度为偶数时还好说,当为奇数时,最后一个字节则需要填充字节,这对于整个pbuf链表来说是不允许的。*low_level_output()实现代码如下:

static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
    struct pbuf *q;
    uint8_t chainflag = 0;
     // the chain flag between pbuf and next pbuf
    uint8_t *dat;       // transfer data pointer
    uint16_t datalength;   // transfer data length
    uint16_t chaindat;  
    // link the last byte of pbuf and the next pbuf first byte
    uint16_t packet_length;   // packet data length
//pad length when send byte is lower than 64 byte 
    uint8_t isr;  // setup IMR

    packet_length = p->tot_len;
    pad_length = 0;
    //min packets (14 mac frame header + 46 data + 4 crc)
    if(packet_length<60) //include 4 byte CRC
    {
        pad_length = 60- packet_length;
        packet_length = 60;
    }

    isr = DM9000_ReadReg(DM9000_IMR);
    isr &= ~IMR_PRI;
    DM9000_WriteReg(isr,DM9000_IMR); // shut rx interrupt
    //sending packet length setup
    DM9000_WriteReg(packet_length&0xff, DM9000_TXPLL);
    DM9000_WriteReg((packet_length&0xff00)>>8, DM9000_TXPLH);

    DM9000->REG = DM9000_MWCMD; // to sram in dm9000

    for(q=p;q!=NULL;q=q->next)
    {
        if(chainflag == 0)   // pbuf first byte is not tranfered
        {
            if(((q->len)&0x01) && (q->next != NULL))                   //pbuf length is odd
            {
                datalength = q->len - 1;
                dat = (u8_t*)(q->payload);
            // link this pbuf last byte and next pbuf first byte
            chaindat = *((u8_t*)(q->payload)+q->len - 1);
            chaindat += (u16_t)(*((u8_t*)(q->next->payload)))<<8;
                chainflag = 1;
            }
            else
            {
                datalength = q->len;
                dat = (u8_t *)(q->payload);
                chainflag = 0;
            }
        }
        else     // fist byte has been transfered with last pbuf
        {
            if(((q->len)&0x01) && (q->next != NULL))
            {
                // one had been transferrd and the last will be transferred by chaindat
                datalength = q->len-2;
                dat = (u8_t*)(q->payload) + 1;
                chaindat = *((u8_t*)(q->payload)+q->len - 1);
                chaindat += (u16_t)(*((u8_t*)(q->next->payload)))<<8;
                chainflag = 1;
            }
            else
            {
                datalength = q->len-1;
                dat = (u8_t *)(q->payload)+1;
                chainflag = 0;
            }
        }
        // send data payload to dm9000's sram
        DM9000_CopyOut(datalength, dat);
        // send chain dat to dm9000's sram
        if(chainflag == 1)
            DM9000->DATA = chaindat;
    }
    // if packet length <60, pad 0 to send
    if(pad_length>0)
        DM9000_OutPad(pad_length);

    DM9000_WriteReg(DM9000_ReadReg(DM9000_TCR)|0x01,DM9000_TCR); // start tx
/ // wait for sending completely
    while(!(DM9000_ReadReg(DM9000_ISR)&0x02));  
    DM9000_WriteReg(0x02,DM9000_ISR);

    isr |= IMR_PRI;
    DM9000_WriteReg(isr, DM9000_IMR); // RX interrupt enable

  return ERR_OK;
}

该函数在发送数据时,首先判断链表中即将发送的pbuf第一个数据是否已经被前一个pbuf 已经连接发送出去,然后根据本次发送pbuf长度的奇偶性复制数据到DM9000中的发送缓存SRAM中。文中注释已详细介绍每条语句的含义,需要注意的是在发送数据时一定要关闭接收中断。附上复制数据到DM9000的SRAM函数DM9000_CopyOut(datalength, dat) 和填充函数DM9000_OutPad(pad_length); 源程序,不做解释。

void DM9000_CopyOut(uint16_t length, uint8_t *databuf)
 // copy databuf to dm9000's sram
{
    uint16_t count;
    uint16_t *datawrite;

    count = WORDMODE?length>>1:length;
    #if WORDMODE
    //address is odd, dm9000 did not happen, for if
        if((uint32_t)databuf & 0x01)  
        {
            while(count--)
            {
                DM9000->DATA = (*databuf) |                 (*((uint16_t *)(databuf+1))<<8);
                databuf += 2;
            }
            if(length&0x01)
                DM9000->DATA = *databuf;
        }
        else       // address is even
        {
            datawrite = (uint16_t *)databuf;
            while(count--)
                DM9000->DATA = *datawrite++;
            if(length&0x01)
                DM9000->DATA = *(uint8_t *)datawrite;
        }
    #else
        DM9000->DATA = *databuf++;  // 8-bit mode
    #endif
}

(4)err_t ethernetif_init(struct netif *netif)

该函数可不用修改,源程序如下:

err_t ethernetif_init(struct netif *netif)
{
#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  netif->output = etharp_output;
  netif->linkoutput = low_level_output;

  /* initialize the hardware */
  low_level_init(netif);
  return ERR_OK;
}

(5)err_t ethernetif_input(struct netif *netif)

源程序如下:

err_t ethernetif_input(struct netif *netif)
{
  struct pbuf *p;
    err_t err;

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* no packet could be read, silently ignore this */
  if (p == NULL)  return ERR_MEM;
    /* full packet send to tcpip_thread to process */

    err = netif->input(p, netif); // use ethernet_input() in the etharp.c to tranfer pbuf to IP

    if (err != ERR_OK)
     { 
         LWIP_DEBUGF(NETIF_DEBUG,           ("ethernetif_input: IP input error\n"));
         pbuf_free(p);
     }

     return err;
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多