基于rt-thread+lwip分析数据是怎么从网卡芯片接收数据到pbuf的(lwip源码解析一)

更新时间:2023-07-05 04:26:04 阅读: 评论:0

基于rt-thread+lwip分析数据是怎么从⽹卡芯⽚接收数据到pbuf
赵慕鹤的(lwip源码解析⼀)
LWIP是嵌⼊式设备的⽹络微协议,基本上实现了标准的TCP/IP的功能,它没有项标准的TCP/IP协议那样有很严格的分层。主要原因是由于嵌⼊式设别的资源有限,所以避免了每层的COPY动作,在不同层之间是之间共⽤同⼀内存操作。那么下⾯我们来介绍下数据到底是怎么从⽹卡接收,然后吧数据交给协议处理的:
⾸先说明下,我⽤的平台是STM32F207+DP83848平台的,lwip1.4.1。
1、⾸先我们初始化⽹卡,这⾥涉及的是硬件上⾯的配置,在这⾥就不做详细说明。在配置好了后,我们需要初始化相关参数。由于我们
的STM32F207⽤的是DMA从PHY那⾥获取数据,然后也是通过DMA把发送的数据送到PHY。所以在这⾥我们⾸先需要初始化好DMA的数据接收缓存和发送缓存。
1-1.在这⾥我们需要知道这两个全局变量:
ETH_DMADESCTypeDef  *DMATxDescToSet;//发送缓冲区指针,指向当前发送数据的⾸地址。
ETH_DMADESCTypeDef  *DMARxDescToGet;//接收缓冲区指针,指向当前接收缓冲区的⾸地址。
结构体成员变量如下:
typedef struct  {
__IO uint32_t  Status;                /*!< Status */
uint32_t  ControlBufferSize;    /*!< Control and Buffer1, Buffer2 lengths */
uint32_t  Buffer1Addr;          /*!< Buffer1 address pointer */
uint32_t  Buffer2NextDescAddr;  /*!< Buffer2 or next descriptor address pointer */
/* Enhanced ETHERNET DMA PTP Descriptors */
#ifdef USE_ENHANCED_DMA_DESCRIPTORS//这个红没有打开,所以我们只关⼼上⾯⼏个变量
uint32_t  ExtendedStatus;        /* Extended status for PTP receive descriptor */
uint32_t  Rerved1;            /* Rerved */
uint32_t  TimeStampLow;          /* Time Stamp Low value for transmit and receive */
uint32_t  TimeStampHigh;        /* Time Stamp High value for transmit and receive */
#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
} ETH_DMADESCTypeDef;
⾸先来讲解下发送缓冲区:由于rt-thread⽤到的是⼀个两维数组做为发送数据的缓冲区,⽽且是把这个⼆维数组做成了⼀个环形的链表,这个就是在初始化需要完成的事情。
下⾯具体看代码实现:
void ETH_DMATxDescChainInit(ETH_DMADESCTypeDef *DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount)
{
uint32_t i = 0;
ETH_DMADESCTypeDef *DMATxDesc;
/* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */
DMATxDescToSet = DMATxDescTab;//⾸先是把数组DMATxDescTab的⾸地址传⼊
/* Fill each DMATxDesc descriptor with the right values */
for(i=0; i < TxBuffCount; i++)//这个for循环就是把数据缓冲区形成⼀个单项链表
{
长沙大学是几本
/* Get the pointer on the ith member of the Tx Desc list */
DMATxDesc = DMATxDescTab + i;
/* Set Second Address Chained bit */
DMATxDesc->Status = ETH_DMATxDesc_TCH;
/* Set Buffer1 address pointer */
DMATxDesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
/
* Initialize the next descriptor with the Next Descriptor Polling Enable */
if(i < (TxBuffCount-1))//这⾥是连接TxBuffCount-1个接收缓冲区,形成的只是⼀个单项链表
{
/* Set next descriptor address register with next descriptor ba address */
DMATxDesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1);//每个结构体
}
el //这⾥才是形成⼀个环形链表,具体操作是把最后⼀个最后⼀个描述符的Buffer2NextDescAddr 指向了第⼀个描述符,这样就形成了⼀个环形链表    {
/* For last descriptor, t next descriptor address register equal to the first descriptor ba address */
DMATxDesc->Buffer2NextDescAddr = (uint32_t) DMATxDescTab;
}
}
/* Set Transmit Desciptor List Address Register */
ETH->DMATDLAR = (uint32_t) DMATxDescTab;//这步就是把描述符的⾸地址赋给DMA的发送寄存器,这样我们就可以通过DMA发送数据给PHY了
}
我们再摘⽤正点原⼦的图⽚说明⼀下这个流程是怎么实现的:
⽽接收缓冲区也是这么实现的:
具体看代码实现:
void ETH_DMARxDescChainInit(ETH_DMADESCTypeDef *DMARxDescTab, uint8_t *RxBuff, uint32_t RxBuffCount)
{
uint32_t i = 0;
ETH_DMADESCTypeDef *DMARxDesc;
/* Set the DMARxDescToGet pointer with the first one of the DMARxDescTab list */
DMARxDescToGet = DMARxDescTab;
/* Fill each DMARxDesc descriptor with the right values */
for(i=0; i < RxBuffCount; i++)//也是形成⼀个简单的单向链表
{
/* Get the pointer on the ith member of the Rx Desc list */
DMARxDesc = DMARxDescTab+i;
/* Set Own bit of the Rx descriptor Status */
DMARxDesc->Status = ETH_DMARxDesc_OWN;
/* Set Buffer1 size and Second Address Chained bit */
二年级语文题
DMARxDesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)ETH_RX_BUF_SIZE;
/* Set Buffer1 address pointer */
DMARxDesc->Buffer1Addr = (uint32_t)(&RxBuff[i*ETH_RX_BUF_SIZE]);
/* Initialize the next descriptor with the Next Descriptor Polling Enable */
if(i < (RxBuffCount-1))
{
/
* Set next descriptor address register with next descriptor ba address */
DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab+i+1);
}
El //这⾥才是把单向链表形成⼀个环形链表
{
/* For last descriptor, t next descriptor address register equal to the first descriptor ba address */
嘴唇上的痣
DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab);
}
}
/* Set Receive Descriptor List Address Register */
ETH->DMARDLAR = (uint32_t) DMARxDescTab; //把接收描述符地址赋给DMA接收寄存器,这样我们就可以通过DMA接收来之PHY的数据了。
DMA_RX_FRAME_infos = &RX_Frame_Descriptor;//这个是有关帧信息的
}
上⾯已经把DMA初始化完成了,那么⽹络数据怎么从⽹卡接收到数据的呢?
其实在rt-thread⾥⾯有⼀个接收⽹络数据的邮箱:eth_rx_thread_mb(发送也是有⼀个邮箱eth_tx_thread_mb),我们主要是讲解下⽹络数据接收邮箱。我们在初始化的时候配置了DMA中断接收⽹络数据的。所以在⽹络数据到来的时候是⾸先进⼊接收中断:
void ETH_IRQHandler(void)
{
rt_uint32_t status;
status = ETH->DMASR;
/* Frame received */
if ( ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R) == SET)出现英文
{
rt_err_t result;
花叶万年青
//rt_kprintf("Frame comming\n");
/* Clear the interrupt flags. */
/* Clear the Eth DMA Rx IT pending bits */
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
/* a frame has been received */
result = eth_device_ready(&(stm32_eth_device.parent));//发送接收数据消息邮箱
if( result != RT_EOK ) rt_kprintf("RX err =%d\n", result );
//RT_ASSERT(result == RT_EOK);
}
if (ETH_GetDMAITStatus(ETH_DMA_IT_T) == SET) /* packet transmission */
正脚背射门{
ETH_DMAClearITPendingBit(ETH_DMA_IT_T);
}
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
}
//发送⽹络接收数据的消息邮箱函数如下:
rt_err_t eth_device_ready(struct eth_device* dev)
{
if (dev->netif)
/* post message to Ethernet thread */
return rt_mb_nd(ð_rx_thread_mb, (rt_uint32_t)dev);
el
return ERR_OK; /* netif is not initialized yet, just return. */
}
在⽹络接收中断⾥⾯发送⼀个接收数据的消息邮箱,在接收数据的线程去接收消息邮箱的数据,接收数据后就把数据传输到ip_input()函数,这样就是到lwip的内核当中了,交给lwip的内核去处理解析相关数据了,这个我们在下⼀章节介绍。
接收⽹络数据的消息邮箱代码:
/* Ethernet Rx Thread */
static void eth_rx_thread_entry(void* parameter)
{
struct eth_device* device;
while (1)
{
if (rt_mb_recv(ð_rx_thread_mb, (rt_uint32_t*)&device, RT_WAITING_FOREVER) == RT_EOK)//接收⽹络数据的消息邮箱
{
struct pbuf *p;
/* check link status */
if (device->link_changed)
{
int status;
rt_uint32_t level;
level = rt_hw_interrupt_disable();
status = device->link_status;
device->link_changed = 0x00;
rt_hw_interrupt_enable(level);
if (status)
netifapi_netif_t_link_up(device->netif);
el
netifapi_netif_t_link_down(device->netif);
}
/* receive all of buffer */
while (1)
徐志摩名言{
p = device->eth_rx(&(device->parent));//这个就是调⽤struct pbuf *rt_stm32_eth_rx(rt_device_t dev)函数接收数据,并把数据存⼊pbuf链表。
if (p != RT_NULL)
{
/* notify to upper layer */
if( device->netif->input(p, device->netif) != ERR_OK )//这个是调⽤ip_input()函数,内核IP层去处理解析接收到的数据,然后在丢给上⼀层(TCP/DDP                    {
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: Input error\n"));
pbuf_free(p);
p = NULL;
}
}
el break;
}
}
el
{
LWIP_ASSERT("Should not happen!\n",0);
}
}
}
2-1:数据是怎么传到LWIP的pbuf的?
还是看代码实现:
struct pbuf *rt_stm32_eth_rx(rt_device_t dev)
{
struct pbuf *p, *q;
u16_t len;
uint32_t l=0,i =0;
FrameTypeDef frame;
static framecnt = 1;

本文发布于:2023-07-05 04:26:04,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1068327.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:数据   接收   邮箱   消息   接收数据   发送   需要   链表
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图