blob: fd157c9e2d43dd5c36d998d56caf555e5df731cd [file] [log] [blame]
/*
* FreeRTOS+UDP V1.0.3 (C) 2014 Real Time Engineers ltd.
* All rights reserved
*
* This file is part of the FreeRTOS+UDP distribution. The FreeRTOS+UDP license
* terms are different to the FreeRTOS license terms.
*
* FreeRTOS+UDP uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+UDP is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+UDP is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals that for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a commercial license before
* incorporating FreeRTOS+UDP into proprietary software for distribution in any
* form. Commercial licenses can be purchased from http://shop.freertos.org/udp
* and do not require any source files to be changed.
*
* FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot
* use FreeRTOS+UDP unless you agree that you use the software 'as is'.
* FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
* implied, expressed, or statutory.
*
* 1 tab == 4 spaces!
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/udp
*
*/
/* Standard includes. */
#include <stdint.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* FreeRTOS+UDP includes. */
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_Sockets.h"
#include "NetworkBufferManagement.h"
/* Sanity check the UDP payload length setting is compatible with the
fragmentation setting. */
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1
#if ( ( ipMAX_UDP_PAYLOAD_LENGTH % 8 ) != 0 )
#error ( ipconfigNETWORK_MTU - 28 ) must be divisible by 8 when fragmentation is used
#endif /* ipMAX_UDP_PAYLOAD_LENGTH */
#endif /* ipconfigFRAGMENT_OUTGOING_PACKETS */
/* The ItemValue of the sockets xBoundSocketListItem member holds the socket's
port number. */
#define socketSET_SOCKET_ADDRESS( pxSocket, usPort ) listSET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ), ( usPort ) )
#define socketGET_SOCKET_ADDRESS( pxSocket ) listGET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ) )
/* xWaitingPacketSemaphore is not created until the socket is bound, so can be
tested to see if bind() has been called. */
#define socketSOCKET_IS_BOUND( pxSocket ) ( ( portBASE_TYPE ) pxSocket->xWaitingPacketSemaphore )
/* If FreeRTOS_sendto() is called on a socket that is not bound to a port
number then, depending on the FreeRTOSIPConfig.h settings, it might be that a
port number is automatically generated for the socket. Automatically generated
port numbers will be between socketAUTO_PORT_ALLOCATION_START_NUMBER and
0xffff. */
#define socketAUTO_PORT_ALLOCATION_START_NUMBER ( ( uint16_t ) 0xc000 )
/* When the automatically generated port numbers overflow, the next value used
is not set back to socketAUTO_PORT_ALLOCATION_START_NUMBER because it is likely
that the first few automatically generated ports will still be in use. Instead
it is reset back to the value defined by this constant. */
#define socketAUTO_PORT_ALLOCATION_RESET_NUMBER ( ( uint16_t ) 0xc100 )
/* The number of octets that make up an IP address. */
#define socketMAX_IP_ADDRESS_OCTETS 4
/*-----------------------------------------------------------*/
/*
* Allocate the next port number from the private allocation range.
*/
static uint16_t prvGetPrivatePortNumber( void );
/*
* Return the list itme from within pxList that has an item value of
* xWantedItemValue. If there is no such list item return NULL.
*/
xListItem * pxListFindListItemWithValue( xList *pxList, portTickType xWantedItemValue );
/*-----------------------------------------------------------*/
typedef struct XSOCKET
{
xSemaphoreHandle xWaitingPacketSemaphore;
xList xWaitingPacketsList;
xListItem xBoundSocketListItem; /* Used to reference the socket from a bound sockets list. */
portTickType xReceiveBlockTime;
portTickType xSendBlockTime;
uint8_t ucSocketOptions;
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
xQueueHandle xSelectQueue;
#endif
} xFreeRTOS_Socket_t;
/* The list that contains mappings between sockets and port numbers. Accesses
to this list must be protected by critical sections of one kind or another. */
static xList xBoundSocketsList;
/*-----------------------------------------------------------*/
xSocket_t FreeRTOS_socket( portBASE_TYPE xDomain, portBASE_TYPE xType, portBASE_TYPE xProtocol )
{
xFreeRTOS_Socket_t *pxSocket;
/* Only UDP on Ethernet is currently supported. */
configASSERT( xDomain == FREERTOS_AF_INET );
configASSERT( xType == FREERTOS_SOCK_DGRAM );
configASSERT( xProtocol == FREERTOS_IPPROTO_UDP );
configASSERT( listLIST_IS_INITIALISED( &xBoundSocketsList ) );
/* Allocate the structure that will hold the socket information. */
pxSocket = ( xFreeRTOS_Socket_t * ) pvPortMalloc( sizeof( xFreeRTOS_Socket_t ) );
if( pxSocket == NULL )
{
pxSocket = ( xFreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET;
iptraceFAILED_TO_CREATE_SOCKET();
}
else
{
/* Initialise the socket's members. The semaphore will be created if
the socket is bound to an address, for now the pointer to the semaphore
is just set to NULL to show it has not been created. */
pxSocket->xWaitingPacketSemaphore = NULL;
vListInitialise( &( pxSocket->xWaitingPacketsList ) );
vListInitialiseItem( &( pxSocket->xBoundSocketListItem ) );
listSET_LIST_ITEM_OWNER( &( pxSocket->xBoundSocketListItem ), ( void * ) pxSocket );
pxSocket->xSendBlockTime = ( portTickType ) 0;
pxSocket->xReceiveBlockTime = portMAX_DELAY;
pxSocket->ucSocketOptions = FREERTOS_SO_UDPCKSUM_OUT;
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
pxSocket->xSelectQueue = NULL;
#endif
}
/* Remove compiler warnings in the case the configASSERT() is not defined. */
( void ) xDomain;
( void ) xType;
( void ) xProtocol;
return ( xSocket_t ) pxSocket;
}
/*-----------------------------------------------------------*/
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
xSocketSet_t FreeRTOS_CreateSocketSet( unsigned portBASE_TYPE uxEventQueueLength )
{
xQueueHandle xSelectQueue;
/* Create the queue into which the address of sockets that are
available to read are posted. */
xSelectQueue = xQueueCreate( uxEventQueueLength, sizeof( xSocket_t ) );
return ( xSocketSet_t ) xSelectQueue;
}
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
portBASE_TYPE FreeRTOS_FD_SET( xSocket_t xSocket, xSocketSet_t xSocketSet )
{
xFreeRTOS_Socket_t *pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;
portBASE_TYPE xReturn = pdFALSE;
unsigned portBASE_TYPE uxMessagesWaiting;
configASSERT( xSocket );
/* Is the socket already a member of a select group? */
if( pxSocket->xSelectQueue == NULL )
{
taskENTER_CRITICAL();
{
/* Are there packets queued on the socket already? */
uxMessagesWaiting = uxQueueMessagesWaiting( pxSocket->xWaitingPacketSemaphore );
/* Are there enough notification spaces in the select queue for the
number of packets already queued on the socket? */
if( uxQueueSpacesAvailable( ( xQueueHandle ) xSocketSet ) >= uxMessagesWaiting )
{
/* Store a pointer to the select group in the socket for
future reference. */
pxSocket->xSelectQueue = ( xQueueHandle ) xSocketSet;
while( uxMessagesWaiting > 0 )
{
/* Add notifications of the number of packets that are
already queued on the socket to the select queue. */
xQueueSendFromISR( pxSocket->xSelectQueue, &pxSocket, NULL );
uxMessagesWaiting--;
}
xReturn = pdPASS;
}
}
taskEXIT_CRITICAL();
}
return xReturn;
}
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
portBASE_TYPE FreeRTOS_FD_CLR( xSocket_t xSocket, xSocketSet_t xSocketSet )
{
xFreeRTOS_Socket_t *pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;
portBASE_TYPE xReturn;
/* Is the socket a member of the select group? */
if( pxSocket->xSelectQueue == ( xQueueHandle ) xSocketSet )
{
/* The socket is no longer a member of the select group. */
pxSocket->xSelectQueue = NULL;
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
xSocket_t FreeRTOS_select( xSocketSet_t xSocketSet, portTickType xBlockTimeTicks )
{
xFreeRTOS_Socket_t *pxSocket;
/* Wait for a socket to be ready to read. */
if( xQueueReceive( ( xQueueHandle ) xSocketSet, &pxSocket, xBlockTimeTicks ) != pdPASS )
{
pxSocket = NULL;
}
return ( xSocket_t ) pxSocket;
}
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/
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;
}
/*-----------------------------------------------------------*/
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1
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;
portTickType 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 */
#else /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */
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;
portTickType 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 */
#endif /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */
/*-----------------------------------------------------------*/
portBASE_TYPE FreeRTOS_bind( xSocket_t xSocket, struct freertos_sockaddr * pxAddress, socklen_t xAddressLength )
{
portBASE_TYPE xReturn = 0; /* In Berkeley sockets, 0 means pass for bind(). */
xFreeRTOS_Socket_t *pxSocket;
#if ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1
struct freertos_sockaddr xAddress;
#endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND */
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 ) xAddressLength;
configASSERT( xSocket );
configASSERT( xSocket != FREERTOS_INVALID_SOCKET );
#if ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1
{
/* pxAddress will be NULL if sendto() was called on a socket without the
socket being bound to an address. In this case, automatically allocate
an address to the socket. There is a very tiny chance that the allocated
port will already be in use - if that is the case, then the check below
[pxListFindListItemWithValue()] will result in an error being returned. */
if( pxAddress == NULL )
{
pxAddress = &xAddress;
pxAddress->sin_port = prvGetPrivatePortNumber();
}
}
#endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 */
/* Sockets must be bound before calling FreeRTOS_sendto() if
ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is not set to 1. */
configASSERT( pxAddress );
if( pxAddress != NULL )
{
if( pxAddress->sin_port == 0 )
{
pxAddress->sin_port = prvGetPrivatePortNumber();
}
vTaskSuspendAll();
{
/* Check to ensure the port is not already in use. */
if( pxListFindListItemWithValue( &xBoundSocketsList, ( portTickType ) pxAddress->sin_port ) != NULL )
{
xReturn = FREERTOS_EADDRINUSE;
}
}
xTaskResumeAll();
/* Check that xReturn has not been set before continuing. */
if( xReturn == 0 )
{
if( pxSocket->xWaitingPacketSemaphore == NULL )
{
/* Create the semaphore used to count the number of packets that
are queued on this socket. */
pxSocket->xWaitingPacketSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFERS, 0 );
if( pxSocket->xWaitingPacketSemaphore != NULL )
{
/* Allocate the port number to the socket. */
socketSET_SOCKET_ADDRESS( pxSocket, pxAddress->sin_port );
taskENTER_CRITICAL();
{
/* Add the socket to the list of bound ports. */
vListInsertEnd( &xBoundSocketsList, &( pxSocket->xBoundSocketListItem ) );
}
taskEXIT_CRITICAL();
}
else
{
/* Out of memory. */
xReturn = FREERTOS_ENOBUFS;
}
}
else
{
/* The socket is already bound. */
xReturn = FREERTOS_EINVAL;
}
}
}
else
{
xReturn = FREERTOS_EADDRNOTAVAIL;
}
if( xReturn != 0 )
{
iptraceBIND_FAILED( xSocket, ( FreeRTOS_ntohs( pxAddress->sin_port ) ) );
}
return xReturn;
} /* Tested */
/*-----------------------------------------------------------*/
portBASE_TYPE 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 */
/*-----------------------------------------------------------*/
void FreeRTOS_SocketsInit( void )
{
vListInitialise( &xBoundSocketsList );
}
/*-----------------------------------------------------------*/
portBASE_TYPE FreeRTOS_setsockopt( xSocket_t xSocket, int32_t lLevel, int32_t lOptionName, const void *pvOptionValue, size_t xOptionLength )
{
/* The standard Berkeley function returns 0 for success. */
portBASE_TYPE xReturn = 0;
portBASE_TYPE lOptionValue;
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 ) lLevel;
( void ) xOptionLength;
configASSERT( xSocket );
switch( lOptionName )
{
case FREERTOS_SO_RCVTIMEO :
/* Receive time out. */
pxSocket->xReceiveBlockTime = *( ( portTickType * ) pvOptionValue );
break;
case FREERTOS_SO_SNDTIMEO :
/* The send time out is capped for the reason stated in the comments
where ipconfigMAX_SEND_BLOCK_TIME_TICKS is defined in
FreeRTOSIPConfig.h (assuming an official configuration file is being
used. */
pxSocket->xSendBlockTime = *( ( portTickType * ) pvOptionValue );
if( pxSocket->xSendBlockTime > ipconfigMAX_SEND_BLOCK_TIME_TICKS )
{
pxSocket->xSendBlockTime = ipconfigMAX_SEND_BLOCK_TIME_TICKS;
}
break;
case FREERTOS_SO_UDPCKSUM_OUT :
/* Turn calculating of the UDP checksum on/off for this socket. */
lOptionValue = ( portBASE_TYPE ) pvOptionValue;
if( lOptionValue == 0 )
{
pxSocket->ucSocketOptions &= ~FREERTOS_SO_UDPCKSUM_OUT;
}
else
{
pxSocket->ucSocketOptions |= FREERTOS_SO_UDPCKSUM_OUT;
}
break;
default :
/* No other options are handled. */
xReturn = FREERTOS_ENOPROTOOPT;
break;
}
return xReturn;
} /* Tested */
/*-----------------------------------------------------------*/
portBASE_TYPE xProcessReceivedUDPPacket( xNetworkBufferDescriptor_t *pxNetworkBuffer, uint16_t usPort )
{
xListItem *pxListItem;
portBASE_TYPE xReturn = pdPASS;
xFreeRTOS_Socket_t *pxSocket;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
vTaskSuspendAll();
{
/* See if there is a list item associated with the port number on the
list of bound sockets. */
pxListItem = pxListFindListItemWithValue( &xBoundSocketsList, ( portTickType ) usPort );
}
xTaskResumeAll();
if( pxListItem != NULL )
{
/* The owner of the list item is the socket itself. */
pxSocket = ( xFreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxListItem );
vTaskSuspendAll();
{
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
{
/* Is the socket a member of a select() group? */
if( pxSocket->xSelectQueue != NULL )
{
/* Can the select group be notified that the socket is
ready to be read? */
if( xQueueSendFromISR( pxSocket->xSelectQueue, &pxSocket, &xHigherPriorityTaskWoken ) != pdPASS )
{
/* Could not notify the select group. */
xReturn = pdFAIL;
iptraceFAILED_TO_NOTIFY_SELECT_GROUP( pxSocket );
}
}
}
#endif
if( xReturn == pdPASS )
{
taskENTER_CRITICAL();
{
/* Add the network packet to the list of packets to be
processed by the socket. */
vListInsertEnd( &( pxSocket->xWaitingPacketsList ), &( pxNetworkBuffer->xBufferListItem ) );
}
taskEXIT_CRITICAL();
/* The socket's counting semaphore records how many packets are
waiting to be processed by the socket. */
xSemaphoreGiveFromISR( pxSocket->xWaitingPacketSemaphore, &xHigherPriorityTaskWoken );
}
}
if( xTaskResumeAll() == pdFALSE )
{
if( xHigherPriorityTaskWoken != pdFALSE )
{
taskYIELD();
}
}
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static uint16_t prvGetPrivatePortNumber( void )
{
static uint16_t usNextPortToUse = socketAUTO_PORT_ALLOCATION_START_NUMBER - 1;
uint16_t usReturn;
/* Assign the next port in the range. */
taskENTER_CRITICAL();
{
usNextPortToUse++;
/* Has it overflowed? */
if( usNextPortToUse == 0U )
{
/* Don't go right back to the start of the dynamic/private port
range numbers as any persistent sockets are likely to have been
create first so the early port numbers may still be in use. */
usNextPortToUse = socketAUTO_PORT_ALLOCATION_RESET_NUMBER;
}
usReturn = FreeRTOS_htons( usNextPortToUse );
}
taskEXIT_CRITICAL();
return usReturn;
} /* Tested */
/*-----------------------------------------------------------*/
xListItem * pxListFindListItemWithValue( xList *pxList, portTickType xWantedItemValue )
{
xListItem *pxIterator, *pxReturn;
pxReturn = NULL;
for( pxIterator = ( xListItem * ) pxList->xListEnd.pxNext; pxIterator != ( xListItem* ) &( pxList->xListEnd ); pxIterator = ( xListItem * ) pxIterator->pxNext )
{
if( pxIterator->xItemValue == xWantedItemValue )
{
pxReturn = pxIterator;
break;
}
}
return pxReturn;
} /* Tested */
/*-----------------------------------------------------------*/
#if ipconfigINCLUDE_FULL_INET_ADDR == 1
uint32_t FreeRTOS_inet_addr( const char *pcIPAddress )
{
const uint8_t ucDecimalBase = 10;
uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ];
const char *pcPointerOnEntering;
uint32_t ulReturn = 0UL, ulOctetNumber, ulValue;
portBASE_TYPE xResult = pdPASS;
for( ulOctetNumber = 0; ulOctetNumber < socketMAX_IP_ADDRESS_OCTETS; ulOctetNumber++ )
{
ulValue = 0;
pcPointerOnEntering = pcIPAddress;
while( ( *pcIPAddress >= ( uint8_t ) '0' ) && ( *pcIPAddress <= ( uint8_t ) '9' ) )
{
/* Move previous read characters into the next decimal
position. */
ulValue *= ucDecimalBase;
/* Add the binary value of the ascii character. */
ulValue += ( *pcIPAddress - ( uint8_t ) '0' );
/* Move to next character in the string. */
pcIPAddress++;
}
/* Check characters were read. */
if( pcIPAddress == pcPointerOnEntering )
{
xResult = pdFAIL;
}
/* Check the value fits in an 8-bit number. */
if( ulValue > 0xffUL )
{
xResult = pdFAIL;
}
else
{
ucOctet[ ulOctetNumber ] = ( uint8_t ) ulValue;
/* Check the next character is as expected. */
if( ulOctetNumber < ( socketMAX_IP_ADDRESS_OCTETS - 1 ) )
{
if( *pcIPAddress != ( uint8_t ) '.' )
{
xResult = pdFAIL;
}
else
{
/* Move past the dot. */
pcIPAddress++;
}
}
}
if( xResult == pdFAIL )
{
/* No point going on. */
break;
}
}
if( *pcIPAddress != ( uint8_t ) 0x00 )
{
/* Expected the end of the string. */
xResult = pdFAIL;
}
if( ulOctetNumber != socketMAX_IP_ADDRESS_OCTETS )
{
/* Didn't read enough octets. */
xResult = pdFAIL;
}
if( xResult == pdPASS )
{
ulReturn = FreeRTOS_inet_addr_quick( ucOctet[ 0 ], ucOctet[ 1 ], ucOctet[ 2 ], ucOctet[ 3 ] );
}
return ulReturn;
}
#endif /* ipconfigINCLUDE_FULL_INET_ADDR */
/*-----------------------------------------------------------*/