static void prvGMACDeferredInterruptHandlerTask( void *pvParameters ) { xNetworkBufferDescriptor_t *pxNetworkBuffer = NULL; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; static const TickType_t xBufferWaitDelay = 1500UL / portTICK_RATE_MS; uint32_t ulReturned; /* This is a very simply but also inefficient implementation. */ ( void ) pvParameters; for( ;; ) { /* Wait for the GMAC interrupt to indicate that another packet has been received. A while loop is used to process all received frames each time this task is notified, so it is ok to clear the notification count on the take (hence the first parameter is pdTRUE ). */ ulTaskNotifyTake( pdTRUE, xBufferWaitDelay ); ulReturned = GMAC_OK; while( ulReturned == GMAC_OK ) { /* Allocate a buffer to hold the data if one is not already held. */ if( pxNetworkBuffer == NULL ) { pxNetworkBuffer = pxNetworkBufferGet( ipTOTAL_ETHERNET_FRAME_SIZE, xBufferWaitDelay ); } if( pxNetworkBuffer != NULL ) { /* Attempt to read data. */ ulReturned = gmac_dev_read( &xGMACStruct, pxNetworkBuffer->pucEthernetBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, ( uint32_t * ) &( pxNetworkBuffer->xDataLength ) ); if( ulReturned == GMAC_OK ) { #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 1 { if( pxNetworkBuffer->xDataLength > 0 ) { /* If the frame would not be processed by the IP stack then don't even bother sending it to the IP stack. */ if( eConsiderFrameForProcessing( pxNetworkBuffer->pucEthernetBuffer ) != eProcessBuffer ) { pxNetworkBuffer->xDataLength = 0; } } } #endif if( pxNetworkBuffer->xDataLength > 0 ) { /* Store a pointer to the network buffer structure in the padding space that was left in front of the Ethernet frame. The pointer is needed to ensure the network buffer structure can be located when it is time for it to be freed if the Ethernet frame gets used as a zero copy buffer. */ *( ( xNetworkBufferDescriptor_t ** ) ( ( pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING ) ) ) = pxNetworkBuffer; /* Data was received and stored. Send it to the IP task for processing. */ xRxEvent.pvData = ( void * ) pxNetworkBuffer; if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( TickType_t ) 0 ) == pdFALSE ) { /* The buffer could not be sent to the IP task. The frame will be dropped and the buffer reused. */ iptraceETHERNET_RX_EVENT_LOST(); } else { iptraceNETWORK_INTERFACE_RECEIVE(); /* The buffer is not owned by the IP task - a new buffer is needed the next time around. */ pxNetworkBuffer = NULL; } } else { /* The buffer does not contain any data so there is no point sending it to the IP task. Re-use the buffer on the next loop. */ iptraceETHERNET_RX_EVENT_LOST(); } } else { /* No data was received, keep the buffer for re-use. The loop will exit as ulReturn is not GMAC_OK. */ } } else { /* Left a frame in the driver as a buffer was not available. Break out of loop. */ ulReturned = GMAC_INVALID; } } } }
static void prvEMACDeferredInterruptHandlerTask( void *pvParameters ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; ( void ) pvParameters; configASSERT( xEMACRxEventSemaphore ); for( ;; ) { /* Wait for the EMAC interrupt to indicate that another packet has been received. The while() loop is only needed if INCLUDE_vTaskSuspend is set to 0 in FreeRTOSConfig.h. If INCLUDE_vTaskSuspend is set to 1 then portMAX_DELAY would be an indefinite block time and xSemaphoreTake() would only return when the semaphore was actually obtained. */ while( xSemaphoreTake( xEMACRxEventSemaphore, portMAX_DELAY ) == pdFALSE ); /* At least one packet has been received. */ while( EMAC_CheckReceiveIndex() != FALSE ) { /* The buffer filled by the DMA is going to be passed into the IP stack. Allocate another buffer for the DMA descriptor. */ pxNetworkBuffer = pxNetworkBufferGet( ipTOTAL_ETHERNET_FRAME_SIZE, ( TickType_t ) 0 ); if( pxNetworkBuffer != NULL ) { /* Swap the buffer just allocated and referenced from the pxNetworkBuffer with the buffer that has already been filled by the DMA. pxNetworkBuffer will then hold a reference to the buffer that already contains the data without any data having been copied between buffers. */ EMAC_NextPacketToRead( pxNetworkBuffer ); #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 1 { if( pxNetworkBuffer->xDataLength > 0 ) { /* If the frame would not be processed by the IP stack then don't even bother sending it to the IP stack. */ if( eConsiderFrameForProcessing( pxNetworkBuffer->pucEthernetBuffer ) != eProcessBuffer ) { pxNetworkBuffer->xDataLength = 0; } } } #endif if( pxNetworkBuffer->xDataLength > 0 ) { /* Store a pointer to the network buffer structure in the padding space that was left in front of the Ethernet frame. The pointer is needed to ensure the network buffer structure can be located when it is time for it to be freed if the Ethernet frame gets used as a zero copy buffer. */ *( ( xNetworkBufferDescriptor_t ** ) ( ( pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING ) ) ) = pxNetworkBuffer; /* Data was received and stored. Send it to the IP task for processing. */ xRxEvent.pvData = ( void * ) pxNetworkBuffer; if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( TickType_t ) 0 ) == pdFALSE ) { /* The buffer could not be sent to the IP task so the buffer must be released. */ vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } else { iptraceNETWORK_INTERFACE_RECEIVE(); } } else { /* The buffer does not contain any data so there is no point sending it to the IP task. Just release it. */ vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } } else { iptraceETHERNET_RX_EVENT_LOST(); } /* Release the descriptor. */ EMAC_UpdateRxConsumeIndex(); } } }
static void prvGMACDeferredInterruptHandlerTask( void *pvParameters ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; static const TickType_t xBufferWaitDelay = 1500UL / portTICK_RATE_MS; uint32_t ulReturned; ( void ) pvParameters; configASSERT( xGMACRxEventSemaphore ); for( ;; ) { /* Wait for the GMAC interrupt to indicate that another packet has been received. The while() loop is only needed if INCLUDE_vTaskSuspend is set to 0 in FreeRTOSConfig.h. If INCLUDE_vTaskSuspend is set to 1 then portMAX_DELAY would be an indefinite block time and xSemaphoreTake() would only return when the semaphore was actually obtained. */ while( xSemaphoreTake( xGMACRxEventSemaphore, portMAX_DELAY ) == pdFALSE ); /* Allocate a buffer to hold the data. */ pxNetworkBuffer = pxNetworkBufferGet( ipTOTAL_ETHERNET_FRAME_SIZE, xBufferWaitDelay ); if( pxNetworkBuffer != NULL ) { /* At least one packet has been received. */ ulReturned = gmac_dev_read( &xGMACStruct, pxNetworkBuffer->pucEthernetBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, ( uint32_t * ) &( pxNetworkBuffer->xDataLength ) ); if( ulReturned == GMAC_OK ) { #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 1 { if( pxNetworkBuffer->xDataLength > 0 ) { /* If the frame would not be processed by the IP stack then don't even bother sending it to the IP stack. */ if( eConsiderFrameForProcessing( pxNetworkBuffer->pucEthernetBuffer ) != eProcessBuffer ) { pxNetworkBuffer->xDataLength = 0; } } } #endif if( pxNetworkBuffer->xDataLength > 0 ) { /* Store a pointer to the network buffer structure in the padding space that was left in front of the Ethernet frame. The pointer is needed to ensure the network buffer structure can be located when it is time for it to be freed if the Ethernet frame gets used as a zero copy buffer. */ *( ( xNetworkBufferDescriptor_t ** ) ( ( pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING ) ) ) = pxNetworkBuffer; /* Data was received and stored. Send it to the IP task for processing. */ xRxEvent.pvData = ( void * ) pxNetworkBuffer; if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( TickType_t ) 0 ) == pdFALSE ) { /* The buffer could not be sent to the IP task so the buffer must be released. */ vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } else { iptraceNETWORK_INTERFACE_RECEIVE(); } } else { /* The buffer does not contain any data so there is no point sending it to the IP task. Just release it. */ vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } } else { vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } } else { /* Left a frame in the driver as a buffer was not available. */ gmac_dev_reset( &xGMACStruct ); } } }
/* The deferred interrupt handler is a standard RTOS task. FreeRTOS's centralised deferred interrupt handling capabilities can also be used. */ static void prvEMACDeferredInterruptHandlerTask(void *pvParameters) { xNetworkBufferDescriptor_t *pxBufferDescriptor; size_t xBytesReceived; /* Used to indicate that xSendEventStructToIPTask() is being called because of an Ethernet receive event. */ xIPStackEvent_t xRxEvent; uint8_t *buffer; __IO ETH_DMADescTypeDef *dmarxdesc; uint32_t payloadoffset = 0; uint32_t byteslefttocopy = 0; uint32_t i = 0; xEMACRxEventSemaphore = xSemaphoreCreateCounting(10, 0); for (;;) { /* Wait for the Ethernet MAC interrupt to indicate that another packet has been received. It is assumed xEMACRxEventSemaphore is a counting semaphore (to count the Rx events) and that the semaphore has already been created (remember this is an example of a simple rather than optimised port layer). */ if (xSemaphoreTake( xEMACRxEventSemaphore, portMAX_DELAY ) == pdTRUE) { /* get received frame */ if (HAL_ETH_GetReceivedFrame_IT(&heth_global) != HAL_OK) return; /* Obtain the size of the packet */ xBytesReceived = (uint16_t) heth_global.RxFrameInfos.length; buffer = (uint8_t *) heth_global.RxFrameInfos.buffer; if (xBytesReceived > 0) { /* Allocate a network buffer descriptor that points to a buffer large enough to hold the received frame. As this is the simple rather than efficient example the received data will just be copied into this buffer. */ pxBufferDescriptor = pxGetNetworkBufferWithDescriptor(xBytesReceived, 0); if (pxBufferDescriptor != NULL) { /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet buffer large enough to hold the received data. Copy the received data into pcNetworkBuffer->pucEthernetBuffer. Here it is assumed ReceiveData() is a peripheral driver function that copies the received data into a buffer passed in as the function's parameter. Remember! While is is a simple robust technique - it is not efficient. An example that uses a zero copy technique is provided further down this page. */ dmarxdesc = heth_global.RxFrameInfos.FSRxDesc; pxBufferDescriptor->xDataLength = xBytesReceived; byteslefttocopy = xBytesReceived; payloadoffset = 0; /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ while (byteslefttocopy > ETH_RX_BUF_SIZE) { /* Copy data to pbuf */ memcpy( (uint8_t*) ((uint8_t*) pxBufferDescriptor->pucEthernetBuffer + payloadoffset), (uint8_t*) ((uint8_t*) buffer), ETH_RX_BUF_SIZE); /* Point to next descriptor */ dmarxdesc = (ETH_DMADescTypeDef *) (dmarxdesc->Buffer2NextDescAddr); buffer = (uint8_t *) (dmarxdesc->Buffer1Addr); byteslefttocopy -= ETH_RX_BUF_SIZE; payloadoffset += ETH_RX_BUF_SIZE; } /* Copy remaining data in pbuf */ memcpy( (uint8_t*) ((uint8_t*) pxBufferDescriptor->pucEthernetBuffer + payloadoffset), (uint8_t*) ((uint8_t*) buffer), byteslefttocopy); } /* See if the data contained in the received Ethernet frame needs to be processed. NOTE! It is preferable to do this in the interrupt service routine itself, which would remove the need to unblock this task for packets that don't need processing. */ if (eConsiderFrameForProcessing(pxBufferDescriptor->pucEthernetBuffer) == eProcessBuffer) { /* The event about to be sent to the TCP/IP is an Rx event. */ xRxEvent.eEventType = eNetworkRxEvent; /* pvData is used to point to the network buffer descriptor that now references the received data. */ xRxEvent.pvData = (void *) pxBufferDescriptor; /* Send the data to the TCP/IP stack. */ if (xSendEventStructToIPTask(&xRxEvent, 0) == pdFALSE) { /* The buffer could not be sent to the IP task so the buffer must be released. */ vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor); /* Make a call to the standard trace macro to log the occurrence. */ iptraceETHERNET_RX_EVENT_LOST(); } else { /* The message was successfully sent to the TCP/IP stack. Call the standard trace macro to log the occurrence. */ iptraceNETWORK_INTERFACE_RECEIVE(); gdb.monit.rx_eth_frames++; } } else { /* The Ethernet frame can be dropped, but the Ethernet buffer must be released. */ vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor); } /* Release descriptors to DMA */ /* Point to first descriptor */ dmarxdesc = heth_global.RxFrameInfos.FSRxDesc; /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ for (i = 0; i < heth_global.RxFrameInfos.SegCount; i++) { dmarxdesc->Status |= ETH_DMARXDESC_OWN; dmarxdesc = (ETH_DMADescTypeDef *) (dmarxdesc->Buffer2NextDescAddr); } /* Clear Segment_Count */ heth_global.RxFrameInfos.SegCount = 0; } else { /* The event was lost because a network buffer was not available. Call the standard trace macro to log the occurrence. */ iptraceETHERNET_RX_EVENT_LOST(); } } /* When Rx Buffer unavailable flag is set: clear it and resume reception */ if ((heth_global.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t) RESET) { /* Clear RBUS ETHERNET DMA flag */ heth_global.Instance->DMASR = ETH_DMASR_RBUS; /* Resume DMA reception */ heth_global.Instance->DMARPDR = 0; } } }