lwip驱动的分析(基于LPC17XX)

首先需要说明的是这个驱动是基于LPC17XX 的芯片。很多代码会涉及到lpc17xx mac寄存器的操作。

驱动文件名:LPC18xx_43xx_emac.c

先看下结构体lpc_enetdata_t信息:

/* LPC EMAC driver data structure*/

typedef struct {

       /* prxs must be 8 bytealigned! */

       ENET_RXSTAT_Tprxs[LPC_NUM_BUFF_RXDESCS];   /*< Pointer to RXstatuses */

       ENET_RXDESC_T prxd[LPC_NUM_BUFF_RXDESCS];  /*Pointer to RX descriptor list */

       ENET_TXSTAT_Tptxs[LPC_NUM_BUFF_TXDESCS];   /*< Pointer to TXstatuses */

       ENET_TXDESC_Tptxd[LPC_NUM_BUFF_TXDESCS];  /* Pointer to TXdescriptor list */

       struct netif *pnetif;                                    /**< Reference back to LWIP parentnetif */

       struct pbuf*rxb[LPC_NUM_BUFF_RXDESCS]; //RX pbuf pointer list, zero-copy mode */

       u32_trx_fill_desc_index;                                  /**< RX descriptornext available index */

       volatile u32_trx_free_descs;                      /**< Count of freeRX descriptors */

       struct pbuf*txb[LPC_NUM_BUFF_TXDESCS]; /*< TX pbuf pointerlist, zero-copy mode */

       u32_t lpc_last_tx_idx;                                /* TX last descriptor index, zero-copy mode */

#if NO_SYS == 0

       sys_sem_t rx_sem;                                    /**< RX receive thread wakeup semaphore */

       sys_sem_t tx_clean_sem;                           /**<TX cleanup thread wakeup semaphore */

       sys_mutex_ttx_lock_mutex;                      /**< TX criticalsection mutex */

       sys_mutex_trx_lock_mutex;                      /**< RX criticalsection mutex */

       xSemaphoreHandlextx_count_sem;            /**< TX free buffercounting semaphore */

#endif

} lpc_enetdata_t;

 

这个结构体是描述LPC驱动的数据项:包括了Rx状态、Rx描述符表、Tx状态、Tx描述符表、网卡配置结构体(用于将数据传递给相应的函数处理)、数据缓存区、等等。

 

第一个函数将一个pbuf加入接收描述符队列

功能分析

RX descriptor Queues接收描述符队列,接收描述符由一个数组组成,而描述符的使用则是循环的使用这个数组。(即到达数组的末尾处,会重新回到数组的[0]位置)

此函数的功能设置emac的接收描述符寄存器,将emac描述符所指向的缓冲区指针指向参数pbuf的空数据块。(即用pbuf来初始化下一个空闲的描述符)

/*将一个pbuf加入到接收描述符的队列中

* Queues a pbuf into the RXdescriptor list

*/

STATIC void  lpc_rxqueue_pbuf(lpc_enetdata_t*lpc_enetif, struct pbuf *p)

{

       u32_tidx;

       /* 取得下一个可用的描述符的序号

*Get next free descriptorindex

*/

       idx = lpc_enetif->rx_fill_desc_index;

       /* Setup descriptorand clear statuses */

       lpc_enetif->prxd[idx].Control= ENET_RCTRL_INT | ((u32_t) ENET_RCTRL_SIZE(p->len));

       lpc_enetif->prxd[idx].Packet= (u32_t) p->payload;//描述符指向缓冲区

       lpc_enetif->prxs[idx].StatusInfo= 0xFFFFFFFF;

       lpc_enetif->prxs[idx].StatusHashCRC= 0xFFFFFFFF;

       /*Save pbuf pointer for push to network layer later */

       lpc_enetif->rxb[idx]= p;//保存pbuf的指针,用于将数据传给网络层

       /* Wrap at end ofdescriptor list */

       idx++;

       if(idx >= LPC_NUM_BUFF_RXDESCS) {

              idx= 0;

       }

       /*Queue descriptor(s) */

       lpc_enetif->rx_free_descs-= 1;//可用的描述符减1

       lpc_enetif->rx_fill_desc_index= idx;//下一个可用的描述符序号

}

 

第二个函数:建立接收描述符缓冲buffer

功能描述建立接收描述符的初始值。

/* Sets up the RX descriptor ringbuffers. */

STATIC err_t   lpc_rx_setup(lpc_enetdata_t*lpc_enetif)

{

       /*建立接收的结构体*/

       Chip_ENET_InitRxDescriptors(LPC_ETHERNET,lpc_enetif->prxd, lpc_enetif->prxs, LPC_NUM_BUFF_RXDESCS);  //初始化mac寄存器的参数,主要是初始化了mac的接收描述符地址、初始化mac接收的状态

       lpc_enetif->rx_free_descs= LPC_NUM_BUFF_RXDESCS;//可用的描述符数量

       lpc_enetif->rx_fill_desc_index= 0;//可用的接收描述序号

       /*新建接收缓冲区和描述符 Build RX buffer and descriptors */

       lpc_rx_queue(lpc_enetif->pnetif);

       return ERR_OK;

}

 

第三个函数:新建一个pbuf并加入到接收队列中(即赋值给mac配置结构体)

功能描述:当有空闲的接收描述符时,新建一个pbuf数据块,并用此数据块来初始化一个空闲的接收描述符,直到没有空闲的接收描述符时,才退出此函数。函数返回成功申请到pbuf的个数。

/* 分配一个pbuf的空间并将其加入到队列中

* Attempt to allocate and requeue a new pbuf for RX

*/

s32_t    lpc_rx_queue(struct netif *netif)

{

       lpc_enetdata_t*lpc_enetif = netif->state;

       struct pbuf *p;

       s32_t queued = 0;

       /* Attempt to requeueas many packets as possible */

       while(lpc_enetif->rx_free_descs > 0) {

              /* Allocate a pbuffrom the pool. We need to allocate at the

                 maximum size as we don't know the size ofthe yet to be

                 received packet. */

              p = pbuf_alloc(PBUF_RAW,(u16_t) ENET_ETH_MAX_FLEN, PBUF_RAM);

              if (p == NULL) {

                     returnqueued;

              }

 

              /* pbufs allocatedfrom the RAM pool should be non-chained. */

              LWIP_ASSERT("lpc_rx_queue:pbuf is not contiguous (chained)",

                                   pbuf_clen(p)<= 1);

              /* Queue packet */

              lpc_rxqueue_pbuf(lpc_enetif,p);

              /* Update queued count*/

              queued++;

       }

       return queued;

}

 

第四个函数:分配一个pbuf同时从输入的报中返回数据

功能描述:从网卡中读取一个pbuf的数据帧。流程:如果有数据,则取得pbuf的数据帧。如果还有空闲的接收描述符,则调用lpc_rx_queue()函数来申请pbuf的数据块并用新得到的pbuf来设置描述符寄存器等。

/* Allocates a pbuf and returns the data from the incoming packet*/

STATIC struct pbuf *  lpc_low_level_input(struct netif *netif) {

       lpc_enetdata_t*lpc_enetif = netif->state;

       struct pbuf *p = NULL;

       u32_t idx, length;

 

#ifdef LOCK_RX_THREAD

#if NO_SYS == 0

       /* Get exclusive access*/

       sys_mutex_lock(&lpc_enetif->rx_lock_mutex);

#endif

#endif

 

       /* Monitor RX overrunstatus. This should never happen unless

          (possibly) the internal bus is behing heldup by something.

          Unless your system is running at a very lowclock speed or

          there are possibilities that the internalbuses may be held

          up for a long time, this can probably safelybe removed. */

       if(Chip_ENET_GetIntStatus(LPC_ETHERNET) & ENET_INT_RXOVERRUN) {

              LINK_STATS_INC(link.err);

              LINK_STATS_INC(link.drop);

 

              /* Temporarilydisable RX */

              Chip_ENET_RXDisable(LPC_ETHERNET);

 

              /* Reset the RXside */

              Chip_ENET_ResetRXLogic(LPC_ETHERNET);

              Chip_ENET_ClearIntStatus(LPC_ETHERNET,ENET_INT_RXOVERRUN);

 

              /* De-allocate all queued RX pbufs */

              for (idx = 0;idx < LPC_NUM_BUFF_RXDESCS; idx++) {

                     if(lpc_enetif->rxb[idx] != NULL) {

                            pbuf_free(lpc_enetif->rxb[idx]);

                            lpc_enetif->rxb[idx]= NULL;

                     }

              }

              /* Start RXside again */

              lpc_rx_setup(lpc_enetif);

              /* Re-enable RX*/

              Chip_ENET_RXEnable(LPC_ETHERNET);

#ifdef LOCK_RX_THREAD

#if NO_SYS == 0

              sys_mutex_unlock(&lpc_enetif->rx_lock_mutex);

#endif

#endif

              return NULL;

       }

       /* Determine if aframe has been received */

       length = 0;

       idx =Chip_ENET_GetRXConsumeIndex(LPC_ETHERNET);

       if (!Chip_ENET_IsRxEmpty(LPC_ETHERNET)){

              /* Handle errors*/

              if(lpc_enetif->prxs[idx].StatusInfo & (ENET_RINFO_CRC_ERR |

                                                                                    ENET_RINFO_SYM_ERR| ENET_RINFO_ALIGN_ERR | ENET_RINFO_LEN_ERR)) {

#if LINK_STATS

                     if(lpc_enetif->prxs[idx].StatusInfo & (ENET_RINFO_CRC_ERR |

                                                                                           ENET_RINFO_SYM_ERR| ENET_RINFO_ALIGN_ERR)) {

                            LINK_STATS_INC(link.chkerr);

                     }

                     if(lpc_enetif->prxs[idx].StatusInfo & ENET_RINFO_LEN_ERR) {

                            LINK_STATS_INC(link.lenerr);

                     }

#endif

                     /* Drop the frame */

                     LINK_STATS_INC(link.drop);

                     /* Re-queue the pbuf for receive */

                     lpc_enetif->rx_free_descs++;

                     p =lpc_enetif->rxb[idx];

                     lpc_enetif->rxb[idx]= NULL;

                     lpc_rxqueue_pbuf(lpc_enetif,p);

                     p = NULL;

              }

              else {

                     /* A packet is waiting, get length */

                     length =ENET_RINFO_SIZE(lpc_enetif->prxs[idx].StatusInfo) - 4; /* Remove FCS */

                     /*Zero-copy */

                     p =lpc_enetif->rxb[idx];

                     p->len= (u16_t) length;

                     /* Free pbuf from desriptor */

                     lpc_enetif->rxb[idx]= NULL;

                     lpc_enetif->rx_free_descs++;

                     /* Queue new buffer(s) */

                     if (lpc_rx_queue(lpc_enetif->pnetif)== 0) {

                            /* Re-queue the pbuffor receive */

                            lpc_rxqueue_pbuf(lpc_enetif,p);

                            /* Drop the frame */

                            LINK_STATS_INC(link.drop);

                            p= NULL;

                     }

                     else {

                            /* Save size */

                            p->tot_len= (u16_t) length;

                            LINK_STATS_INC(link.recv);

                     }

              }

              /* UpdateConsume index */

              Chip_ENET_IncRXConsumeIndex(LPC_ETHERNET);

       }

#ifdef LOCK_RX_THREAD

#if NO_SYS == 0

       sys_mutex_unlock(&lpc_enetif->rx_lock_mutex);

#endif

#endif

       return p;

}

第五个函数:接收线程

功能描述等待接收中断发来的信号,然后读取数据进行处理。线程会一直阻塞,直到接收中断发送来的信号为止。

/* Packet reception task for FreeRTOS */

STATIC void vPacketReceiveTask(void *pvParameters)

{

       lpc_enetdata_t*lpc_enetif = pvParameters;

       while (1) {

              /* 等到接收中断的信号量唤醒该线程*/

              sys_arch_sem_wait(&lpc_enetif->rx_sem,0);

              /* Process packets until all empty */

              while(!Chip_ENET_IsRxEmpty(LPC_ETHERNET)) {

                     lpc_enetif_input(lpc_enetif->pnetif);

              }

       }

}

第六个函数:emac端口接收数据的处理函数

功能描述从网卡中读取一个pbuf的数据帧。并将此帧数据通过回调函数传递给tcpip层协议栈处理。

/* Attempt to read a packetfrom the EMAC interface */

void lpc_enetif_input(struct netif *netif)

{

       struct eth_hdr *ethhdr;

       struct pbuf *p;

       /* move received packet into a new pbuf */

       p =lpc_low_level_input(netif);

       if (p == NULL) {

              return;

       }

       /* points to packet payload, which starts with anEthernet header */

       ethhdr = p->payload;

       switch(htons(ethhdr->type)) {

       case ETHTYPE_IP:

       case ETHTYPE_ARP:

#if PPPOE_SUPPORT

       case ETHTYPE_PPPOEDISC:

       case ETHTYPE_PPPOE:

#endif /* PPPOE_SUPPORT */

              /* full packet send to tcpip_thread to process */

              if (netif->input(p, netif) != ERR_OK) {                  

/*如果调用失败,则释放pbuf数据 */

                     pbuf_free(p);

              }

              break;

       default:

              /* Returnbuffer */

              pbuf_free(p);

              break;

       }

}

在上一节中我们已分析了,网卡的初始化函数新建了了接收线程这件事。而这一节则是讲接收线程是怎么获取的数据的?是怎样实现从网卡中读取数据的。接收线程会循环调用while(1) lpc_enetif_input(lpc_enetif->pnetif)这个函数。而这个函数又调用了p = lpc_low_level_input(netif)从网卡中读取了pbuf包。然后在将pbuf传递给netif->input()协议栈处理。线程会一直会循环执行这样的任务。可是数据是怎样从网卡中读到pbuf中的呢?lpc_low_level_input(netif)到底是怎样实现读取数据的呢?

现在,我们再分析分析lpc_low_level_input(netif)这个函数的功能,首先函数判断emac是否有数据,如有数据则将pbuf *p的指针指向描述符的缓冲区。如果还有空闲的描述符,函数会调用lpc_rx_queue()函数,为每个描述符new一段数据空间,然后lpc_rx_queue()会调用lpc_rxqueue_pbuf(lpc_enetif,p)将emac的描述符寄存器指向new到的pbuf。其中比较的重要的是lpc_rx_queue()会一直执行这样的操作,直到没有空闲的描述符为止。执行完lpc_rxqueue_pbuf()后,lpc_low_level_input(netif)会返回前面得到的pbuf *p指针。 从上面的流程分析中可以看出,整个执行过程中,都没有用到数据的copy,这也是lwip的优点之一(zero copy)。看到这里应该对整个驱动框架有很大的了解了吧!^0^ 

 

你可能感兴趣的:(NetWork,tcpip,以太网,Lwip)