[TriCore][官方例程][TC397以太网例程详解] - 18.ECHO 应用 - 接收报文

关键词

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以太网例程 - 1.概览

TC397以太网例程 - 2.STM 定时器初始化

TC397以太网例程 - 3.LwIP 配置及初始化

TC397以太网例程 - 4.ASCLIN 串口配置

TC397以太网例程 - 5.IP 地址声明

TC397以太网例程 - 6.内存 & 协议等初始化

TC397以太网例程 - 7.netif 网卡配置

TC397以太网例程 - 8.硬件初始化

TC397以太网例程 - 9.基本设置

TC397以太网例程 - 10.默认/用户配置

TC397以太网例程 - 11.RTL8211F 复位

TC397以太网例程 - 12.GETH 初始化

TC397以太网例程 - 13.PHY 初始化

TC397以太网例程 - 14.Tx/Rx 使能

TC397以太网例程 - 15.其他配置

TC397以太网例程 - 16.ECHO 应用 - 初始化

TC397以太网例程 - 17.ECHO 应用 - 轮询定时器

TC397以太网例程 - 18.ECHO 应用 - 接收报文


目录

1.流程概览

2.硬件接收 - low_level_input()

2.1.获取 GETH 寄存器组

2.2.检查是否可以操作 Rx 的描述符

2.3.获取待接收报文的长度

2.4.分配 pbuf

2.5.读取报文

2.5.1.获取软件 Buffer 的地址

2.5.2.唤醒 DMA

2.6.将报文存入 pbuf

2.7.通知报文接收完成

3.报文处理 - ethernet_input()


所有模块的初始化流程结束后,在主函数的循环中运行 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(&ethhdr->dest, &ethbroadcast)) {
      /* 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() 进一步处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值