cancel
Showing results for 
Search instead for 
Did you mean: 

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

PHolt.1
Senior III

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);
 
}

1 REPLY 1
PHolt.1
Senior III

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.