portBASE_TYPE xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { xEthernetHeader_t *pxEthernetHeader; xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL }; extern uint8_t xDefaultPartUDPPacketHeader[]; static const xMACAddress_t xBroadcastMACAddress = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; portBASE_TYPE xCanLoopback; pxEthernetHeader = ( xEthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer; if( memcmp( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &xBroadcastMACAddress, sizeof( xMACAddress_t ) ) == 0 ) { /* This is a broadcast. */ xCanLoopback = pdTRUE; } else if( memcmp( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) xDefaultPartUDPPacketHeader, sizeof( xMACAddress_t ) ) == 0 ) { /* This is being sent to itself. */ xCanLoopback = pdTRUE; } else { /* This is being sent externally. */ xCanLoopback = pdFALSE; } iptraceNETWORK_INTERFACE_TRANSMIT(); if( xCanLoopback == pdTRUE ) { /* Just loop the frame back to the input queue. Here the loopback is sending a message to itself, so a block time cannot be used for fear of deadlocking. */ xRxEvent.pvData = ( void * ) pxNetworkBuffer; if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( portTickType ) 0 ) == pdFALSE ) { vNetworkBufferRelease( pxNetworkBuffer ); iptraceETHERNET_RX_EVENT_LOST(); } else { iptraceNETWORK_INTERFACE_RECEIVE(); } } else { /* Send the packet. */ xSemaphoreTake( xPCAPMutex, portMAX_DELAY ); { pcap_sendpacket( pxOpenedInterfaceHandle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); } xSemaphoreGive( xPCAPMutex ); /* The buffer has been transmitted so can be released. */ vNetworkBufferRelease( pxNetworkBuffer ); } return pdPASS; }
BaseType_t xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { BaseType_t xReturn = pdFAIL; int32_t x; /* Attempt to obtain access to a Tx descriptor. */ for( x = 0; x < niMAX_TX_ATTEMPTS; x++ ) { if( gmac_dev_write( &xGMACStruct, pxNetworkBuffer->pucEthernetBuffer, ( uint32_t ) pxNetworkBuffer->xDataLength, NULL ) == GMAC_OK ) { /* The Tx has been initiated. */ xReturn = pdPASS; break; } else { iptraceWAITING_FOR_TX_DMA_DESCRIPTOR(); vTaskDelay( niTX_BUFFER_FREE_WAIT ); } } /* Finished with the network buffer. */ vNetworkBufferRelease( pxNetworkBuffer ); return xReturn; }
portBASE_TYPE xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { extern void vEMACCopyWrite( uint8_t * pucBuffer, uint16_t usLength ); vEMACCopyWrite( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); /* Finished with the network buffer. */ vNetworkBufferRelease( pxNetworkBuffer ); return pdTRUE; }
portBASE_TYPE xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { xSemaphoreTake( xPCAPMutex, portMAX_DELAY ); { iptraceNETWORK_INTERFACE_TRANSMIT(); pcap_sendpacket( pxOpenedInterfaceHandle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); } xSemaphoreGive( xPCAPMutex ); /* The buffer has been transmitted so can be released. */ vNetworkBufferRelease( pxNetworkBuffer ); return pdPASS; }
portBASE_TYPE xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { portBASE_TYPE xReturn = pdFAIL; int32_t x; extern void EMAC_StartTransmitNextBuffer( uint32_t ulLength ); extern void EMAC_SetNextPacketToSend( uint8_t * pucBuffer ); /* Attempt to obtain access to a Tx buffer. */ for( x = 0; x < niMAX_TX_ATTEMPTS; x++ ) { if( EMAC_CheckTransmitIndex() == TRUE ) { /* Will the data fit in the Tx buffer? */ if( pxNetworkBuffer->xDataLength < EMAC_ETH_MAX_FLEN ) /*_RB_ The size needs to come from FreeRTOSIPConfig.h. */ { /* Assign the buffer to the Tx descriptor that is now known to be free. */ EMAC_SetNextPacketToSend( pxNetworkBuffer->pucEthernetBuffer ); /* The EMAC now owns the buffer. */ pxNetworkBuffer->pucEthernetBuffer = NULL; /* Initiate the Tx. */ EMAC_StartTransmitNextBuffer( pxNetworkBuffer->xDataLength ); iptraceNETWORK_INTERFACE_TRANSMIT(); /* The Tx has been initiated. */ xReturn = pdPASS; } break; } else { vTaskDelay( niTX_BUFFER_FREE_WAIT ); } } /* Finished with the network buffer. */ vNetworkBufferRelease( pxNetworkBuffer ); return xReturn; }
BaseType_t xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) { BaseType_t xReturn = pdFAIL; int32_t x; /* Attempt to obtain access to a Tx descriptor. */ for( x = 0; x < niMAX_TX_ATTEMPTS; x++ ) { if( EMAC_CheckTransmitIndex() == TRUE ) { /* Assign the buffer being transmitted to the Tx descriptor. */ EMAC_SetNextPacketToSend( pxNetworkBuffer->pucEthernetBuffer ); /* The EMAC now owns the buffer and will free it when it has been transmitted. Set pucBuffer to NULL to ensure the buffer is not freed when the network buffer structure is returned to the pool of network buffers. */ pxNetworkBuffer->pucEthernetBuffer = NULL; /* Initiate the Tx. */ EMAC_StartTransmitNextBuffer( pxNetworkBuffer->xDataLength ); iptraceNETWORK_INTERFACE_TRANSMIT(); /* The Tx has been initiated. */ xReturn = pdPASS; break; } else { iptraceWAITING_FOR_TX_DMA_DESCRIPTOR(); vTaskDelay( niTX_BUFFER_FREE_WAIT ); } } /* Finished with the network buffer. */ vNetworkBufferRelease( pxNetworkBuffer ); return xReturn; }
BaseType_t FreeRTOS_closesocket( xSocket_t xSocket ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; xFreeRTOS_Socket_t *pxSocket; pxSocket = ( xFreeRTOS_Socket_t * ) xSocket; configASSERT( pxSocket ); configASSERT( pxSocket != FREERTOS_INVALID_SOCKET ); /* Socket must be unbound first, to ensure no more packets are queued on it. */ if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) { taskENTER_CRITICAL(); { uxListRemove( &( pxSocket->xBoundSocketListItem ) ); } taskEXIT_CRITICAL(); } /* Now the socket is not bound the list of waiting packets can be drained. */ if( pxSocket->xWaitingPacketSemaphore != NULL ) { while( listCURRENT_LIST_LENGTH( &( pxSocket->xWaitingPacketsList ) ) > 0U ) { pxNetworkBuffer = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->xWaitingPacketsList ) ); uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); vNetworkBufferRelease( pxNetworkBuffer ); } vSemaphoreDelete( pxSocket->xWaitingPacketSemaphore ); } vPortFree( pxSocket ); return 0; } /* Tested */
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(); } } }
xNetworkBufferDescriptor_t *pxNetworkBufferGet( size_t xRequestedSizeBytes, TickType_t xBlockTimeTicks ) { xNetworkBufferDescriptor_t *pxReturn = NULL; if( ( xRequestedSizeBytes != 0 ) && ( xRequestedSizeBytes < sizeof( xARPPacket_t ) ) ) { /* ARP packets can replace application packets, so the storage must be at least large enough to hold an ARP. */ xRequestedSizeBytes = sizeof( xARPPacket_t ); } /* If there is a semaphore available, there is a network buffer available. */ if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS ) { /* Protect the structure as it is accessed from tasks and interrupts. */ taskENTER_CRITICAL(); { pxReturn = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); uxListRemove( &( pxReturn->xBufferListItem ) ); } taskEXIT_CRITICAL(); /* Allocate storage of exactly the requested size to the buffer. */ configASSERT( pxReturn->pucEthernetBuffer == NULL ); if( xRequestedSizeBytes > 0 ) { /* Extra space is obtained so a pointer to the network buffer can be stored at the beginning of the buffer. */ pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING ); if( pxReturn->pucEthernetBuffer == NULL ) { /* The attempt to allocate storage for the buffer payload failed, so the network buffer structure cannot be used and must be released. */ vNetworkBufferRelease( pxReturn ); pxReturn = NULL; } else { /* Store a pointer to the network buffer structure in the buffer storage area, then move the buffer pointer on past the stored pointer so the pointer value is not overwritten by the application when the buffer is used. */ *( ( xNetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn; pxReturn->pucEthernetBuffer += ipBUFFER_PADDING; iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); /* Store the actual size of the allocated buffer, which may be greater than the requested size. */ pxReturn->xDataLength = xRequestedSizeBytes; } } else { iptraceNETWORK_BUFFER_OBTAINED( pxReturn ); } } if( pxReturn == NULL ) { iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); } return pxReturn; }
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 */
int32_t FreeRTOS_recvfrom( xSocket_t xSocket, void *pvBuffer, size_t xBufferLength, uint32_t ulFlags, struct freertos_sockaddr *pxSourceAddress, socklen_t *pxSourceAddressLength ) { xNetworkBufferDescriptor_t *pxNetworkBuffer; int32_t lReturn; 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 ) pxSourceAddressLength; if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) { /* The semaphore is given when received data is queued on the socket. */ if( xSemaphoreTake( pxSocket->xWaitingPacketSemaphore, pxSocket->xReceiveBlockTime ) == pdPASS ) { taskENTER_CRITICAL(); { configASSERT( ( listCURRENT_LIST_LENGTH( &( pxSocket->xWaitingPacketsList ) ) > 0U ) ); /* The owner of the list item is the network buffer. */ pxNetworkBuffer = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->xWaitingPacketsList ) ); /* Remove the network buffer from the list of buffers waiting to be processed by the socket. */ uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); } taskEXIT_CRITICAL(); if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 ) { /* The zero copy flag is not set. Truncate the length if it won't fit in the provided buffer. */ if( pxNetworkBuffer->xDataLength > xBufferLength ) { iptraceRECVFROM_DISCARDING_BYTES( ( xBufferLength - pxNetworkBuffer->xDataLength ) ); pxNetworkBuffer->xDataLength = xBufferLength; } /* Copy the received data into the provided buffer, then release the network buffer. */ memcpy( pvBuffer, ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ), pxNetworkBuffer->xDataLength ); vNetworkBufferRelease( pxNetworkBuffer ); } else { /* The zero copy flag was set. pvBuffer is not a buffer into which the received data can be copied, but a pointer that must be set to point to the buffer in which the received data has already been placed. */ *( ( void** ) pvBuffer ) = ( void * ) ( &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ) ); } /* The returned value is the data length, which may have been capped to the receive buffer size. */ lReturn = ( int32_t ) pxNetworkBuffer->xDataLength; if( pxSourceAddress != NULL ) { pxSourceAddress->sin_port = pxNetworkBuffer->usPort; pxSourceAddress->sin_addr = pxNetworkBuffer->ulIPAddress; } } else { lReturn = FREERTOS_EWOULDBLOCK; iptraceRECVFROM_TIMEOUT(); } } else { lReturn = FREERTOS_EINVAL; } return lReturn; }
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(); } } }