/* | |
* FreeRTOS+TCP V2.0.1 | |
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "semphr.h" | |
/* FreeRTOS+TCP includes. */ | |
#include "FreeRTOS_UDP_IP.h" | |
#include "FreeRTOS_IP.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "FreeRTOS_IP_Private.h" | |
#include "FreeRTOS_DNS.h" | |
#include "NetworkBufferManagement.h" | |
/* The ItemValue of the sockets xBoundSocketListItem member holds the socket's | |
port number. */ | |
#define socketSET_SOCKET_PORT( pxSocket, usPort ) listSET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ), ( usPort ) ) | |
#define socketGET_SOCKET_PORT( pxSocket ) listGET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ) ) | |
/* Test if a socket it bound which means it is either included in | |
xBoundUDPSocketsList or xBoundTCPSocketsList */ | |
#define socketSOCKET_IS_BOUND( pxSocket ) ( listLIST_ITEM_CONTAINER( & ( pxSocket )->xBoundSocketListItem ) != NULL ) | |
/* 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. */ | |
/* _HT_ thinks that the default of 0xc000 is pretty high */ | |
#if !defined( socketAUTO_PORT_ALLOCATION_START_NUMBER ) | |
#define socketAUTO_PORT_ALLOCATION_START_NUMBER ( ( uint16_t ) 0xc000 ) | |
#endif | |
/* 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 ) | |
#define socketAUTO_PORT_ALLOCATION_MAX_NUMBER ( ( uint16_t ) 0xff00 ) | |
/* The number of octets that make up an IP address. */ | |
#define socketMAX_IP_ADDRESS_OCTETS 4u | |
/* A block time of 0 simply means "don't block". */ | |
#define socketDONT_BLOCK ( ( TickType_t ) 0 ) | |
#if( ( ipconfigUSE_TCP == 1 ) && !defined( ipTCP_TIMER_PERIOD_MS ) ) | |
#define ipTCP_TIMER_PERIOD_MS ( 1000 ) | |
#endif | |
/* The next private port number to use when binding a client socket is stored in | |
the usNextPortToUse[] array - which has either 1 or two indexes depending on | |
whether TCP is being supported. */ | |
#if( ipconfigUSE_TCP == 1 ) | |
#define socketPROTOCOL_COUNT 2 | |
#else | |
#define socketPROTOCOL_COUNT 1 | |
#endif | |
/* Indexes into the usNextPortToUse[] array for UDP and TCP sockets | |
respectively. */ | |
#define socketNEXT_UDP_PORT_NUMBER_INDEX 0 | |
#define socketNEXT_TCP_PORT_NUMBER_INDEX 1 | |
/*-----------------------------------------------------------*/ | |
/* | |
* Allocate the next port number from the private allocation range. | |
* TCP and UDP each have their own series of port numbers | |
* ulProtocol is either ipPROTOCOL_UDP or ipPROTOCOL_TCP | |
*/ | |
static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol ); | |
/* | |
* Return the list item from within pxList that has an item value of | |
* xWantedItemValue. If there is no such list item return NULL. | |
*/ | |
static const ListItem_t * pxListFindListItemWithValue( const List_t *pxList, TickType_t xWantedItemValue ); | |
/* | |
* Return pdTRUE only if pxSocket is valid and bound, as far as can be | |
* determined. | |
*/ | |
static BaseType_t prvValidSocket( FreeRTOS_Socket_t *pxSocket, BaseType_t xProtocol, BaseType_t xIsBound ); | |
/* | |
* Before creating a socket, check the validity of the parameters used | |
* and find the size of the socket space, which is different for UDP and TCP | |
*/ | |
static BaseType_t prvDetermineSocketSize( BaseType_t xDomain, BaseType_t xType, BaseType_t xProtocol, size_t *pxSocketSize ); | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Create a txStream or a rxStream, depending on the parameter 'xIsInputStream' | |
*/ | |
static StreamBuffer_t *prvTCPCreateStream (FreeRTOS_Socket_t *pxSocket, BaseType_t xIsInputStream ); | |
#endif /* ipconfigUSE_TCP == 1 */ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Called from FreeRTOS_send(): some checks which will be done before | |
* sending a TCP packed. | |
*/ | |
static int32_t prvTCPSendCheck( FreeRTOS_Socket_t *pxSocket, size_t xDataLength ); | |
#endif /* ipconfigUSE_TCP */ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* When a child socket gets closed, make sure to update the child-count of the parent | |
*/ | |
static void prvTCPSetSocketCount( FreeRTOS_Socket_t *pxSocketToDelete ); | |
#endif /* ipconfigUSE_TCP == 1 */ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Called from FreeRTOS_connect(): make some checks and if allowed, send a | |
* message to the IP-task to start connecting to a remote socket | |
*/ | |
static BaseType_t prvTCPConnectStart( FreeRTOS_Socket_t *pxSocket, struct freertos_sockaddr *pxAddress ); | |
#endif /* ipconfigUSE_TCP */ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* Executed by the IP-task, it will check all sockets belonging to a set */ | |
static FreeRTOS_Socket_t *prvFindSelectedSocket( SocketSelect_t *pxSocketSet ); | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
/* 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. */ | |
List_t xBoundUDPSocketsList; | |
#if ipconfigUSE_TCP == 1 | |
List_t xBoundTCPSocketsList; | |
#endif /* ipconfigUSE_TCP == 1 */ | |
/* Holds the next private port number to use when binding a client socket for | |
UDP, and if ipconfigUSE_TCP is set to 1, also TCP. UDP uses index | |
socketNEXT_UDP_PORT_NUMBER_INDEX and TCP uses index | |
socketNEXT_TCP_PORT_NUMBER_INDEX. The initial value is set to be between | |
socketAUTO_PORT_ALLOCATION_RESET_NUMBER and socketAUTO_PORT_ALLOCATION_MAX_NUMBER | |
when the IP stack is initialised. Note ipconfigRAND32() is used, which must be | |
seeded prior to the IP task being started. */ | |
static uint16_t usNextPortToUse[ socketPROTOCOL_COUNT ] = { 0 }; | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvValidSocket( FreeRTOS_Socket_t *pxSocket, BaseType_t xProtocol, BaseType_t xIsBound ) | |
{ | |
BaseType_t xReturn = pdTRUE; | |
if( ( pxSocket == NULL ) || ( pxSocket == FREERTOS_INVALID_SOCKET ) ) | |
{ | |
xReturn = pdFALSE; | |
} | |
else if( ( xIsBound != pdFALSE ) && ( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE ) ) | |
{ | |
/* The caller expects the socket to be bound, but it isn't. */ | |
xReturn = pdFALSE; | |
} | |
else if( pxSocket->ucProtocol != ( uint8_t ) xProtocol ) | |
{ | |
/* Socket has a wrong type (UDP != TCP). */ | |
xReturn = pdFALSE; | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void vNetworkSocketsInit( void ) | |
{ | |
const uint32_t ulAutoPortRange = socketAUTO_PORT_ALLOCATION_MAX_NUMBER - socketAUTO_PORT_ALLOCATION_RESET_NUMBER; | |
uint32_t ulRandomPort; | |
vListInitialise( &xBoundUDPSocketsList ); | |
/* Determine the first anonymous UDP port number to get assigned. Give it | |
a random value in order to avoid confusion about port numbers being used | |
earlier, before rebooting the device. Start with the first auto port | |
number, then add a random offset up to a maximum of the range of numbers. */ | |
ulRandomPort = socketAUTO_PORT_ALLOCATION_START_NUMBER; | |
ulRandomPort += ( ipconfigRAND32() % ulAutoPortRange ); | |
usNextPortToUse[ socketNEXT_UDP_PORT_NUMBER_INDEX ] = ( uint16_t ) ulRandomPort; | |
#if( ipconfigUSE_TCP == 1 ) | |
{ | |
extern uint32_t ulNextInitialSequenceNumber; | |
ulNextInitialSequenceNumber = ipconfigRAND32(); | |
/* Determine the first anonymous TCP port number to get assigned. */ | |
ulRandomPort = socketAUTO_PORT_ALLOCATION_START_NUMBER; | |
ulRandomPort += ( ipconfigRAND32() % ulAutoPortRange ); | |
usNextPortToUse[ socketNEXT_TCP_PORT_NUMBER_INDEX ] = ( uint16_t ) ulRandomPort; | |
vListInitialise( &xBoundTCPSocketsList ); | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
} | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvDetermineSocketSize( BaseType_t xDomain, BaseType_t xType, BaseType_t xProtocol, size_t *pxSocketSize ) | |
{ | |
BaseType_t xReturn = pdPASS; | |
FreeRTOS_Socket_t *pxSocket; | |
/* Asserts must not appear before it has been determined that the network | |
task is ready - otherwise the asserts will fail. */ | |
if( xIPIsNetworkTaskReady() == pdFALSE ) | |
{ | |
xReturn = pdFAIL; | |
} | |
else | |
{ | |
/* Only Ethernet is currently supported. */ | |
configASSERT( xDomain == FREERTOS_AF_INET ); | |
/* Check if the UDP socket-list has been initialised. */ | |
configASSERT( listLIST_IS_INITIALISED( &xBoundUDPSocketsList ) ); | |
#if( ipconfigUSE_TCP == 1 ) | |
{ | |
/* Check if the TCP socket-list has been initialised. */ | |
configASSERT( listLIST_IS_INITIALISED( &xBoundTCPSocketsList ) ); | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
if( xProtocol == FREERTOS_IPPROTO_UDP ) | |
{ | |
if( xType != FREERTOS_SOCK_DGRAM ) | |
{ | |
xReturn = pdFAIL; | |
} | |
/* In case a UDP socket is created, do not allocate space for TCP data. */ | |
*pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xUDP ); | |
} | |
#if( ipconfigUSE_TCP == 1 ) | |
else if( xProtocol == FREERTOS_IPPROTO_TCP ) | |
{ | |
if( xType != FREERTOS_SOCK_STREAM ) | |
{ | |
xReturn = pdFAIL; | |
} | |
*pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xTCP ); | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
else | |
{ | |
xReturn = pdFAIL; | |
} | |
} | |
/* In case configASSERT() is not used */ | |
( void )xDomain; | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
/* FreeRTOS_socket() allocates and initiates a socket */ | |
Socket_t FreeRTOS_socket( BaseType_t xDomain, BaseType_t xType, BaseType_t xProtocol ) | |
{ | |
FreeRTOS_Socket_t *pxSocket; | |
size_t uxSocketSize; | |
EventGroupHandle_t xEventGroup; | |
Socket_t xReturn; | |
if( prvDetermineSocketSize( xDomain, xType, xProtocol, &uxSocketSize ) == pdFAIL ) | |
{ | |
xReturn = FREERTOS_INVALID_SOCKET; | |
} | |
else | |
{ | |
/* Allocate the structure that will hold the socket information. The | |
size depends on the type of socket: UDP sockets need less space. A | |
define 'pvPortMallocSocket' will used to allocate the necessary space. | |
By default it points to the FreeRTOS function 'pvPortMalloc()'. */ | |
pxSocket = ( FreeRTOS_Socket_t * ) pvPortMallocSocket( uxSocketSize ); | |
if( pxSocket == NULL ) | |
{ | |
pxSocket = ( FreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET; | |
iptraceFAILED_TO_CREATE_SOCKET(); | |
} | |
else if( ( xEventGroup = xEventGroupCreate() ) == NULL ) | |
{ | |
vPortFreeSocket( pxSocket ); | |
pxSocket = ( FreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET; | |
iptraceFAILED_TO_CREATE_EVENT_GROUP(); | |
} | |
else | |
{ | |
/* Clear the entire space to avoid nulling individual entries. */ | |
memset( pxSocket, '\0', uxSocketSize ); | |
pxSocket->xEventGroup = xEventGroup; | |
/* 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. */ | |
if( xProtocol == FREERTOS_IPPROTO_UDP ) | |
{ | |
vListInitialise( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); | |
#if( ipconfigUDP_MAX_RX_PACKETS > 0 ) | |
{ | |
pxSocket->u.xUDP.uxMaxPackets = ( UBaseType_t ) ipconfigUDP_MAX_RX_PACKETS; | |
} | |
#endif /* ipconfigUDP_MAX_RX_PACKETS > 0 */ | |
} | |
vListInitialiseItem( &( pxSocket->xBoundSocketListItem ) ); | |
listSET_LIST_ITEM_OWNER( &( pxSocket->xBoundSocketListItem ), ( void * ) pxSocket ); | |
pxSocket->xReceiveBlockTime = ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME; | |
pxSocket->xSendBlockTime = ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME; | |
pxSocket->ucSocketOptions = ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT; | |
pxSocket->ucProtocol = ( uint8_t ) xProtocol; /* protocol: UDP or TCP */ | |
#if( ipconfigUSE_TCP == 1 ) | |
{ | |
if( xProtocol == FREERTOS_IPPROTO_TCP ) | |
{ | |
/* StreamSize is expressed in number of bytes */ | |
/* Round up buffer sizes to nearest multiple of MSS */ | |
pxSocket->u.xTCP.usInitMSS = pxSocket->u.xTCP.usCurMSS = ipconfigTCP_MSS; | |
pxSocket->u.xTCP.uxRxStreamSize = ( size_t ) ipconfigTCP_RX_BUFFER_LENGTH; | |
pxSocket->u.xTCP.uxTxStreamSize = ( size_t ) FreeRTOS_round_up( ipconfigTCP_TX_BUFFER_LENGTH, ipconfigTCP_MSS ); | |
/* Use half of the buffer size of the TCP windows */ | |
#if ( ipconfigUSE_TCP_WIN == 1 ) | |
{ | |
pxSocket->u.xTCP.uxRxWinSize = FreeRTOS_max_uint32( 1UL, ( uint32_t ) ( pxSocket->u.xTCP.uxRxStreamSize / 2 ) / ipconfigTCP_MSS ); | |
pxSocket->u.xTCP.uxTxWinSize = FreeRTOS_max_uint32( 1UL, ( uint32_t ) ( pxSocket->u.xTCP.uxTxStreamSize / 2 ) / ipconfigTCP_MSS ); | |
} | |
#else | |
{ | |
pxSocket->u.xTCP.uxRxWinSize = 1u; | |
pxSocket->u.xTCP.uxTxWinSize = 1u; | |
} | |
#endif | |
/* The above values are just defaults, and can be overridden by | |
calling FreeRTOS_setsockopt(). No buffers will be allocated until a | |
socket is connected and data is exchanged. */ | |
} | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
} | |
xReturn = ( Socket_t ) pxSocket; | |
} | |
/* Remove compiler warnings in the case the configASSERT() is not defined. */ | |
( void ) xDomain; | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
SocketSet_t FreeRTOS_CreateSocketSet( void ) | |
{ | |
SocketSelect_t *pxSocketSet; | |
pxSocketSet = ( SocketSelect_t * ) pvPortMalloc( sizeof( *pxSocketSet ) ); | |
if( pxSocketSet != NULL ) | |
{ | |
memset( pxSocketSet, '\0', sizeof( *pxSocketSet ) ); | |
pxSocketSet->xSelectGroup = xEventGroupCreate(); | |
if( pxSocketSet->xSelectGroup == NULL ) | |
{ | |
vPortFree( ( void* ) pxSocketSet ); | |
pxSocketSet = NULL; | |
} | |
} | |
return ( SocketSet_t * ) pxSocketSet; | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
void FreeRTOS_DeleteSocketSet( SocketSet_t xSocketSet ) | |
{ | |
SocketSelect_t *pxSocketSet = ( SocketSelect_t*) xSocketSet; | |
vEventGroupDelete( pxSocketSet->xSelectGroup ); | |
vPortFree( ( void* ) pxSocketSet ); | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* Add a socket to a set */ | |
void FreeRTOS_FD_SET( Socket_t xSocket, SocketSet_t xSocketSet, EventBits_t xSelectBits ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
SocketSelect_t *pxSocketSet = ( SocketSelect_t * ) xSocketSet; | |
configASSERT( pxSocket != NULL ); | |
configASSERT( xSocketSet != NULL ); | |
/* Make sure we're not adding bits which are reserved for internal use, | |
such as eSELECT_CALL_IP */ | |
pxSocket->xSelectBits |= ( xSelectBits & eSELECT_ALL ); | |
if( ( pxSocket->xSelectBits & eSELECT_ALL ) != 0 ) | |
{ | |
/* Adding a socket to a socket set. */ | |
pxSocket->pxSocketSet = ( SocketSelect_t * ) xSocketSet; | |
/* Now have the IP-task call vSocketSelect() to see if the set contains | |
any sockets which are 'ready' and set the proper bits. | |
By setting 'bApiCalled = false', vSocketSelect() knows that it was | |
not called from a user API */ | |
pxSocketSet->bApiCalled = pdFALSE; | |
prvFindSelectedSocket( pxSocketSet ); | |
} | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* Clear select bits for a socket | |
If the mask becomes 0, remove the socket from the set */ | |
void FreeRTOS_FD_CLR( Socket_t xSocket, SocketSet_t xSocketSet, EventBits_t xSelectBits ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
configASSERT( pxSocket != NULL ); | |
configASSERT( xSocketSet != NULL ); | |
pxSocket->xSelectBits &= ~( xSelectBits & eSELECT_ALL ); | |
if( ( pxSocket->xSelectBits & eSELECT_ALL ) != 0 ) | |
{ | |
pxSocket->pxSocketSet = ( SocketSelect_t *)xSocketSet; | |
} | |
else | |
{ | |
/* disconnect it from the socket set */ | |
pxSocket->pxSocketSet = ( SocketSelect_t *)NULL; | |
} | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* Test if a socket belongs to a socket-set */ | |
EventBits_t FreeRTOS_FD_ISSET( Socket_t xSocket, SocketSet_t xSocketSet ) | |
{ | |
EventBits_t xReturn; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
configASSERT( pxSocket != NULL ); | |
configASSERT( xSocketSet != NULL ); | |
if( xSocketSet == ( SocketSet_t ) pxSocket->pxSocketSet ) | |
{ | |
/* Make sure we're not adding bits which are reserved for internal | |
use. */ | |
xReturn = pxSocket->xSocketBits & eSELECT_ALL; | |
} | |
else | |
{ | |
xReturn = 0; | |
} | |
return xReturn; | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* The select() statement: wait for an event to occur on any of the sockets | |
included in a socket set */ | |
BaseType_t FreeRTOS_select( SocketSet_t xSocketSet, TickType_t xBlockTimeTicks ) | |
{ | |
TimeOut_t xTimeOut; | |
TickType_t xRemainingTime; | |
SocketSelect_t *pxSocketSet = ( SocketSelect_t*) xSocketSet; | |
BaseType_t xResult; | |
configASSERT( xSocketSet != NULL ); | |
/* Only in the first round, check for non-blocking */ | |
xRemainingTime = xBlockTimeTicks; | |
/* Fetch the current time */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
for( ;; ) | |
{ | |
/* Find a socket which might have triggered the bit | |
This function might return immediately or block for a limited time */ | |
xResult = ( BaseType_t ) xEventGroupWaitBits( pxSocketSet->xSelectGroup, eSELECT_ALL, pdFALSE, pdFALSE, xRemainingTime ); | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
{ | |
if( ( xResult & eSELECT_INTR ) != 0u ) | |
{ | |
xEventGroupClearBits( pxSocketSet->xSelectGroup, eSELECT_INTR ); | |
FreeRTOS_debug_printf( ( "FreeRTOS_select: interrupted\n" ) ); | |
break; | |
} | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
/* Have the IP-task find the socket which had an event */ | |
pxSocketSet->bApiCalled = pdTRUE; | |
prvFindSelectedSocket( pxSocketSet ); | |
xResult = ( BaseType_t ) xEventGroupGetBits( pxSocketSet->xSelectGroup ); | |
if( xResult != 0 ) | |
{ | |
break; | |
} | |
/* Has the timeout been reached? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) | |
{ | |
break; | |
} | |
} | |
return xResult; | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
/* Send a message to the IP-task to have it check all sockets belonging to | |
'pxSocketSet' */ | |
static FreeRTOS_Socket_t *prvFindSelectedSocket( SocketSelect_t *pxSocketSet ) | |
{ | |
IPStackEvent_t xSelectEvent; | |
FreeRTOS_Socket_t *xReturn; | |
xSelectEvent.eEventType = eSocketSelectEvent; | |
xSelectEvent.pvData = ( void * ) pxSocketSet; | |
/* while the IP-task works on the request, the API will block on | |
'eSELECT_CALL_IP'. So clear it first. */ | |
xEventGroupClearBits( pxSocketSet->xSelectGroup, eSELECT_CALL_IP ); | |
/* Now send the socket select event */ | |
if( xSendEventStructToIPTask( &xSelectEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) | |
{ | |
/* Oops, we failed to wake-up the IP task. No use to wait for it. */ | |
FreeRTOS_debug_printf( ( "prvFindSelectedSocket: failed\n" ) ); | |
xReturn = NULL; | |
} | |
else | |
{ | |
/* As soon as the IP-task is ready, it will set 'eSELECT_CALL_IP' to | |
wakeup the calling API */ | |
xEventGroupWaitBits( pxSocketSet->xSelectGroup, eSELECT_CALL_IP, pdTRUE, pdFALSE, portMAX_DELAY ); | |
/* Return 'pxSocket' which is set by the IP-task */ | |
xReturn = pxSocketSet->pxSocket; | |
} | |
return xReturn; | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */ | |
/*-----------------------------------------------------------*/ | |
/* | |
* FreeRTOS_recvfrom: receive data from a bound socket | |
* In this library, the function can only be used with connectionsless sockets | |
* (UDP) | |
*/ | |
int32_t FreeRTOS_recvfrom( Socket_t xSocket, void *pvBuffer, size_t xBufferLength, BaseType_t xFlags, struct freertos_sockaddr *pxSourceAddress, socklen_t *pxSourceAddressLength ) | |
{ | |
BaseType_t lPacketCount = 0; | |
NetworkBufferDescriptor_t *pxNetworkBuffer; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
TickType_t xRemainingTime = ( TickType_t ) 0; /* Obsolete assignment, but some compilers output a warning if its not done. */ | |
BaseType_t xTimed = pdFALSE; | |
TimeOut_t xTimeOut; | |
int32_t lReturn; | |
EventBits_t xEventBits = ( EventBits_t ) 0; | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_UDP, pdTRUE ) == pdFALSE ) | |
{ | |
return -pdFREERTOS_ERRNO_EINVAL; | |
} | |
lPacketCount = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); | |
/* The function prototype is designed to maintain the expected Berkeley | |
sockets standard, but this implementation does not use all the parameters. */ | |
( void ) pxSourceAddressLength; | |
while( lPacketCount == 0 ) | |
{ | |
if( xTimed == pdFALSE ) | |
{ | |
/* Check to see if the socket is non blocking on the first | |
iteration. */ | |
xRemainingTime = pxSocket->xReceiveBlockTime; | |
if( xRemainingTime == ( TickType_t ) 0 ) | |
{ | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
{ | |
/* Just check for the interrupt flag. */ | |
xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_INTR, | |
pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK ); | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
break; | |
} | |
if( ( xFlags & FREERTOS_MSG_DONTWAIT ) != 0 ) | |
{ | |
break; | |
} | |
/* To ensure this part only executes once. */ | |
xTimed = pdTRUE; | |
/* Fetch the current time. */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
} | |
/* Wait for arrival of data. While waiting, the IP-task may set the | |
'eSOCKET_RECEIVE' bit in 'xEventGroup', if it receives data for this | |
socket, thus unblocking this API call. */ | |
xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_RECEIVE | eSOCKET_INTR, | |
pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
{ | |
if( ( xEventBits & eSOCKET_INTR ) != 0 ) | |
{ | |
if( ( xEventBits & eSOCKET_RECEIVE ) != 0 ) | |
{ | |
/* Shouldn't have cleared the eSOCKET_RECEIVE flag. */ | |
xEventGroupSetBits( pxSocket->xEventGroup, eSOCKET_RECEIVE ); | |
} | |
break; | |
} | |
} | |
#else | |
{ | |
( void ) xEventBits; | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
lPacketCount = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); | |
if( lPacketCount != 0 ) | |
{ | |
break; | |
} | |
/* Has the timeout been reached ? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) ) | |
{ | |
break; | |
} | |
} /* while( lPacketCount == 0 ) */ | |
if( lPacketCount != 0 ) | |
{ | |
taskENTER_CRITICAL(); | |
{ | |
/* The owner of the list item is the network buffer. */ | |
pxNetworkBuffer = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); | |
if( ( xFlags & FREERTOS_MSG_PEEK ) == 0 ) | |
{ | |
/* Remove the network buffer from the list of buffers waiting to | |
be processed by the socket. */ | |
uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); | |
} | |
} | |
taskEXIT_CRITICAL(); | |
/* 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; | |
} | |
if( ( xFlags & FREERTOS_ZERO_COPY ) == 0 ) | |
{ | |
/* The zero copy flag is not set. Truncate the length if it won't | |
fit in the provided buffer. */ | |
if( lReturn > ( int32_t ) xBufferLength ) | |
{ | |
iptraceRECVFROM_DISCARDING_BYTES( ( xBufferLength - lReturn ) ); | |
lReturn = ( int32_t )xBufferLength; | |
} | |
/* Copy the received data into the provided buffer, then release the | |
network buffer. */ | |
memcpy( pvBuffer, ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ), ( size_t )lReturn ); | |
if( ( xFlags & FREERTOS_MSG_PEEK ) == 0 ) | |
{ | |
vReleaseNetworkBufferAndDescriptor( 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_IPv4 ] ) ); | |
} | |
} | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
else if( ( xEventBits & eSOCKET_INTR ) != 0 ) | |
{ | |
lReturn = -pdFREERTOS_ERRNO_EINTR; | |
iptraceRECVFROM_INTERRUPTED(); | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
else | |
{ | |
lReturn = -pdFREERTOS_ERRNO_EWOULDBLOCK; | |
iptraceRECVFROM_TIMEOUT(); | |
} | |
return lReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
int32_t FreeRTOS_sendto( Socket_t xSocket, const void *pvBuffer, size_t xTotalDataLength, BaseType_t xFlags, const struct freertos_sockaddr *pxDestinationAddress, socklen_t xDestinationAddressLength ) | |
{ | |
NetworkBufferDescriptor_t *pxNetworkBuffer; | |
IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; | |
TimeOut_t xTimeOut; | |
TickType_t xTicksToWait; | |
int32_t lReturn = 0; | |
FreeRTOS_Socket_t *pxSocket; | |
pxSocket = ( FreeRTOS_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( pvBuffer ); | |
if( xTotalDataLength <= ( size_t ) ipMAX_UDP_PAYLOAD_LENGTH ) | |
{ | |
/* 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. */ | |
if( ( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) || | |
( FreeRTOS_bind( xSocket, NULL, 0u ) == 0 ) ) | |
{ | |
xTicksToWait = pxSocket->xSendBlockTime; | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
{ | |
if( xIsCallingFromIPTask() != pdFALSE ) | |
{ | |
/* If this send function is called from within a call-back | |
handler it may not block, otherwise chances would be big to | |
get a deadlock: the IP-task waiting for itself. */ | |
xTicksToWait = ( TickType_t )0; | |
} | |
} | |
#endif /* ipconfigUSE_CALLBACKS */ | |
if( ( xFlags & FREERTOS_MSG_DONTWAIT ) != 0 ) | |
{ | |
xTicksToWait = ( TickType_t ) 0; | |
} | |
if( ( xFlags & FREERTOS_ZERO_COPY ) == 0 ) | |
{ | |
/* Zero copy is not set, so obtain a network buffer into | |
which the payload will be copied. */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
/* Block until a buffer becomes available, or until a | |
timeout has been reached */ | |
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( xTotalDataLength + sizeof( UDPPacket_t ), xTicksToWait ); | |
if( pxNetworkBuffer != NULL ) | |
{ | |
memcpy( ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ), ( void * ) pvBuffer, xTotalDataLength ); | |
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE ) | |
{ | |
/* The entire block time has been used up. */ | |
xTicksToWait = ( TickType_t ) 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. */ | |
pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( (void*)pvBuffer ); | |
} | |
if( pxNetworkBuffer != NULL ) | |
{ | |
pxNetworkBuffer->xDataLength = xTotalDataLength; | |
pxNetworkBuffer->usPort = pxDestinationAddress->sin_port; | |
pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_PORT( 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; | |
/* Ask the IP-task to send this packet */ | |
if( xSendEventStructToIPTask( &xStackTxEvent, xTicksToWait ) == pdPASS ) | |
{ | |
/* The packet was successfully sent to the IP task. */ | |
lReturn = ( int32_t ) xTotalDataLength; | |
#if( ipconfigUSE_CALLBACKS == 1 ) | |
{ | |
if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleSent ) ) | |
{ | |
pxSocket->u.xUDP.pxHandleSent( (Socket_t *)pxSocket, xTotalDataLength ); | |
} | |
} | |
#endif /* ipconfigUSE_CALLBACKS */ | |
} | |
else | |
{ | |
/* If the buffer was allocated in this function, release | |
it. */ | |
if( ( xFlags & FREERTOS_ZERO_COPY ) == 0 ) | |
{ | |
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); | |
} | |
iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); | |
} | |
} | |
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. */ | |
iptraceSENDTO_DATA_TOO_LONG(); | |
} | |
return lReturn; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
/* | |
* FreeRTOS_bind() : binds a sockt to a local port number. If port 0 is | |
* provided, a system provided port number will be assigned. This function can | |
* be used for both UDP and TCP sockets. The actual binding will be performed | |
* by the IP-task to avoid mutual access to the bound-socket-lists | |
* (xBoundUDPSocketsList or xBoundTCPSocketsList). | |
*/ | |
BaseType_t FreeRTOS_bind( Socket_t xSocket, struct freertos_sockaddr * pxAddress, socklen_t xAddressLength ) | |
{ | |
IPStackEvent_t xBindEvent; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
BaseType_t xReturn = 0; | |
( void ) xAddressLength; | |
if( ( pxSocket == NULL ) || ( pxSocket == FREERTOS_INVALID_SOCKET ) ) | |
{ | |
xReturn = -pdFREERTOS_ERRNO_EINVAL; | |
} | |
/* Once a socket is bound to a port, it can not be bound to a different | |
port number */ | |
else if( socketSOCKET_IS_BOUND( pxSocket) != pdFALSE ) | |
{ | |
/* The socket is already bound. */ | |
FreeRTOS_debug_printf( ( "vSocketBind: Socket already bound to %d\n", pxSocket->usLocalPort ) ); | |
xReturn = -pdFREERTOS_ERRNO_EINVAL; | |
} | |
else | |
{ | |
/* Prepare a messages to the IP-task in order to perform the binding. | |
The desired port number will be passed in usLocalPort. */ | |
xBindEvent.eEventType = eSocketBindEvent; | |
xBindEvent.pvData = ( void * ) xSocket; | |
if( pxAddress != NULL ) | |
{ | |
pxSocket->usLocalPort = FreeRTOS_ntohs( pxAddress->sin_port ); | |
} | |
else | |
{ | |
/* Caller wants to bind to a random port number. */ | |
pxSocket->usLocalPort = 0u; | |
} | |
/* portMAX_DELAY is used as a the time-out parameter, as binding *must* | |
succeed before the socket can be used. _RB_ The use of an infinite | |
block time needs be changed as it could result in the task hanging. */ | |
if( xSendEventStructToIPTask( &xBindEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL ) | |
{ | |
/* Failed to wake-up the IP-task, no use to wait for it */ | |
FreeRTOS_debug_printf( ( "FreeRTOS_bind: send event failed\n" ) ); | |
xReturn = -pdFREERTOS_ERRNO_ECANCELED; | |
} | |
else | |
{ | |
/* The IP-task will set the 'eSOCKET_BOUND' bit when it has done its | |
job. */ | |
xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_BOUND, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, portMAX_DELAY ); | |
if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE ) | |
{ | |
xReturn = -pdFREERTOS_ERRNO_EINVAL; | |
} | |
} | |
} | |
return xReturn; | |
} | |
/* | |
* vSocketBind(): internal version of bind() that should not be called directly. | |
* 'xInternal' is used for TCP sockets only: it allows to have several | |
* (connected) child sockets bound to the same server port. | |
*/ | |
BaseType_t vSocketBind( FreeRTOS_Socket_t *pxSocket, struct freertos_sockaddr * pxAddress, size_t uxAddressLength, BaseType_t xInternal ) | |
{ | |
BaseType_t xReturn = 0; /* In Berkeley sockets, 0 means pass for bind(). */ | |
List_t *pxSocketList; | |
#if( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 ) | |
struct freertos_sockaddr xAddress; | |
#endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND */ | |
#if( ipconfigUSE_TCP == 1 ) | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
pxSocketList = &xBoundTCPSocketsList; | |
} | |
else | |
#endif /* ipconfigUSE_TCP == 1 */ | |
{ | |
pxSocketList = &xBoundUDPSocketsList; | |
} | |
/* The function prototype is designed to maintain the expected Berkeley | |
sockets standard, but this implementation does not use all the parameters. */ | |
( void ) uxAddressLength; | |
configASSERT( pxSocket ); | |
configASSERT( pxSocket != 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; | |
/* For now, put it to zero, will be assigned later */ | |
pxAddress->sin_port = 0u; | |
} | |
} | |
#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 == 0u ) | |
{ | |
pxAddress->sin_port = prvGetPrivatePortNumber( ( BaseType_t ) pxSocket->ucProtocol ); | |
} | |
/* If vSocketBind() is called from the API FreeRTOS_bind() it has been | |
confirmed that the socket was not yet bound to a port. If it is called | |
from the IP-task, no such check is necessary. */ | |
/* Check to ensure the port is not already in use. If the bind is | |
called internally, a port MAY be used by more than one socket. */ | |
if( ( ( xInternal == pdFALSE ) || ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) ) && | |
( pxListFindListItemWithValue( pxSocketList, ( TickType_t ) pxAddress->sin_port ) != NULL ) ) | |
{ | |
FreeRTOS_debug_printf( ( "vSocketBind: %sP port %d in use\n", | |
pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ? "TC" : "UD", | |
FreeRTOS_ntohs( pxAddress->sin_port ) ) ); | |
xReturn = -pdFREERTOS_ERRNO_EADDRINUSE; | |
} | |
else | |
{ | |
/* Allocate the port number to the socket. | |
This macro will set 'xBoundSocketListItem->xItemValue' */ | |
socketSET_SOCKET_PORT( pxSocket, pxAddress->sin_port ); | |
/* And also store it in a socket field 'usLocalPort' in host-byte-order, | |
mostly used for logging and debugging purposes */ | |
pxSocket->usLocalPort = FreeRTOS_ntohs( pxAddress->sin_port ); | |
/* Add the socket to the list of bound ports. */ | |
{ | |
/* If the network driver can iterate through 'xBoundUDPSocketsList', | |
by calling xPortHasUDPSocket() then the IP-task must temporarily | |
suspend the scheduler to keep the list in a consistent state. */ | |
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) | |
{ | |
vTaskSuspendAll(); | |
} | |
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ | |
/* Add the socket to 'xBoundUDPSocketsList' or 'xBoundTCPSocketsList' */ | |
vListInsertEnd( pxSocketList, &( pxSocket->xBoundSocketListItem ) ); | |
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) | |
{ | |
xTaskResumeAll(); | |
} | |
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ | |
} | |
} | |
} | |
else | |
{ | |
xReturn = -pdFREERTOS_ERRNO_EADDRNOTAVAIL; | |
FreeRTOS_debug_printf( ( "vSocketBind: Socket no addr\n" ) ); | |
} | |
if( xReturn != 0 ) | |
{ | |
iptraceBIND_FAILED( xSocket, ( FreeRTOS_ntohs( pxAddress->sin_port ) ) ); | |
} | |
return xReturn; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
/* | |
* Close a socket and free the allocated space | |
* In case of a TCP socket: the connection will not be closed automatically | |
* Subsequent messages for the closed socket will be responded to with a RST | |
* The IP-task will actually close the socket, after receiving a 'eSocketCloseEvent' message | |
*/ | |
BaseType_t FreeRTOS_closesocket( Socket_t xSocket ) | |
{ | |
BaseType_t xResult; | |
#if( ipconfigUSE_TCP == 1 ) && ( ipconfigUSE_CALLBACKS == 1 ) | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * )xSocket; | |
#endif | |
IPStackEvent_t xCloseEvent; | |
xCloseEvent.eEventType = eSocketCloseEvent; | |
xCloseEvent.pvData = ( void * ) xSocket; | |
if( ( xSocket == NULL ) || ( xSocket == FREERTOS_INVALID_SOCKET ) ) | |
{ | |
xResult = 0; | |
} | |
else | |
{ | |
#if( ( ipconfigUSE_TCP == 1 ) && ( ipconfigUSE_CALLBACKS == 1 ) ) | |
{ | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
/* Make sure that IP-task won't call the user callback's anymore */ | |
pxSocket->u.xTCP.pxHandleConnected = NULL; | |
pxSocket->u.xTCP.pxHandleReceive = NULL; | |
pxSocket->u.xTCP.pxHandleSent = NULL; | |
} | |
} | |
#endif /* ( ( ipconfigUSE_TCP == 1 ) && ( ipconfigUSE_CALLBACKS == 1 ) ) */ | |
/* Let the IP task close the socket to keep it synchronised with the | |
packet handling. */ | |
/* Note when changing the time-out value below, it must be checked who is calling | |
this function. If it is called by the IP-task, a deadlock could occur. | |
The IP-task would only call it in case of a user call-back */ | |
if( xSendEventStructToIPTask( &xCloseEvent, ( TickType_t ) 0 ) == pdFAIL ) | |
{ | |
FreeRTOS_debug_printf( ( "FreeRTOS_closesocket: failed\n" ) ); | |
xResult = -1; | |
} | |
else | |
{ | |
xResult = 1; | |
} | |
} | |
return xResult; | |
} | |
/* This is the internal version of FreeRTOS_closesocket() | |
* It will be called by the IPtask only to avoid problems with synchronicity | |
*/ | |
void *vSocketClose( FreeRTOS_Socket_t *pxSocket ) | |
{ | |
NetworkBufferDescriptor_t *pxNetworkBuffer; | |
#if( ipconfigUSE_TCP == 1 ) | |
{ | |
/* For TCP: clean up a little more. */ | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
#if( ipconfigUSE_TCP_WIN == 1 ) | |
{ | |
if( pxSocket->u.xTCP.pxAckMessage != NULL ) | |
{ | |
vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); | |
} | |
/* Free the resources which were claimed by the tcpWin member */ | |
vTCPWindowDestroy( &pxSocket->u.xTCP.xTCPWindow ); | |
} | |
#endif /* ipconfigUSE_TCP_WIN */ | |
/* Free the input and output streams */ | |
if( pxSocket->u.xTCP.rxStream != NULL ) | |
{ | |
vPortFreeLarge( pxSocket->u.xTCP.rxStream ); | |
} | |
if( pxSocket->u.xTCP.txStream != NULL ) | |
{ | |
vPortFreeLarge( pxSocket->u.xTCP.txStream ); | |
} | |
/* In case this is a child socket, make sure the child-count of the | |
parent socket is decreased. */ | |
prvTCPSetSocketCount( pxSocket ); | |
} | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
/* Socket must be unbound first, to ensure no more packets are queued on | |
it. */ | |
if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE ) | |
{ | |
/* If the network driver can iterate through 'xBoundUDPSocketsList', | |
by calling xPortHasUDPSocket(), then the IP-task must temporarily | |
suspend the scheduler to keep the list in a consistent state. */ | |
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) | |
{ | |
vTaskSuspendAll(); | |
} | |
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ | |
uxListRemove( &( pxSocket->xBoundSocketListItem ) ); | |
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) | |
{ | |
xTaskResumeAll(); | |
} | |
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ | |
} | |
/* Now the socket is not bound the list of waiting packets can be | |
drained. */ | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) | |
{ | |
while( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) > 0U ) | |
{ | |
pxNetworkBuffer = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList ) ); | |
uxListRemove( &( pxNetworkBuffer->xBufferListItem ) ); | |
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); | |
} | |
} | |
if( pxSocket->xEventGroup ) | |
{ | |
vEventGroupDelete( pxSocket->xEventGroup ); | |
} | |
#if( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_DEBUG_PRINTF != 0 ) | |
{ | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
FreeRTOS_debug_printf( ( "FreeRTOS_closesocket[%u to %lxip:%u]: buffers %lu socks %lu\n", | |
pxSocket->usLocalPort, | |
pxSocket->u.xTCP.ulRemoteIP, | |
pxSocket->u.xTCP.usRemotePort, | |
uxGetNumberOfFreeNetworkBuffers(), | |
listCURRENT_LIST_LENGTH( &xBoundTCPSocketsList ) ) ); | |
} | |
} | |
#endif /* ( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_DEBUG_PRINTF != 0 ) */ | |
/* Anf finally, after all resources have been freed, free the socket space */ | |
vPortFreeSocket( pxSocket ); | |
return 0; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
#if ipconfigUSE_TCP == 1 | |
/* | |
* When a child socket gets closed, make sure to update the child-count of the | |
* parent. When a listening parent socket is closed, make sure no child-sockets | |
* keep a pointer to it. | |
*/ | |
static void prvTCPSetSocketCount( FreeRTOS_Socket_t *pxSocketToDelete ) | |
{ | |
const ListItem_t *pxIterator; | |
const MiniListItem_t *pxEnd = ( const MiniListItem_t* )listGET_END_MARKER( &xBoundTCPSocketsList ); | |
FreeRTOS_Socket_t *pxOtherSocket; | |
uint16_t usLocalPort = pxSocketToDelete->usLocalPort; | |
for( pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd ); | |
pxIterator != ( const ListItem_t * ) pxEnd; | |
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) | |
{ | |
pxOtherSocket = ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); | |
if( ( pxOtherSocket->u.xTCP.ucTCPState == eTCP_LISTEN ) && | |
( pxOtherSocket->usLocalPort == usLocalPort ) && | |
( pxOtherSocket->u.xTCP.usChildCount ) ) | |
{ | |
pxOtherSocket->u.xTCP.usChildCount--; | |
FreeRTOS_debug_printf( ( "Lost: Socket %u now has %u / %u child%s\n", | |
pxOtherSocket->usLocalPort, | |
pxOtherSocket->u.xTCP.usChildCount, | |
pxOtherSocket->u.xTCP.usBacklog, | |
pxOtherSocket->u.xTCP.usChildCount == 1u ? "" : "ren" ) ); | |
break; | |
} | |
} | |
} | |
#endif /* ipconfigUSE_TCP == 1 */ | |
/*-----------------------------------------------------------*/ | |
BaseType_t FreeRTOS_setsockopt( Socket_t xSocket, int32_t lLevel, int32_t lOptionName, const void *pvOptionValue, size_t xOptionLength ) | |
{ | |
/* The standard Berkeley function returns 0 for success. */ | |
BaseType_t xReturn = -pdFREERTOS_ERRNO_EINVAL; | |
BaseType_t lOptionValue; | |
FreeRTOS_Socket_t *pxSocket; | |
pxSocket = ( FreeRTOS_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 = *( ( TickType_t * ) pvOptionValue ); | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_SNDTIMEO : | |
pxSocket->xSendBlockTime = *( ( TickType_t * ) pvOptionValue ); | |
if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP ) | |
{ | |
/* The send time out is capped for the reason stated in the | |
comments where ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS is defined | |
in FreeRTOSIPConfig.h (assuming an official configuration file | |
is being used. */ | |
if( pxSocket->xSendBlockTime > ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ) | |
{ | |
pxSocket->xSendBlockTime = ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS; | |
} | |
} | |
else | |
{ | |
/* For TCP socket, it isn't necessary to limit the blocking time | |
because the FreeRTOS_send() function does not wait for a network | |
buffer to become available. */ | |
} | |
xReturn = 0; | |
break; | |
#if( ipconfigUDP_MAX_RX_PACKETS > 0 ) | |
case FREERTOS_SO_UDP_MAX_RX_PACKETS: | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
pxSocket->u.xUDP.uxMaxPackets = *( ( UBaseType_t * ) pvOptionValue ); | |
xReturn = 0; | |
break; | |
#endif /* ipconfigUDP_MAX_RX_PACKETS */ | |
case FREERTOS_SO_UDPCKSUM_OUT : | |
/* Turn calculating of the UDP checksum on/off for this socket. */ | |
lOptionValue = ( BaseType_t ) pvOptionValue; | |
if( lOptionValue == 0 ) | |
{ | |
pxSocket->ucSocketOptions &= ( uint8_t ) ~FREERTOS_SO_UDPCKSUM_OUT; | |
} | |
else | |
{ | |
pxSocket->ucSocketOptions |= ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT; | |
} | |
xReturn = 0; | |
break; | |
#if( ipconfigUSE_CALLBACKS == 1 ) | |
#if( ipconfigUSE_TCP == 1 ) | |
case FREERTOS_SO_TCP_CONN_HANDLER: /* Set a callback for (dis)connection events */ | |
case FREERTOS_SO_TCP_RECV_HANDLER: /* Install a callback for receiving TCP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ | |
case FREERTOS_SO_TCP_SENT_HANDLER: /* Install a callback for sending TCP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ | |
#endif /* ipconfigUSE_TCP */ | |
case FREERTOS_SO_UDP_RECV_HANDLER: /* Install a callback for receiving UDP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ | |
case FREERTOS_SO_UDP_SENT_HANDLER: /* Install a callback for sending UDP data. Supply pointer to 'F_TCP_UDP_Handler_t' (see below) */ | |
{ | |
#if( ipconfigUSE_TCP == 1 ) | |
{ | |
UBaseType_t uxProtocol; | |
if( ( lOptionName == FREERTOS_SO_UDP_RECV_HANDLER ) || | |
( lOptionName == FREERTOS_SO_UDP_SENT_HANDLER ) ) | |
{ | |
uxProtocol = ( UBaseType_t ) FREERTOS_IPPROTO_UDP; | |
} | |
else | |
{ | |
uxProtocol = ( UBaseType_t ) FREERTOS_IPPROTO_TCP; | |
} | |
if( pxSocket->ucProtocol != ( uint8_t ) uxProtocol ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
} | |
#else | |
{ | |
/* No need to check if the socket has the right | |
protocol, because only UDP socket can be created. */ | |
} | |
#endif /* ipconfigUSE_TCP */ | |
switch( lOptionName ) | |
{ | |
#if ipconfigUSE_TCP == 1 | |
case FREERTOS_SO_TCP_CONN_HANDLER: | |
pxSocket->u.xTCP.pxHandleConnected = ((F_TCP_UDP_Handler_t *)pvOptionValue)->pxOnTCPConnected; | |
break; | |
case FREERTOS_SO_TCP_RECV_HANDLER: | |
pxSocket->u.xTCP.pxHandleReceive = ((F_TCP_UDP_Handler_t *)pvOptionValue)->pxOnTCPReceive; | |
break; | |
case FREERTOS_SO_TCP_SENT_HANDLER: | |
pxSocket->u.xTCP.pxHandleSent = ((F_TCP_UDP_Handler_t *)pvOptionValue)->pxOnTCPSent; | |
break; | |
#endif /* ipconfigUSE_TCP */ | |
case FREERTOS_SO_UDP_RECV_HANDLER: | |
pxSocket->u.xUDP.pxHandleReceive = ((F_TCP_UDP_Handler_t *)pvOptionValue)->pxOnUDPReceive; | |
break; | |
case FREERTOS_SO_UDP_SENT_HANDLER: | |
pxSocket->u.xUDP.pxHandleSent = ((F_TCP_UDP_Handler_t *)pvOptionValue)->pxOnUDPSent; | |
break; | |
default: | |
break; | |
} | |
} | |
xReturn = 0; | |
break; | |
#endif /* ipconfigUSE_CALLBACKS */ | |
#if( ipconfigUSE_TCP != 0 ) | |
#if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) | |
/* Each socket has a semaphore on which the using task normally | |
sleeps. */ | |
case FREERTOS_SO_SET_SEMAPHORE: | |
{ | |
pxSocket->pxUserSemaphore = *( ( SemaphoreHandle_t * ) pvOptionValue ); | |
xReturn = 0; | |
} | |
break; | |
#endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ | |
#if( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK != 0 ) | |
case FREERTOS_SO_WAKEUP_CALLBACK: | |
{ | |
/* Each socket can have a callback function that is executed | |
when there is an event the socket's owner might want to | |
process. */ | |
pxSocket->pxUserWakeCallback = ( SocketWakeupCallback_t ) pvOptionValue; | |
xReturn = 0; | |
} | |
break; | |
#endif /* ipconfigSOCKET_HAS_USER_WAKE_CALLBACK */ | |
case FREERTOS_SO_SNDBUF: /* Set the size of the send buffer, in units of MSS (TCP only) */ | |
case FREERTOS_SO_RCVBUF: /* Set the size of the receive buffer, in units of MSS (TCP only) */ | |
{ | |
uint32_t ulNewValue; | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
FreeRTOS_debug_printf( ( "Set SO_%sBUF: wrong socket type\n", | |
( lOptionName == FREERTOS_SO_SNDBUF ) ? "SND" : "RCV" ) ); | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( ( ( lOptionName == FREERTOS_SO_SNDBUF ) && ( pxSocket->u.xTCP.txStream != NULL ) ) || | |
( ( lOptionName == FREERTOS_SO_RCVBUF ) && ( pxSocket->u.xTCP.rxStream != NULL ) ) ) | |
{ | |
FreeRTOS_debug_printf( ( "Set SO_%sBUF: buffer already created\n", | |
( lOptionName == FREERTOS_SO_SNDBUF ) ? "SND" : "RCV" ) ); | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
ulNewValue = *( ( uint32_t * ) pvOptionValue ); | |
if( lOptionName == FREERTOS_SO_SNDBUF ) | |
{ | |
/* Round up to nearest MSS size */ | |
ulNewValue = FreeRTOS_round_up( ulNewValue, ( uint32_t ) pxSocket->u.xTCP.usInitMSS ); | |
pxSocket->u.xTCP.uxTxStreamSize = ulNewValue; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.uxRxStreamSize = ulNewValue; | |
} | |
} | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_WIN_PROPERTIES: /* Set all buffer and window properties in one call, parameter is pointer to WinProperties_t */ | |
{ | |
WinProperties_t* pxProps; | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
FreeRTOS_debug_printf( ( "Set SO_WIN_PROP: wrong socket type\n" ) ); | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( ( pxSocket->u.xTCP.txStream != NULL ) || ( pxSocket->u.xTCP.rxStream != NULL ) ) | |
{ | |
FreeRTOS_debug_printf( ( "Set SO_WIN_PROP: buffer already created\n" ) ); | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
pxProps = ( ( WinProperties_t * ) pvOptionValue ); | |
FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDBUF, &( pxProps->lTxBufSize ), sizeof( pxProps->lTxBufSize ) ); | |
FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVBUF, &( pxProps->lRxBufSize ), sizeof( pxProps->lRxBufSize ) ); | |
#if( ipconfigUSE_TCP_WIN == 1 ) | |
{ | |
pxSocket->u.xTCP.uxRxWinSize = ( uint32_t )pxProps->lRxWinSize; /* Fixed value: size of the TCP reception window */ | |
pxSocket->u.xTCP.uxTxWinSize = ( uint32_t )pxProps->lTxWinSize; /* Fixed value: size of the TCP transmit window */ | |
} | |
#else | |
{ | |
pxSocket->u.xTCP.uxRxWinSize = 1u; | |
pxSocket->u.xTCP.uxTxWinSize = 1u; | |
} | |
#endif | |
/* In case the socket has already initialised its tcpWin, | |
adapt the window size parameters */ | |
if( pxSocket->u.xTCP.xTCPWindow.u.bits.bHasInit != pdFALSE_UNSIGNED ) | |
{ | |
pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength = pxSocket->u.xTCP.uxRxWinSize * pxSocket->u.xTCP.usInitMSS; | |
pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength = pxSocket->u.xTCP.uxTxWinSize * pxSocket->u.xTCP.usInitMSS; | |
} | |
} | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_REUSE_LISTEN_SOCKET: /* If true, the server-socket will turn into a connected socket */ | |
{ | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( *( ( BaseType_t * ) pvOptionValue ) != 0 ) | |
{ | |
pxSocket->u.xTCP.bits.bReuseSocket = pdTRUE_UNSIGNED; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.bits.bReuseSocket = pdFALSE_UNSIGNED; | |
} | |
} | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_CLOSE_AFTER_SEND: /* As soon as the last byte has been transmitted, finalise the connection */ | |
{ | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( *( ( BaseType_t * ) pvOptionValue ) != 0 ) | |
{ | |
pxSocket->u.xTCP.bits.bCloseAfterSend = pdTRUE_UNSIGNED; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.bits.bCloseAfterSend = pdFALSE_UNSIGNED; | |
} | |
} | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_SET_FULL_SIZE: /* Refuse to send packets smaller than MSS */ | |
{ | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( *( ( BaseType_t * ) pvOptionValue ) != 0 ) | |
{ | |
pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdTRUE_UNSIGNED; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdFALSE_UNSIGNED; | |
} | |
if( ( pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize == pdFALSE_UNSIGNED ) && | |
( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && | |
( FreeRTOS_outstanding( pxSocket ) != 0 ) ) | |
{ | |
pxSocket->u.xTCP.usTimeout = 1u; /* to set/clear bSendFullSize */ | |
xSendEventToIPTask( eTCPTimerEvent ); | |
} | |
} | |
xReturn = 0; | |
break; | |
case FREERTOS_SO_STOP_RX: /* Refuse to receive more packts */ | |
{ | |
if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
break; /* will return -pdFREERTOS_ERRNO_EINVAL */ | |
} | |
if( *( ( BaseType_t * ) pvOptionValue ) != 0 ) | |
{ | |
pxSocket->u.xTCP.bits.bRxStopped = pdTRUE_UNSIGNED; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.bits.bRxStopped = pdFALSE_UNSIGNED; | |
} | |
pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; | |
pxSocket->u.xTCP.usTimeout = 1u; /* to set/clear bRxStopped */ | |
xSendEventToIPTask( eTCPTimerEvent ); | |
} | |
xReturn = 0; | |
break; | |
#endif /* ipconfigUSE_TCP == 1 */ | |
default : | |
/* No other options are handled. */ | |
xReturn = -pdFREERTOS_ERRNO_ENOPROTOOPT; | |
break; | |
} | |
return xReturn; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
/* Get a free private ('anonymous') port number */ | |
static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol ) | |
{ | |
uint16_t usResult; | |
BaseType_t xIndex; | |
const List_t *pxList; | |
#if ipconfigUSE_TCP == 1 | |
if( xProtocol == ( BaseType_t ) FREERTOS_IPPROTO_TCP ) | |
{ | |
xIndex = socketNEXT_TCP_PORT_NUMBER_INDEX; | |
pxList = &xBoundTCPSocketsList; | |
} | |
else | |
#endif | |
{ | |
xIndex = socketNEXT_UDP_PORT_NUMBER_INDEX; | |
pxList = &xBoundUDPSocketsList; | |
} | |
/* Avoid compiler warnings if ipconfigUSE_TCP is not defined. */ | |
( void ) xProtocol; | |
/* Assign the next port in the range. Has it overflowed? */ | |
/*_RB_ This needs to be randomised rather than sequential. */ | |
/* _HT_ Agreed, although many OS's use sequential port numbers, see | |
https://www.cymru.com/jtk/misc/ephemeralports.html */ | |
for ( ;; ) | |
{ | |
++( usNextPortToUse[ xIndex ] ); | |
if( usNextPortToUse[ xIndex ] >= socketAUTO_PORT_ALLOCATION_MAX_NUMBER ) | |
{ | |
/* 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[ xIndex ] = socketAUTO_PORT_ALLOCATION_RESET_NUMBER; | |
} | |
usResult = FreeRTOS_htons( usNextPortToUse[ xIndex ] ); | |
if( pxListFindListItemWithValue( pxList, ( TickType_t ) usResult ) == NULL ) | |
{ | |
break; | |
} | |
} | |
return usResult; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
/* pxListFindListItemWithValue: find a list item in a bound socket list | |
'xWantedItemValue' refers to a port number */ | |
static const ListItem_t * pxListFindListItemWithValue( const List_t *pxList, TickType_t xWantedItemValue ) | |
{ | |
const ListItem_t * pxResult = NULL; | |
if( ( xIPIsNetworkTaskReady() != pdFALSE ) && ( pxList != NULL ) ) | |
{ | |
const ListItem_t *pxIterator; | |
const MiniListItem_t *pxEnd = ( const MiniListItem_t* )listGET_END_MARKER( pxList ); | |
for( pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd ); | |
pxIterator != ( const ListItem_t * ) pxEnd; | |
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) | |
{ | |
if( listGET_LIST_ITEM_VALUE( pxIterator ) == xWantedItemValue ) | |
{ | |
pxResult = pxIterator; | |
break; | |
} | |
} | |
} | |
return pxResult; | |
} /* Tested */ | |
/*-----------------------------------------------------------*/ | |
FreeRTOS_Socket_t *pxUDPSocketLookup( UBaseType_t uxLocalPort ) | |
{ | |
const ListItem_t *pxListItem; | |
FreeRTOS_Socket_t *pxSocket = NULL; | |
/* Looking up a socket is quite simple, find a match with the local port. | |
See if there is a list item associated with the port number on the | |
list of bound sockets. */ | |
pxListItem = pxListFindListItemWithValue( &xBoundUDPSocketsList, ( TickType_t ) uxLocalPort ); | |
if( pxListItem != NULL ) | |
{ | |
/* The owner of the list item is the socket itself. */ | |
pxSocket = ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxListItem ); | |
configASSERT( pxSocket != NULL ); | |
} | |
return pxSocket; | |
} | |
/*-----------------------------------------------------------*/ | |
#if ipconfigINCLUDE_FULL_INET_ADDR == 1 | |
uint32_t FreeRTOS_inet_addr( const char * pcIPAddress ) | |
{ | |
const uint32_t ulDecimalBase = 10u; | |
uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ]; | |
const char *pcPointerOnEntering; | |
uint32_t ulReturn = 0UL, ulValue; | |
UBaseType_t uxOctetNumber; | |
BaseType_t xResult = pdPASS; | |
for( uxOctetNumber = 0u; uxOctetNumber < socketMAX_IP_ADDRESS_OCTETS; uxOctetNumber++ ) | |
{ | |
ulValue = 0ul; | |
pcPointerOnEntering = pcIPAddress; | |
while( ( *pcIPAddress >= '0' ) && ( *pcIPAddress <= '9' ) ) | |
{ | |
/* Move previous read characters into the next decimal | |
position. */ | |
ulValue *= ulDecimalBase; | |
/* Add the binary value of the ascii character. */ | |
ulValue += ( ( uint32_t ) ( *pcIPAddress ) - ( uint32_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[ uxOctetNumber ] = ( uint8_t ) ulValue; | |
/* Check the next character is as expected. */ | |
if( uxOctetNumber < ( socketMAX_IP_ADDRESS_OCTETS - 1u ) ) | |
{ | |
if( *pcIPAddress != '.' ) | |
{ | |
xResult = pdFAIL; | |
} | |
else | |
{ | |
/* Move past the dot. */ | |
pcIPAddress++; | |
} | |
} | |
} | |
if( xResult == pdFAIL ) | |
{ | |
/* No point going on. */ | |
break; | |
} | |
} | |
if( *pcIPAddress != ( char ) 0 ) | |
{ | |
/* Expected the end of the string. */ | |
xResult = pdFAIL; | |
} | |
if( uxOctetNumber != 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 */ | |
/*-----------------------------------------------------------*/ | |
/* Function to get the local address and IP port */ | |
size_t FreeRTOS_GetLocalAddress( Socket_t xSocket, struct freertos_sockaddr *pxAddress ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
/* IP address of local machine. */ | |
pxAddress->sin_addr = *ipLOCAL_IP_ADDRESS_POINTER; | |
/* Local port on this machine. */ | |
pxAddress->sin_port = FreeRTOS_htons( pxSocket->usLocalPort ); | |
return sizeof( *pxAddress ); | |
} | |
/*-----------------------------------------------------------*/ | |
void vSocketWakeUpUser( FreeRTOS_Socket_t *pxSocket ) | |
{ | |
/* _HT_ must work this out, now vSocketWakeUpUser will be called for any important | |
* event or transition */ | |
#if( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 ) | |
{ | |
if( pxSocket->pxUserSemaphore != NULL ) | |
{ | |
xSemaphoreGive( pxSocket->pxUserSemaphore ); | |
} | |
} | |
#endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ | |
#if( ipconfigSOCKET_HAS_USER_WAKE_CALLBACK == 1 ) | |
{ | |
if( pxSocket->pxUserWakeCallback != NULL ) | |
{ | |
pxSocket->pxUserWakeCallback( pxSocket ); | |
} | |
} | |
#endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */ | |
#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) | |
{ | |
if( pxSocket->pxSocketSet != NULL ) | |
{ | |
EventBits_t xSelectBits = ( pxSocket->xEventBits >> SOCKET_EVENT_BIT_COUNT ) & eSELECT_ALL; | |
if( xSelectBits != 0ul ) | |
{ | |
pxSocket->xSocketBits |= xSelectBits; | |
xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, xSelectBits ); | |
} | |
} | |
pxSocket->xEventBits &= eSOCKET_ALL; | |
} | |
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */ | |
if( ( pxSocket->xEventGroup != NULL ) && ( pxSocket->xEventBits != 0u ) ) | |
{ | |
xEventGroupSetBits( pxSocket->xEventGroup, pxSocket->xEventBits ); | |
} | |
pxSocket->xEventBits = 0ul; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) | |
/* This define makes it possible for network-card drivers to inspect | |
* UDP message and see if there is any UDP socket bound to a given port | |
* number. | |
* This is probably only useful in systems with a minimum of RAM and | |
* when lots of anonymous broadcast messages come in | |
*/ | |
BaseType_t xPortHasUDPSocket( uint16_t usPortNr ) | |
{ | |
BaseType_t xFound = pdFALSE; | |
vTaskSuspendAll(); | |
{ | |
if( ( pxListFindListItemWithValue( &xBoundUDPSocketsList, ( TickType_t ) usPortNr ) != NULL ) ) | |
{ | |
xFound = pdTRUE; | |
} | |
} | |
xTaskResumeAll(); | |
return xFound; | |
} | |
#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
static BaseType_t bMayConnect( FreeRTOS_Socket_t *pxSocket ); | |
static BaseType_t bMayConnect( FreeRTOS_Socket_t *pxSocket ) | |
{ | |
switch( pxSocket->u.xTCP.ucTCPState ) | |
{ | |
case eCLOSED: | |
case eCLOSE_WAIT: return 0; | |
case eCONNECT_SYN: return -pdFREERTOS_ERRNO_EINPROGRESS; | |
default: return -pdFREERTOS_ERRNO_EAGAIN; | |
} | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
static BaseType_t prvTCPConnectStart( FreeRTOS_Socket_t *pxSocket, struct freertos_sockaddr *pxAddress ) | |
{ | |
BaseType_t xResult = 0; | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdFALSE ) | |
{ | |
/* Not a valid socket or wrong type */ | |
xResult = -pdFREERTOS_ERRNO_EBADF; | |
} | |
else if( FreeRTOS_issocketconnected( pxSocket ) > 0 ) | |
{ | |
/* The socket is already connected. */ | |
xResult = -pdFREERTOS_ERRNO_EISCONN; | |
} | |
else if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE ) | |
{ | |
/* Bind the socket to the port that the client task will send from. | |
Non-standard, so the error returned is that returned by bind(). */ | |
xResult = FreeRTOS_bind( ( Socket_t ) pxSocket, NULL, 0u ); | |
} | |
if( xResult == 0 ) | |
{ | |
/* Check if it makes any sense to wait for a connect event, this condition | |
might change while sleeping, so it must be checked within each loop */ | |
xResult = bMayConnect( pxSocket ); /* -EINPROGRESS, -EAGAIN, or 0 for OK */ | |
/* Start the connect procedure, kernel will start working on it */ | |
if( xResult == 0 ) | |
{ | |
pxSocket->u.xTCP.bits.bConnPrepared = pdFALSE_UNSIGNED; | |
pxSocket->u.xTCP.ucRepCount = 0u; | |
FreeRTOS_debug_printf( ( "FreeRTOS_connect: %u to %lxip:%u\n", | |
pxSocket->usLocalPort, FreeRTOS_ntohl( pxAddress->sin_addr ), FreeRTOS_ntohs( pxAddress->sin_port ) ) ); | |
/* Port on remote machine. */ | |
pxSocket->u.xTCP.usRemotePort = FreeRTOS_ntohs( pxAddress->sin_port ); | |
/* IP address of remote machine. */ | |
pxSocket->u.xTCP.ulRemoteIP = FreeRTOS_ntohl( pxAddress->sin_addr ); | |
/* (client) internal state: socket wants to send a connect. */ | |
vTCPStateChange( pxSocket, eCONNECT_SYN ); | |
/* To start an active connect. */ | |
pxSocket->u.xTCP.usTimeout = 1u; | |
if( xSendEventToIPTask( eTCPTimerEvent ) != pdPASS ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_ECANCELED; | |
} | |
} | |
} | |
return xResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* FreeRTOS_connect: socket wants to connect to a remote port | |
*/ | |
BaseType_t FreeRTOS_connect( Socket_t xClientSocket, struct freertos_sockaddr *pxAddress, socklen_t xAddressLength ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t* ) xClientSocket; | |
TickType_t xRemainingTime; | |
BaseType_t xTimed = pdFALSE; | |
BaseType_t xResult; | |
TimeOut_t xTimeOut; | |
( void ) xAddressLength; | |
xResult = prvTCPConnectStart( pxSocket, pxAddress ); | |
if( xResult == 0 ) | |
{ | |
/* And wait for the result */ | |
for( ;; ) | |
{ | |
if( xTimed == pdFALSE ) | |
{ | |
/* Only in the first round, check for non-blocking */ | |
xRemainingTime = pxSocket->xReceiveBlockTime; | |
if( xRemainingTime == ( TickType_t )0 ) | |
{ | |
/* Not yet connected, correct state, non-blocking. */ | |
xResult = -pdFREERTOS_ERRNO_EWOULDBLOCK; | |
break; | |
} | |
/* Don't get here a second time. */ | |
xTimed = pdTRUE; | |
/* Fetch the current time */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
} | |
/* Did it get connected while sleeping ? */ | |
xResult = FreeRTOS_issocketconnected( pxSocket ); | |
/* Returns positive when connected, negative means an error */ | |
if( xResult < 0 ) | |
{ | |
/* Return the error */ | |
break; | |
} | |
if( xResult > 0 ) | |
{ | |
/* Socket now connected, return a zero */ | |
xResult = 0; | |
break; | |
} | |
/* Is it allowed to sleep more? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_ETIMEDOUT; | |
break; | |
} | |
/* Go sleeping until we get any down-stream event */ | |
xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_CONNECT, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); | |
} | |
} | |
return xResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* FreeRTOS_accept: can return a new connected socket | |
* if the server socket is in listen mode and receives a connection request | |
* The new socket will be bound already to the same port number as the listing | |
* socket. | |
*/ | |
Socket_t FreeRTOS_accept( Socket_t xServerSocket, struct freertos_sockaddr *pxAddress, socklen_t *pxAddressLength ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xServerSocket; | |
FreeRTOS_Socket_t *pxClientSocket = NULL; | |
TickType_t xRemainingTime; | |
BaseType_t xTimed = pdFALSE, xAsk = pdFALSE; | |
TimeOut_t xTimeOut; | |
IPStackEvent_t xAskEvent; | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) | |
{ | |
/* Not a valid socket or wrong type */ | |
pxClientSocket = ( FreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET; | |
} | |
else if( ( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) && | |
( pxSocket->u.xTCP.ucTCPState != eTCP_LISTEN ) ) | |
{ | |
/* Parent socket is not in listening mode */ | |
pxClientSocket = ( FreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET; | |
} | |
else | |
{ | |
/* Loop will stop with breaks. */ | |
for( ; ; ) | |
{ | |
/* Is there a new client? */ | |
vTaskSuspendAll(); | |
{ | |
if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) | |
{ | |
pxClientSocket = pxSocket->u.xTCP.pxPeerSocket; | |
} | |
else | |
{ | |
pxClientSocket = pxSocket; | |
} | |
if( pxClientSocket != NULL ) | |
{ | |
pxSocket->u.xTCP.pxPeerSocket = NULL; | |
/* Is it still not taken ? */ | |
if( pxClientSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) | |
{ | |
pxClientSocket->u.xTCP.bits.bPassAccept = pdFALSE_UNSIGNED; | |
} | |
else | |
{ | |
pxClientSocket = NULL; | |
} | |
} | |
} | |
xTaskResumeAll(); | |
if( pxClientSocket != NULL ) | |
{ | |
if( pxAddress != NULL ) | |
{ | |
/* IP address of remote machine. */ | |
pxAddress->sin_addr = FreeRTOS_ntohl( pxClientSocket->u.xTCP.ulRemoteIP ); | |
/* Port on remote machine. */ | |
pxAddress->sin_port = FreeRTOS_ntohs( pxClientSocket->u.xTCP.usRemotePort ); | |
} | |
if( pxAddressLength != NULL ) | |
{ | |
*pxAddressLength = sizeof( *pxAddress ); | |
} | |
if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) | |
{ | |
xAsk = pdTRUE; | |
} | |
} | |
if( xAsk != pdFALSE ) | |
{ | |
/* Ask to set an event in 'xEventGroup' as soon as a new | |
client gets connected for this listening socket. */ | |
xAskEvent.eEventType = eTCPAcceptEvent; | |
xAskEvent.pvData = ( void * ) pxSocket; | |
xSendEventStructToIPTask( &xAskEvent, portMAX_DELAY ); | |
} | |
if( pxClientSocket != NULL ) | |
{ | |
break; | |
} | |
if( xTimed == pdFALSE ) | |
{ | |
/* Only in the first round, check for non-blocking */ | |
xRemainingTime = pxSocket->xReceiveBlockTime; | |
if( xRemainingTime == ( TickType_t ) 0 ) | |
{ | |
break; | |
} | |
/* Don't get here a second time */ | |
xTimed = pdTRUE; | |
/* Fetch the current time */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
} | |
/* Has the timeout been reached? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) | |
{ | |
break; | |
} | |
/* Go sleeping until we get any down-stream event */ | |
xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_ACCEPT, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); | |
} | |
} | |
return ( Socket_t ) pxClientSocket; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Read incoming data from a TCP socket | |
* Only after the last byte has been read, a close error might be returned | |
*/ | |
BaseType_t FreeRTOS_recv( Socket_t xSocket, void *pvBuffer, size_t xBufferLength, BaseType_t xFlags ) | |
{ | |
BaseType_t xByteCount; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
TickType_t xRemainingTime; | |
BaseType_t xTimed = pdFALSE; | |
TimeOut_t xTimeOut; | |
EventBits_t xEventBits = ( EventBits_t ) 0; | |
/* Check if the socket is valid, has type TCP and if it is bound to a | |
port. */ | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) | |
{ | |
xByteCount = -pdFREERTOS_ERRNO_EINVAL; | |
} | |
else | |
{ | |
if( pxSocket->u.xTCP.rxStream != NULL ) | |
{ | |
xByteCount = ( BaseType_t )uxStreamBufferGetSize ( pxSocket->u.xTCP.rxStream ); | |
} | |
else | |
{ | |
xByteCount = 0; | |
} | |
while( xByteCount == 0 ) | |
{ | |
switch( pxSocket->u.xTCP.ucTCPState ) | |
{ | |
case eCLOSED: | |
case eCLOSE_WAIT: /* (server + client) waiting for a connection termination request from the local user. */ | |
case eCLOSING: /* (server + client) waiting for a connection termination request acknowledgement from the remote TCP. */ | |
if( pxSocket->u.xTCP.bits.bMallocError != pdFALSE_UNSIGNED ) | |
{ | |
/* The no-memory error has priority above the non-connected error. | |
Both are fatal and will elad to closing the socket. */ | |
xByteCount = -pdFREERTOS_ERRNO_ENOMEM; | |
} | |
else | |
{ | |
xByteCount = -pdFREERTOS_ERRNO_ENOTCONN; | |
} | |
/* Call continue to break out of the switch and also the while | |
loop. */ | |
continue; | |
default: | |
break; | |
} | |
if( xTimed == pdFALSE ) | |
{ | |
/* Only in the first round, check for non-blocking. */ | |
xRemainingTime = pxSocket->xReceiveBlockTime; | |
if( xRemainingTime == ( TickType_t ) 0 ) | |
{ | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
{ | |
/* Just check for the interrupt flag. */ | |
xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_INTR, | |
pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK ); | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
break; | |
} | |
if( ( xFlags & FREERTOS_MSG_DONTWAIT ) != 0 ) | |
{ | |
break; | |
} | |
/* Don't get here a second time. */ | |
xTimed = pdTRUE; | |
/* Fetch the current time. */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
} | |
/* Has the timeout been reached? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) | |
{ | |
break; | |
} | |
/* Block until there is a down-stream event. */ | |
xEventBits = xEventGroupWaitBits( pxSocket->xEventGroup, | |
eSOCKET_RECEIVE | eSOCKET_CLOSED | eSOCKET_INTR, | |
pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
{ | |
if( ( xEventBits & eSOCKET_INTR ) != 0u ) | |
{ | |
break; | |
} | |
} | |
#else | |
{ | |
( void ) xEventBits; | |
} | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
if( pxSocket->u.xTCP.rxStream != NULL ) | |
{ | |
xByteCount = ( BaseType_t ) uxStreamBufferGetSize ( pxSocket->u.xTCP.rxStream ); | |
} | |
else | |
{ | |
xByteCount = 0; | |
} | |
} | |
#if( ipconfigSUPPORT_SIGNALS != 0 ) | |
if( ( xEventBits & eSOCKET_INTR ) != 0 ) | |
{ | |
if( ( xEventBits & ( eSOCKET_RECEIVE | eSOCKET_CLOSED ) ) != 0 ) | |
{ | |
/* Shouldn't have cleared other flags. */ | |
xEventBits &= ~eSOCKET_INTR; | |
xEventGroupSetBits( pxSocket->xEventGroup, xEventBits ); | |
} | |
xByteCount = -pdFREERTOS_ERRNO_EINTR; | |
} | |
else | |
#endif /* ipconfigSUPPORT_SIGNALS */ | |
if( xByteCount > 0 ) | |
{ | |
if( ( xFlags & FREERTOS_ZERO_COPY ) == 0 ) | |
{ | |
xByteCount = ( BaseType_t ) uxStreamBufferGet( pxSocket->u.xTCP.rxStream, 0ul, ( uint8_t * ) pvBuffer, ( size_t ) xBufferLength, ( xFlags & FREERTOS_MSG_PEEK ) != 0 ); | |
if( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) | |
{ | |
/* We had reached the low-water mark, now see if the flag | |
can be cleared */ | |
size_t uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); | |
if( uxFrontSpace >= pxSocket->u.xTCP.uxEnoughSpace ) | |
{ | |
pxSocket->u.xTCP.bits.bLowWater = pdFALSE_UNSIGNED; | |
pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; | |
pxSocket->u.xTCP.usTimeout = 1u; /* because bLowWater is cleared. */ | |
xSendEventToIPTask( eTCPTimerEvent ); | |
} | |
} | |
} | |
else | |
{ | |
/* Zero-copy reception of data: pvBuffer is a pointer to a pointer. */ | |
xByteCount = ( BaseType_t ) uxStreamBufferGetPtr( pxSocket->u.xTCP.rxStream, (uint8_t **)pvBuffer ); | |
} | |
} | |
} /* prvValidSocket() */ | |
return xByteCount; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
static int32_t prvTCPSendCheck( FreeRTOS_Socket_t *pxSocket, size_t xDataLength ) | |
{ | |
int32_t xResult = 1; | |
/* Is this a socket of type TCP and is it already bound to a port number ? */ | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_EINVAL; | |
} | |
else if( pxSocket->u.xTCP.bits.bMallocError != pdFALSE_UNSIGNED ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_ENOMEM; | |
} | |
else if( pxSocket->u.xTCP.ucTCPState == eCLOSED ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_ENOTCONN; | |
} | |
else if( pxSocket->u.xTCP.bits.bFinSent != pdFALSE_UNSIGNED ) | |
{ | |
/* This TCP connection is closing already, the FIN flag has been sent. | |
Maybe it is still delivering or receiving data. | |
Return OK in order not to get closed/deleted too quickly */ | |
xResult = 0; | |
} | |
else if( xDataLength == 0ul ) | |
{ | |
/* send() is being called to send zero bytes */ | |
xResult = 0; | |
} | |
else if( pxSocket->u.xTCP.txStream == NULL ) | |
{ | |
/* Create the outgoing stream only when it is needed */ | |
prvTCPCreateStream( pxSocket, pdFALSE ); | |
if( pxSocket->u.xTCP.txStream == NULL ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_ENOMEM; | |
} | |
} | |
return xResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* Get a direct pointer to the circular transmit buffer. | |
'*pxLength' will contain the number of bytes that may be written. */ | |
uint8_t *FreeRTOS_get_tx_head( Socket_t xSocket, BaseType_t *pxLength ) | |
{ | |
uint8_t *pucReturn; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
StreamBuffer_t *pxBuffer = pxSocket->u.xTCP.txStream; | |
if( pxBuffer != NULL ) | |
{ | |
BaseType_t xSpace = ( BaseType_t ) uxStreamBufferGetSpace( pxBuffer ); | |
BaseType_t xRemain = ( BaseType_t ) ( pxBuffer->LENGTH - pxBuffer->uxHead ); | |
*pxLength = FreeRTOS_min_BaseType( xSpace, xRemain ); | |
pucReturn = pxBuffer->ucArray + pxBuffer->uxHead; | |
} | |
else | |
{ | |
*pxLength = 0; | |
pucReturn = NULL; | |
} | |
return pucReturn; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Send data using a TCP socket. It is not necessary to have the socket | |
* connected already. Outgoing data will be stored and delivered as soon as | |
* the socket gets connected. | |
*/ | |
BaseType_t FreeRTOS_send( Socket_t xSocket, const void *pvBuffer, size_t uxDataLength, BaseType_t xFlags ) | |
{ | |
BaseType_t xByteCount; | |
BaseType_t xBytesLeft; | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
TickType_t xRemainingTime; | |
BaseType_t xTimed = pdFALSE; | |
TimeOut_t xTimeOut; | |
BaseType_t xCloseAfterSend; | |
/* Prevent compiler warnings about unused parameters. The parameter | |
may be used in future versions. */ | |
( void ) xFlags; | |
xByteCount = ( BaseType_t ) prvTCPSendCheck( pxSocket, uxDataLength ); | |
if( xByteCount > 0 ) | |
{ | |
/* xBytesLeft is number of bytes to send, will count to zero. */ | |
xBytesLeft = ( BaseType_t ) uxDataLength; | |
/* xByteCount is number of bytes that can be sent now. */ | |
xByteCount = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); | |
/* While there are still bytes to be sent. */ | |
while( xBytesLeft > 0 ) | |
{ | |
/* If txStream has space. */ | |
if( xByteCount > 0 ) | |
{ | |
/* Don't send more than necessary. */ | |
if( xByteCount > xBytesLeft ) | |
{ | |
xByteCount = xBytesLeft; | |
} | |
/* Is the close-after-send flag set and is this really the | |
last transmission? */ | |
if( ( pxSocket->u.xTCP.bits.bCloseAfterSend != pdFALSE_UNSIGNED ) && ( xByteCount == xBytesLeft ) ) | |
{ | |
xCloseAfterSend = pdTRUE; | |
} | |
else | |
{ | |
xCloseAfterSend = pdFALSE; | |
} | |
/* The flag 'bCloseAfterSend' can be set before sending data | |
using setsockopt() | |
When the last data packet is being sent out, a FIN flag will | |
be included to let the peer know that no more data is to be | |
expected. The use of 'bCloseAfterSend' is not mandatory, it | |
is just a faster way of transferring files (e.g. when using | |
FTP). */ | |
if( xCloseAfterSend != pdFALSE ) | |
{ | |
/* Now suspend the scheduler: sending the last data and | |
setting bCloseRequested must be done together */ | |
vTaskSuspendAll(); | |
pxSocket->u.xTCP.bits.bCloseRequested = pdTRUE_UNSIGNED; | |
} | |
xByteCount = ( BaseType_t ) uxStreamBufferAdd( pxSocket->u.xTCP.txStream, 0ul, ( const uint8_t * ) pvBuffer, ( size_t ) xByteCount ); | |
if( xCloseAfterSend != pdFALSE ) | |
{ | |
/* Now when the IP-task transmits the data, it will also | |
see that bCloseRequested is true and include the FIN | |
flag to start closure of the connection. */ | |
xTaskResumeAll(); | |
} | |
/* Send a message to the IP-task so it can work on this | |
socket. Data is sent, let the IP-task work on it. */ | |
pxSocket->u.xTCP.usTimeout = 1u; | |
if( xIsCallingFromIPTask() == pdFALSE ) | |
{ | |
/* Only send a TCP timer event when not called from the | |
IP-task. */ | |
xSendEventToIPTask( eTCPTimerEvent ); | |
} | |
xBytesLeft -= xByteCount; | |
if( xBytesLeft == 0 ) | |
{ | |
break; | |
} | |
/* As there are still bytes left to be sent, increase the | |
data pointer. */ | |
pvBuffer = ( void * ) ( ( ( const uint8_t * ) pvBuffer) + xByteCount ); | |
} | |
/* Not all bytes have been sent. In case the socket is marked as | |
blocking sleep for a while. */ | |
if( xTimed == pdFALSE ) | |
{ | |
/* Only in the first round, check for non-blocking. */ | |
xRemainingTime = pxSocket->xSendBlockTime; | |
#if( ipconfigUSE_CALLBACKS != 0 ) | |
{ | |
if( xIsCallingFromIPTask() != pdFALSE ) | |
{ | |
/* If this send function is called from within a | |
call-back handler it may not block, otherwise | |
chances would be big to get a deadlock: the IP-task | |
waiting for itself. */ | |
xRemainingTime = ( TickType_t ) 0; | |
} | |
} | |
#endif /* ipconfigUSE_CALLBACKS */ | |
if( xRemainingTime == ( TickType_t ) 0 ) | |
{ | |
break; | |
} | |
if( ( xFlags & FREERTOS_MSG_DONTWAIT ) != 0 ) | |
{ | |
break; | |
} | |
/* Don't get here a second time. */ | |
xTimed = pdTRUE; | |
/* Fetch the current time. */ | |
vTaskSetTimeOutState( &xTimeOut ); | |
} | |
else | |
{ | |
/* Has the timeout been reached? */ | |
if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE ) | |
{ | |
break; | |
} | |
} | |
/* Go sleeping until down-stream events are received. */ | |
xEventGroupWaitBits( pxSocket->xEventGroup, eSOCKET_SEND | eSOCKET_CLOSED, | |
pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime ); | |
xByteCount = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream ); | |
} | |
/* How much was actually sent? */ | |
xByteCount = ( ( BaseType_t ) uxDataLength ) - xBytesLeft; | |
if( xByteCount == 0 ) | |
{ | |
if( pxSocket->u.xTCP.ucTCPState > eESTABLISHED ) | |
{ | |
xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOTCONN; | |
} | |
else | |
{ | |
if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) | |
{ | |
FreeRTOS_debug_printf( ( "FreeRTOS_send: %u -> %lxip:%d: no space\n", | |
pxSocket->usLocalPort, | |
pxSocket->u.xTCP.ulRemoteIP, | |
pxSocket->u.xTCP.usRemotePort ) ); | |
} | |
xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOSPC; | |
} | |
} | |
} | |
return xByteCount; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* Request to put a socket in listen mode | |
*/ | |
BaseType_t FreeRTOS_listen( Socket_t xSocket, BaseType_t xBacklog ) | |
{ | |
FreeRTOS_Socket_t *pxSocket; | |
BaseType_t xResult = 0; | |
pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
/* listen() is allowed for a valid TCP socket in Closed state and already | |
bound. */ | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) | |
{ | |
xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; | |
} | |
else if( ( pxSocket->u.xTCP.ucTCPState != eCLOSED ) && ( pxSocket->u.xTCP.ucTCPState != eCLOSE_WAIT ) ) | |
{ | |
/* Socket is in a wrong state. */ | |
xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; | |
} | |
else | |
{ | |
/* Backlog is interpreted here as "the maximum number of child | |
sockets. */ | |
pxSocket->u.xTCP.usBacklog = ( uint16_t )FreeRTOS_min_int32( ( int32_t ) 0xffff, ( int32_t ) xBacklog ); | |
/* This cleaning is necessary only if a listening socket is being | |
reused as it might have had a previous connection. */ | |
if( pxSocket->u.xTCP.bits.bReuseSocket ) | |
{ | |
if( pxSocket->u.xTCP.rxStream != NULL ) | |
{ | |
vStreamBufferClear( pxSocket->u.xTCP.rxStream ); | |
} | |
if( pxSocket->u.xTCP.txStream != NULL ) | |
{ | |
vStreamBufferClear( pxSocket->u.xTCP.txStream ); | |
} | |
memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, '\0', sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); | |
memset( &pxSocket->u.xTCP.xTCPWindow, '\0', sizeof( pxSocket->u.xTCP.xTCPWindow ) ); | |
memset( &pxSocket->u.xTCP.bits, '\0', sizeof( pxSocket->u.xTCP.bits ) ); | |
/* Now set the bReuseSocket flag again, because the bits have | |
just been cleared. */ | |
pxSocket->u.xTCP.bits.bReuseSocket = pdTRUE_UNSIGNED; | |
} | |
vTCPStateChange( pxSocket, eTCP_LISTEN ); | |
} | |
return xResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* shutdown - shut down part of a full-duplex connection */ | |
BaseType_t FreeRTOS_shutdown( Socket_t xSocket, BaseType_t xHow ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; | |
BaseType_t xResult; | |
if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdTRUE ) == pdFALSE ) | |
{ | |
/*_RB_ Is this comment correct? The socket is not of a type that | |
supports the listen() operation. */ | |
xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; | |
} | |
else if ( pxSocket->u.xTCP.ucTCPState != eESTABLISHED ) | |
{ | |
/*_RB_ Is this comment correct? The socket is not of a type that | |
supports the listen() operation. */ | |
xResult = -pdFREERTOS_ERRNO_EOPNOTSUPP; | |
} | |
else | |
{ | |
pxSocket->u.xTCP.bits.bUserShutdown = pdTRUE_UNSIGNED; | |
/* Let the IP-task perform the shutdown of the connection. */ | |
pxSocket->u.xTCP.usTimeout = 1u; | |
xSendEventToIPTask( eTCPTimerEvent ); | |
xResult = 0; | |
} | |
(void) xHow; | |
return xResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* A TCP timer has expired, now check all TCP sockets for: | |
* - Active connect | |
* - Send a delayed ACK | |
* - Send new data | |
* - Send a keep-alive packet | |
* - Check for timeout (in non-connected states only) | |
*/ | |
TickType_t xTCPTimerCheck( BaseType_t xWillSleep ) | |
{ | |
FreeRTOS_Socket_t *pxSocket; | |
TickType_t xShortest = pdMS_TO_TICKS( ( TickType_t ) ipTCP_TIMER_PERIOD_MS ); | |
TickType_t xNow = xTaskGetTickCount(); | |
static TickType_t xLastTime = 0u; | |
TickType_t xDelta = xNow - xLastTime; | |
ListItem_t* pxEnd = ( ListItem_t * ) listGET_END_MARKER( &xBoundTCPSocketsList ); | |
ListItem_t *pxIterator = ( ListItem_t * ) listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); | |
xLastTime = xNow; | |
if( xDelta == 0u ) | |
{ | |
xDelta = 1u; | |
} | |
while( pxIterator != pxEnd ) | |
{ | |
pxSocket = ( FreeRTOS_Socket_t * )listGET_LIST_ITEM_OWNER( pxIterator ); | |
pxIterator = ( ListItem_t * ) listGET_NEXT( pxIterator ); | |
/* Sockets with 'tmout == 0' do not need any regular attention. */ | |
if( pxSocket->u.xTCP.usTimeout == 0u ) | |
{ | |
continue; | |
} | |
if( xDelta < ( TickType_t ) pxSocket->u.xTCP.usTimeout ) | |
{ | |
pxSocket->u.xTCP.usTimeout = ( uint16_t ) ( ( ( TickType_t ) pxSocket->u.xTCP.usTimeout ) - xDelta ); | |
} | |
else | |
{ | |
int rc ; | |
pxSocket->u.xTCP.usTimeout = 0u; | |
rc = xTCPSocketCheck( pxSocket ); | |
/* Within this function, the socket might want to send a delayed | |
ack or send out data or whatever it needs to do. */ | |
if( rc < 0 ) | |
{ | |
/* Continue because the socket was deleted. */ | |
continue; | |
} | |
} | |
/* In xEventBits the driver may indicate that the socket has | |
important events for the user. These are only done just before the | |
IP-task goes to sleep. */ | |
if( pxSocket->xEventBits != 0u ) | |
{ | |
if( xWillSleep != pdFALSE ) | |
{ | |
/* The IP-task is about to go to sleep, so messages can be | |
sent to the socket owners. */ | |
vSocketWakeUpUser( pxSocket ); | |
} | |
else | |
{ | |
/* Or else make sure this will be called again to wake-up | |
the sockets' owner. */ | |
xShortest = ( TickType_t ) 0; | |
} | |
} | |
if( ( pxSocket->u.xTCP.usTimeout != 0u ) && ( xShortest > ( TickType_t ) pxSocket->u.xTCP.usTimeout ) ) | |
{ | |
xShortest = ( TickType_t ) pxSocket->u.xTCP.usTimeout; | |
} | |
} | |
return xShortest; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
/* | |
* TCP: as multiple sockets may be bound to the same local port number | |
* looking up a socket is a little more complex: | |
* Both a local port, and a remote port and IP address are being used | |
* For a socket in listening mode, the remote port and IP address are both 0 | |
*/ | |
FreeRTOS_Socket_t *pxTCPSocketLookup( uint32_t ulLocalIP, UBaseType_t uxLocalPort, uint32_t ulRemoteIP, UBaseType_t uxRemotePort ) | |
{ | |
ListItem_t *pxIterator; | |
FreeRTOS_Socket_t *pxResult = NULL, *pxListenSocket = NULL; | |
MiniListItem_t *pxEnd = ( MiniListItem_t* )listGET_END_MARKER( &xBoundTCPSocketsList ); | |
/* Parameter not yet supported. */ | |
( void ) ulLocalIP; | |
for( pxIterator = ( ListItem_t * ) listGET_NEXT( pxEnd ); | |
pxIterator != ( ListItem_t * ) pxEnd; | |
pxIterator = ( ListItem_t * ) listGET_NEXT( pxIterator ) ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); | |
if( pxSocket->usLocalPort == ( uint16_t ) uxLocalPort ) | |
{ | |
if( pxSocket->u.xTCP.ucTCPState == eTCP_LISTEN ) | |
{ | |
/* If this is a socket listening to uxLocalPort, remember it | |
in case there is no perfect match. */ | |
pxListenSocket = pxSocket; | |
} | |
else if( ( pxSocket->u.xTCP.usRemotePort == ( uint16_t ) uxRemotePort ) && ( pxSocket->u.xTCP.ulRemoteIP == ulRemoteIP ) ) | |
{ | |
/* For sockets not in listening mode, find a match with | |
xLocalPort, ulRemoteIP AND xRemotePort. */ | |
pxResult = pxSocket; | |
break; | |
} | |
} | |
} | |
if( pxResult == NULL ) | |
{ | |
/* An exact match was not found, maybe a listening socket was | |
found. */ | |
pxResult = pxListenSocket; | |
} | |
return pxResult; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
const struct xSTREAM_BUFFER *FreeRTOS_get_rx_buf( Socket_t xSocket ) | |
{ | |
FreeRTOS_Socket_t *pxSocket = (FreeRTOS_Socket_t *)xSocket; | |
return pxSocket->u.xTCP.rxStream; | |
} | |
#endif /* ipconfigUSE_TCP */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigUSE_TCP == 1 ) | |
static StreamBuffer_t *prvTCPCreateStream( FreeRTOS_Socket_t *pxSocket, BaseType_t xIsInputStream ) | |
{ | |
StreamBuffer_t *pxBuffer; | |
size_t uxLength; | |
size_t uxSize; | |
/* Now that a stream is created, the maximum size is fixed before | |
creation, it could still be changed with setsockopt(). */ | |
if( xIsInputStream != pdFALSE ) | |
{ | |
/* Flow control for input streams works with a low- and a high-water mark. | |
1) If the RX-space becomes less than uxLittleSpace, the flag 'bLowWater' will | |
be set, and a TCP window update message will be sent to the peer. | |
2) The data will be read from the socket by recv() and when RX-space becomes | |
larger than or equal to than 'uxEnoughSpace', a new TCP window update | |
message will be sent to the peer, and 'bLowWater' will get cleared again. | |
By default: | |
uxLittleSpace == 1/5 x uxRxStreamSize | |
uxEnoughSpace == 4/5 x uxRxStreamSize | |
How-ever it is very inefficient to make 'uxLittleSpace' smaller than the actual MSS. | |
*/ | |
uxLength = pxSocket->u.xTCP.uxRxStreamSize; | |
if( pxSocket->u.xTCP.uxLittleSpace == 0ul ) | |
{ | |
pxSocket->u.xTCP.uxLittleSpace = ( 1ul * pxSocket->u.xTCP.uxRxStreamSize ) / 5u; /*_RB_ Why divide by 5? Can this be changed to a #define? */ | |
if( ( pxSocket->u.xTCP.uxLittleSpace < pxSocket->u.xTCP.usCurMSS ) && ( pxSocket->u.xTCP.uxRxStreamSize >= 2u * pxSocket->u.xTCP.usCurMSS ) ) | |
{ | |
pxSocket->u.xTCP.uxLittleSpace = pxSocket->u.xTCP.usCurMSS; | |
} | |
} | |
if( pxSocket->u.xTCP.uxEnoughSpace == 0ul ) | |
{ | |
pxSocket->u.xTCP.uxEnoughSpace = ( 4ul * pxSocket->u.xTCP.uxRxStreamSize ) / 5u; /*_RB_ Why multiply by 4? Maybe sock80_PERCENT?*/ | |
} | |
} | |
else | |
{ | |
uxLength = pxSocket->u.xTCP.uxTxStreamSize; | |
} | |
/* Add an extra 4 (or 8) bytes. */ | |
uxLength += sizeof( size_t ); | |
/* And make the length a multiple of sizeof( size_t ). */ | |
uxLength &= ~( sizeof( size_t ) - 1u ); | |
uxSize = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ) + uxLength; | |
pxBuffer = ( StreamBuffer_t * )pvPortMallocLarge( uxSize ); | |
if( pxBuffer == NULL ) | |
{ | |
FreeRTOS_debug_printf( ( "prvTCPCreateStream: malloc failed\n" ) ); | |
pxSocket->u.xTCP.bits.bMallocError = pdTRUE_UNSIGNED; | |
vTCPStateChange( pxSocket, eCLOSE_WAIT ); | |
} | |
else | |
{ | |
/* Clear the markers of the stream */ | |
memset( pxBuffer, '\0', sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ) ); | |
pxBuffer->LENGTH = ( size_t ) uxLength ; | |
if( xTCPWindowLoggingLevel != 0 ) | |
{ | |
FreeRTOS_debug_printf( ( "prvTCPCreateStream: %cxStream created %lu bytes (total %lu)\n", xIsInputStream ? 'R' : 'T', uxLength, uxSize ) ); | |
} | |
if( xIsInputStream != 0 ) | |
{ | |
pxSocket-> |