/*
 * FreeRTOS+TCP V3.1.0
 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * 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
 */

/**
 * @file FreeRTOS_Sockets.c
 * @brief Implements the Sockets API based on Berkeley sockets for the FreeRTOS+TCP network stack.
 *        Sockets are used by the application processes to interact with the IP-task which in turn
 *        interacts with the hardware.
 */

/* 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. */
/** @brief Set the port number for the socket in the xBoundSocketListItem. */
#define socketSET_SOCKET_PORT( pxSocket, usPort )    listSET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ), ( usPort ) )
/** @brief Get the port number for the socket in the xBoundSocketListItem. */
#define socketGET_SOCKET_PORT( pxSocket )            listGET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ) )

/** @brief 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 )

/** @brief 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.
 *
 * @note 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

/** @brief Maximum value of port number which can be auto assigned. */
#define socketAUTO_PORT_ALLOCATION_MAX_NUMBER    ( ( uint16_t ) 0xffff )

/** @brief The number of octets that make up an IP address. */
#define socketMAX_IP_ADDRESS_OCTETS              4U

/** @brief A block time of 0 simply means "don't block". */
#define socketDONT_BLOCK                         ( ( TickType_t ) 0 )

/** @brief TCP timer period in milliseconds. */
#if ( ( ipconfigUSE_TCP == 1 ) && !defined( ipTCP_TIMER_PERIOD_MS ) )
    #define ipTCP_TIMER_PERIOD_MS    ( 1000U )
#endif

/* Some helper macro's for defining the 20/80 % limits of uxLittleSpace / uxEnoughSpace. */
#define sock20_PERCENT            20U  /**< 20% of the defined limit. */
#define sock80_PERCENT            80U  /**< 80% of the defined limit. */
#define sock100_PERCENT           100U /**< 100% of the defined limit. */

/** @brief When ucASCIIToHex() can not convert a character,
 *         the value 255 will be returned.
 */
#define socketINVALID_HEX_CHAR    0xffU

/*
 * 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( const FreeRTOS_Socket_t * pxSocket,
                                  BaseType_t xProtocol,
                                  BaseType_t xIsBound );

#if ( ipconfigUSE_TCP == 1 )

/*
 * Internal function prvSockopt_so_buffer(): sets FREERTOS_SO_SNDBUF or
 * FREERTOS_SO_RCVBUF properties of a socket.
 */
    static BaseType_t prvSockopt_so_buffer( FreeRTOS_Socket_t * pxSocket,
                                            int32_t lOptionName,
                                            const void * pvOptionValue );
#endif /* ipconfigUSE_TCP == 1 */

/*
 * 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 );

static uint8_t ucASCIIToHex( char cChar );

#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 uxDataLength );
#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 const * 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 const * pxAddress );
#endif /* ipconfigUSE_TCP */

#if ( ipconfigUSE_TCP == 1 )

/*
 * Check if it makes any sense to wait for a connect event.
 * It may return: -EINPROGRESS, -EAGAIN, or 0 for OK.
 */
    static BaseType_t bMayConnect( FreeRTOS_Socket_t const * pxSocket );
#endif /* ipconfigUSE_TCP */

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/* Executed by the IP-task, it will check all sockets belonging to a set */
    static void prvFindSelectedSocket( SocketSelect_t * pxSocketSet );

#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

/** @brief The list that contains mappings between sockets and port numbers.
 *         Accesses to this list must be protected by critical sections of
 *         some kind.
 */
List_t xBoundUDPSocketsList;

#if ipconfigUSE_TCP == 1

/** @brief The list that contains mappings between sockets and port numbers.
 *         Accesses to this list must be protected by critical sections of
 *         some kind.
 */
    List_t xBoundTCPSocketsList;

#endif /* ipconfigUSE_TCP == 1 */

/*-----------------------------------------------------------*/

/**
 * @brief Check whether the socket is valid or not.
 *
 * @param[in] pxSocket: The socket being checked.
 * @param[in] xProtocol: The protocol for which the socket was created.
 * @param[in] xIsBound: pdTRUE when the socket should be bound, otherwise pdFALSE.
 *
 * @return If the socket is valid, then pdPASS is returned or else, pdFAIL
 *         is returned.
 */
static BaseType_t prvValidSocket( const FreeRTOS_Socket_t * pxSocket,
                                  BaseType_t xProtocol,
                                  BaseType_t xIsBound )
{
    BaseType_t xReturn;

    /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    if( ( pxSocket == NULL ) || ( pxSocket == FREERTOS_INVALID_SOCKET ) )
    {
        xReturn = pdFALSE;
    }
    else if( ( xIsBound != pdFALSE ) && !socketSOCKET_IS_BOUND( pxSocket ) )
    {
        /* 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;
    }
    else
    {
        xReturn = pdTRUE;
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief Initialise the bound TCP/UDP socket lists.
 */
void vNetworkSocketsInit( void )
{
    vListInitialise( &xBoundUDPSocketsList );

    #if ( ipconfigUSE_TCP == 1 )
        {
            vListInitialise( &xBoundTCPSocketsList );
        }
    #endif /* ipconfigUSE_TCP == 1 */
}
/*-----------------------------------------------------------*/

/**
 * @brief Determine the socket size for the given protocol.
 *
 * @param[in] xDomain: The domain for which the size of socket is being determined.
 * @param[in] xType: Is this a datagram socket or a stream socket.
 * @param[in] xProtocol: The protocol being used.
 * @param[out] pxSocketSize: Pointer to a variable in which the size shall be returned
 *                           if all checks pass.
 *
 * @return pdPASS if socket size was determined and put in the parameter pxSocketSize
 *         correctly, else pdFAIL.
 */
static BaseType_t prvDetermineSocketSize( BaseType_t xDomain,
                                          BaseType_t xType,
                                          BaseType_t xProtocol,
                                          size_t * pxSocketSize )
{
    BaseType_t xReturn = pdPASS;
    FreeRTOS_Socket_t const * pxSocket = NULL;

    /* 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 == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */
            }

            /* 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 == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */
                }

                *pxSocketSize = ( sizeof( *pxSocket ) - sizeof( pxSocket->u ) ) + sizeof( pxSocket->u.xTCP );
            }
        #endif /* ipconfigUSE_TCP == 1 */
        else
        {
            xReturn = pdFAIL;
            configASSERT( xReturn == pdPASS ); /* LCOV_EXCL_BR_LINE Exclude this line from branch coverage as the not-taken condition will never happen. */
        }
    }

    /* In case configASSERT() is not used */
    ( void ) xDomain;
    ( void ) pxSocket; /* Was only used for sizeof. */
    return xReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief allocate and initialise a socket.
 *
 * @param[in] xDomain: The domain in which the socket should be created.
 * @param[in] xType: The type of the socket.
 * @param[in] xProtocol: The protocol of the socket.
 *
 * @return FREERTOS_INVALID_SOCKET if the allocation failed, or if there was
 *         a parameter error, otherwise a valid socket.
 */
Socket_t FreeRTOS_socket( BaseType_t xDomain,
                          BaseType_t xType,
                          BaseType_t xProtocol )
{
    FreeRTOS_Socket_t * pxSocket;

/* Note that this value will be over-written by the call to prvDetermineSocketSize. */
    size_t uxSocketSize = 1;
    EventGroupHandle_t xEventGroup;
    Socket_t xReturn;
    BaseType_t xProtocolCpy = xProtocol;

    /* The special protocol FREERTOS_SOCK_DEPENDENT_PROTO, which is equivalent
     * to passing 0 as defined by POSIX, indicates to the socket layer that it
     * should pick a sensible default protocol based off the given socket type.
     * If we can't, prvDetermineSocketSize will catch it as an invalid
     * type/protocol combo.
     */
    if( xProtocol == FREERTOS_SOCK_DEPENDENT_PROTO )
    {
        switch( xType )
        {
            case FREERTOS_SOCK_DGRAM:
                xProtocolCpy = FREERTOS_IPPROTO_UDP;
                break;

            case FREERTOS_SOCK_STREAM:
                xProtocolCpy = FREERTOS_IPPROTO_TCP;
                break;

            default:

                /* incorrect xType. this will be caught by
                 * prvDetermineSocketSize.
                 */
                break;
        }
    }

    if( prvDetermineSocketSize( xDomain, xType, xProtocolCpy, &uxSocketSize ) == pdFAIL )
    {
        /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
        /* coverity[misra_c_2012_rule_11_4_violation] */
        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 )
        {
            /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
            /* coverity[misra_c_2012_rule_11_4_violation] */
            xReturn = FREERTOS_INVALID_SOCKET;
            iptraceFAILED_TO_CREATE_SOCKET();
        }
        else
        {
            xEventGroup = xEventGroupCreate();

            if( xEventGroup == NULL )
            {
                vPortFreeSocket( pxSocket );

                /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
                /* coverity[misra_c_2012_rule_11_4_violation] */
                xReturn = FREERTOS_INVALID_SOCKET;
                iptraceFAILED_TO_CREATE_EVENT_GROUP();
            }
            else
            {
                if( xProtocolCpy == FREERTOS_IPPROTO_UDP )
                {
                    iptraceMEM_STATS_CREATE( tcpSOCKET_UDP, pxSocket, uxSocketSize + sizeof( StaticEventGroup_t ) );
                }
                else
                {
                    /* Lint wants at least a comment, in case the macro is empty. */
                    iptraceMEM_STATS_CREATE( tcpSOCKET_TCP, pxSocket, uxSocketSize + sizeof( StaticEventGroup_t ) );
                }

                /* Clear the entire space to avoid nulling individual entries. */
                ( void ) 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( xProtocolCpy == FREERTOS_IPPROTO_UDP )
                {
                    vListInitialise( &( pxSocket->u.xUDP.xWaitingPacketsList ) );

                    #if ( ipconfigUDP_MAX_RX_PACKETS > 0U )
                        {
                            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 ) xProtocolCpy; /* protocol: UDP or TCP */

                #if ( ipconfigUSE_TCP == 1 )
                    {
                        if( xProtocolCpy == FREERTOS_IPPROTO_TCP )
                        {
                            /* StreamSize is expressed in number of bytes */
                            /* Round up buffer sizes to nearest multiple of MSS */
                            pxSocket->u.xTCP.usMSS = ( uint16_t ) 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( 1U, ( uint32_t ) ( pxSocket->u.xTCP.uxRxStreamSize / 2U ) / ipconfigTCP_MSS );
                                    pxSocket->u.xTCP.uxTxWinSize = FreeRTOS_max_uint32( 1U, ( uint32_t ) ( pxSocket->u.xTCP.uxTxStreamSize / 2U ) / 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 = pxSocket;
            }
        }
    }

    /* Remove compiler warnings in the case the configASSERT() is not defined. */
    ( void ) xDomain;

    return xReturn;
}
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Create a socket set.
 *
 * @return The new socket set which was created, or NULL when allocation has failed.
 */
    SocketSet_t FreeRTOS_CreateSocketSet( void )
    {
        SocketSelect_t * pxSocketSet;

        pxSocketSet = ( ( SocketSelect_t * ) pvPortMalloc( sizeof( *pxSocketSet ) ) );

        if( pxSocketSet != NULL )
        {
            ( void ) memset( pxSocketSet, 0, sizeof( *pxSocketSet ) );
            pxSocketSet->xSelectGroup = xEventGroupCreate();

            if( pxSocketSet->xSelectGroup == NULL )
            {
                vPortFree( pxSocketSet );
                pxSocketSet = NULL;
            }
            else
            {
                /* Lint wants at least a comment, in case the macro is empty. */
                iptraceMEM_STATS_CREATE( tcpSOCKET_SET, pxSocketSet, sizeof( *pxSocketSet ) + sizeof( StaticEventGroup_t ) );
            }
        }

        return ( SocketSet_t ) pxSocketSet;
    }

#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Delete a given socket set.
 *
 * @param[in] xSocketSet: The socket set being deleted.
 */
    void FreeRTOS_DeleteSocketSet( SocketSet_t xSocketSet )
    {
        IPStackEvent_t xCloseEvent;


        xCloseEvent.eEventType = eSocketSetDeleteEvent;
        xCloseEvent.pvData = ( void * ) xSocketSet;

        if( xSendEventStructToIPTask( &xCloseEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL )
        {
            FreeRTOS_printf( ( "FreeRTOS_DeleteSocketSet: xSendEventStructToIPTask failed\n" ) );
        }
    }

#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Add a socket to a set.
 *
 * @param[in] xSocket: The socket being added.
 * @param[in] xSocketSet: The socket set being added to.
 * @param[in] xBitsToSet: The event bits to set, a combination of the values defined
 *                        in 'eSelectEvent_t', for read, write, exception, etc.
 */
    void FreeRTOS_FD_SET( Socket_t xSocket,
                          SocketSet_t xSocketSet,
                          EventBits_t xBitsToSet )
    {
        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 |= xBitsToSet & ( ( EventBits_t ) eSELECT_ALL );

        if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_ALL ) ) != ( EventBits_t ) 0U )
        {
            /* 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. */
            prvFindSelectedSocket( pxSocketSet );
        }
    }

#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Clear select bits for a socket. If the mask becomes 0,
 *        remove the socket from the set.
 *
 * @param[in] xSocket: The socket whose select bits are being cleared.
 * @param[in] xSocketSet: The socket set of the socket.
 * @param[in] xBitsToClear: The bits to be cleared. Every '1' means that the
 *                corresponding bit will be cleared. See 'eSelectEvent_t' for
 *                the possible values.
 */
    void FreeRTOS_FD_CLR( Socket_t xSocket,
                          SocketSet_t xSocketSet,
                          EventBits_t xBitsToClear )
    {
        FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket;

        configASSERT( pxSocket != NULL );
        configASSERT( xSocketSet != NULL );

        pxSocket->xSelectBits &= ~( xBitsToClear & ( ( EventBits_t ) eSELECT_ALL ) );

        if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_ALL ) ) != ( EventBits_t ) 0U )
        {
            pxSocket->pxSocketSet = ( SocketSelect_t * ) xSocketSet;
        }
        else
        {
            /* disconnect it from the socket set */
            pxSocket->pxSocketSet = NULL;
        }
    }


#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/


#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Test if a socket belongs to a socket-set and if so, which event bit(s)
 *        are set.
 *
 * @param[in] xSocket: The socket of interest.
 * @param[in] xSocketSet: The socket set to which the socket belongs.
 *
 * @return If the socket belongs to the socket set: the event bits, otherwise zero.
 */
    EventBits_t FreeRTOS_FD_ISSET( const ConstSocket_t xSocket,
                                   const ConstSocketSet_t xSocketSet )
    {
        EventBits_t xReturn;
        const FreeRTOS_Socket_t * pxSocket = ( const 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 & ( ( EventBits_t ) eSELECT_ALL );
        }
        else
        {
            xReturn = 0;
        }

        return xReturn;
    }


#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief The select() statement: wait for an event to occur on any of the sockets
 *        included in a socket set.
 *
 * @param[in] xSocketSet: The socket set including the sockets on which we are
 *                        waiting for an event to occur.
 * @param[in] xBlockTimeTicks: Maximum time ticks to wait for an event to occur.
 *                   If the value is 'portMAX_DELAY' then the function will wait
 *                   indefinitely for an event to occur.
 *
 * @return The socket which might have triggered the event bit.
 */
    BaseType_t FreeRTOS_select( SocketSet_t xSocketSet,
                                TickType_t xBlockTimeTicks )
    {
        TimeOut_t xTimeOut;
        TickType_t xRemainingTime;
        SocketSelect_t * pxSocketSet = ( SocketSelect_t * ) xSocketSet;
        EventBits_t uxResult;

        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 */
            uxResult = xEventGroupWaitBits( pxSocketSet->xSelectGroup, ( ( EventBits_t ) eSELECT_ALL ), pdFALSE, pdFALSE, xRemainingTime );

            #if ( ipconfigSUPPORT_SIGNALS != 0 )
                {
                    if( ( uxResult & ( ( EventBits_t ) eSELECT_INTR ) ) != 0U )
                    {
                        ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_INTR );
                        FreeRTOS_debug_printf( ( "FreeRTOS_select: interrupted\n" ) );
                        break;
                    }
                }
            #endif /* ipconfigSUPPORT_SIGNALS */

            /* Have the IP-task find the socket which had an event */
            prvFindSelectedSocket( pxSocketSet );

            uxResult = xEventGroupGetBits( pxSocketSet->xSelectGroup );

            if( uxResult != 0U )
            {
                break;
            }

            /* Has the timeout been reached? */
            if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE )
            {
                break;
            }
        }

        return ( BaseType_t ) uxResult;
    }


#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief Send a message to the IP-task to have it check all sockets belonging to
 *        'pxSocketSet'
 *
 * @param[in] pxSocketSet: The socket set being asked to check.
 */
    static void prvFindSelectedSocket( SocketSelect_t * pxSocketSet )
    {
        IPStackEvent_t xSelectEvent;

        #if ( ipconfigSELECT_USES_NOTIFY != 0 )
            SocketSelectMessage_t xSelectMessage;
        #endif

        xSelectEvent.eEventType = eSocketSelectEvent;
        #if ( ipconfigSELECT_USES_NOTIFY != 0 )
            {
                xSelectMessage.pxSocketSet = pxSocketSet;
                xSelectMessage.xTaskhandle = xTaskGetCurrentTaskHandle();
                xSelectEvent.pvData = &( xSelectMessage );
            }
        #else
            {
                xSelectEvent.pvData = pxSocketSet;

                /* while the IP-task works on the request, the API will block on
                 * 'eSELECT_CALL_IP'.  So clear it first. */
                ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, ( BaseType_t ) eSELECT_CALL_IP );
            }
        #endif /* if ( ipconfigSELECT_USES_NOTIFY != 0 ) */

        /* 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" ) );
        }
        else
        {
            /* As soon as the IP-task is ready, it will set 'eSELECT_CALL_IP' to
             * wakeup the calling API */
            #if ( ipconfigSELECT_USES_NOTIFY != 0 )
                {
                    ( void ) ulTaskNotifyTake( pdFALSE, portMAX_DELAY );
                }
            #else
                {
                    ( void ) xEventGroupWaitBits( pxSocketSet->xSelectGroup, ( BaseType_t ) eSELECT_CALL_IP, pdTRUE, pdFALSE, portMAX_DELAY );
                }
            #endif
        }
    }


#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

/**
 * @brief Receive data from a bound socket. In this library, the function
 *        can only be used with connection-less sockets (UDP). For TCP sockets,
 *        please use FreeRTOS_recv().
 *
 * @param[in] xSocket: The socket to which the data is sent i.e. the
 *                     listening socket.
 * @param[out] pvBuffer: The buffer in which the data being received is to
 *                      be stored.
 * @param[in] uxBufferLength: The length of the buffer.
 * @param[in] xFlags: The flags to indicate preferences while calling this function.
 * @param[out] pxSourceAddress: The source address from which the data is being sent.
 * @param[out] pxSourceAddressLength: This parameter is used only to adhere to Berkeley
 *                              sockets standard. It is not used internally.
 *
 * @return The number of bytes received. Or else, an error code is returned. When it
 *         returns a negative value, the cause can be looked-up in
 *         'FreeRTOS_errno_TCP.h'.
 */
int32_t FreeRTOS_recvfrom( const ConstSocket_t xSocket,
                           void * pvBuffer,
                           size_t uxBufferLength,
                           BaseType_t xFlags,
                           struct freertos_sockaddr * pxSourceAddress,
                           const socklen_t * pxSourceAddressLength )
{
    BaseType_t lPacketCount;
    NetworkBufferDescriptor_t * pxNetworkBuffer;
    const void * pvCopySource;
    FreeRTOS_Socket_t const * pxSocket = 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;
    size_t uxPayloadLength;

    if( prvValidSocket( pxSocket, FREERTOS_IPPROTO_UDP, pdTRUE ) == pdFALSE )
    {
        lReturn = -pdFREERTOS_ERRNO_EINVAL;
    }
    else
    {
        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, ( EventBits_t ) eSOCKET_INTR,
                                                              pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK );
                        }
                    #endif /* ipconfigSUPPORT_SIGNALS */
                    break;
                }

                if( ( ( ( UBaseType_t ) xFlags ) & ( ( UBaseType_t ) FREERTOS_MSG_DONTWAIT ) ) != 0U )
                {
                    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, ( ( EventBits_t ) eSOCKET_RECEIVE ) | ( ( EventBits_t ) eSOCKET_INTR ),
                                              pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime );

            #if ( ipconfigSUPPORT_SIGNALS != 0 )
                {
                    if( ( xEventBits & ( EventBits_t ) eSOCKET_INTR ) != 0U )
                    {
                        if( ( xEventBits & ( EventBits_t ) eSOCKET_RECEIVE ) != 0U )
                        {
                            /* Shouldn't have cleared the eSOCKET_RECEIVE flag. */
                            ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_RECEIVE );
                        }

                        break;
                    }
                }
            #else /* if ( ipconfigSUPPORT_SIGNALS != 0 ) */
                {
                    ( 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 ) != pdFALSE )
            {
                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( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_PEEK ) == 0U )
                {
                    /* Remove the network buffer from the list of buffers waiting to
                     * be processed by the socket. */
                    ( void ) 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()'. */
            uxPayloadLength = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
            lReturn = ( int32_t ) uxPayloadLength;

            if( pxSourceAddress != NULL )
            {
                pxSourceAddress->sin_port = pxNetworkBuffer->usPort;
                pxSourceAddress->sin_addr = pxNetworkBuffer->ulIPAddress;
            }

            if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U )
            {
                /* The zero copy flag is not set.  Truncate the length if it won't
                 * fit in the provided buffer. */
                if( lReturn > ( int32_t ) uxBufferLength )
                {
                    iptraceRECVFROM_DISCARDING_BYTES( ( uxBufferLength - lReturn ) );
                    lReturn = ( int32_t ) uxBufferLength;
                }

                /* Copy the received data into the provided buffer, then release the
                 * network buffer. */
                pvCopySource = ( const void * ) &pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ];
                ( void ) memcpy( pvBuffer, pvCopySource, ( size_t ) lReturn );

                if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_PEEK ) == 0U )
                {
                    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 & ( EventBits_t ) eSOCKET_INTR ) != 0U )
            {
                lReturn = -pdFREERTOS_ERRNO_EINTR;
                iptraceRECVFROM_INTERRUPTED();
            }
        #endif /* ipconfigSUPPORT_SIGNALS */
        else
        {
            lReturn = -pdFREERTOS_ERRNO_EWOULDBLOCK;
            iptraceRECVFROM_TIMEOUT();
        }
    }

    return lReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief Check if a socket is a valid UDP socket. In case it is not
 *        yet bound, bind it to port 0 ( random port ).
 * @param[in] pxSocket: The socket that must be bound to a port number.
 * @return Returns pdTRUE if the socket was already bound, or if the
 *         socket has been bound successfully.
 */
static BaseType_t prvMakeSureSocketIsBound( FreeRTOS_Socket_t * pxSocket )
{
    /* Check if this is a valid UDP socket, does not have to be bound yet. */
    BaseType_t xReturn = prvValidSocket( pxSocket, FREERTOS_IPPROTO_UDP, pdFALSE );

    if( ( xReturn == pdTRUE ) && ( !socketSOCKET_IS_BOUND( pxSocket ) ) )
    {
        /* The socket is valid but it is not yet bound. */
        if( FreeRTOS_bind( pxSocket, NULL, 0U ) != 0 )
        {
            /* The socket was not yet bound, and binding it has failed. */
            xReturn = pdFALSE;
        }
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief Send data to a socket. The socket must have already been created by a
 *        successful call to FreeRTOS_socket(). It works for UDP-sockets only.
 *
 * @param[in] xSocket: The socket being sent to.
 * @param[in] pvBuffer: Pointer to the data being sent.
 * @param[in] uxTotalDataLength: Length (in bytes) of the data being sent.
 * @param[in] xFlags: Flags used to communicate preferences to the function.
 *                    Possibly FREERTOS_MSG_DONTWAIT and/or FREERTOS_ZERO_COPY.
 * @param[in] pxDestinationAddress: The address to which the data is to be sent.
 * @param[in] xDestinationAddressLength: This parameter is present to adhere to the
 *                  Berkeley sockets standard. Else, it is not used.
 *
 * @return When positive: the total number of bytes sent, when negative an error
 *         has occurred: it can be looked-up in 'FreeRTOS_errno_TCP.h'.
 */
int32_t FreeRTOS_sendto( Socket_t xSocket,
                         const void * pvBuffer,
                         size_t uxTotalDataLength,
                         BaseType_t xFlags,
                         const struct freertos_sockaddr * pxDestinationAddress,
                         socklen_t xDestinationAddressLength )
{
    NetworkBufferDescriptor_t * pxNetworkBuffer;
    void * pvCopyDest;
    IPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL };
    TimeOut_t xTimeOut;
    TickType_t xTicksToWait;
    int32_t lReturn = 0;
    FreeRTOS_Socket_t * pxSocket;
    const size_t uxMaxPayloadLength = ipMAX_UDP_PAYLOAD_LENGTH;
    const size_t uxPayloadOffset = ipUDP_PAYLOAD_OFFSET_IPv4;


    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 != NULL );

    if( uxTotalDataLength <= ( size_t ) uxMaxPayloadLength )
    {
        /* 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( prvMakeSureSocketIsBound( pxSocket ) == pdTRUE )
        {
            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( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_MSG_DONTWAIT ) != 0U )
            {
                xTicksToWait = ( TickType_t ) 0;
            }

            if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U )
            {
                /* 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( uxPayloadOffset + uxTotalDataLength, xTicksToWait );

                if( pxNetworkBuffer != NULL )
                {
                    pvCopyDest = ( void * ) &pxNetworkBuffer->pucEthernetBuffer[ uxPayloadOffset ];
                    ( void ) memcpy( pvCopyDest, pvBuffer, uxTotalDataLength );

                    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( pvBuffer );
            }

            if( pxNetworkBuffer != NULL )
            {
                /* xDataLength is the size of the total packet, including the Ethernet header. */
                pxNetworkBuffer->xDataLength = uxTotalDataLength + 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 ) uxTotalDataLength;
                    #if ( ipconfigUSE_CALLBACKS == 1 )
                        {
                            if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleSent ) )
                            {
                                pxSocket->u.xUDP.pxHandleSent( xSocket, uxTotalDataLength );
                            }
                        }
                    #endif /* ipconfigUSE_CALLBACKS */
                }
                else
                {
                    /* If the buffer was allocated in this function, release
                     * it. */
                    if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_ZERO_COPY ) == 0U )
                    {
                        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
        {
            /* No comment. */
            iptraceSENDTO_SOCKET_NOT_BOUND();
        }
    }
    else
    {
        /* The data is longer than the available buffer space. */
        iptraceSENDTO_DATA_TOO_LONG();
    }

    return lReturn;
} /* Tested */
/*-----------------------------------------------------------*/

/**
 * @brief binds a socket 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).
 *
 * @param[in] xSocket: The socket being bound.
 * @param[in] pxAddress: The address struct carrying the port number to which
 *                       this socket is to be bound.
 * @param[in] xAddressLength: This parameter is not used internally. The
 *                       function signature is used to adhere to standard
 *                       Berkeley sockets API.
 *
 * @return The return value is 0 if the bind is successful.
 *         If some error occurred, then a negative value is returned.
 */
BaseType_t FreeRTOS_bind( Socket_t xSocket,
                          struct freertos_sockaddr const * pxAddress,
                          socklen_t xAddressLength )
{
    IPStackEvent_t xBindEvent;
    FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket;
    BaseType_t xReturn = 0;

    ( void ) xAddressLength;

    configASSERT( xIsCallingFromIPTask() == pdFALSE );

    /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    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 ) )
    {
        /* 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 = 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. */
            ( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_BOUND, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, portMAX_DELAY );

            if( !socketSOCKET_IS_BOUND( pxSocket ) )
            {
                xReturn = -pdFREERTOS_ERRNO_EINVAL;
            }
        }
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief 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.
 *
 * @param[in] pxSocket: The socket is to be bound.
 * @param[in] pxBindAddress: The port to which this socket should be bound.
 * @param[in] uxAddressLength: The address length.
 * @param[in] xInternal: pdTRUE is calling internally, else pdFALSE.
 *
 * @return If the socket was bound to a port successfully, then a 0 is returned.
 *         Or else, an error code is returned.
 */
BaseType_t vSocketBind( FreeRTOS_Socket_t * pxSocket,
                        struct freertos_sockaddr * pxBindAddress,
                        size_t uxAddressLength,
                        BaseType_t xInternal )
{
    BaseType_t xReturn = 0; /* In Berkeley sockets, 0 means pass for bind(). */
    List_t * pxSocketList;
    struct freertos_sockaddr * pxAddress = pxBindAddress;

    #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 )
        struct freertos_sockaddr xAddress;
    #endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND */

    configASSERT( pxSocket != NULL );
    configASSERT( pxSocket != FREERTOS_INVALID_SOCKET );

    #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;

    #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 )
        {
            /* pxAddress will be NULL if sendto() was called on a socket without the
             * socket being bound to an address. In this case, automatically allocate
             * an address to the socket.  There is a small chance that the allocated
             * port will already be in use - if that is the case, then the check below
             * [pxListFindListItemWithValue()] will result in an error being returned. */
            if( pxAddress == NULL )
            {
                pxAddress = &xAddress;
                /* 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 != NULL );

    #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 )
        /* pxAddress is not NULL, no testing needed. */
    #else
        if( pxAddress != NULL )
    #endif
    {
        /* Add a do-while loop to facilitate use of 'break' statements. */
        do
        {
            if( pxAddress->sin_port == 0U )
            {
                pxAddress->sin_port = prvGetPrivatePortNumber( ( BaseType_t ) pxSocket->ucProtocol );

                if( pxAddress->sin_port == ( uint16_t ) 0U )
                {
                    xReturn = -pdFREERTOS_ERRNO_EADDRNOTAVAIL;
                    break;
                }
            }

            /* 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 )
                        {
                            ( void ) xTaskResumeAll();
                        }
                    #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
                }
            }
        } while( ipFALSE_BOOL );
    }

    #if ( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 0 )
        else
        {
            xReturn = -pdFREERTOS_ERRNO_EADDRNOTAVAIL;
            FreeRTOS_debug_printf( ( "vSocketBind: Socket no addr\n" ) );
        }
    #endif

    if( xReturn != 0 )
    {
        iptraceBIND_FAILED( xSocket, ( FreeRTOS_ntohs( pxAddress->sin_port ) ) );
    }

    return xReturn;
} /* Tested */
/*-----------------------------------------------------------*/

/**
 * @brief 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.
 *
 * @param[in] xSocket: the socket being closed.
 *
 * @return There are three distinct values which can be returned:
 *         0: If the xSocket is NULL/invalid.
 *         1: If the socket was successfully closed (read the brief above).
 *        -1: If the socket was valid but could not be closed because the message
 *            could not be delivered to the IP-task. Try again later.
 */
BaseType_t FreeRTOS_closesocket( Socket_t xSocket )
{
    BaseType_t xResult;

    #if ( ipconfigUSE_CALLBACKS == 1 )
        FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket;
    #endif /* ipconfigUSE_CALLBACKS == 1 */
    IPStackEvent_t xCloseEvent;
    xCloseEvent.eEventType = eSocketCloseEvent;
    xCloseEvent.pvData = xSocket;

    /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    if( ( xSocket == NULL ) || ( xSocket == FREERTOS_INVALID_SOCKET ) )
    {
        xResult = 0;
    }
    else
    {
        #if ( ipconfigUSE_CALLBACKS == 1 )
            {
                #if ( ipconfigUSE_TCP == 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;
                    }
                    else
                #endif /* ipconfigUSE_TCP == 1 */

                if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP )
                {
                    /* Clear the two UDP handlers. */
                    pxSocket->u.xUDP.pxHandleReceive = NULL;
                    pxSocket->u.xUDP.pxHandleSent = NULL;
                }
            }
        #endif /* ipconfigUSE_CALLBACKS == 1 */

        /* Let the IP task close the socket to keep it synchronised with the
         * packet handling. */

        /* The timeout value below is only used if this function is called from
         * a user task. If this function is called by the IP-task, it may fail
         * to close the socket when the event queue is full.
         * This should only happen in case of a user call-back. */
        if( xSendEventStructToIPTask( &xCloseEvent, ( TickType_t ) portMAX_DELAY ) == pdFAIL )
        {
            FreeRTOS_debug_printf( ( "FreeRTOS_closesocket: failed\n" ) );
            xResult = -1;
        }
        else
        {
            xResult = 1;
        }
    }

    return xResult;
}

/**
 * @brief This is the internal version of FreeRTOS_closesocket(). It will
 *        be called by the IPtask only to avoid problems with synchronicity.
 *
 * @param[in] pxSocket: The socket descriptor of the socket being closed.
 *
 * @return Returns NULL, always.
 */
/* MISRA Ref 17.2.1 [Sockets and limited recursion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */
/* coverity[misra_c_2012_rule_17_2_violation] */
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 )
                {
                    iptraceMEM_STATS_DELETE( pxSocket->u.xTCP.rxStream );
                    vPortFreeLarge( pxSocket->u.xTCP.rxStream );
                }

                if( pxSocket->u.xTCP.txStream != NULL )
                {
                    iptraceMEM_STATS_DELETE( pxSocket->u.xTCP.txStream );
                    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 ) )
    {
        /* 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 */

        ( void ) uxListRemove( &( pxSocket->xBoundSocketListItem ) );

        #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )
            {
                ( void ) 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 ) ) );
            ( void ) uxListRemove( &( pxNetworkBuffer->xBufferListItem ) );
            vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
        }
    }

    if( pxSocket->xEventGroup != NULL )
    {
        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 %xip:%u]: buffers %u socks %u\n",
                                         pxSocket->usLocalPort,
                                         ( unsigned ) pxSocket->u.xTCP.ulRemoteIP,
                                         pxSocket->u.xTCP.usRemotePort,
                                         ( unsigned ) uxGetNumberOfFreeNetworkBuffers(),
                                         ( unsigned ) listCURRENT_LIST_LENGTH( &xBoundTCPSocketsList ) ) );
            }
        }
    #endif /* ( ipconfigUSE_TCP == 1 ) && ( ipconfigHAS_DEBUG_PRINTF != 0 ) */

    /* And finally, after all resources have been freed, free the socket space */
    iptraceMEM_STATS_DELETE( pxSocket );
    vPortFreeSocket( pxSocket );

    return NULL;
} /* Tested */

/*-----------------------------------------------------------*/

#if ipconfigUSE_TCP == 1

/**
 * @brief 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 to close also
 *        all orphaned child-sockets.
 *
 * @param[in] pxSocketToDelete: The socket being closed.
 */
    /* MISRA Ref 17.2.1 [Sockets and limited recursion] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */
    /* coverity[misra_c_2012_rule_17_2_violation] */
    /* coverity[recursive_step] */
    static void prvTCPSetSocketCount( FreeRTOS_Socket_t const * pxSocketToDelete )
    {
        const ListItem_t * pxIterator;

        /* MISRA Ref 11.3.1 [Misaligned access] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
        /* coverity[misra_c_2012_rule_11_3_violation] */
        const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) );
        FreeRTOS_Socket_t * pxOtherSocket;
        uint16_t usLocalPort = pxSocketToDelete->usLocalPort;

        if( pxSocketToDelete->u.xTCP.eTCPState == eTCP_LISTEN )
        {
            pxIterator = listGET_NEXT( pxEnd );

            while( pxIterator != pxEnd )
            {
                pxOtherSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );

                /* This needs to be done here, before calling vSocketClose. */
                pxIterator = listGET_NEXT( pxIterator );

                if( ( pxOtherSocket->u.xTCP.eTCPState != eTCP_LISTEN ) &&
                    ( pxOtherSocket->usLocalPort == usLocalPort ) &&
                    ( ( pxOtherSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) ||
                      ( pxOtherSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) )
                {
                    /* MISRA Ref 17.2.1 [Sockets and limited recursion] */
                    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-172 */
                    /* coverity[misra_c_2012_rule_17_2_violation] */
                    /* coverity[recursive_step] */
                    ( void ) vSocketClose( pxOtherSocket );
                }
            }
        }
        else
        {
            for( pxIterator = listGET_NEXT( pxEnd );
                 pxIterator != pxEnd;
                 pxIterator = listGET_NEXT( pxIterator ) )
            {
                pxOtherSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );

                if( ( pxOtherSocket->u.xTCP.eTCPState == eTCP_LISTEN ) &&
                    ( pxOtherSocket->usLocalPort == usLocalPort ) &&
                    ( pxOtherSocket->u.xTCP.usChildCount != 0U ) )
                {
                    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 */

/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Set the value of receive/send buffer after some preliminary checks.
 *
 * @param[in] pxSocket: The socket whose options are being set.
 * @param[in] lOptionName: The option name: either FREERTOS_SO_SNDBUF or
 *                         FREERTOS_SO_SNDBUF.
 * @param[in] pvOptionValue: The value of the option being set.
 *
 * @return If there is no error, then 0 is returned. Or a negative errno
 *         value is returned.
 */
    static BaseType_t prvSockopt_so_buffer( FreeRTOS_Socket_t * pxSocket,
                                            int32_t lOptionName,
                                            const void * pvOptionValue )
    {
        uint32_t ulNewValue;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            FreeRTOS_debug_printf( ( "Set SO_%sBUF: wrong socket type\n",
                                     ( lOptionName == FREERTOS_SO_SNDBUF ) ? "SND" : "RCV" ) );
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else 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" ) );
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            ulNewValue = *( ( const uint32_t * ) pvOptionValue );

            if( lOptionName == FREERTOS_SO_SNDBUF )
            {
                /* Round up to nearest MSS size */
                ulNewValue = FreeRTOS_round_up( ulNewValue, ( uint32_t ) pxSocket->u.xTCP.usMSS );
                pxSocket->u.xTCP.uxTxStreamSize = ulNewValue;
            }
            else
            {
                pxSocket->u.xTCP.uxRxStreamSize = ulNewValue;
            }

            xReturn = 0;
        }

        return xReturn;
    }
#endif /* ipconfigUSE_TCP == 1 */
/*-----------------------------------------------------------*/

/* FreeRTOS_setsockopt calls itself, but in a very limited way,
 * only when FREERTOS_SO_WIN_PROPERTIES is being set. */

/**
 * @brief Set the socket options for the given socket.
 *
 * @param[in] xSocket: The socket for which the options are to be set.
 * @param[in] lLevel: Not used. Parameter is used to maintain the Berkeley sockets
 *                    standard.
 * @param[in] lOptionName: The name of the option to be set.
 * @param[in] pvOptionValue: The value of the option to be set.
 * @param[in] uxOptionLength: Not used. Parameter is used to maintain the Berkeley
 *                            sockets standard.
 *
 * @return If the option can be set with the given value, then 0 is returned. Else,
 *         an error code is returned.
 */
BaseType_t FreeRTOS_setsockopt( Socket_t xSocket,
                                int32_t lLevel,
                                int32_t lOptionName,
                                const void * pvOptionValue,
                                size_t uxOptionLength )
{
/* The standard Berkeley function returns 0 for success. */
    BaseType_t xReturn = -pdFREERTOS_ERRNO_EINVAL;
    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 ) uxOptionLength;

    /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    if( ( pxSocket != NULL ) && ( pxSocket != FREERTOS_INVALID_SOCKET ) )
    {
        switch( lOptionName )
        {
            case FREERTOS_SO_RCVTIMEO:
                /* Receive time out. */
                pxSocket->xReceiveBlockTime = *( ( const TickType_t * ) pvOptionValue );
                xReturn = 0;
                break;

            case FREERTOS_SO_SNDTIMEO:
                pxSocket->xSendBlockTime = *( ( const 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 > 0U )
                    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 = *( ( const 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. If pvOptionValue
                 * is anything else than NULL, the checksum generation will be turned on. */

                if( pvOptionValue == NULL )
                {
                    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 /* if ( ipconfigUSE_TCP == 1 ) */
                               {
                                   /* 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 = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPConnected;
                                       break;

                                   case FREERTOS_SO_TCP_RECV_HANDLER:
                                       pxSocket->u.xTCP.pxHandleReceive = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPReceive;
                                       break;

                                   case FREERTOS_SO_TCP_SENT_HANDLER:
                                       pxSocket->u.xTCP.pxHandleSent = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnTCPSent;
                                       break;
                               #endif /* ipconfigUSE_TCP */
                               case FREERTOS_SO_UDP_RECV_HANDLER:
                                   pxSocket->u.xUDP.pxHandleReceive = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnUDPReceive;
                                   break;

                               case FREERTOS_SO_UDP_SENT_HANDLER:
                                   pxSocket->u.xUDP.pxHandleSent = ( ( const F_TCP_UDP_Handler_t * ) pvOptionValue )->pxOnUDPSent;
                                   break;

                               default:   /* LCOV_EXCL_LINE The default case is required by MISRA but control flow will never ever reach
                                           * here since the switch statement enclosing this switch prevents that. */
                                   /* Should it throw an error here? */
                                   break; /* LCOV_EXCL_LINE. Since the default case will never reach, this break statement will not execute as well. */
                           }
                       }

                        xReturn = 0;
                        break;
                #endif /* ipconfigUSE_CALLBACKS */

                #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. */

                        /* The type cast of the pointer expression "A" to
                         * type "B" removes const qualifier from the pointed to type. */

                        /* MISRA Ref 11.8.1 [Function pointer and use of const pointer] */
                        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-118 */

                        /* MISRA Ref 11.1.1 [ Conversion between pointer to
                         * a function and another type ] */
                        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-111 */
                        /* coverity[misra_c_2012_rule_11_8_violation] */
                        /* coverity[misra_c_2012_rule_11_1_violation] */
                        pxSocket->pxUserWakeCallback = ( SocketWakeupCallback_t ) pvOptionValue;
                        xReturn = 0;
                        break;
                #endif /* ipconfigSOCKET_HAS_USER_WAKE_CALLBACK */

                #if ( ipconfigUSE_TCP != 0 )
                    case FREERTOS_SO_SET_LOW_HIGH_WATER:
                       {
                           const LowHighWater_t * pxLowHighWater = ( const 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) */
                        xReturn = prvSockopt_so_buffer( pxSocket, lOptionName, pvOptionValue );
                        break;

                    case FREERTOS_SO_WIN_PROPERTIES: /* Set all buffer and window properties in one call, parameter is pointer to WinProperties_t */
                       {
                           IPTCPSocket_t * pxTCP = &( pxSocket->u.xTCP );
                           const 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 */
                           }

                           pxProps = ( const WinProperties_t * ) pvOptionValue;

                           /* Validity of txStream will be checked by the function below. */
                           xReturn = prvSockopt_so_buffer( pxSocket, FREERTOS_SO_SNDBUF, &( pxProps->lTxBufSize ) );

                           if( xReturn != 0 )
                           {
                               break; /* will return an error. */
                           }

                           /* Validity of rxStream will be checked by the function below. */
                           xReturn = prvSockopt_so_buffer( pxSocket, FREERTOS_SO_RCVBUF, &( pxProps->lRxBufSize ) );

                           if( xReturn != 0 )
                           {
                               break; /* will return an error. */
                           }

                           #if ( ipconfigUSE_TCP_WIN == 1 )
                               {
                                   pxTCP->uxRxWinSize = ( uint32_t ) pxProps->lRxWinSize; /* Fixed value: size of the TCP reception window */
                                   pxTCP->uxTxWinSize = ( uint32_t ) pxProps->lTxWinSize; /* Fixed value: size of the TCP transmit window */
                               }
                           #else
                               {
                                   pxTCP->uxRxWinSize = 1U;
                                   pxTCP->uxTxWinSize = 1U;
                               }
                           #endif

                           /* In case the socket has already initialised its tcpWin,
                            * adapt the window size parameters */
                           if( pxTCP->xTCPWindow.u.bits.bHasInit != pdFALSE_UNSIGNED )
                           {
                               pxTCP->xTCPWindow.xSize.ulRxWindowLength = ( uint32_t ) ( pxTCP->uxRxWinSize * pxTCP->usMSS );
                               pxTCP->xTCPWindow.xSize.ulTxWindowLength = ( uint32_t ) ( pxTCP->uxTxWinSize * pxTCP->usMSS );
                           }
                       }

                        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( *( ( const BaseType_t * ) pvOptionValue ) != 0 )
                           {
                               pxSocket->u.xTCP.bits.bReuseSocket = pdTRUE;
                           }
                           else
                           {
                               pxSocket->u.xTCP.bits.bReuseSocket = pdFALSE;
                           }
                       }
                        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( *( ( const BaseType_t * ) pvOptionValue ) != 0 )
                           {
                               pxSocket->u.xTCP.bits.bCloseAfterSend = pdTRUE;
                           }
                           else
                           {
                               pxSocket->u.xTCP.bits.bCloseAfterSend = pdFALSE;
                           }
                       }
                        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( *( ( const BaseType_t * ) pvOptionValue ) != 0 )
                           {
                               pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdTRUE;
                           }
                           else
                           {
                               pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize = pdFALSE;
                           }

                           if( ( pxSocket->u.xTCP.xTCPWindow.u.bits.bSendFullSize == pdFALSE_UNSIGNED ) &&
                               ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) &&
                               ( FreeRTOS_outstanding( pxSocket ) > 0 ) )
                           {
                               pxSocket->u.xTCP.usTimeout = 1U; /* to set/clear bSendFullSize */
                               ( void ) xSendEventToIPTask( eTCPTimerEvent );
                           }
                       }
                        xReturn = 0;
                        break;

                    case FREERTOS_SO_STOP_RX: /* Refuse to receive more packets. */
                       {
                           if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
                           {
                               break; /* will return -pdFREERTOS_ERRNO_EINVAL */
                           }

                           if( *( ( const BaseType_t * ) pvOptionValue ) != 0 )
                           {
                               pxSocket->u.xTCP.bits.bRxStopped = pdTRUE;
                           }
                           else
                           {
                               pxSocket->u.xTCP.bits.bRxStopped = pdFALSE;
                           }

                           pxSocket->u.xTCP.bits.bWinChange = pdTRUE;
                           pxSocket->u.xTCP.usTimeout = 1U; /* to set/clear bRxStopped */
                           ( void ) xSendEventToIPTask( eTCPTimerEvent );
                       }
                        xReturn = 0;
                        break;
                #endif /* ipconfigUSE_TCP == 1 */

            default:
                /* No other options are handled. */
                xReturn = -pdFREERTOS_ERRNO_ENOPROTOOPT;
                break;
        }
    }
    else
    {
        xReturn = -pdFREERTOS_ERRNO_EINVAL;
    }

    return xReturn;
} /* Tested */

/*-----------------------------------------------------------*/

/**
 * @brief Find an available port number per https://tools.ietf.org/html/rfc6056.
 *
 * @param[in] xProtocol: FREERTOS_IPPROTO_TCP/FREERTOS_IPPROTO_UDP.
 *
 * @return If an available protocol port is found then that port number is returned.
 *         Or else, 0 is returned.
 */
static uint16_t prvGetPrivatePortNumber( BaseType_t xProtocol )
{
    const uint16_t usEphemeralPortCount =
        socketAUTO_PORT_ALLOCATION_MAX_NUMBER - ( socketAUTO_PORT_ALLOCATION_START_NUMBER - 1U );
    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 > 0U );

    return usResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Find a list item associated with the wanted-item.
 *
 * @param[in] pxList: The list through which the search is to be conducted.
 * @param[in] xWantedItemValue: The wanted item whose association is to be found.
 *
 * @return The list item holding the value being searched for. If nothing is found,
 *         then a NULL is returned.
 */
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;

        /* MISRA Ref 11.3.1 [Misaligned access] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
        /* coverity[misra_c_2012_rule_11_3_violation] */
        const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( pxList->xListEnd ) );

        for( pxIterator = listGET_NEXT( pxEnd );
             pxIterator != pxEnd;
             pxIterator = listGET_NEXT( pxIterator ) )
        {
            if( listGET_LIST_ITEM_VALUE( pxIterator ) == xWantedItemValue )
            {
                pxResult = pxIterator;
                break;
            }
        }
    }

    return pxResult;
} /* Tested */

/*-----------------------------------------------------------*/

/**
 * @brief Find the UDP socket corresponding to the port number.
 *
 * @param[in] uxLocalPort: The port whose corresponding bound UDP socket
 *                         is to be found.
 *
 * @return The socket owning the port if found or else NULL.
 */
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;
}

/*-----------------------------------------------------------*/

#define sockDIGIT_COUNT    ( 3U ) /**< Each nibble is expressed in at most 3 digits such as "192". */

/**
 * @brief Convert the 32-bit representation of the IP-address to the dotted decimal
 *        notation after some checks.
 *        A safe alternative is FreeRTOS_inet_ntop4().
 *
 * @param[in] ulIPAddress: 32-bit representation of the IP-address.
 * @param[out] pcBuffer: The buffer where the dotted decimal representation will be
 *                      stored if all checks pass. The buffer must be at least 16
 *                      bytes long.
 *
 * @return The pointer returned will be same as pcBuffer and will have the address
 *         stored in the location.
 */
const char * FreeRTOS_inet_ntoa( uint32_t ulIPAddress,
                                 char * pcBuffer )
{
    socklen_t uxNibble;
    socklen_t uxIndex = 0;
    const uint8_t * pucAddress = ( const uint8_t * ) &( ulIPAddress );
    const char * pcResult = pcBuffer;

    for( uxNibble = 0; uxNibble < ipSIZE_OF_IPv4_ADDRESS; uxNibble++ )
    {
        uint8_t pucDigits[ sockDIGIT_COUNT ];
        uint8_t ucValue = pucAddress[ uxNibble ];
        socklen_t uxSource = ( socklen_t ) sockDIGIT_COUNT - ( socklen_t ) 1U;

        for( ; ; )
        {
            pucDigits[ uxSource ] = ucValue % ( uint8_t ) 10U;
            ucValue /= ( uint8_t ) 10U;

            if( uxSource == 1U )
            {
                break;
            }

            uxSource--;
        }

        pucDigits[ 0 ] = ucValue;

        /* Skip leading zeros. */
        for( uxSource = 0; uxSource < ( ( socklen_t ) sockDIGIT_COUNT - ( socklen_t ) 1U ); uxSource++ )
        {
            if( pucDigits[ uxSource ] != 0U )
            {
                break;
            }
        }

        for( ; uxSource < ( socklen_t ) sockDIGIT_COUNT; uxSource++ )
        {
            pcBuffer[ uxIndex ] = ( char ) ( pucDigits[ uxSource ] + ( char ) '0' );
            uxIndex++;
        }

        if( uxNibble < ( ipSIZE_OF_IPv4_ADDRESS - 1U ) )
        {
            pcBuffer[ uxIndex ] = '.';
        }
        else
        {
            pcBuffer[ uxIndex ] = '\0';
        }

        uxIndex++;
    }

    return pcResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert the dotted decimal format of the IP-address to the 32-bit representation.
 *
 * @param[in] xAddressFamily: The Address family to which the IP-address belongs to. Only
 *                            FREERTOS_AF_INET (IPv4) is supported.
 * @param[in] pcSource: Pointer to the string holding the dotted decimal representation of
 *                      the IP-address.
 * @param[out] pvDestination: The pointer to the address struct/variable where the converted
 *                            IP-address will be stored. The buffer must be 4 bytes long
 *                            in case of a IPv4 address.
 *
 * @return If all checks pass, then pdPASS is returned or else pdFAIL is returned.
 */
BaseType_t FreeRTOS_inet_pton( BaseType_t xAddressFamily,
                               const char * pcSource,
                               void * pvDestination )
{
    BaseType_t xResult;

    /* Printable string to struct sockaddr. */
    switch( xAddressFamily )
    {
        case FREERTOS_AF_INET:
            xResult = FreeRTOS_inet_pton4( pcSource, pvDestination );
            break;

        default:
            xResult = -pdFREERTOS_ERRNO_EAFNOSUPPORT;
            break;
    }

    return xResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert the 32-bit representation of the IP-address to the dotted
 *        decimal format based on the Address Family. (Only FREERTOS_AF_INET
 *        is allowed).
 *
 * @param[in] xAddressFamily: The address family of the IP-address.
 * @param[in] pvSource: Pointer to the 32-bit representation of IP-address.
 * @param[out] pcDestination: The pointer to the character array where the dotted
 *                            decimal address will be stored if every check does pass.
 * @param[in] uxSize: Size of the character array. This value makes sure that the code
 *                    doesn't write beyond it's bounds.
 *
 * @return If every check does pass, then the pointer to the pcDestination is returned
 *         holding the dotted decimal format of IP-address. Else, a NULL is returned.
 */
const char * FreeRTOS_inet_ntop( BaseType_t xAddressFamily,
                                 const void * pvSource,
                                 char * pcDestination,
                                 socklen_t uxSize )
{
    const char * pcResult;

    /* Printable struct sockaddr to string. */
    switch( xAddressFamily )
    {
        case FREERTOS_AF_INET:
            pcResult = FreeRTOS_inet_ntop4( pvSource, pcDestination, uxSize );
            break;

        default:
            /* errno should be set to pdFREERTOS_ERRNO_EAFNOSUPPORT. */
            pcResult = NULL;
            break;
    }

    return pcResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert the 32-bit representation of the IP-address to the dotted decimal format.
 *
 * @param[in] pvSource: The pointer to the 32-bit representation of the IP-address.
 * @param[out] pcDestination: The pointer to a character array where the string of the
 *                           dotted decimal IP format.
 * @param[in] uxSize: Size of the character array. This value makes sure that the code
 *                    doesn't write beyond it's bounds.
 *
 * @return The pointer to the string holding the dotted decimal format of the IP-address. If
 *         everything passes correctly, then the pointer being returned is the same as
 *         pcDestination, else a NULL is returned.
 */
const char * FreeRTOS_inet_ntop4( const void * pvSource,
                                  char * pcDestination,
                                  socklen_t uxSize )
{
    uint32_t ulIPAddress;
    void * pvCopyDest;
    const char * pcReturn;

    if( uxSize < 16U )
    {
        /* There must be space for "255.255.255.255". */
        pcReturn = NULL;
    }
    else
    {
        pvCopyDest = ( void * ) &ulIPAddress;
        ( void ) memcpy( pvCopyDest, pvSource, sizeof( ulIPAddress ) );
        ( void ) FreeRTOS_inet_ntoa( ulIPAddress, pcDestination );
        pcReturn = pcDestination;
    }

    return pcReturn;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert an ASCII character to its corresponding hexadecimal value.
 *        Accepted characters are 0-9, a-f, and A-F.
 *
 * @param[in] cChar: The character to be converted.
 *
 * @return The hexadecimal value, between 0 and 15.
 *         When the character is not valid, socketINVALID_HEX_CHAR will be returned.
 */
static uint8_t ucASCIIToHex( char cChar )
{
    char cValue = cChar;
    uint8_t ucNew;

    if( ( cValue >= '0' ) && ( cValue <= '9' ) )
    {
        cValue -= ( char ) '0';
        /* The value will be between 0 and 9. */
        ucNew = ( uint8_t ) cValue;
    }
    else if( ( cValue >= 'a' ) && ( cValue <= 'f' ) )
    {
        cValue -= ( char ) 'a';
        ucNew = ( uint8_t ) cValue;
        /* The value will be between 10 and 15. */
        ucNew += ( uint8_t ) 10;
    }
    else if( ( cValue >= 'A' ) && ( cValue <= 'F' ) )
    {
        cValue -= ( char ) 'A';
        ucNew = ( uint8_t ) cValue;
        /* The value will be between 10 and 15. */
        ucNew += ( uint8_t ) 10;
    }
    else
    {
        /* The character does not represent a valid hex number, return 255. */
        ucNew = ( uint8_t ) socketINVALID_HEX_CHAR;
    }

    return ucNew;
}
/*-----------------------------------------------------------*/

/**
 * @brief This function converts a 48-bit MAC address to a human readable string.
 *
 * @param[in] pucSource: A pointer to an array of 6 bytes.
 * @param[out] pcTarget: A buffer that is 18 bytes long, it will contain the resulting string.
 * @param[in] cTen: Either an 'A' or an 'a'. It determines whether the hex numbers will use
 *                  capital or small letters.
 * @param[in] cSeparator: The separator that should appear between the bytes, either ':' or '-'.
 */
void FreeRTOS_EUI48_ntop( const uint8_t * pucSource,
                          char * pcTarget,
                          char cTen,
                          char cSeparator )
{
    size_t uxIndex;
    size_t uxNibble;
    size_t uxTarget = 0U;

    for( uxIndex = 0U; uxIndex < ipMAC_ADDRESS_LENGTH_BYTES; uxIndex++ )
    {
        uint8_t ucByte = pucSource[ uxIndex ];

        for( uxNibble = 0; uxNibble < 2U; uxNibble++ )
        {
            uint8_t ucNibble;
            char cResult;

            if( uxNibble == 0U )
            {
                ucNibble = ucByte >> 4;
            }
            else
            {
                ucNibble = ucByte & 0x0FU;
            }

            if( ucNibble <= 0x09U )
            {
                cResult = '0';
                cResult = cResult + ucNibble;
            }
            else
            {
                cResult = cTen; /* Either 'a' or 'A' */
                cResult = cResult + ( ucNibble - 10U );
            }

            pcTarget[ uxTarget ] = cResult;
            uxTarget++;
        }

        if( uxIndex == ( ipMAC_ADDRESS_LENGTH_BYTES - 1U ) )
        {
            pcTarget[ uxTarget ] = ( char ) 0;
            uxTarget++;
        }
        else
        {
            pcTarget[ uxTarget ] = cSeparator;
            uxTarget++;
        }
    }
}
/*-----------------------------------------------------------*/

/**
 * @brief This function converts a human readable string, representing an 48-bit MAC address,
 *        into a 6-byte address. Valid inputs are e.g. "62:48:5:83:A0:b2" and "0-12-34-fe-dc-ba".
 *
 * @param[in] pcSource: The null terminated string to be parsed.
 * @param[out] pucTarget: A buffer that is 6 bytes long, it will contain the MAC address.
 *
 * @return pdTRUE in case the string got parsed correctly, otherwise pdFALSE.
 */
BaseType_t FreeRTOS_EUI48_pton( const char * pcSource,
                                uint8_t * pucTarget )
{
    BaseType_t xResult = pdFALSE;
    size_t uxByteNr = 0U;
    size_t uxSourceIndex;
    size_t uxNibbleCount = 0U;
    size_t uxLength = strlen( pcSource );
    uint32_t uxSum = 0U;
    uint8_t ucHex;
    char cChar;

    /* Ignore the following line from branch coverage since the exits from this loop are
     * covered by break statements. The loop is kept as is to future proof the code against
     * any changes. LCOV_EXCL_BR_START */
    for( uxSourceIndex = 0U; uxSourceIndex <= uxLength; uxSourceIndex++ )
    /* LCOV_EXCL_BR_STOP */
    {
        cChar = pcSource[ uxSourceIndex ];
        ucHex = ucASCIIToHex( cChar );

        if( ucHex != socketINVALID_HEX_CHAR )
        {
            /* A valid nibble was found. Shift it into the accumulator. */
            uxSum = uxSum << 4;

            if( uxSum > 0xffU )
            {
                /* A hex value was too big. */
                break;
            }

            uxSum |= ucHex;
            uxNibbleCount++;
        }
        else
        {
            if( uxNibbleCount != 2U )
            {
                /* Each number should have 2 nibbles. */
                break;
            }

            pucTarget[ uxByteNr ] = ( uint8_t ) uxSum;
            uxSum = 0U;
            uxNibbleCount = 0U;
            uxByteNr++;

            if( uxByteNr == ipMAC_ADDRESS_LENGTH_BYTES )
            {
                xResult = pdTRUE;
                break;
            }

            if( ( cChar != ':' ) && ( cChar != '-' ) )
            {
                /* Invalid character. */
                break;
            }
        }
    }

    return xResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief This function converts the character string pcSource into a network address
 *        structure, then copies the network address structure to pvDestination.
 *        pvDestination is written in network byte order.
 *
 * @param[in] pcSource: The character string in holding the IP address.
 * @param[out] pvDestination: The returned network address in 32-bit network-endian format.
 *
 * @return pdPASS if the translation was successful or else pdFAIL.
 */
BaseType_t FreeRTOS_inet_pton4( const char * pcSource,
                                void * pvDestination )
{
    const uint32_t ulDecimalBase = 10U;
    uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ];
    uint32_t ulReturn = 0U, ulValue;
    UBaseType_t uxOctetNumber;
    BaseType_t xResult = pdPASS;
    const char * pcIPAddress = pcSource;
    const void * pvCopySource;

    ( void ) memset( pvDestination, 0, sizeof( ulReturn ) );

    /* Translate "192.168.2.100" to a 32-bit number, network-endian. */
    for( uxOctetNumber = 0U; uxOctetNumber < socketMAX_IP_ADDRESS_OCTETS; uxOctetNumber++ )
    {
        ulValue = 0U;

        if( pcIPAddress[ 0 ] == '0' )
        {
            /* Test for the sequence "0[0-9]", which would make it an octal representation. */
            if( ( pcIPAddress[ 1 ] >= '0' ) && ( pcIPAddress[ 1 ] <= '9' ) )
            {
                FreeRTOS_printf( ( "Octal representation of IP-addresses is not supported." ) );
                /* Don't support octal numbers. */
                xResult = pdFAIL;
                break;
            }
        }

        while( ( *pcIPAddress >= '0' ) && ( *pcIPAddress <= '9' ) )
        {
            BaseType_t xChar;

            /* Move previous read characters into the next decimal
             * position. */
            ulValue *= ulDecimalBase;

            /* Add the binary value of the ascii character. */
            xChar = ( BaseType_t ) pcIPAddress[ 0 ];
            xChar = xChar - ( BaseType_t ) '0';
            ulValue += ( uint32_t ) xChar;

            /* Move to next character in the string. */
            pcIPAddress++;
        }

        /* Check characters were read. */
        if( pcIPAddress == pcSource )
        {
            xResult = pdFAIL;
        }

        /* Check the value fits in an 8-bit number. */
        if( ulValue > 0xffU )
        {
            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 )
    {
        /* lint: ucOctet has been set because xResult == pdPASS. */
        ulReturn = FreeRTOS_inet_addr_quick( ucOctet[ 0 ], ucOctet[ 1 ], ucOctet[ 2 ], ucOctet[ 3 ] );
    }
    else
    {
        ulReturn = 0U;
    }

    if( xResult == pdPASS )
    {
        pvCopySource = ( const void * ) &ulReturn;
        ( void ) memcpy( pvDestination, pvCopySource, sizeof( ulReturn ) );
    }

    return xResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert the IP address from "w.x.y.z" (dotted decimal) format to the 32-bit format.
 *
 * @param[in] pcIPAddress: The character string pointer holding the IP-address in the "W.X.Y.Z"
 *                         (dotted decimal) format.
 *
 * @return The 32-bit representation of IP(v4) address.
 */
uint32_t FreeRTOS_inet_addr( const char * pcIPAddress )
{
    uint32_t ulReturn = 0U;

    /* inet_pton AF_INET target is a 4-byte 'struct in_addr'. */
    if( pdFAIL == FreeRTOS_inet_pton4( pcIPAddress, &( ulReturn ) ) )
    {
        /* Return 0 if translation failed. */
        ulReturn = 0U;
    }

    return ulReturn;
}
/*-----------------------------------------------------------*/


/**
 * @brief Function to get the local address and IP port of the given socket.
 *
 * @param[in] xSocket: Socket whose port is to be added to the pxAddress.
 * @param[out] pxAddress: Structure in which the IP address and the port number
 *                        is returned.
 *
 * @return Size of the freertos_sockaddr structure.
 */
size_t FreeRTOS_GetLocalAddress( ConstSocket_t xSocket,
                                 struct freertos_sockaddr * pxAddress )
{
    const FreeRTOS_Socket_t * pxSocket = ( const 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 );
}

/*-----------------------------------------------------------*/

/**
 * @brief Wake up the user of the given socket through event-groups.
 *
 * @param[in] pxSocket: The socket whose user is to be woken up.
 */
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 )
            {
                ( void ) 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_WAKE_CALLBACK */

    #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
        {
            if( pxSocket->pxSocketSet != NULL )
            {
                EventBits_t xSelectBits = ( pxSocket->xEventBits >> SOCKET_EVENT_BIT_COUNT ) & ( ( EventBits_t ) eSELECT_ALL );

                if( xSelectBits != 0U )
                {
                    pxSocket->xSocketBits |= xSelectBits;
                    ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, xSelectBits );
                }
            }

            pxSocket->xEventBits &= ( EventBits_t ) eSOCKET_ALL;
        }
    #endif /* ipconfigSUPPORT_SELECT_FUNCTION */

    if( ( pxSocket->xEventGroup != NULL ) && ( pxSocket->xEventBits != 0U ) )
    {
        ( void ) xEventGroupSetBits( pxSocket->xEventGroup, pxSocket->xEventBits );
    }

    pxSocket->xEventBits = 0U;
}

/*-----------------------------------------------------------*/

#if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )

/**
 * @brief This define makes it possible for network interfaces to inspect
 *        UDP messages and see if there is any UDP socket bound to a given port
 *        number.  This is probably only useful in systems with a minimum of
 *        RAM and when lots of anonymous broadcast messages come in.
 *
 * @param[in] usPortNr: the port number to look for.
 *
 * @return xFound if a socket with the port number is found.
 */
    BaseType_t xPortHasUDPSocket( uint16_t usPortNr )
    {
        BaseType_t xFound = pdFALSE;

        vTaskSuspendAll();
        {
            if( ( pxListFindListItemWithValue( &xBoundUDPSocketsList, ( TickType_t ) usPortNr ) != NULL ) )
            {
                xFound = pdTRUE;
            }
        }
        ( void ) xTaskResumeAll();

        return xFound;
    }

#endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */

/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Check if it makes any sense to wait for a connect event.
 *
 * @param[in] pxSocket: The socket trying to connect.
 *
 * @return It may return: -EINPROGRESS, -EAGAIN, or 0 for OK.
 */
    static BaseType_t bMayConnect( FreeRTOS_Socket_t const * pxSocket )
    {
        BaseType_t xResult;

        eIPTCPState_t eState = pxSocket->u.xTCP.eTCPState;

        switch( eState )
        {
            case eCLOSED:
            case eCLOSE_WAIT:
                xResult = 0;
                break;

            case eCONNECT_SYN:
                xResult = -pdFREERTOS_ERRNO_EINPROGRESS;
                break;

            case eTCP_LISTEN:
            case eSYN_FIRST:
            case eSYN_RECEIVED:
            case eESTABLISHED:
            case eFIN_WAIT_1:
            case eFIN_WAIT_2:
            case eCLOSING:
            case eLAST_ACK:
            case eTIME_WAIT:
            default:
                xResult = -pdFREERTOS_ERRNO_EAGAIN;
                break;
        }

        return xResult;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Called from #FreeRTOS_connect(): make some checks and if allowed,
 *        send a message to the IP-task to start connecting to a remote socket.
 *
 * @param[in] pxSocket: The socket attempting to connect to a remote port.
 * @param[in] pxAddress: The address the socket is trying to connect to.
 *
 * @return 0 on successful checks or a negative error code.
 */
    static BaseType_t prvTCPConnectStart( FreeRTOS_Socket_t * pxSocket,
                                          struct freertos_sockaddr const * pxAddress )
    {
        BaseType_t xResult = 0;

        if( pxAddress == NULL )
        {
            /* NULL address passed to the function. Invalid value. */
            xResult = -pdFREERTOS_ERRNO_EINVAL;
        }
        else 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 ) )
        {
            /* 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( pxSocket, NULL, 0U );
        }
        else
        {
            /* The socket is valid, not yet connected, and already bound to a port number. */
        }

        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;
                pxSocket->u.xTCP.ucRepCount = 0U;

                FreeRTOS_debug_printf( ( "FreeRTOS_connect: %u to %xip:%u\n",
                                         pxSocket->usLocalPort, ( unsigned ) 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 )

/**
 * @brief Connect to a remote port.
 *
 * @param[in] xClientSocket: The socket initiating the connection.
 * @param[in] pxAddress: The address of the remote socket.
 * @param[in] xAddressLength: This parameter is not used. It is kept in
 *                   the function signature to adhere to the Berkeley
 *                   sockets standard.
 *
 * @return 0 is returned on a successful connection, else a negative
 *         error code is returned.
 */
    BaseType_t FreeRTOS_connect( Socket_t xClientSocket,
                                 const 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 = -pdFREERTOS_ERRNO_EINVAL;
        TimeOut_t xTimeOut;

        ( void ) xAddressLength;

        xResult = prvTCPConnectStart( pxSocket, pxAddress );

        if( xResult == 0 )
        {
            /* And wait for the result */
            for( ; ; )
            {
                EventBits_t uxEvents;

                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 );

                if( xResult > 0 )
                {
                    /* Socket now connected, return a zero */
                    xResult = 0;
                    break;
                }

                /* Is it allowed to sleep more? */
                if( xTaskCheckForTimeOut( &xTimeOut, &xRemainingTime ) != pdFALSE )
                {
                    xResult = -pdFREERTOS_ERRNO_ETIMEDOUT;
                    break;
                }

                /* Go sleeping until we get any down-stream event */
                uxEvents = xEventGroupWaitBits( pxSocket->xEventGroup,
                                                ( EventBits_t ) eSOCKET_CONNECT | ( EventBits_t ) eSOCKET_CLOSED,
                                                pdTRUE /*xClearOnExit*/,
                                                pdFALSE /*xWaitAllBits*/,
                                                xRemainingTime );

                if( ( uxEvents & eSOCKET_CLOSED ) != 0U )
                {
                    xResult = -pdFREERTOS_ERRNO_ENOTCONN;
                    FreeRTOS_debug_printf( ( "FreeRTOS_connect() stopped due to an error\n" ) );
                    break;
                }
            }
        }

        return xResult;
    }

#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Accept a connection on an listening socket.
 *
 * @param[in] xServerSocket: The socket in listening mode.
 * @param[out] pxAddress: The address of the machine trying to connect to this node
 *                        is returned in this pointer.
 * @param[out] pxAddressLength: The length of the address of the remote machine.
 *
 * @return 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 listening 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 */

            /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
            /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
            /* coverity[misra_c_2012_rule_11_4_violation] */
            pxClientSocket = FREERTOS_INVALID_SOCKET;
        }
        else if( ( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) &&
                 ( pxSocket->u.xTCP.eTCPState != eTCP_LISTEN ) )
        {
            /* Parent socket is not in listening mode */

            /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
            /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
            /* coverity[misra_c_2012_rule_11_4_violation] */
            pxClientSocket = 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;
                        }
                        else
                        {
                            pxClientSocket = NULL;
                        }
                    }
                }
                ( void ) 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 = ( socklen_t ) 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 = pxSocket;
                    ( void ) 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;
                }

                /* Put the calling task to 'sleep' until a down-stream event is received. */
                ( void ) xEventGroupWaitBits( pxSocket->xEventGroup,
                                              ( EventBits_t ) eSOCKET_ACCEPT,
                                              pdTRUE /*xClearOnExit*/,
                                              pdFALSE /*xWaitAllBits*/,
                                              xRemainingTime );
            }
        }

        return pxClientSocket;
    }

#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Read incoming data from a TCP socket. Only after the last
 *        byte has been read, a close error might be returned.
 *
 * @param[in] xSocket: The socket owning the connection.
 * @param[out] pvBuffer: The buffer to store the incoming data in.
 * @param[in] uxBufferLength: The length of the buffer so that the function
 *                            does not do out of bound access.
 * @param[in] xFlags: The flags for conveying preference. The values
 *                    FREERTOS_MSG_DONTWAIT, FREERTOS_ZERO_COPY and/or
 *                    FREERTOS_MSG_PEEK can be used.
 *
 * @return The number of bytes actually received and stored in the pvBuffer.
 */
    BaseType_t FreeRTOS_recv( Socket_t xSocket,
                              void * pvBuffer,
                              size_t uxBufferLength,
                              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( ( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_ZERO_COPY ) != 0U ) &&
                 ( pvBuffer == NULL ) )
        {
            /* In zero-copy mode, pvBuffer is a pointer to a pointer ( not NULL ). */
            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 )
            {
                eIPTCPState_t eState = pxSocket->u.xTCP.eTCPState;

                switch( eState )
                {
                    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 lead to closing the socket. */
                            xByteCount = -pdFREERTOS_ERRNO_ENOMEM;
                        }
                        else
                        {
                            xByteCount = -pdFREERTOS_ERRNO_ENOTCONN;
                        }

                        break;

                    case eTCP_LISTEN:
                    case eCONNECT_SYN:
                    case eSYN_FIRST:
                    case eSYN_RECEIVED:
                    case eESTABLISHED:
                    case eFIN_WAIT_1:
                    case eFIN_WAIT_2:
                    case eLAST_ACK:
                    case eTIME_WAIT:
                    default:
                        /* Nothing. */
                        break;
                }

                if( xByteCount < 0 )
                {
                    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, ( EventBits_t ) eSOCKET_INTR,
                                                                  pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, socketDONT_BLOCK );
                            }
                        #endif /* ipconfigSUPPORT_SIGNALS */
                        break;
                    }

                    if( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_DONTWAIT ) != 0U )
                    {
                        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,
                                                  ( EventBits_t ) eSOCKET_RECEIVE | ( EventBits_t ) eSOCKET_CLOSED | ( EventBits_t ) eSOCKET_INTR,
                                                  pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime );
                #if ( ipconfigSUPPORT_SIGNALS != 0 )
                    {
                        if( ( xEventBits & ( EventBits_t ) 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 & ( EventBits_t ) eSOCKET_INTR ) != 0U )
                {
                    if( ( xEventBits & ( ( EventBits_t ) eSOCKET_RECEIVE | ( EventBits_t ) eSOCKET_CLOSED ) ) != 0U )
                    {
                        /* Shouldn't have cleared other flags. */
                        xEventBits &= ~( ( EventBits_t ) eSOCKET_INTR );
                        ( void ) xEventGroupSetBits( pxSocket->xEventGroup, xEventBits );
                    }

                    xByteCount = -pdFREERTOS_ERRNO_EINTR;
                }
                else
            #endif /* ipconfigSUPPORT_SIGNALS */

            if( xByteCount > 0 )
            {
                if( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_ZERO_COPY ) == 0U )
                {
                    BaseType_t xIsPeek = ( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_PEEK ) != 0U ) ? 1L : 0L;

                    xByteCount = ( BaseType_t )
                                 uxStreamBufferGet( pxSocket->u.xTCP.rxStream,
                                                    0U,
                                                    ( uint8_t * ) pvBuffer,
                                                    ( size_t ) uxBufferLength,
                                                    xIsPeek );

                    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;
                            pxSocket->u.xTCP.bits.bWinChange = pdTRUE;
                            pxSocket->u.xTCP.usTimeout = 1U; /* because bLowWater is cleared. */
                            ( void ) 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 );
                }
            }
            else
            {
                /* Nothing. */
            }
        } /* prvValidSocket() */

        return xByteCount;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Called from FreeRTOS_send(): some checks which will be done before
 *        sending a TCP packed.
 *
 * @param[in] pxSocket: The socket owning the connection.
 * @param[in] uxDataLength: The length of the data to be sent.
 *
 * @return 0: representing OK, else a negative error code will be returned.
 */
    static int32_t prvTCPSendCheck( FreeRTOS_Socket_t * pxSocket,
                                    size_t uxDataLength )
    {
        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.eTCPState == eCLOSED ) ||
                 ( pxSocket->u.xTCP.eTCPState == eCLOSE_WAIT ) ||
                 ( pxSocket->u.xTCP.eTCPState == 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( uxDataLength == 0U )
        {
            /* 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 */
            ( void ) prvTCPCreateStream( pxSocket, pdFALSE );

            if( pxSocket->u.xTCP.txStream == NULL )
            {
                xResult = -pdFREERTOS_ERRNO_ENOMEM;
            }
        }
        else
        {
            /* Nothing. */
        }

        return xResult;
    }

#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Get a direct pointer to the circular transmit buffer.
 *
 * @param[in] xSocket: The socket owning the buffer.
 * @param[in] pxLength: This will contain the number of bytes that may be written.
 *
 * @return Head of the circular transmit buffer if all checks pass. Or else, NULL
 *         is returned.
 */
    uint8_t * FreeRTOS_get_tx_head( ConstSocket_t xSocket,
                                    BaseType_t * pxLength )
    {
        uint8_t * pucReturn = NULL;
        const FreeRTOS_Socket_t * pxSocket = ( const 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 )
            {
                size_t uxSpace = uxStreamBufferGetSpace( pxBuffer );
                size_t uxRemain = pxBuffer->LENGTH - pxBuffer->uxHead;

                if( uxRemain <= uxSpace )
                {
                    *pxLength = ( BaseType_t ) uxRemain;
                }
                else
                {
                    *pxLength = ( BaseType_t ) uxSpace;
                }

                pucReturn = &( pxBuffer->ucArray[ pxBuffer->uxHead ] );
            }
        }

        return pucReturn;
    }
#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief 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.
 *
 * @param[in] xSocket: The socket owning the connection.
 * @param[in] pvBuffer: The buffer containing the data. The value of this pointer
 *                      may be NULL in case zero-copy transmissions are used.
 *                      It is used in combination with 'FreeRTOS_get_tx_head()'.
 * @param[in] uxDataLength: The length of the data to be added.
 * @param[in] xFlags: This parameter is not used. (zero or FREERTOS_MSG_DONTWAIT).
 *
 * @return The number of bytes actually sent. Zero when nothing could be sent
 *         or a negative error code in case an error occurred.
 */
    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;
        const uint8_t * pucSource = ( const uint8_t * ) pvBuffer;

        /* 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 );

            /* Try sending till there is a timeout or all bytes have been sent. */
            while( ipTRUE_BOOL )
            {
                /* 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;
                    }

                    xByteCount = ( BaseType_t ) uxStreamBufferAdd( pxSocket->u.xTCP.txStream, 0U, pucSource, ( 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. */
                        ( void ) 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. */
                        ( void ) xSendEventToIPTask( eTCPTimerEvent );
                    }

                    xBytesLeft -= xByteCount;

                    if( ( xBytesLeft == 0 ) || ( pvBuffer == NULL ) )
                    {
                        /* pvBuffer can be NULL in case TCP zero-copy transmissions are used. */
                        break;
                    }

                    /* As there are still bytes left to be sent, increase the
                     * data pointer. */
                    pucSource = &( pucSource[ 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( ( ( uint32_t ) xFlags & ( uint32_t ) FREERTOS_MSG_DONTWAIT ) != 0U )
                    {
                        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. */
                ( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_SEND | ( EventBits_t ) 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.eTCPState > eESTABLISHED )
                {
                    xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOTCONN;
                }
                else
                {
                    if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) )
                    {
                        FreeRTOS_debug_printf( ( "FreeRTOS_send: %u -> %xip:%d: no space\n",
                                                 pxSocket->usLocalPort,
                                                 ( unsigned ) pxSocket->u.xTCP.ulRemoteIP,
                                                 pxSocket->u.xTCP.usRemotePort ) );
                    }

                    xByteCount = ( BaseType_t ) -pdFREERTOS_ERRNO_ENOSPC;
                }
            }
        }

        return xByteCount;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Request to put a socket in listen mode.
 *
 * @param[in] xSocket: the socket to be put in listening mode.
 * @param[in] xBacklog: Maximum number of child sockets.
 *
 * @return 0 in case of success, or else a negative error code is
 *         returned.
 */
    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.eTCPState != eCLOSED ) && ( pxSocket->u.xTCP.eTCPState != 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 != pdFALSE_UNSIGNED )
            {
                if( pxSocket->u.xTCP.rxStream != NULL )
                {
                    vStreamBufferClear( pxSocket->u.xTCP.rxStream );
                }

                if( pxSocket->u.xTCP.txStream != NULL )
                {
                    vStreamBufferClear( pxSocket->u.xTCP.txStream );
                }

                ( void ) memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, 0, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) );
                ( void ) memset( &pxSocket->u.xTCP.xTCPWindow, 0, sizeof( pxSocket->u.xTCP.xTCPWindow ) );
                ( void ) 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;
            }

            vTCPStateChange( pxSocket, eTCP_LISTEN );
        }

        return xResult;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Shutdown - This function will shut down the connection in both
 *        directions. However, it will first deliver all data queued for
 *        transmission, and also it will first wait to receive any missing
 *        packets from the peer.
 *
 * @param[in] xSocket: The socket owning the connection.
 * @param[in] xHow: Not used. Just present to stick to Berkeley standard.
 *
 * @return 0 on successful shutdown or else a negative error code.
 */
    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.eTCPState != eESTABLISHED )
        {
            /* The socket is not connected. */
            xResult = -pdFREERTOS_ERRNO_ENOTCONN;
        }
        else
        {
            pxSocket->u.xTCP.bits.bUserShutdown = pdTRUE_UNSIGNED;

            /* Let the IP-task perform the shutdown of the connection. */
            pxSocket->u.xTCP.usTimeout = 1U;
            ( void ) xSendEventToIPTask( eTCPTimerEvent );
            xResult = 0;
        }

        ( void ) xHow;

        return xResult;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief 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)
 *
 * @param[in] xWillSleep: Whether the calling task is going to sleep.
 *
 * @return Minimum amount of time before the timer shall expire.
 */
    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;

        /* MISRA Ref 11.3.1 [Misaligned access] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
        /* coverity[misra_c_2012_rule_11_3_violation] */
        const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) );

        /* MISRA Ref 11.3.1 [Misaligned access] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
        /* coverity[misra_c_2012_rule_11_3_violation] */
        const ListItem_t * pxIterator = ( const 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 'timeout == 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
            {
                BaseType_t xRc;

                pxSocket->u.xTCP.usTimeout = 0U;
                xRc = 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( xRc < 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 )

/**
 * @brief 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 to find a match.
 *        For a socket in listening mode, the remote port and IP address
 *        are both 0.
 *
 * @param[in] ulLocalIP: Local IP address. Ignored for now.
 * @param[in] uxLocalPort: Local port number.
 * @param[in] ulRemoteIP: Remote (peer) IP address.
 * @param[in] uxRemotePort: Remote (peer) port.
 *
 * @return The socket which was found.
 */
    FreeRTOS_Socket_t * pxTCPSocketLookup( uint32_t ulLocalIP,
                                           UBaseType_t uxLocalPort,
                                           uint32_t ulRemoteIP,
                                           UBaseType_t uxRemotePort )
    {
        const ListItem_t * pxIterator;
        FreeRTOS_Socket_t * pxResult = NULL, * pxListenSocket = NULL;

        /* MISRA Ref 11.3.1 [Misaligned access] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
        /* coverity[misra_c_2012_rule_11_3_violation] */
        const ListItem_t * pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) );

        /* Parameter not yet supported. */
        ( void ) ulLocalIP;

        for( pxIterator = listGET_NEXT( pxEnd );
             pxIterator != pxEnd;
             pxIterator = 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.eTCPState == 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;
                }
                else
                {
                    /* This 'pxSocket' doesn't match. */
                }
            }
        }

        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 )

/**
 * @brief For the web server: borrow the circular Rx buffer for inspection.
 *        HTML driver wants to see if a sequence of 13/10/13/10 is available.
 *
 * @param[in] xSocket: The socket whose Rx stream is to be returned.
 *
 * @return The Rx stream of the socket if all checks pass, else NULL.
 */
    const struct xSTREAM_BUFFER * FreeRTOS_get_rx_buf( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        const 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 )

/**
 * @brief Create the stream buffer for the given socket.
 *
 * @param[in] pxSocket: the socket to create the stream for.
 * @param[in] xIsInputStream: Is this input stream? pdTRUE/pdFALSE?
 *
 * @return The stream buffer.
 */
    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 )
        {
            size_t uxLittlePerc = sock20_PERCENT;
            size_t uxEnoughPerc = sock80_PERCENT;
            size_t uxSegmentCount = pxSocket->u.xTCP.uxRxStreamSize / pxSocket->u.xTCP.usMSS;
            static const struct Percent
            {
                size_t uxPercLittle, uxPercEnough;
            }
            xPercTable[] =
            {
                { 0U,  100U }, /* 1 segment. */
                { 50U, 100U }, /* 2 segments. */
                { 34U, 100U }, /* 3 segments. */
                { 25U, 100U }, /* 4 segments. */
            };

            if( ( uxSegmentCount > 0U ) &&
                ( uxSegmentCount <= ARRAY_USIZE( xPercTable ) ) )
            {
                uxLittlePerc = xPercTable[ uxSegmentCount - 1U ].uxPercLittle;
                uxEnoughPerc = xPercTable[ uxSegmentCount - 1U ].uxPercEnough;
            }

            uxLength = pxSocket->u.xTCP.uxRxStreamSize;

            if( pxSocket->u.xTCP.uxLittleSpace == 0U )
            {
                pxSocket->u.xTCP.uxLittleSpace = ( uxLittlePerc * pxSocket->u.xTCP.uxRxStreamSize ) / sock100_PERCENT;
            }

            if( pxSocket->u.xTCP.uxEnoughSpace == 0U )
            {
                pxSocket->u.xTCP.uxEnoughSpace = ( uxEnoughPerc * 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 ) + uxLength ) - sizeof( pxBuffer->ucArray );

        pxBuffer = ( ( StreamBuffer_t * ) pvPortMallocLarge( uxSize ) );

        if( pxBuffer == NULL )
        {
            FreeRTOS_debug_printf( ( "prvTCPCreateStream: malloc failed\n" ) );
            pxSocket->u.xTCP.bits.bMallocError = pdTRUE;
            vTCPStateChange( pxSocket, eCLOSE_WAIT );
        }
        else
        {
            /* Clear the markers of the stream */
            ( void ) memset( pxBuffer, 0, sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ) );
            pxBuffer->LENGTH = ( size_t ) uxLength;

            if( xTCPWindowLoggingLevel != 0 )
            {
                FreeRTOS_debug_printf( ( "prvTCPCreateStream: %cxStream created %u bytes (total %u)\n", ( xIsInputStream != 0 ) ? 'R' : 'T', ( unsigned ) uxLength, ( unsigned ) uxSize ) );
            }

            if( xIsInputStream != 0 )
            {
                iptraceMEM_STATS_CREATE( tcpRX_STREAM_BUFFER, pxBuffer, uxSize );
                pxSocket->u.xTCP.rxStream = pxBuffer;
            }
            else
            {
                iptraceMEM_STATS_CREATE( tcpTX_STREAM_BUFFER, pxBuffer, uxSize );
                pxSocket->u.xTCP.txStream = pxBuffer;
            }
        }

        return pxBuffer;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief 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.
 *
 * @param[in] pxSocket: The socket to whose RxStream data is to be added.
 * @param[in] uxOffset: Offset of the packet.
 * @param[in] pcData: The data to be added to the RxStream.
 * @param[in] ulByteCount: Number of bytes in the data.
 *
 * @return The number of bytes actually added to the RxStream. Or else, a negative
 *         error code is returned.
 */
    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 = 0;

        #if ( ipconfigUSE_CALLBACKS == 1 )
            BaseType_t bHasHandler = ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleReceive ) ? pdTRUE : pdFALSE;
            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 )
            {
                xResult = -1;
            }
        }

        if( xResult >= 0 )
        {
            #if ( ipconfigUSE_CALLBACKS == 1 )
                {
                    if( ( bHasHandler != pdFALSE ) && ( uxStreamBufferGetSize( pxStream ) == 0U ) && ( uxOffset == 0U ) && ( pcData != NULL ) )
                    {
                        /* Data can be passed directly to the user */
                        pucBuffer = pcData;

                        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 %u: %d/%u bytes (tail %u head %u space %u front %u)\n",
                                                 ( unsigned int ) uxOffset,
                                                 ( int ) xResult,
                                                 ( unsigned int ) ulByteCount,
                                                 ( unsigned int ) pxStream->uxTail,
                                                 ( unsigned int ) pxStream->uxHead,
                                                 ( unsigned int ) uxStreamBufferFrontSpace( pxStream ),
                                                 ( unsigned int ) 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 == 0U )
                            {
                                break;
                            }

                            ( void ) pxSocket->u.xTCP.pxHandleReceive( pxSocket, ucReadPtr, ( size_t ) ulCount );
                            ( void ) uxStreamBufferGet( pxStream, 0U, 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;
                            pxSocket->u.xTCP.bits.bWinChange = pdTRUE;

                            /* bLowWater was reached, send the changed window size. */
                            pxSocket->u.xTCP.usTimeout = 1U;
                            ( void ) 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 |= ( EventBits_t ) eSOCKET_RECEIVE;

                    #if ipconfigSUPPORT_SELECT_FUNCTION == 1
                        {
                            if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != 0U )
                            {
                                pxSocket->xEventBits |= ( ( ( EventBits_t ) eSELECT_READ ) << SOCKET_EVENT_BIT_COUNT );
                            }
                        }
                    #endif
                }
            }
        }

        return xResult;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Function to get the remote IP-address and port number.
 *
 * @param[in] xSocket: Socket owning the connection.
 * @param[out] pxAddress: The address pointer to which the address
 *                        is to be added.
 *
 * @return The size of the address being returned. Or else a negative
 *         error code will be returned.
 */
    BaseType_t FreeRTOS_GetRemoteAddress( ConstSocket_t xSocket,
                                          struct freertos_sockaddr * pxAddress )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const 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 )

/**
 * @brief Check the number of bytes that may be added to txStream.
 *
 * @param[in] xSocket: The socket to be checked.
 *
 * @return the number of bytes that may be added to txStream or
 *         else a negative error code.
 */
    BaseType_t FreeRTOS_maywrite( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xResult;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xResult = -pdFREERTOS_ERRNO_EINVAL;
        }
        else if( pxSocket->u.xTCP.eTCPState != eESTABLISHED )
        {
            if( ( pxSocket->u.xTCP.eTCPState < eCONNECT_SYN ) || ( pxSocket->u.xTCP.eTCPState > eESTABLISHED ) )
            {
                xResult = -1;
            }
            else
            {
                xResult = 0;
            }
        }
        else if( pxSocket->u.xTCP.txStream == NULL )
        {
            xResult = ( BaseType_t ) pxSocket->u.xTCP.uxTxStreamSize;
        }
        else
        {
            xResult = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream );
        }

        return xResult;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Get the number of bytes that can be written in the Tx buffer
 *        of the given socket.
 *
 * @param[in] xSocket: the socket to be checked.
 *
 * @return The bytes that can be written. Or else an error code.
 */
    BaseType_t FreeRTOS_tx_space( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            if( pxSocket->u.xTCP.txStream != NULL )
            {
                xReturn = ( BaseType_t ) uxStreamBufferGetSpace( pxSocket->u.xTCP.txStream );
            }
            else
            {
                xReturn = ( BaseType_t ) pxSocket->u.xTCP.uxTxStreamSize;
            }
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Returns the number of bytes stored in the Tx buffer.
 *
 * @param[in] xSocket: The socket to be checked.
 *
 * @return The number of bytes stored in the Tx buffer of the socket.
 *         Or an error code.
 */
    BaseType_t FreeRTOS_tx_size( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            if( pxSocket->u.xTCP.txStream != NULL )
            {
                xReturn = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.txStream );
            }
            else
            {
                xReturn = 0;
            }
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Is the socket connected.
 *
 * @param[in] xSocket: The socket being checked.
 *
 * @return pdTRUE if TCP socket is connected.
 */
    BaseType_t FreeRTOS_issocketconnected( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn = pdFALSE;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            if( pxSocket->u.xTCP.eTCPState >= eESTABLISHED )
            {
                if( pxSocket->u.xTCP.eTCPState < eCLOSE_WAIT )
                {
                    xReturn = pdTRUE;
                }
            }
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Get the actual value of Maximum Segment Size ( MSS ) being used.
 *
 * @param[in] xSocket: The socket whose MSS is to be returned.
 *
 * @return the actual size of MSS being used or an error code.
 */
    BaseType_t FreeRTOS_mss( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            /* usMSS is declared as uint16_t to save space.  FreeRTOS_mss()
             * will often be used in signed native-size expressions cast it to
             * BaseType_t. */
            xReturn = ( BaseType_t ) ( pxSocket->u.xTCP.usMSS );
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Get the connection status. The values correspond to the members
 *        of the enum 'eIPTCPState_t'.
 *
 * @param[in] xSocket: Socket to get the connection status from.
 *
 * @return The connection status or an error code.
 *
 * @note For internal use only.
 */
    BaseType_t FreeRTOS_connstatus( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        {
            /* Cast it to BaseType_t. */
            xReturn = ( BaseType_t ) ( pxSocket->u.xTCP.eTCPState );
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Returns the number of bytes which can be read from the RX stream buffer.
 *
 * @param[in] xSocket: the socket to get the number of bytes from.
 *
 * @return Returns the number of bytes which can be read. Or an error
 *         code is returned.
 */
    BaseType_t FreeRTOS_rx_size( ConstSocket_t xSocket )
    {
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_TCP )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else if( pxSocket->u.xTCP.rxStream != NULL )
        {
            xReturn = ( BaseType_t ) uxStreamBufferGetSize( pxSocket->u.xTCP.rxStream );
        }
        else
        {
            xReturn = 0;
        }

        return xReturn;
    }


#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

/**
 * @brief Check whether a given socket is valid or not. Validity is defined
 *        as the socket not being NULL and not being Invalid.
 * @param[in] xSocket: The socket to be checked.
 * @return pdTRUE if the socket is valid, else pdFALSE.
 *
 */
BaseType_t xSocketValid( const ConstSocket_t xSocket )
{
    BaseType_t xReturnValue = pdFALSE;

    /* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    if( ( xSocket != FREERTOS_INVALID_SOCKET ) && ( xSocket != NULL ) )
    {
        xReturnValue = pdTRUE;
    }

    return xReturnValue;
}
/*-----------------------------------------------------------*/

#if 0

/**
 * @brief Returns the number of packets that are stored in a UDP socket.
 *
 * @param[in] xSocket: the socket to get the number of bytes from.
 *
 * @return Returns the number of packets that are stored.  Use FreeRTOS_recvfrom()
 *         to retrieve those packets.
 */
    BaseType_t FreeRTOS_udp_rx_size( Socket_t xSocket )
    {
        BaseType_t xReturn = 0;
        const FreeRTOS_Socket_t * pxSocket = ( const FreeRTOS_Socket_t * ) xSocket;

        if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_UDP )
        {
            xReturn = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) );
        }
        else
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }

        return xReturn;
    }
#endif /* 0 */

#if ( ipconfigUSE_TCP == 1 )

/**
 * @brief Get the net status. The IP-task will print a summary of all sockets and
 *        their connections.
 */
    void FreeRTOS_netstat( void )
    {
        IPStackEvent_t xAskEvent;

        /* Ask the IP-task to call vTCPNetStat()
         * to avoid accessing xBoundTCPSocketsList
         */
        xAskEvent.eEventType = eTCPNetStat;
        xAskEvent.pvData = ( void * ) NULL;
        ( void ) xSendEventStructToIPTask( &xAskEvent, pdMS_TO_TICKS( 1000U ) );
    }

#endif /* ipconfigUSE_TCP */
/*-----------------------------------------------------------*/

#if ( ( ipconfigHAS_PRINTF != 0 ) && ( ipconfigUSE_TCP == 1 ) )

/**
 * @brief Print a summary of all sockets and their connections.
 */
    void vTCPNetStat( void )
    {
        /* Show a simple listing of all created sockets and their connections */
        const ListItem_t * pxIterator;
        BaseType_t count = 0;
        size_t uxMinimum = uxGetMinimumFreeNetworkBuffers();
        size_t uxCurrent = uxGetNumberOfFreeNetworkBuffers();

        if( !listLIST_IS_INITIALISED( &xBoundTCPSocketsList ) )
        {
            FreeRTOS_printf( ( "PLUS-TCP not initialized\n" ) );
        }
        else
        {
            const ListItem_t * pxEndTCP = listGET_END_MARKER( &xBoundTCPSocketsList );
            const ListItem_t * pxEndUDP = listGET_END_MARKER( &xBoundUDPSocketsList );
            FreeRTOS_printf( ( "Prot Port IP-Remote       : Port  R/T Status       Alive  tmout Child\n" ) );

            for( pxIterator = listGET_HEAD_ENTRY( &xBoundTCPSocketsList );
                 pxIterator != pxEndTCP;
                 pxIterator = listGET_NEXT( pxIterator ) )
            {
                const FreeRTOS_Socket_t * pxSocket = ( ( const FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );
                #if ( ipconfigTCP_KEEP_ALIVE == 1 )
                    TickType_t age = xTaskGetTickCount() - pxSocket->u.xTCP.xLastAliveTime;
                #else
                    TickType_t age = 0U;
                #endif

                char ucChildText[ 16 ] = "";

                if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN )
                {
                    /* Using function "snprintf". */
                    const int32_t copied_len = snprintf( ucChildText, sizeof( ucChildText ), " %d/%d",
                                                         pxSocket->u.xTCP.usChildCount,
                                                         pxSocket->u.xTCP.usBacklog );
                    ( void ) copied_len;
                    /* These should never evaluate to false since the buffers are both shorter than 5-6 characters (<=65535) */
                    configASSERT( copied_len >= 0 );                                /* LCOV_EXCL_BR_LINE the 'taken' branch will never execute. See the above comment. */
                    configASSERT( copied_len < ( int32_t ) sizeof( ucChildText ) ); /* LCOV_EXCL_BR_LINE the 'taken' branch will never execute. See the above comment. */
                }

                FreeRTOS_printf( ( "TCP %5u %-16xip:%5u %d/%d %-13.13s %6u %6u%s\n",
                                   pxSocket->usLocalPort,                    /* Local port on this machine */
                                   ( unsigned ) pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine */
                                   pxSocket->u.xTCP.usRemotePort,            /* Port on remote machine */
                                   ( pxSocket->u.xTCP.rxStream != NULL ) ? 1 : 0,
                                   ( pxSocket->u.xTCP.txStream != NULL ) ? 1 : 0,
                                   FreeRTOS_GetTCPStateName( pxSocket->u.xTCP.eTCPState ),
                                   ( unsigned ) ( ( age > 999999U ) ? 999999U : age ), /* Format 'age' for printing */
                                   pxSocket->u.xTCP.usTimeout,
                                   ucChildText ) );
                count++;
            }

            for( pxIterator = listGET_HEAD_ENTRY( &xBoundUDPSocketsList );
                 pxIterator != pxEndUDP;
                 pxIterator = listGET_NEXT( pxIterator ) )
            {
                /* Local port on this machine */
                FreeRTOS_printf( ( "UDP Port %5u\n",
                                   FreeRTOS_ntohs( listGET_LIST_ITEM_VALUE( pxIterator ) ) ) );
                count++;
            }

            FreeRTOS_printf( ( "FreeRTOS_netstat: %d sockets %u < %u < %u buffers free\n",
                               ( int ) count,
                               ( unsigned ) uxMinimum,
                               ( unsigned ) uxCurrent,
                               ( unsigned ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) );
        }
    }

#endif /* ( ( ipconfigHAS_PRINTF != 0 ) && ( ipconfigUSE_TCP == 1 ) ) */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )

/**
 * @brief This internal non-blocking function will check all sockets that belong
 *        to a select set.  The events bits of each socket will be updated, and it
 *        will check if an ongoing select() call must be interrupted because of an
 *        event has occurred.
 *
 * @param[in] pxSocketSet: The socket-set which is to be waited on for change.
 */
    void vSocketSelect( const SocketSelect_t * pxSocketSet )
    {
        BaseType_t xRound;
        EventBits_t xSocketBits, xBitsToClear;

        #if ipconfigUSE_TCP == 1
            BaseType_t xLastRound = 1;
        #else
            BaseType_t xLastRound = 0;
        #endif

        /* These flags will be switched on after checking the socket status. */
        EventBits_t xGroupBits = 0;

        for( xRound = 0; xRound <= xLastRound; xRound++ )
        {
            const ListItem_t * pxIterator;
            const ListItem_t * pxEnd;

            if( xRound == 0 )
            {
                /* MISRA Ref 11.3.1 [Misaligned access] */
                /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
                /* coverity[misra_c_2012_rule_11_3_violation] */
                pxEnd = ( ( const ListItem_t * ) &( xBoundUDPSocketsList.xListEnd ) );
            }

            #if ipconfigUSE_TCP == 1
                else
                {
                    /* MISRA Ref 11.3.1 [Misaligned access] */
                    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
                    /* coverity[misra_c_2012_rule_11_3_violation] */
                    pxEnd = ( ( const ListItem_t * ) &( xBoundTCPSocketsList.xListEnd ) );
                }
            #endif /* ipconfigUSE_TCP == 1 */

            for( pxIterator = listGET_NEXT( pxEnd );
                 pxIterator != pxEnd;
                 pxIterator = listGET_NEXT( pxIterator ) )
            {
                FreeRTOS_Socket_t * pxSocket = ( ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );

                if( pxSocket->pxSocketSet != pxSocketSet )
                {
                    /* Socket does not belong to this select group. */
                    continue;
                }

                xSocketBits = 0;

                #if ( ipconfigUSE_TCP == 1 )
                    if( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP )
                    {
                        /* Check if the socket has already been accepted by the
                         * owner.  If not, it is useless to return it from a
                         * select(). */
                        BaseType_t bAccepted = pdFALSE;

                        if( pxSocket->u.xTCP.bits.bPassQueued == pdFALSE_UNSIGNED )
                        {
                            if( pxSocket->u.xTCP.bits.bPassAccept == pdFALSE_UNSIGNED )
                            {
                                bAccepted = pdTRUE;
                            }
                        }

                        /* Is the set owner interested in READ events? */
                        if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != ( EventBits_t ) 0U )
                        {
                            if( pxSocket->u.xTCP.eTCPState == eTCP_LISTEN )
                            {
                                if( ( pxSocket->u.xTCP.pxPeerSocket != NULL ) && ( pxSocket->u.xTCP.pxPeerSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) )
                                {
                                    xSocketBits |= ( EventBits_t ) eSELECT_READ;
                                }
                            }
                            else if( ( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED ) && ( pxSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) )
                            {
                                /* This socket has the re-use flag. After connecting it turns into
                                 * a connected socket. Set the READ event, so that accept() will be called. */
                                xSocketBits |= ( EventBits_t ) eSELECT_READ;
                            }
                            else if( ( bAccepted != 0 ) && ( FreeRTOS_recvcount( pxSocket ) > 0 ) )
                            {
                                xSocketBits |= ( EventBits_t ) eSELECT_READ;
                            }
                            else
                            {
                                /* Nothing. */
                            }
                        }

                        /* Is the set owner interested in EXCEPTION events? */
                        if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_EXCEPT ) != 0U )
                        {
                            if( ( pxSocket->u.xTCP.eTCPState == eCLOSE_WAIT ) || ( pxSocket->u.xTCP.eTCPState == eCLOSED ) )
                            {
                                xSocketBits |= ( EventBits_t ) eSELECT_EXCEPT;
                            }
                        }

                        /* Is the set owner interested in WRITE events? */
                        if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_WRITE ) != 0U )
                        {
                            BaseType_t bMatch = pdFALSE;

                            if( bAccepted != 0 )
                            {
                                if( FreeRTOS_tx_space( pxSocket ) > 0 )
                                {
                                    bMatch = pdTRUE;
                                }
                            }

                            if( bMatch == pdFALSE )
                            {
                                if( ( pxSocket->u.xTCP.bits.bConnPrepared != pdFALSE_UNSIGNED ) &&
                                    ( pxSocket->u.xTCP.eTCPState >= eESTABLISHED ) &&
                                    ( pxSocket->u.xTCP.bits.bConnPassed == pdFALSE_UNSIGNED ) )
                                {
                                    pxSocket->u.xTCP.bits.bConnPassed = pdTRUE;
                                    bMatch = pdTRUE;
                                }
                            }

                            if( bMatch != pdFALSE )
                            {
                                xSocketBits |= ( EventBits_t ) eSELECT_WRITE;
                            }
                        }
                    }
                    else
                #endif /* ipconfigUSE_TCP == 1 */
                {
                    /* Select events for UDP are simpler. */
                    if( ( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_READ ) != 0U ) &&
                        ( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) > 0U ) )
                    {
                        xSocketBits |= ( EventBits_t ) eSELECT_READ;
                    }

                    /* The WRITE and EXCEPT bits are not used for UDP */
                } /* if( pxSocket->ucProtocol == FREERTOS_IPPROTO_TCP ) */

                /* Each socket keeps its own event flags, which are looked-up
                 * by FreeRTOS_FD_ISSSET() */
                pxSocket->xSocketBits = xSocketBits;

                /* The ORed value will be used to set the bits in the event
                 * group. */
                xGroupBits |= xSocketBits;
            } /* for( pxIterator ... ) */
        }     /* for( xRound = 0; xRound <= xLastRound; xRound++ ) */

        xBitsToClear = xEventGroupGetBits( pxSocketSet->xSelectGroup );

        /* Now set the necessary bits. */
        xBitsToClear = ( xBitsToClear & ~xGroupBits ) & ( ( EventBits_t ) eSELECT_ALL );

        #if ( ipconfigSUPPORT_SIGNALS != 0 )
            {
                /* Maybe the socketset was signalled, but don't
                 * clear the 'eSELECT_INTR' bit here, as it will be used
                 * and cleared in FreeRTOS_select(). */
                xBitsToClear &= ~( ( EventBits_t ) eSELECT_INTR );
            }
        #endif /* ipconfigSUPPORT_SIGNALS */

        if( xBitsToClear != 0U )
        {
            ( void ) xEventGroupClearBits( pxSocketSet->xSelectGroup, xBitsToClear );
        }

        /* Now include eSELECT_CALL_IP to wakeup the caller. */
        ( void ) xEventGroupSetBits( pxSocketSet->xSelectGroup, xGroupBits | ( EventBits_t ) eSELECT_CALL_IP );
    }


#endif /* ipconfigSUPPORT_SELECT_FUNCTION == 1 */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SIGNALS != 0 )

/**
 * @brief Send a signal to the task which reads from this socket.
 *        The socket will receive an event of the type 'eSOCKET_INTR'.
 *        Any ongoing blocking API ( e.g. FreeRTOS_recv() ) will be terminated
 *        and return the value -pdFREERTOS_ERRNO_EINTR ( -4 ).
 *
 * @param[in] xSocket: The socket that will be signalled.
 */
    BaseType_t FreeRTOS_SignalSocket( Socket_t xSocket )
    {
        FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;

        if( pxSocket == NULL )
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }
        else
        #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
            if( ( pxSocket->pxSocketSet != NULL ) && ( pxSocket->pxSocketSet->xSelectGroup != NULL ) )
            {
                ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_INTR );
                xReturn = 0;
            }
            else
        #endif /* ipconfigSUPPORT_SELECT_FUNCTION */
        if( pxSocket->xEventGroup != NULL )
        {
            ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_INTR );
            xReturn = 0;
        }
        else
        {
            xReturn = -pdFREERTOS_ERRNO_EINVAL;
        }

        return xReturn;
    }

#endif /* ipconfigSUPPORT_SIGNALS */
/*-----------------------------------------------------------*/

#if ( ipconfigSUPPORT_SIGNALS != 0 )

/**
 * @brief The same as 'FreeRTOS_SignalSocket()', except that this function should
 *        be called from an ISR context.
 *
 * @param[in] xSocket: The socket that will be signalled.
 * @param[in,out] pxHigherPriorityTaskWoken: will be set to non-zero in case a higher-
 *                priority task has become runnable.
 */
    BaseType_t FreeRTOS_SignalSocketFromISR( Socket_t xSocket,
                                             BaseType_t * pxHigherPriorityTaskWoken )
    {
        FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) xSocket;
        BaseType_t xReturn;
        IPStackEvent_t xEvent;

        configASSERT( pxSocket != NULL );
        configASSERT( pxSocket->ucProtocol == ( uint8_t ) FREERTOS_IPPROTO_TCP );
        configASSERT( pxSocket->xEventGroup != NULL );

        xEvent.eEventType = eSocketSignalEvent;
        xEvent.pvData = pxSocket;

        /* The IP-task will call FreeRTOS_SignalSocket for this socket. */
        xReturn = xQueueSendToBackFromISR( xNetworkEventQueue, &xEvent, pxHigherPriorityTaskWoken );

        return xReturn;
    }

#endif /* ipconfigSUPPORT_SIGNALS */
/*-----------------------------------------------------------*/

#if 0
    #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
        struct pollfd
        {
            Socket_t fd;         /* file descriptor */
            EventBits_t events;  /* requested events */
            EventBits_t revents; /* returned events */
        };

        typedef BaseType_t nfds_t;

        BaseType_t poll( struct pollfd * fds,
                         nfds_t nfds,
                         BaseType_t timeout );
        BaseType_t poll( struct pollfd * fds,
                         nfds_t nfds,
                         BaseType_t timeout )
        {
            BaseType_t index;
            SocketSelect_t * pxSocketSet = NULL;
            BaseType_t xReturn = 0;

            /* See which socket-sets have been created and bound to the sockets involved. */
            for( index = 0; index < nfds; index++ )
            {
                FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd;

                if( pxSocket->pxSocketSet != NULL )
                {
                    if( pxSocketSet == NULL )
                    {
                        /* Use this socket-set. */
                        pxSocketSet = pxSocket->pxSocketSet;
                        xReturn = 1;
                    }
                    else if( pxSocketSet == pxSocket->pxSocketSet )
                    {
                        /* Good: associated with the same socket-set. */
                    }
                    else
                    {
                        /* More than one socket-set is found: can not do a select on 2 sets. */
                        xReturn = -1;
                        break;
                    }
                }
            }

            if( xReturn == 0 )
            {
                /* Create a new socket-set, and attach all sockets to it. */
                pxSocketSet = FreeRTOS_CreateSocketSet();

                if( pxSocketSet != NULL )
                {
                    xReturn = 1;
                }
                else
                {
                    xReturn = -2;
                }

                /* Memory leak: when the last socket closes, there is no more reference to
                 * this socket-set.  It should be marked as an automatic or anonymous socket-set,
                 * so when closing the last member, its memory will be freed. */
            }

            if( xReturn > 0 )
            {
                /* Only one socket-set is found.  Connect all sockets to this socket-set. */
                for( index = 0; index < nfds; index++ )
                {
                    FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd;
                    EventBits_t xEventBits = fds[ index ].events;

                    FreeRTOS_FD_SET( pxSocket, pxSocketSet, xEventBits );
                    FreeRTOS_FD_CLR( pxSocket, pxSocketSet, ( EventBits_t ) ~xEventBits );
                }

                /* And sleep until an event happens or a time-out. */
                xReturn = FreeRTOS_select( pxSocketSet, timeout );

                /* Now set the return events, copying from the socked field 'xSocketBits'. */
                for( index = 0; index < nfds; index++ )
                {
                    FreeRTOS_Socket_t * pxSocket = ( FreeRTOS_Socket_t * ) fds[ index ].fd;

                    fds[ index ].revents = pxSocket->xSocketBits & ( ( EventBits_t ) eSELECT_ALL );
                }
            }
            else
            {
                /* -1: Sockets are connected to different socket sets. */
                /* -2: FreeRTOS_CreateSocketSet() failed. */
            }

            return xReturn;
        }

    #endif /* ipconfigSUPPORT_SELECT_FUNCTION */
#endif /* 0 */
