Skip to main content
Graduate
January 26, 2023
Question

32F417: mod for low_level_input to discard broadcast packets before LWIP

  • January 26, 2023
  • 1 reply
  • 754 views

This should be possible, in the code which moves data from the ETH controller's buffers to LWIP's buffers.

I am aware of the BFD bit of ETH_MACFFR solution, which works, but it makes the box useless unless it is addressed by MAC# directly. The broadcasts do need to be processed by the CPU at least for switches etc to work. I just want to not pass them to LWIP.

The source for my current code is below and I will offer $100 to anyone who delivers a working solution : - )

I don't really understand this code in detail; it is standard stuff from ST Cube MX and ST ETH-LWIP interfacing documents, with various patches from this forum and elsewhere applied. It uses polling instead of interrupts, partly because I never found properly a working interrupt version and partly because this approach auto protects from a too-rapid rx traffic.

Thank you in advance.

/**
 * @brief 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 struct pbuf * low_level_input(struct netif *netif)
{
 struct pbuf *p = NULL, *q = NULL;
 uint16_t len = 0;
 uint8_t *buffer;
 __IO ETH_DMADescTypeDef *dmarxdesc;
 uint32_t bufferoffset = 0;
 uint32_t payloadoffset = 0;
 uint32_t byteslefttocopy = 0;
 uint32_t i=0;
 
 /* get received frame */
 
 HAL_StatusTypeDef status = IF_HAL_ETH_GetReceivedFrame(&EthHandle);
 
 if (status != HAL_OK)
 {
	 return NULL;		// Return if no RX data
 }
 else
 {
	 rxactive=true;	// set "seen rx data" flag
 }
 
 /* Obtain the size of the packet and put it into the "len" variable. */
 len = EthHandle.RxFrameInfos.length;
 buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer;
 
 if (len > 0)
 {
 /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
 p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
 }
 
 if (p != NULL)
 {
 dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
 bufferoffset = 0;
 
 for(q = p; q != NULL; q = q->next)
 {
 byteslefttocopy = q->len;
 payloadoffset = 0;
 
 /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */
 while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
 {
 /* Copy data to pbuf */
 memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
 
 /* Point to next descriptor */
 dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
 buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);
 
 byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
 payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
 bufferoffset = 0;
 }
 
 /* Copy remaining data in pbuf */
 memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
 bufferoffset = bufferoffset + byteslefttocopy;
 }
 }
 
 /* Release descriptors to DMA */
 /* Point to first descriptor */
 dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
 /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
 for (i=0; i< EthHandle.RxFrameInfos.SegCount; i++)
 { 
	//__DMB(); - fossil code for the 32F417, apparently.
 dmarxdesc->Status |= ETH_DMARXDESC_OWN;
 dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
 }
 
 /* Clear Segment_Count */
 EthHandle.RxFrameInfos.SegCount =0;
 
 /* When Rx Buffer unavailable flag is set: clear it and resume reception */
 if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) 
 {
 /* Clear RBUS ETHERNET DMA flag */
 EthHandle.Instance->DMASR = ETH_DMASR_RBUS;
 /* Resume DMA reception */
 EthHandle.Instance->DMARPDR = 0;
 }
 return p;
}
 
 
// Timeout for ethernetif_input "activity" status which drops fast poll to slow poll.
// FreeRTOS timer needs to have a callback, even if this function is empty.
// The other way to check timer status is
// if (xTimerIsTimerActive(logout_timer) == pdTRUE) - gives True if not expired
 
static void RXactiveTimerCallback( TimerHandle_t rxactive_timer )
{
	rx_poll_period=ETH_RX_SLOW_POLL_INTERVAL;
}
 
 
/**
 * This function is the ethernetif_input task. It uses the function low_level_input()
 * that handles the actual reception of bytes from the network interface.
 *
 * This is a standalone RTOS task so is a forever loop.
 * Could be done with interrupts but then we have the risk of hanging the unit with fast input
 * (unlikely if ETH is via a switch, but you could have a faulty LAN with lots of
 * broadcasts).
 *
 */
 
void ethernetif_input( void * argument )
{
 
	struct pbuf *p;
	struct netif *netif = (struct netif *) argument;
 
	// Define RX activity timer, for dropping fast poll down to slow poll
	TimerHandle_t *rxactive_timer = xTimerCreate("ETH RX active timer", pdMS_TO_TICKS(ETH_SLOW_POLL_DELAY), pdFALSE, NULL, RXactiveTimerCallback);
 
	// Start "rx active" timer
	xTimerStart(rxactive_timer, 20);	// 20 is just a wait time for timer allocation
 
	do
 {
		p = low_level_input( netif );	// This sets rxactive=true if it sees data
 
		if (p!=NULL)
		{
			if (netif->input( p, netif) != ERR_OK )
			{
				pbuf_free(p);
			}
		}
 
		if (rxactive)
		{
			rxactive=false;
			// Seen rx data - reload timeout
			xTimerReset(rxactive_timer, 20);	// Reload "rx active" timeout (with ETH_SLOW_POLL_DELAY)
			// and get osDelay below to run fast
			rx_poll_period=ETH_RX_FAST_POLL_INTERVAL;
		}
 
		// This has a dramatic effect on ETH speed, both ways (TCP/IP acks all packets)
		osDelay(rx_poll_period);
 
 } while(true);
 
}

    This topic has been closed for replies.

    1 reply

    PHolt.1Author
    Graduate
    January 29, 2023

    Again, a post with more than 10 lines of source rarely gets a reply : - ) but it has been solved here

    https://www.eevblog.com/forum/microcontrollers/anyone-here-familiar-with-lwip/

    There does seem to be a strange issue with the way LWIP behaves if ETH PHY has just 2 RX buffers allocated, and a broadcast packet arrives into one while a normal data packet is in the other. You get a "lockout" of a few seconds. It is some kind of a timeout but a lot longer than one would expect for a missing packet.

    The solution is to have more RX buffers; I found 3 fixes it so I am using 4. That happens to be the value originally used by Cube MX. Perhaps somebody found this, or just got lucky.