首先需要说明的是这个驱动是基于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^