ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
与low\_level\_output()函数相反的是low\_level\_input()函数,该函数用于从网卡中接收一个数据包,并将数据包封装在pbuf中递交给上层。low\_level\_input()函数的编写也需要用户熟悉pbuf与网卡底层驱动,该函数的实现具体见 ``` 1 static struct pbuf * low_level_input(struct netif *netif) 2 { 3 struct pbuf *p = NULL; 4 struct pbuf *q = NULL; 5 uint16_t len = 0; 6 uint8_t *buffer; 7 __IO ETH_DMADescTypeDef *dmarxdesc; 8 uint32_t bufferoffset = 0; 9 uint32_t payloadoffset = 0; 10 uint32_t byteslefttocopy = 0; 11 uint32_t i=0; 12 13 14 /* get received frame */ 15 if (HAL_ETH_GetReceivedFrame(&heth) != HAL_OK) (1) 16 { 17 PRINT_ERR("receive frame faild\n"); 18 return NULL; 19 } 20 /*Obtain the size of the packet and put it into the "len" variable. */ 21 len = heth.RxFrameInfos.length; 22 buffer = (uint8_t *)heth.RxFrameInfos.buffer; (2) 23 24 PRINT_INFO("receive frame len : %d\n", len); 25 26 if (len > 0) 27 { 28 /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ 29 p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); (3) 30 } 31 32 if (p != NULL) 33 { 34 dmarxdesc = heth.RxFrameInfos.FSRxDesc; 35 bufferoffset = 0; 36 for (q = p; q != NULL; q = q->next) (4) 37 { 38 byteslefttocopy = q->len; 39 payloadoffset = 0; 40 41 /* Check if the length of bytes to copy in 42 current pbuf is bigger than Rx buffer size*/ 43 while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE) (5) 44 { 45 /* Copy data to pbuf */ 46 memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), 47 (uint8_t*)((uint8_t*)buffer + bufferoffset), 48 (ETH_RX_BUF_SIZE - bufferoffset)); (6) 49 50 /* Point to next descriptor */ 51 dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); 52 buffer = (uint8_t *)(dmarxdesc->Buffer1Addr); (7) 53 54 byteslefttocopy = byteslefttocopy -(ETH_RX_BUF_SIZE - bufferoffset); 55 payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); 56 bufferoffset = 0; (8) 57 } 58 /* Copy remaining data in pbuf */ 59 memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), 60 (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy); 61 bufferoffset = bufferoffset + byteslefttocopy; (9) 62 } 63 } 64 65 /* Release descriptors to DMA */ 66 /* Point to first descriptor */ 67 dmarxdesc = heth.RxFrameInfos.FSRxDesc; (10) 68 /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ 69 for (i=0; i< heth.RxFrameInfos.SegCount; i++) 70 { 71 dmarxdesc->Status |= ETH_DMARXDESC_OWN; (11) 72 dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); 73 } 74 75 /* Clear Segment_Count */ 76 heth.RxFrameInfos.SegCount =0; (12) 77 78 /* When Rx Buffer unavailable flag is set: clear it and resume reception */ 79 if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) 80 { 81 /* Clear RBUS ETHERNET DMA flag */ 82 heth.Instance->DMASR = ETH_DMASR_RBUS; (13) 83 /* Resume DMA reception */ 84 heth.Instance->DMARPDR = 0; 85 } 86 return p; (14) 87 } ``` (1):看看是否接收到数据,如果没有直接返回NULL,如果有则将数据获取到heth数据结构中。 (2):获取接收到数据包大小并将其放入len变量中,以及获取数据区域存放在buffer指针中。 (3):调用pbuf_alloc()函数从LwIP的内存池中分配pbuf。 (4):如果pbuf分配成功,则将内存拷贝到pbuf中,因为pbuf可能不止一个,就需要遍历pbuf链表将所有数据都存储进来。 (5):检查一下要拷贝的数据大小于接收缓冲区ETH_RX_BUF_SIZE的大小,如果要拷贝的数据大于ETH_RX_BUF_SIZE的大小,那么就要分几次拷贝。 (6):将接收到的数据拷贝到pbuf中payload指向的数据区域。 (7):指向下一个描述符,得到未拷贝的数据区域地址buffer。 (8):重新计算还需要拷贝的数据大小byteslefttocopy,pbuf中数据的偏移量payloadoffset,重置接收缓冲区偏移量bufferoffset,然后重复进行拷贝,直到要拷贝的数据小于ETH_RX_BUF_SIZE的值,才退出while循环。 (9):将剩余的数据拷贝到pbuf中。 (10):dmarxdesc变量指向第一个接收描述符。 (11):进行遍历描述符列表,将描述符中Status状态设置为ETH_DMARXDESC_OWN。 (12):清除描述符中SegCount变量的值。 (13):清除DMA标志。 (14):重新恢复DMA接收。