关键词
TC397 官方例程;TC397 以太网例程;TC397 GETH;
简介
本篇为 Aurix TriCore TC397 以太网官方例程分析,重点关注其硬件行为
调试所用的开发板型号:KIT-A2G-TC397-5V-TFT
所使用的例程:Ethernet_1_KIT_TC397_TFT
英飞凌 TriCore 官方例程下载地址:
https://github.com/Infineon/AURIX_code_examples
系列文章链接
TC397以太网例程 - 17.ECHO 应用 - 轮询定时器
TC397以太网例程 - 18.ECHO 应用 - 接收报文
目录
所有模块的初始化流程结束后,在主函数的循环中运行 ECHO 应用,其分为两个部分:
-
Ifx_Lwip_pollTimerFlags():轮询各协议的定时器,检查是否超时,若超时则调用对应的回调函数;
-
Ifx_Lwip_pollReceiveFlags():检查是否产生数据接收请求,如果有则调用 ifx_netif_input() 接收数据;
// Cpu0_Main.c
void core0_main (void)
{
...
while (1)
{
// 轮询定时器并进行相应的处理
/* Poll LwIP timers and trigger protocols execution if required */
Ifx_Lwip_pollTimerFlags();
// 报文接收
/* Receive data package through ETH */
Ifx_Lwip_pollReceiveFlags();
}
}
本章节讨论报文接收的流程,重点关注其中涉及到的硬件操作:low_level_input()
1.流程概览
Ifx_Lwip_pollReceiveFlags() 调用 ifx_netif_input() 进行轮询操作,检查是否存在需要接收的报文:
// Libraries/Ethernet/lwip/port/src/Ifx_Lwip.c
/* Polling the ETH receive event flags */
void Ifx_Lwip_pollReceiveFlags(void)
{
/* We are assuming that the only interrupt source is an incoming packet */
ifx_netif_input(&g_Lwip.netif);
}
ifx_netif_input() 用于从接口中接收数据包,实际调用 low_level_input() 将数据包存入硬件的内存中,然后,调用 netif->input 接口即 ethernet_input() 对数据包进行处理:
// Libraries/Ethernet/lwip/port/src/netif.c
err_t ifx_netif_input(netif_t *netif)
{
eth_hdr_t *ethhdr;
pbuf_t *p;
// ------------------------------------------------
// 接收数据包,将其存入 pbuf 中
/* move received packet into a new pbuf */
p = low_level_input(netif);
// ------------------------------------------------
// ------------------------------------------------
// 若 pbuf 为空说明没有数据需要接收,直接返回
/* no packet could be read, silently ignore this */
if (p == NULL)
{
return ERR_OK;
}
// ------------------------------------------------
// ------------------------------------------------
// 指向该数据包的 payload
/* points to packet payload, which starts with an Ethernet header */
ethhdr = p->payload;
// ------------------------------------------------
// 解析数据包的类型
switch (htons(ethhdr->type))
{
/* IP or ARP packet? */
case ETHTYPE_IP:
case ETHTYPE_ARP:
...
// ------------------------------------------------
// 调用 input 回调接收数据包:实际调用 ethernet_input()
/* full packet send to tcpip_thread to process */
if (netif->input(p, netif) != ERR_OK)
// ------------------------------------------------
{
LWIP_DEBUGF(NETIF_DEBUG, ("ifx_netif_input: IP input error\n"));
pbuf_free(p); // 释放 pbuf
p = NULL;
}
break;
default:
LWIP_DEBUGF(NETIF_DEBUG, ("ifx_netif_input: type unknown\n"));
pbuf_free(p);
p = NULL;
break;
}
return ERR_OK;
}
2.硬件接收 - low_level_input()
low_level_input() 使用 pbuf_alloc() 分配新的 pbuf,并通过 IfxGeth_Eth_getReceiveBuffer() 将报文存入其中:
// Libraries/Ethernet/lwip/port/src/netif.c
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static pbuf_t *low_level_input(netif_t *netif)
{
...
// ---------------------------------------------------------
// 从内存池中分配 pbuf
/* We allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
// ---------------------------------------------------------
...
if (p != NULL)
{
...
// ---------------------------------------------------------
// 通过 DMA 从将报文存入 Rx 的软件 Buffer
u8_t *src = IfxGeth_Eth_getReceiveBuffer(ethernetif, IfxGeth_RxDmaChannel_0);
// ---------------------------------------------------------
/* We iterate over the pbuf chain until we have read the entire
* packet into the pbuf. */
for (q = p; q != NULL; q = q->next)
{
/* Read enough bytes to fill this pbuf in the chain. The
* available data in the pbuf is given by the q->len variable.
* This does not necessarily have to be a memcpy, you can also preallocate
* pbufs for a DMA-enabled MAC and after receiving truncate it to the
* actually received size. In this case, ensure the tot_len member of the
* pbuf is the sum of the chained pbuf len members.
*/
// ---------------------------------------------------------
//read data into(q->payload, q->len);
// 将报文从 Rx 的软件 Buffer 中拷贝至 pbuf
memcpy(q->payload, src, q->len);
// ---------------------------------------------------------
src = &src[q->len];
...
}
2.1.获取 GETH 寄存器组
网卡初始化时,将 GETH 模块与 netif->state 成员绑定,因此,通过该成员可直接获取到 GETH 模块:
err_t ifx_netif_init(netif_t *netif)
{
IfxGeth_Eth *ethernetif;
...
// 获取全局 GETH 指针
ethernetif = IfxGeth_get();
|--> return &g_IfxGeth;
...
// 该成员用于,将底层硬件的相关信息,传递给上层
netif->state = ethernetif;
---------------------------------------------------------------
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
IfxGeth_Eth *ethernetif = netif->state;
2.2.检查是否可以操作 Rx 的描述符
检查 Rx DMA 通道 0 描述符的 RDES3.OWN,OWN == 1 表示该描述符的所有权归 DMA,OWN == 0 表示该描述符的所有权归软件,即 OWN 为 1 表示该描述符被 DMA 占用,此时软件无法操作该描述符:
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
// 检查软件是否可以操作 Rx DMA 通道 0 的描述符
if (IfxGeth_Eth_isRxDataAvailable(ethernetif, IfxGeth_RxDmaChannel_0) != FALSE) // IfxGeth_RxDmaChannel_0 == 0
...
-----------------------------------------------------------------------------------
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.h
IFX_INLINE boolean IfxGeth_Eth_isRxDataAvailable(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId) // channelId == 0
{
return IfxGeth_Eth_getActualRxDescriptor(geth, channelId)->RDES3.R.OWN == 0;
|--> return geth->rxChannel[channelId].rxDescrPtr; // geth->rxChannel[0].rxDescrPtr
}
DMA 完成报文接收或该描述符关联的软件 Buffer 已满时,会将该位置 0
2.3.获取待接收报文的长度
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
IfxGeth_Eth *ethernetif = netif->state;
...
u16_t len;
len = 0;
// 检查软件是否可以操作 Rx DMA 通道 0 的描述符
if (IfxGeth_Eth_isRxDataAvailable(ethernetif, IfxGeth_RxDmaChannel_0) != FALSE)
{ // 如果软件可以操作描述符,则获取待接收报文的长度
len = GetRxFrameSize((IfxGeth_RxDescr *)IfxGeth_Eth_getActualRxDescriptor(ethernetif, IfxGeth_RxDmaChannel_0));
}
// 如果待接收的报文长度为 0 则直接返回
if (len == 0)
{
return (pbuf_t *)0;
}
通过 IfxGeth_Eth_getActualRxDescriptor() 获取描述符,Rx DMA 共包含 4 条通道,这里传入的参数为 IfxGeth_RxDmaChannel_0,即获取 Rx DMA 通道 0 的描述符:
// Libraries/iLLD/TC39B/Tricore/Geth/Std/IfxGeth.h
/* Rx DMA channel */
typedef enum
{
IfxGeth_RxDmaChannel_0, /* Rx Dma Channel 0 */
IfxGeth_RxDmaChannel_1, /* Rx Dma Channel 1 */
IfxGeth_RxDmaChannel_2, /* Rx Dma Channel 2 */
IfxGeth_RxDmaChannel_3 /* Rx Dma Channel 3 */
} IfxGeth_RxDmaChannel;
------------------------------------------------------------
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.h
IFX_INLINE volatile IfxGeth_RxDescr *IfxGeth_Eth_getActualRxDescriptor(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
return geth->rxChannel[channelId].rxDescrPtr; // geth->rxChannel[0].rxDescrPtr
}
Rx 描述符包含 RDES0 ~ RDES3 共 4 个成员,Write-Back 格式中 RDES3 的 [14:0] 为报文长度:
GetRxFrameSize() 获取数据包的长度,其中会检查是否有错误产生:
-
rdes3 & (1UL << 15):检查 RDES3.ES 位,该位为 "1" 表示有相关错误产生,这里不能为 "1";
-
rdes1 & (1UL << 7):检查 RDES1.IPCE 位,该位为 "1" 表示产生了 IP 地址相关的错误,这里不能为 "1";
-
rdes3 & (1UL << 28):检查 RDES3.LD 位,该位为 "1" 标记该描述符指向的是报文的最后一个数据包;
RDES3 的低 15 位为数据包的长度 RDES3.PL [14:0],CRC 校验位占 4 个字节,减去该长度从而获取实际的数据长度:
// Libraries/iLLD/TC39B/Tricore/Geth/Std/IfxGeth.h
/* Rx Descriptor */
typedef struct
{
IfxGeth_RxDescr0 RDES0; /* Rx Descriptor DWORD 0 */
IfxGeth_RxDescr1 RDES1; /* Rx Descriptor DWORD 1 */
IfxGeth_RxDescr2 RDES2; /* Rx Descriptor DWORD 2 */
IfxGeth_RxDescr3 RDES3; /* Rx Descriptor DWORD 3 */
} IfxGeth_RxDescr;
--------------------------------------------------------------
// Libraries/Ethernet/lwip/port/src/netif.c
static uint16 GetRxFrameSize(IfxGeth_RxDescr *descr)
{
uint16 len;
uint32 rdes3 = descr->RDES3.U;
uint32 rdes1 = descr->RDES1.U;
...
else
{
/* Subtract CRC */
len = (rdes3 & 0x7FFF) - 4U; // 获取报文长度(减去 CRC 校验位)
}
return len;
}
示例中,为了保证 4 字节对齐,在报文前面加上了 2 字节的空数据,因此实际长度需要加上该值:
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
#if ETH_PAD_SIZE
len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
2.4.分配 pbuf
-
pbuf 的标签为 PBUF_RAW,表示该 pbuf 用于接收数据;
-
pbuf 的类型为 PBUF_POOL,表示该 pbuf 从内存池中分配,payload 为 RAM 的引用;
-
pbuf 的长度为 len;
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
/* We allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
2.5.读取报文
读取报文前,通过 pbuf_header() 去除用于保证对齐的 2 字节空间,然后调用 IfxGeth_Eth_getReceiveBuffer() 将报文读取至 Rx 的软件 Buffer 中,之后将报文拷贝至新建的 pbuf 中,最后调用 pbuf_header() 恢复报文头长度:
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
if (p != NULL)
{
#if ETH_PAD_SIZE // 去除 2 字节的空间
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
// 通过 Rx DMA 通道 0 读取报文
u8_t *src = IfxGeth_Eth_getReceiveBuffer(ethernetif, IfxGeth_RxDmaChannel_0);
...
#if ETH_PAD_SIZE // 恢复 2 字节的空间
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
2.5.1.获取软件 Buffer 的地址
IfxGeth_Eth_getReceiveBuffer() 通知 DMA 将报文读取至 Rx 的软件 Buffer 中:
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.c
void *IfxGeth_Eth_getReceiveBuffer(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
void *result = 0;
volatile IfxGeth_RxDescr *descr;
// 判断软件是否可以操作描述符
if (IfxGeth_Eth_isRxDataAvailable(geth, channelId))
{ // 获取软件 Buffer 的地址
geth->rxChannel[channelId].rxCount++;
descr = IfxGeth_Eth_getActualRxDescriptor(geth, channelId);
|--> return geth->rxChannel[channelId].rxDescrPtr;
result = (void *)(descr->RDES0.U);
}
// 唤醒 Rx 的 DMA 接收报文
IfxGeth_Eth_wakeupReceiver(geth, channelId);
return result;
}
low_level_init() 初始化 GETH 时,将软件 Buffer 的地址保存在 Rx 描述符的 RDES0 中:
// Libraries/Ethernet/lwip/port/include/Ifx_Lwip.h
IFX_EXTERN uint8 channel0RxBuffer1[IFXGETH_MAX_RX_DESCRIPTORS][IFXGETH_MAX_RX_BUFFER_SIZE];
----------------------------------------------------------------------------------
// Libraries/Ethernet/lwip/port/src/Ifx_Lwip.c
uint8 channel0RxBuffer1[IFXGETH_MAX_RX_DESCRIPTORS][IFXGETH_MAX_RX_BUFFER_SIZE];
----------------------------------------------------------------------------------
// Libraries/Ethernet/lwip/port/src/netif.c
static void low_level_init(netif_t *netif)
{
...
// DMA 设置
...
// Rx DMA 通道索引:0
GethConfig.dma.rxChannel[0].channelId = IfxGeth_RxDmaChannel_0;
// Rx 描述符链表
GethConfig.dma.rxChannel[0].rxDescrList = (IfxGeth_RxDescrList *)&IfxGeth_Eth_rxDescrList[0];
// ---------------------------------------------------------
// Rx DMA 通道 0 的软件 Buffer
GethConfig.dma.rxChannel[0].rxBuffer1StartAddress = (uint32 *)&channel0RxBuffer1[0][0]; // user buffer
// ---------------------------------------------------------
// Rx 软件 Buffer 的大小:2560 + 14 + 2
GethConfig.dma.rxChannel[0].rxBuffer1Size = IFXGETH_MAX_RX_BUFFER_SIZE; // user defined variable
----------------------------------------------------------------------------------
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.c
void IfxGeth_Eth_initReceiveDescriptors(IfxGeth_Eth *geth, IfxGeth_Eth_RxChannelConfig *config)
{
...
uint32 buffer1StartAddress = (uint32)config->rxBuffer1StartAddress;
...
/* Initialize descriptors in ring mode */
for (i = 0; i < IFXGETH_MAX_RX_DESCRIPTORS; i++)
{
descr->RDES0.U = (uint32)(config->rxBuffer1Size * i) + buffer1StartAddress;
descr->RDES2.U = 0; /* buffer2 not used */
...
2.5.2.唤醒 DMA
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.c
void *IfxGeth_Eth_getReceiveBuffer(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
void *result = 0;
volatile IfxGeth_RxDescr *descr;
// 判断软件是否可以操作描述符
if (IfxGeth_Eth_isRxDataAvailable(geth, channelId))
{ // 获取软件 Buffer 的地址
geth->rxChannel[channelId].rxCount++;
descr = IfxGeth_Eth_getActualRxDescriptor(geth, channelId);
|--> return geth->rxChannel[channelId].rxDescrPtr;
result = (void *)(descr->RDES0.U);
}
// 唤醒 Rx 的 DMA 接收报文
IfxGeth_Eth_wakeupReceiver(geth, channelId);
return result;
}
IfxGeth_Eth_wakeupReceiver() 检查 DMA 是否空闲,若 DMA 空闲则将其唤醒并接收报文:
-
读取 DMA_CHi_STATUS.RPS(Receive Process Stopped),判断当前 DMA 是否空闲(为 "1");
-
如果 DMA 空闲,则读取 DMA_CHi_STATUS.RBU(Receive Buffer Unavailable),判断当前 DMA 是否拥有下一个描述符的控制权,如果不是则调用 IfxGeth_dma_clearInterruptFlag() 向该位写 "1",获得控制权;
-
获得控制权后,设置 MAC_CONFIGURATION.RE(Receiver Enable)为 "1",使能 DMA 接收;
-
最后,设置 DMA_CHi_RX_CONTROL.SR(Start or Stop Receive)为 "1",开始接收报文;
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.c
void IfxGeth_Eth_wakeupReceiver(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
/* check if receiver suspended */
if (IfxGeth_dma_isInterruptFlagSet(geth->gethSFR, (IfxGeth_DmaChannel)channelId, IfxGeth_DmaInterruptFlag_receiveStopped))
|--> uint32 value = (1 << flag);
|--> return gethSFR->DMA_CH[channelId].STATUS.U & (Ifx_UReg_32Bit)value;
{
/* check if receive buffer unavailable */
if (IfxGeth_dma_isInterruptFlagSet(geth->gethSFR, (IfxGeth_DmaChannel)channelId, IfxGeth_DmaInterruptFlag_receiveBufferUnavailable))
{
IfxGeth_dma_clearInterruptFlag(geth->gethSFR, (IfxGeth_DmaChannel)channelId, IfxGeth_DmaInterruptFlag_receiveBufferUnavailable);
|--> uint32 value = (1 << flag);
|--> gethSFR->DMA_CH[channelId].STATUS.U = value;
}
// 唤醒 DMA 接收报文
IfxGeth_Eth_startReceiver(geth, channelId);
|--> IfxGeth_mac_enableReceiver(geth->gethSFR);
|--> gethSFR->MAC_CONFIGURATION.B.RE = 1;
|--> IfxGeth_dma_startReceiver(geth->gethSFR, channelId);
|--> gethSFR->DMA_CH[channel].RX_CONTROL.B.SR = TRUE;
}
}
2.6.将报文存入 pbuf
通过 DMA 将报文读取至软件 Buffer 后,将其存入新建的 pbuf 中:
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
/* We iterate over the pbuf chain until we have read the entire
* packet into the pbuf. */
for (q = p; q != NULL; q = q->next)
{
/* Read enough bytes to fill this pbuf in the chain. The
* available data in the pbuf is given by the q->len
* variable.
* This does not necessarily have to be a memcpy, you can also preallocate
* pbufs for a DMA-enabled MAC and after receiving truncate it to the
* actually received size. In this case, ensure the tot_len member of the
* pbuf is the sum of the chained pbuf len members.
*/
// --------------------------------------------------
//read data into(q->payload, q->len);
memcpy(q->payload, src, q->len);
// --------------------------------------------------
src = &src[q->len];
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE, ("low_level_input: payload=0x%x, len=%d\n", q->payload, q->len));
}
2.7.通知报文接收完成
报文接收完成后,调用 IfxGeth_Eth_freeReceiveBuffer() 更新 Rx 的描述符:
// Libraries/Ethernet/lwip/port/src/netif.c
static pbuf_t *low_level_input(netif_t *netif)
{
...
//acknowledge that packet has been read();
IfxGeth_Eth_freeReceiveBuffer(ethernetif, IfxGeth_RxDmaChannel_0);
---------------------------------------------------------------------------
// Libraries/Ethernet/lwip/port/src/netif.c
void IfxGeth_Eth_freeReceiveBuffer(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
volatile IfxGeth_RxDescr *descr = IfxGeth_Eth_getActualRxDescriptor(geth, channelId);
IfxGeth_RxDescr3 rdes3;
rdes3.U = 0;
rdes3.R.BUF1V = 1; /* buffer 1 valid */
rdes3.R.BUF2V = 0; /* buffer 2 not valid */
rdes3.R.IOC = 1; /* interrupt enabled */
rdes3.R.OWN = 1; /* owned by DMA */
descr->RDES3.U = rdes3.U;
// 刷新 Rx 描述符
IfxGeth_Eth_shuffleRxDescriptor(geth, channelId);
}
---------------------------------------------------------------------------
// Libraries/iLLD/TC39B/Tricore/Geth/Eth/IfxGeth_Eth.c
void IfxGeth_Eth_shuffleRxDescriptor(IfxGeth_Eth *geth, IfxGeth_RxDmaChannel channelId)
{
volatile IfxGeth_RxDescr *currentDescr = geth->rxChannel[channelId].rxDescrPtr;
volatile IfxGeth_RxDescr *lastDescr = &geth->rxChannel[channelId].rxDescrList->descr[IFXGETH_MAX_RX_DESCRIPTORS - 1];
if (currentDescr == lastDescr)
{
/* wrap around the descriptors */
geth->rxChannel[channelId].rxDescrPtr = IfxGeth_Eth_getBaseRxDescriptor(geth, channelId);
}
else
{
/* point to the next descriptor */
geth->rxChannel[channelId].rxDescrPtr = &geth->rxChannel[channelId].rxDescrPtr[1];
}
}
3.报文处理 - ethernet_input()
DMA 将报文存入 pbuf 后,调用 netif->input() 对报文进行处理:
// Libraries/Ethernet/lwip/port/src/netif.c
err_t ifx_netif_input(netif_t *netif)
{
...
// --------------------------------------------------
// 接收数据包,将其存入 pbuf 中
/* move received packet into a new pbuf */
p = low_level_input(netif);
// --------------------------------------------------
...
// 解析数据包的类型
switch (htons(ethhdr->type))
{
/* IP or ARP packet? */
case ETHTYPE_IP:
case ETHTYPE_ARP:
...
// --------------------------------------------------
// 调用 input 回调接收数据包:实际调用 ethernet_input()
/* full packet send to tcpip_thread to process */
if (netif->input(p, netif) != ERR_OK)
// --------------------------------------------------
ethernet_input() 处理经由硬件接收到的报文,准备进行后续的处理(如 IP 层处理、ARP 处理等),如果报文无效或不支持,则执行错误统计和丢弃操作:
// Libraries/Ethernet/lwip/src/netif/ethernet.c
err_t ethernet_input(struct pbuf *p, struct netif *netif)
{
// 报文头指针
struct eth_hdr *ethhdr;
// 报文类型
u16_t type;
// 指向下一个报文头:SIZEOF_ETH_HDR = 14 + 2
u16_t next_hdr_offset = SIZEOF_ETH_HDR;
...
// --------------------------------------------------
// 如果 pbuf 长度小于等于 SIZEOF_ETH_HDR 则说明其只含有报文头,没有数据
if (p->len <= SIZEOF_ETH_HDR) {
/* a packet with only an ethernet header (or less) is not valid for us */
ETHARP_STATS_INC(etharp.proterr); // ARP 协议错误次数 + 1
ETHARP_STATS_INC(etharp.drop); // ARP 丢弃的数据包 + 1
MIB2_STATS_NETIF_INC(netif, ifinerrors); // MIB 错误次数 + 1
goto free_and_return; // 释放该 pbuf 并直接返回
}
// --------------------------------------------------
// --------------------------------------------------
// 设置网卡索引
if (p->if_idx == NETIF_NO_INDEX) {
p->if_idx = netif_get_index(netif);
|--> (u8_t)((netif)->num + 1)
}
// --------------------------------------------------
// --------------------------------------------------
// 获取 payload 指针,并将其转换为以太网报文头指针
/* points to packet payload, which starts with an Ethernet header */
ethhdr = (struct eth_hdr *)p->payload;
// --------------------------------------------------
// --------------------------------------------------
// 调试信息打印:目的 MAC 地址、源 MAC 地址、数据包类型
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
(unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2],
(unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5],
(unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2],
(unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5],
lwip_htons(ethhdr->type)));
// --------------------------------------------------
// --------------------------------------------------
// 获取报文类型
type = ethhdr->type;
// --------------------------------------------------
...
// --------------------------------------------------
// 通过报文头判断是否为广播或组播
if (ethhdr->dest.addr[0] & 1) {
// 如果该报文为组播,则设置组播标志
/* this might be a multicast or broadcast packet */
if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
(ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) {
/* mark the pbuf as link-layer multicast */
p->flags |= PBUF_FLAG_LLMCAST;
}
}
...
// 如果该报文为广播则设置广播标志
else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) {
/* mark the pbuf as link-layer broadcast */
p->flags |= PBUF_FLAG_LLBCAST;
}
}
// --------------------------------------------------
// --------------------------------------------------
// 根据报文类型进行对应的处理
switch (type) {
// 如果报文为 IP 报文,则调用 ip4_input() 进行处理
/* IP packet? */
case PP_HTONS(ETHTYPE_IP):
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}// 检查报文头长度是否有效
/* skip Ethernet header (min. size checked above) */
if (pbuf_remove_header(p, next_hdr_offset)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n",
p->tot_len, next_hdr_offset));
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
goto free_and_return;
} else {
/* pass to IP layer */
ip4_input(p, netif);
}
break;
// --------------------------------------------------
// --------------------------------------------------
// 如果报文为 ARP 报文,则调用 etharp_input() 进行处理
case PP_HTONS(ETHTYPE_ARP):
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}// 检查报文头是否有效
/* skip Ethernet header (min. size checked above) */
if (pbuf_remove_header(p, next_hdr_offset)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n",
p->tot_len, next_hdr_offset));
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
ETHARP_STATS_INC(etharp.lenerr);
ETHARP_STATS_INC(etharp.drop);
goto free_and_return;
} else {
/* pass p to ARP module */
etharp_input(p, netif);
}
break;
// --------------------------------------------------
...
// --------------------------------------------------
default:
...
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
goto free_and_return;
// --------------------------------------------------
}
/* This means the pbuf is freed or consumed,
so the caller doesn't have to free it again */
return ERR_OK;
// --------------------------------------------------
// 错误报文处理:释放 pbuf 并直接返回
free_and_return:
pbuf_free(p);
return ERR_OK;
// --------------------------------------------------
}
该例程中,只会处理 IP 和 ARP 两种类型的报文,其分别调用 ip4_input() 和 etharp_input() 进一步处理