| /* |
| * FreeRTOS+TCP V2.2.0 |
| * 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://aws.amazon.com/freertos |
| * http://www.FreeRTOS.org |
| */ |
| |
| /* 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. |
| |
| Per https://tools.ietf.org/html/rfc6056, "the dynamic ports consist of the range |
| 49152-65535. However, ephemeral port selection algorithms should use the whole |
| range 1024-65535" excluding those already in use (inbound or outbound). */ |
| #if !defined( socketAUTO_PORT_ALLOCATION_START_NUMBER ) |
| #define socketAUTO_PORT_ALLOCATION_START_NUMBER ( ( uint16_t ) 0x0400 ) |
| #endif |
| |
| #define socketAUTO_PORT_ALLOCATION_MAX_NUMBER ( ( uint16_t ) 0xffff ) |
| |
| /* 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 |
| |
| /* Some helper macro's for defining the 20/80 % limits of uxLittleSpace / uxEnoughSpace. */ |
| #define sock20_PERCENT 20 |
| #define sock80_PERCENT 80 |
| #define sock100_PERCENT 100 |
| |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * 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 */ |
| |
| /*-----------------------------------------------------------*/ |
| |
| 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; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| BaseType_t vNetworkSocketsInit( void ) |
| { |
| vListInitialise( &xBoundUDPSocketsList ); |
| |
| #if( ipconfigUSE_TCP == 1 ) |
| { |
| vListInitialise( &xBoundTCPSocketsList ); |
| } |
| #endif /* ipconfigUSE_TCP == 1 */ |
| |
| return pdTRUE; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| 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; |
| configASSERT( xReturn ); |
| } |
| /* 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; |
| configASSERT( xReturn ); |
| } |
| |
| *pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xTCP ); |
| } |
| #endif /* ipconfigUSE_TCP == 1 */ |
| else |
| { |
| xReturn = pdFAIL; |
| configASSERT( xReturn ); |
| } |
| } |
| /* 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 length of the payload data, which is |
| calculated at the total packet size minus the headers. |
| The validity of `xDataLength` prvProcessIPPacket has been confirmed |
| in 'prvProcessIPPacket()'. */ |
| lReturn = ( int32_t ) ( pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ) ); |
| |
| 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 ) |
| { |
| /* xDataLength is the size of the total packet, including the Ethernet header. */ |
| pxNetworkBuffer->xDataLength = xTotalDataLength + sizeof( UDPPacket_t ); |
| 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 and port to the socket. */ |
| if( pxAddress == NULL ) |
| { |
| pxAddress = &xAddress; |
| /* Put the port to zero to 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( 0 == pxAddress->sin_port ) |
| { |
| return -pdFREERTOS_ERRNO_EADDRNOTAVAIL; |
| } |
| } |
| |
| /* 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_SET_LOW_HIGH_WATER: |
| { |
| LowHighWater_t *pxLowHighWater = ( LowHighWater_t * ) pvOptionValue; |
| |
| if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) |
| { |
| /* It is not allowed to access 'pxSocket->u.xTCP'. */ |
| FreeRTOS_debug_printf( ( "FREERTOS_SO_SET_LOW_HIGH_WATER: wrong socket type\n" ) ); |
| break; /* will return -pdFREERTOS_ERRNO_EINVAL */ |
| } |
| if( ( pxLowHighWater->uxLittleSpace >= pxLowHighWater->uxEnoughSpace ) || |
| ( pxLowHighWater->uxEnoughSpace > pxSocket->u.xTCP.uxRxStreamSize ) ) |
| { |
| /* Impossible values. */ |
| FreeRTOS_debug_printf( ( "FREERTOS_SO_SET_LOW_HIGH_WATER: bad values\n" ) ); |
| break; /* will return -pdFREERTOS_ERRNO_EINVAL */ |
| } |
| /* Send a STOP when buffer space drops below 'uxLittleSpace' bytes. */ |
| pxSocket->u.xTCP.uxLittleSpace = pxLowHighWater->uxLittleSpace; |
| /* Send a GO when buffer space grows above 'uxEnoughSpace' bytes. */ |
| pxSocket->u.xTCP.uxEnoughSpace = pxLowHighWater->uxEnoughSpace; |
| xReturn = 0; |
| } |
| break; |
| |
| 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 ); |
| |
| if ( FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDBUF, &( pxProps->lTxBufSize ), sizeof( pxProps->lTxBufSize ) ) != 0 ) |
| { |
| break; /* will return -pdFREERTOS_ERRNO_EINVAL */ |
| } |
| |
| if ( FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVBUF, &( pxProps->lRxBufSize ), sizeof( pxProps->lRxBufSize ) ) != 0 ) |
| { |
| break; /* will return -pdFREERTOS_ERRNO_EINVAL */ |
| } |
| |
| #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.ulTxWindowLength = 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 */ |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* Find an available port number per https://tools.ietf.org/html/rfc6056. */ |
| static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol ) |
| { |
| const uint16_t usEphemeralPortCount = |
| socketAUTO_PORT_ALLOCATION_MAX_NUMBER - socketAUTO_PORT_ALLOCATION_START_NUMBER + 1; |
| uint16_t usIterations = usEphemeralPortCount; |
| uint32_t ulRandomSeed = 0; |
| uint16_t usResult = 0; |
| const List_t *pxList; |
| |
| #if ipconfigUSE_TCP == 1 |
| if( xProtocol == ( BaseType_t ) FREERTOS_IPPROTO_TCP ) |
| { |
| pxList = &xBoundTCPSocketsList; |
| } |
| else |
| #endif |
| { |
| pxList = &xBoundUDPSocketsList; |
| } |
| |
| /* Avoid compiler warnings if ipconfigUSE_TCP is not defined. */ |
| ( void ) xProtocol; |
| |
| /* Find the next available port using the random seed as a starting |
| point. */ |
| do |
| { |
| /* Only proceed if the random number generator succeeded. */ |
| if( xApplicationGetRandomNumber( &( ulRandomSeed ) ) == pdFALSE ) |
| { |
| break; |
| } |
| |
| /* Map the random to a candidate port. */ |
| usResult = |
| socketAUTO_PORT_ALLOCATION_START_NUMBER + |
| ( ( ( uint16_t )ulRandomSeed ) % usEphemeralPortCount ); |
| |
| /* Check if there's already an open socket with the same protocol |
| and port. */ |
| if( NULL == pxListFindListItemWithValue( |
| pxList, |
| ( TickType_t )FreeRTOS_htons( usResult ) ) ) |
| { |
| usResult = FreeRTOS_htons( usResult ); |
| break; |
| } |
| else |
| { |
| usResult = 0; |
| } |
| |
| usIterations--; |
| } |
| while( usIterations > 0 ); |
| |
| return usResult; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /* 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 usefull 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 || |
| pxSocket->u.xTCP.ucTCPState == eCLOSE_WAIT || |
| pxSocket->u.xTCP.ucTCPState == eCLOSING ) |
| { |
| 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 = NULL; |
| FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; |
| StreamBuffer_t *pxBuffer = NULL; |
| |
| *pxLength = 0; |
| |
| /* Confirm that this is a TCP socket before dereferencing structure |
| member pointers. */ |
| if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdTRUE ) |
| { |
| 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; |
| } |
| } |
| |
| 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; |
| struct xSTREAM_BUFFER *pxReturn = NULL; |
| |
| /* Confirm that this is a TCP socket before dereferencing structure |
| member pointers. */ |
| if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_TCP, pdFALSE ) == pdTRUE ) |
| { |
| pxReturn = pxSocket->u.xTCP.rxStream; |
| } |
| |
| return pxReturn; |
| } |
| |
| #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 ) |
| { |
| uxLength = pxSocket->u.xTCP.uxRxStreamSize; |
| |
| if( pxSocket->u.xTCP.uxLittleSpace == 0ul ) |
| { |
| pxSocket->u.xTCP.uxLittleSpace = ( sock20_PERCENT * pxSocket->u.xTCP.uxRxStreamSize ) / sock100_PERCENT; |
| } |
| |
| if( pxSocket->u.xTCP.uxEnoughSpace == 0ul ) |
| { |
| pxSocket->u.xTCP.uxEnoughSpace = ( sock80_PERCENT * pxSocket->u.xTCP.uxRxStreamSize ) / sock100_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->u.xTCP.rxStream = pxBuffer; |
| } |
| else |
| { |
| pxSocket->u.xTCP.txStream = pxBuffer; |
| } |
| } |
| |
| return pxBuffer; |
| } |
| |
| #endif /* ipconfigUSE_TCP */ |
| /*-----------------------------------------------------------*/ |
| |
| #if( ipconfigUSE_TCP == 1 ) |
| |
| /* |
| * Add data to the RxStream. When uxOffset > 0, data has come in out-of-order |
| * and will be put in front of the head so it can not be popped by the user. |
| */ |
| int32_t lTCPAddRxdata( FreeRTOS_Socket_t *pxSocket, size_t uxOffset, const uint8_t *pcData, uint32_t ulByteCount ) |
| { |
| StreamBuffer_t *pxStream = pxSocket->u.xTCP.rxStream; |
| int32_t xResult; |
| #if( ipconfigUSE_CALLBACKS == 1 ) |
| BaseType_t bHasHandler = ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleReceive ); |
| const uint8_t *pucBuffer = NULL; |
| #endif /* ipconfigUSE_CALLBACKS */ |
| |
| /* int32_t uxStreamBufferAdd( pxBuffer, uxOffset, pucData, aCount ) |
| if( pucData != NULL ) copy data the the buffer |
| if( pucData == NULL ) no copying, just advance rxHead |
| if( uxOffset != 0 ) Just store data which has come out-of-order |
| if( uxOffset == 0 ) Also advance rxHead */ |
| if( pxStream == NULL ) |
| { |
| pxStream = prvTCPCreateStream( pxSocket, pdTRUE ); |
| if( pxStream == NULL ) |
| { |
| return -1; |
| } |
| } |
| |
| #if( ipconfigUSE_CALLBACKS == 1 ) |
| { |
| if( ( bHasHandler != pdFALSE ) && ( uxStreamBufferGetSize( pxStream ) == 0u ) && ( uxOffset == 0ul ) && ( pcData != NULL ) ) |
| { |
| /* Data can be passed directly to the user */ |
| pucBuffer = pcData; |
| |
| /* Zero-copy for call-back: no need to add the bytes to the |
| stream, only the pointer will be advanced by uxStreamBufferAdd(). */ |
| pcData = NULL; |
| } |
| } |
| #endif /* ipconfigUSE_CALLBACKS */ |
| |
| xResult = ( int32_t ) uxStreamBufferAdd( pxStream, uxOffset, pcData, ( size_t ) ulByteCount ); |
| |
| #if( ipconfigHAS_DEBUG_PRINTF != 0 ) |
| { |
| if( xResult != ( int32_t ) ulByteCount ) |
| { |
| FreeRTOS_debug_printf( ( "lTCPAddRxdata: at %ld: %ld/%lu bytes (tail %lu head %lu space %lu front %lu)\n", |
| uxOffset, xResult, ulByteCount, |
| pxStream->uxTail, |
| pxStream->uxHead, |
| uxStreamBufferFrontSpace( pxStream ), |
| pxStream->uxFront ) ); |
| } |
| } |
| #endif /* ipconfigHAS_DEBUG_PRINTF */ |
| |
| if( uxOffset == 0u ) |
| { |
| /* Data is being added to rxStream at the head (offs = 0) */ |
| #if( ipconfigUSE_CALLBACKS == 1 ) |
| if( bHasHandler != pdFALSE ) |
| { |
| /* The socket owner has installed an OnReceive handler. Pass the |
| Rx data, without copying from the rxStream, to the user. */ |
| for (;;) |
| { |
| uint8_t *ucReadPtr = NULL; |
| uint32_t ulCount; |
| if( pucBuffer != NULL ) |
| { |
| ucReadPtr = ( uint8_t * )pucBuffer; |
| ulCount = ulByteCount; |
| pucBuffer = NULL; |
| } |
| else |
| { |
| ulCount = ( uint32_t ) uxStreamBufferGetPtr( pxStream, &( ucReadPtr ) ); |
| } |
| |
| if( ulCount == 0ul ) |
| { |
| break; |
| } |
| |
| pxSocket->u.xTCP.pxHandleReceive( ( Socket_t )pxSocket, ( void* )ucReadPtr, ( size_t ) ulCount ); |
| uxStreamBufferGet( pxStream, 0ul, NULL, ( size_t ) ulCount, pdFALSE ); |
| } |
| } else |
| #endif /* ipconfigUSE_CALLBACKS */ |
| { |
| /* See if running out of space. */ |
| if( pxSocket->u.xTCP.bits.bLowWater == pdFALSE_UNSIGNED ) |
| { |
| size_t uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); |
| if( uxFrontSpace <= pxSocket->u.xTCP.uxLittleSpace ) |
| { |
| pxSocket->u.xTCP.bits.bLowWater = pdTRUE_UNSIGNED; |
| pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; |
| |
| /* bLowWater was reached, send the changed window size. */ |
| pxSocket->u.xTCP.usTimeout = 1u; |
| xSendEventToIPTask( eTCPTimerEvent ); |
| } |
| } |
| |
| /* New incoming data is available, wake up the user. User's |
| semaphores will be set just before the IP-task goes asleep. */ |
| pxSocket->xEventBits |= eSOCKET_RECEIVE; |
| |
| #if ipconfigSUPPORT_SELECT_FUNCTION == 1 |
| { |
| if( ( pxSocket->xSelectBits & eSELECT_READ ) != 0 ) |
| { |
| pxSocket->xEventBits |= ( eSELECT_READ << SOCKET_EVENT_BIT_COUNT ); |
| } |
| } |
| #endif |
| } |
| } |
| |
| return xResult; |
| } |
| |
| #endif /* ipconfigUSE_TCP */ |
| /*-----------------------------------------------------------*/ |
| |
| #if( ipconfigUSE_TCP == 1 ) |
| |
| /* Function to get the remote address and IP port */ |
| BaseType_t FreeRTOS_GetRemoteAddress( Socket_t xSocket, struct freertos_sockaddr *pxAddress ) |
| { |
| FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; |
| BaseType_t xResult; |
| |
| if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) |
| { |
| xResult = -pdFREERTOS_ERRNO_EINVAL; |
| } |
| else |
| { |
| /* BSD style sockets communicate IP and port addresses in network |
| byte order. |
| |
| IP address of remote machine. */ |
| pxAddress->sin_addr = FreeRTOS_htonl ( pxSocket->u.xTCP.ulRemoteIP ); |
| |
| /* Port on remote machine. */ |
| pxAddress->sin_port = FreeRTOS_htons ( pxSocket->u.xTCP.usRemotePort ); |
| |
| xResult = ( BaseType_t ) sizeof( ( *pxAddress ) ); |
| } |
| |
| return xResult; |
| } |
| |
| #endif /* ipconfigUSE_TCP */ |
| |
| /*-----------------------------------------------------------*/ |
| |
| #if( ipconfigUSE_TCP == 1 ) |
| |
| /* Returns the number of bytes that may be added to txStream */ |
| BaseType_t FreeRTOS_maywrite( Socket_t xSocket ) |
| { |
| FreeRTOS_Socket_t *pxSocket = ( FreeRTOS_Socket_t * ) xSocket; |
| BaseType_t xResult; |
| |
| if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP ) |
| { |
|