You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1038 lines
34 KiB
C
1038 lines
34 KiB
C
/*
|
|
* FreeRTOS+UDP V1.0.0 (C) 2013 Real Time Engineers ltd.
|
|
*
|
|
* 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 pure GPL open source license (as opposed to the modified GPL license
|
|
* under which FreeRTOS is distributed) or a commercial license. 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 ) ( ( uint32_t ) 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;
|
|
|
|
/* Is the socket already a member of a select group? */
|
|
if( pxSocket->xSelectQueue == NULL )
|
|
{
|
|
/* Store a pointer to the select group in the socket for future
|
|
reference. */
|
|
pxSocket->xSelectQueue = ( xQueueHandle ) xSocketSet;
|
|
xReturn = pdPASS;
|
|
}
|
|
else
|
|
{
|
|
/* The socket is already a member of a select group so cannot be added
|
|
to another. */
|
|
xReturn = pdFAIL;
|
|
}
|
|
|
|
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++;
|
|
taskEXIT_CRITICAL();
|
|
|
|
/* 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 );
|
|
|
|
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 uint8_t * pucIPAddress )
|
|
{
|
|
const uint8_t ucDecimalBase = 10;
|
|
uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ];
|
|
const uint8_t *pucPointerOnEntering;
|
|
uint32_t ulReturn = 0UL, ulOctetNumber, ulValue;
|
|
portBASE_TYPE xResult = pdPASS;
|
|
|
|
for( ulOctetNumber = 0; ulOctetNumber < socketMAX_IP_ADDRESS_OCTETS; ulOctetNumber++ )
|
|
{
|
|
ulValue = 0;
|
|
pucPointerOnEntering = pucIPAddress;
|
|
|
|
while( ( *pucIPAddress >= ( uint8_t ) '0' ) && ( *pucIPAddress <= ( uint8_t ) '9' ) )
|
|
{
|
|
/* Move previous read characters into the next decimal
|
|
position. */
|
|
ulValue *= ucDecimalBase;
|
|
|
|
/* Add the binary value of the ascii character. */
|
|
ulValue += ( *pucIPAddress - ( uint8_t ) '0' );
|
|
|
|
/* Move to next character in the string. */
|
|
pucIPAddress++;
|
|
}
|
|
|
|
/* Check characters were read. */
|
|
if( pucIPAddress == pucPointerOnEntering )
|
|
{
|
|
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( *pucIPAddress != ( uint8_t ) '.' )
|
|
{
|
|
xResult = pdFAIL;
|
|
}
|
|
else
|
|
{
|
|
/* Move past the dot. */
|
|
pucIPAddress++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( xResult == pdFAIL )
|
|
{
|
|
/* No point going on. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( *pucIPAddress != ( 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 */
|
|
/*-----------------------------------------------------------*/
|
|
|