2023-01-26 10:03 AM
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);
}
2023-01-28 11:41 PM
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.