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^

时间: 2024-08-11 09:56:18

lwip驱动的分析(基于LPC17XX)的相关文章

lwip的架构分析(基于LPC17xx)

终于开始这部分的工作,计划了很久,但一直都没有实施.以前一直只使用TCP/IP但对其处理的流程确一知半解.计划拿出几天的时间好好的学习下,理解其运行的基本原理. 一个嵌入式的网络架构一般都由3部分构成:1.应用层.2.协议层.3.网卡层驱动.为了较好的理解lwip的架构.我们从应用层开始一层一层的剥开整个过程,了解整个系统时怎样串联起来的, 首先是应用层,这里会初始化网络参数.参见下面用户初始化网络的代码: tcpip_init(tcpip_init_done_signal,(void *) &

nvdimm 驱动的分析

nvdimm 驱动的分析 1.驱动的简介 nvdimm在内核中的驱动,是基于一个典型的字符类型的设备驱动模型去实现的.因此,同样需要实现设备驱动的几个典型操作:open/release/mmap/ioctl. 在open阶段通过efi或者e820 table探测到nvdimm的起始物理地址和长度,通过set_mtrr()设置nvdimm对应程序空间的cache属性,通过io_remmap()把物理地址映射到内核虚拟地址:在release阶段,释放上面的资源.在内核实现mmap,为用户实现用户态m

Android : 跟我学Binder --- (4) 驱动情景分析

目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 Android : 跟我学Binder --- (3) C程序示例 Android : 跟我学Binder --- (4) 驱动情景分析 Android : 跟我学Binder --- (5) server的多线程实现(未写) Android : 跟我学Binder --- (6) c++实现_(

Spark资源调度机制源码分析--基于spreadOutApps及非spreadOutApps两种资源调度算法

Spark资源调度机制源码分析--基于spreadOutApps及非spreadOutApps两种资源调度算法 1.spreadOutApp尽量平均分配到每个executor上: 2.非spreadOutApp尽量在使用单个executor的资源. 源码分析 org.apache.spark.deploy.master.Master 1.首先判断,master状态不是ALIVE的话,直接返回2.调度driver3. Application的调度机制(核心之核心,重中之重) 源码如下: 1 /*

intel万兆网卡驱动简要分析

原创文章,转载请注明: 转载自pagefault 本文链接地址: intel万兆网卡驱动简要分析 这里分析的驱动代码是给予linux kernel 3.4.4 对应的文件在drivers/net/ethernet/intel 目录下,这个分析不涉及到很细节的地方,主要目的是理解下数据在协议栈和驱动之间是如何交互的. 首先我们知道网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动.并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的. 首先我们来看对

Linux下hp打印机驱动hplip分析

Hplip分析 版本是2.14,源码位置:http://hplipopensource.com.图的来源:http://hplipopensource.com/node/128. 1.D-Bus初始化流程 D-Bus的初始化同样是在ui4/devmgr5.py开始的. ui4/devmgr5.py 01 class DevMgr5(QMainWindow,  Ui_MainWindow): 02     ...... 03     # TODO: Make sbus init mandatory

AtomicInteger源码分析——基于CAS的乐观锁实现

AtomicInteger源码分析--基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及到清空寄存器,缓存数据.然后重新加载新的thread所需数据.当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来.在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻

Linux网卡驱动架构分析

一.网卡驱动架构 由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动. 二.重要数据结构 1.Linux内核中每一个网卡由一个net_device结构来描述. 2.网卡操作函数集:net_device_ops,这个数据结构是上面net_device的一个成员. 3.网络数据包:sk_buff. 三.网卡驱动代码分析 所用文件为cs89x0.c,主要分析三个部分:网卡初始化.发送数据.接收数据. ㈠网卡初始化 网卡驱动初始化主要在函数init_module

Linux USB驱动框架分析 【转】

转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发.但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催货.当然,还会顺带提一下其他的驱动程序写法. 事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更