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 prvInterruptSimulatorTask( void *pvParameters ) { static struct pcap_pkthdr *pxHeader; const uint8_t *pucPacketData; long lResult; xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; eFrameProcessingResult_t eResult; /* Just to kill the compiler warning. */ ( void ) pvParameters; for( ;; ) { /* Get the next packet. */ xSemaphoreTake( xPCAPMutex, portMAX_DELAY ); { lResult = pcap_next_ex( pxOpenedInterfaceHandle, &pxHeader, &pucPacketData ); } xSemaphoreGive( xPCAPMutex ); if( lResult == 1 ) { eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData ); if( eResult == eProcessBuffer ) { /* Will the data fit into the frame buffer? */ if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE ) { /* Obtain a buffer into which the data can be placed. This is only an interrupt simulator, not a real interrupt, so it is ok to call the task level function here. */ xSemaphoreTake( xPCAPMutex, portMAX_DELAY ); { pxNetworkBuffer = pxNetworkBufferGet( pxHeader->len, 0 ); } xSemaphoreGive( xPCAPMutex ); if( pxNetworkBuffer != NULL ) { memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len ); pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len; xRxEvent.pvData = ( void * ) pxNetworkBuffer; /* Data was received and stored. Send a message to the IP task to let it know. */ if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( portTickType ) 0 ) == pdFALSE ) { /* The buffer could not be sent to the stack so must be released again. This is only an interrupt simulator, not a real interrupt, so it is ok to use the task level function here. */ vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } else { iptraceNETWORK_INTERFACE_RECEIVE(); } } else { iptraceETHERNET_RX_EVENT_LOST(); } } else { /* Log that a packet was dropped because it would have overflowed the buffer. */ } } } else { /* There is no real way of simulating an interrupt. Make sure other tasks can run. */ vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ); } } }
static void prvEMACHandlerTask( void *pvParameters ) { size_t xDataLength; const uint16_t usCRCLength = 4; xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; /* This is not included in the header file for some reason. */ extern uint8_t *EMAC_NextPacketToRead( void ); ( 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. */ while( xSemaphoreTake( xEMACRxEventSemaphore, portMAX_DELAY ) == pdFALSE ); /* At least one packet has been received. */ while( EMAC_CheckReceiveIndex() != FALSE ) { /* Obtain the length, minus the CRC. The CRC is four bytes but the length is already minus 1. */ xDataLength = ( size_t ) EMAC_GetReceiveDataSize() - ( usCRCLength - 1U ); if( xDataLength > 0U ) { /* Obtain a network buffer to pass this data into the stack. No storage is required as the network buffer will point directly to the buffer that already holds the received data. */ pxNetworkBuffer = pxNetworkBufferGet( 0, ( portTickType ) 0 ); if( pxNetworkBuffer != NULL ) { pxNetworkBuffer->pucEthernetBuffer = EMAC_NextPacketToRead(); pxNetworkBuffer->xDataLength = xDataLength; xRxEvent.pvData = ( void * ) pxNetworkBuffer; /* Data was received and stored. Send a message to the IP task to let it know. */ if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( portTickType ) 0 ) == pdFALSE ) { vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } } else { iptraceETHERNET_RX_EVENT_LOST(); } iptraceNETWORK_INTERFACE_RECEIVE(); } /* Release the frame. */ 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 ); } } }
int32_t FreeRTOS_sendto( xSocket_t xSocket, const void *pvBuffer, size_t xTotalDataLength, uint32_t ulFlags, const struct freertos_sockaddr *pxDestinationAddress, socklen_t xDestinationAddressLength ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; extern xQueueHandle xNetworkEventQueue; xTimeOutType xTimeOut; TickType_t xTicksToWait; int32_t lReturn = 0; xFreeRTOS_Socket_t *pxSocket; uint8_t *pucBuffer; pxSocket = ( xFreeRTOS_Socket_t * ) xSocket; /* The function prototype is designed to maintain the expected Berkeley sockets standard, but this implementation does not use all the parameters. */ ( void ) xDestinationAddressLength; configASSERT( xNetworkEventQueue ); configASSERT( pvBuffer ); if( xTotalDataLength <= ipMAX_UDP_PAYLOAD_LENGTH ) { if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE ) { /* If the socket is not already bound to an address, bind it now. Passing NULL as the address parameter tells FreeRTOS_bind() to select the address to bind to. */ FreeRTOS_bind( pxSocket, NULL, 0 ); } if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) { xTicksToWait = pxSocket->xSendBlockTime; if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { /* Zero copy is not set, so obtain a network buffer into which the payload will be copied. */ vTaskSetTimeOutState( &xTimeOut ); pxNetworkBuffer = pxNetworkBufferGet( xTotalDataLength + sizeof( xUDPPacket_t ), xTicksToWait ); if( pxNetworkBuffer != NULL ) { memcpy( ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ), ( void * ) pvBuffer, xTotalDataLength ); if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE ) { /* The entire block time has been used up. */ xTicksToWait = 0; } } } else { /* When zero copy is used, pvBuffer is a pointer to the payload of a buffer that has already been obtained from the stack. Obtain the network buffer pointer from the buffer. */ pucBuffer = ( uint8_t * ) pvBuffer; pucBuffer -= ( ipBUFFER_PADDING + sizeof( xUDPPacket_t ) ); pxNetworkBuffer = * ( ( xNetworkBufferDescriptor_t ** ) pucBuffer ); } if( pxNetworkBuffer != NULL ) { pxNetworkBuffer->xDataLength = xTotalDataLength; pxNetworkBuffer->usPort = pxDestinationAddress->sin_port; pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_ADDRESS( pxSocket ); pxNetworkBuffer->ulIPAddress = pxDestinationAddress->sin_addr; /* The socket options are passed to the IP layer in the space that will eventually get used by the Ethernet header. */ pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = pxSocket->ucSocketOptions; /* Tell the networking task that the packet needs sending. */ xStackTxEvent.pvData = pxNetworkBuffer; if( xQueueSendToBack( xNetworkEventQueue, &xStackTxEvent, xTicksToWait ) != pdPASS ) { /* If the buffer was allocated in this function, release it. */ if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { vNetworkBufferRelease( pxNetworkBuffer ); } iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); } else { lReturn = ( int32_t ) xTotalDataLength; } } else { /* If errno was available, errno would be set to FREERTOS_ENOPKTS. As it is, the function must return the number of transmitted bytes, so the calling function knows how much data was actually sent. */ iptraceNO_BUFFER_FOR_SENDTO(); } } else { iptraceSENDTO_SOCKET_NOT_BOUND(); } } else { /* The data is longer than the available buffer space. Setting ipconfigCAN_FRAGMENT_OUTGOING_PACKETS to 1 may allow this packet to be sent. */ iptraceSENDTO_DATA_TOO_LONG(); } return lReturn; } /* Tested */
int32_t FreeRTOS_sendto( xSocket_t xSocket, const void *pvBuffer, size_t xTotalDataLength, uint32_t ulFlags, const struct freertos_sockaddr *pxDestinationAddress, socklen_t xDestinationAddressLength ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; xIPFragmentParameters_t *pxFragmentParameters; size_t xBytesToSend, xBytesRemaining; xIPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; extern xQueueHandle xNetworkEventQueue; uint8_t *pucBuffer; xTimeOutType xTimeOut; TickType_t xTicksToWait; uint16_t usFragmentOffset; xFreeRTOS_Socket_t *pxSocket; pxSocket = ( xFreeRTOS_Socket_t * ) xSocket; /* The function prototype is designed to maintain the expected Berkeley sockets standard, but this implementation does not use all the parameters. */ ( void ) xDestinationAddressLength; configASSERT( xNetworkEventQueue ); configASSERT( pvBuffer ); xBytesRemaining = xTotalDataLength; if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE ) { /* If the socket is not already bound to an address, bind it now. Passing NULL as the address parameter tells FreeRTOS_bind() to select the address to bind to. */ FreeRTOS_bind( xSocket, NULL, 0 ); } if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) { /* pucBuffer will be reset if this send turns out to be a zero copy send because in that case pvBuffer is actually a pointer to an xUserData_t structure, not the UDP payload. */ pucBuffer = ( uint8_t * ) pvBuffer; vTaskSetTimeOutState( &xTimeOut ); xTicksToWait = pxSocket->xSendBlockTime; /* The data being transmitted will be sent in ipMAX_UDP_PAYLOAD_LENGTH chunks if xDataLength is greater than the network buffer payload size. Loop until all the data is sent. */ while( xBytesRemaining > 0 ) { if( xBytesRemaining > ipMAX_UDP_PAYLOAD_LENGTH ) { /* Cap the amount being sent in this packet to the maximum UDP payload size. This will be a multiple of 8 already, removing the need to check in the code. */ xBytesToSend = ipMAX_UDP_PAYLOAD_LENGTH; } else { /* Send all remaining bytes - which may well be the total number of bytes if the packet was not chopped up. */ xBytesToSend = xBytesRemaining; } /* If the zero copy flag is set, then the data is already in a network buffer. Otherwise, get a new network buffer. */ if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE ) { xTicksToWait = 0; } pxNetworkBuffer = pxNetworkBufferGet( xBytesToSend + sizeof( xUDPPacket_t ), xTicksToWait ); } else { if( xTotalDataLength > ipMAX_UDP_PAYLOAD_LENGTH ) { /* The packet needs fragmenting, but zero copy buffers cannot be fragmented. */ pxNetworkBuffer = NULL; } else { /* When zero copy is used, pvBuffer is a pointer to the payload of a buffer that has already been obtained from the stack. Obtain the network buffer pointer from the buffer. */ pucBuffer = ( uint8_t * ) pvBuffer; pucBuffer -= ( ipBUFFER_PADDING + sizeof( xUDPPacket_t ) ); pxNetworkBuffer = * ( ( xNetworkBufferDescriptor_t ** ) pucBuffer ); } } if( pxNetworkBuffer != NULL ) { /* Use the part of the network buffer that will be completed by the IP layer as temporary storage to pass extra information required by the IP layer. */ pxFragmentParameters = ( xIPFragmentParameters_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipFRAGMENTATION_PARAMETERS_OFFSET ] ); pxFragmentParameters->ucSocketOptions = pxSocket->ucSocketOptions; if( xBytesRemaining > ipMAX_UDP_PAYLOAD_LENGTH ) { /* The packet is being chopped up, and more data will follow. */ pxFragmentParameters->ucSocketOptions = ( pxSocket->ucSocketOptions | FREERTOS_NOT_LAST_IN_FRAGMENTED_PACKET ); } if( xTotalDataLength > ipMAX_UDP_PAYLOAD_LENGTH ) { /* Let the IP layer know this packet has been chopped up, and supply the IP layer with any addition information it needs to make sense of it. */ pxFragmentParameters->ucSocketOptions |= FREERTOS_FRAGMENTED_PACKET; usFragmentOffset = ( uint16_t ) ( xTotalDataLength - xBytesRemaining ); pxFragmentParameters->usFragmentedPacketOffset = usFragmentOffset; pxFragmentParameters->usFragmentLength = ( uint16_t ) xBytesToSend; } else { usFragmentOffset = 0; } /* Write the payload into the packet. The IP layer is queried to find where in the IP payload the data should be written. This is because the necessary offset is different for the first packet, because the first packet leaves space for a UDP header. Note that this changes usFragmentOffset from the offset in the entire UDP packet, to the offset in the IP packet. */ if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { /* Only copy the data if it is not already in the expected location. */ usFragmentOffset = ipGET_UDP_PAYLOAD_OFFSET_FOR_FRAGMENT( usFragmentOffset ); memcpy( ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ usFragmentOffset ] ), ( void * ) pucBuffer, xBytesToSend ); } pxNetworkBuffer->xDataLength = xTotalDataLength; pxNetworkBuffer->usPort = pxDestinationAddress->sin_port; pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_ADDRESS( pxSocket ); pxNetworkBuffer->ulIPAddress = pxDestinationAddress->sin_addr; /* Tell the networking task that the packet needs sending. */ xStackTxEvent.pvData = pxNetworkBuffer; if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE ) { xTicksToWait = 0; } if( xQueueSendToBack( xNetworkEventQueue, &xStackTxEvent, xTicksToWait ) != pdPASS ) { /* If the buffer was allocated in this function, release it. */ if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { vNetworkBufferRelease( pxNetworkBuffer ); } iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); break; } /* Adjust counters ready to either exit the loop, or send another chunk of data. */ xBytesRemaining -= xBytesToSend; pucBuffer += xBytesToSend; } else { /* If errno was available, errno would be set to FREERTOS_ENOPKTS. As it is, the function must return the number of transmitted bytes, so the calling function knows how much data was actually sent. */ break; } } } return ( xTotalDataLength - xBytesRemaining ); } /* Tested */
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(); } } }