/*
 * FreeRTOS Secure Sockets for NXP54018_IoT_Module V1.0.0 Beta 4
 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/*
 * Copyright (C) NXP 2017-2019.
 */

/* Define _SECURE_SOCKETS_WRAPPER_NOT_REDEFINE to prevent secure sockets functions
 * from redefining in iot_secure_sockets_wrapper_metrics.h */
#define _SECURE_SOCKETS_WRAPPER_NOT_REDEFINE

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "iot_secure_sockets.h"
#include "iot_tls.h"
#include "task.h"

/* Logging includes. */
//#include "iot_logging_task.h"

/* Third-party wifi driver include. */
#include "qcom_api.h"
#include "iot_wifi.h"
#include "custom_stack_offload.h"
#include "atheros_stack_offload.h"

#undef _SECURE_SOCKETS_WRAPPER_NOT_REDEFINE

/**
 * @brief Flag indicating that socket send operations are not permitted.
 *
 * If a WR shutdown in SOCKETS_Shutdown() is invoked, this flag is
 * set in the socket's xShutdownFlags member.
 */
#define nxpsecuresocketsSOCKET_WRITE_CLOSED_FLAG    ( 1UL << 2 )

/**
 * @brief Flag indicating that socket receive operations are not permitted.
 *
 * If a RD shutdown in SOCKETS_Shutdown() is invoked, this flag is
 * set in the socket's xShutdownFlags member.
 */
#define nxpsecuresocketsSOCKET_READ_CLOSED_FLAG     ( 1UL << 1 )

/**
 * @brief Delay used between network select attempts when effecting a receive timeout.
 *
 * Timeouts are mocked in the secure sockets layer, and this constant sets the
 * sleep time between each read attempt during the receive timeout period.
 */
#define nxpsecuresocketsFIVE_MILLISECONDS           ( pdMS_TO_TICKS( 5 ) )

/**
 * @brief The timeout supplied to the t_select function.
 *
 * Receive timeout are emulated in secure sockets layer and therefore we
 * do not want the Inventek module to block. Setting to zero means
 * no timeout, so one is the smallest value we can set it to.
 */
#define nxpsecuresocketsONE_MILLISECOND             ( 1 )
#define nxpsecuresocketsSOCKET_CONNECTED_FLAG       ( 1 )

/* Internal context structure. */
typedef struct SSOCKETContext
{
    Socket_t xSocket;
    char * pcDestination;
    void * pvTLSContext;
    BaseType_t xRequireTLS;
    BaseType_t xSendFlags;
    BaseType_t xRecvFlags;
    BaseType_t xShutdownFlags;
    uint32_t ulSendTimeout;
    uint32_t ulRecvTimeout;
    char * pcServerCertificate;
    uint32_t ulServerCertificateLength;
    uint32_t ulState;
    char ** ppcAlpnProtocols;
    uint32_t ulAlpnProtocolsCount;
} SSOCKETContext_t, * SSOCKETContextPtr_t;

/*
 * Helper routines.
 */

/*
 * @brief Network send callback.
 */
static BaseType_t prvNetworkSend( void * pvContext,
                                  const unsigned char * pucData,
                                  size_t xDataLength )
{
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) pvContext;

    /* Do not send data on unconnected socket */
    if( !( pxContext->ulState & nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
    {
        return -1;
    }

    char * sendBuf = custom_alloc( xDataLength );

    if( sendBuf == NULL )
    {
        return -1;
    }

    memcpy( sendBuf, pucData, xDataLength );
    int ret = qcom_send( ( int ) pxContext->xSocket,
                         sendBuf,
                         xDataLength,
                         pxContext->xSendFlags );
    custom_free( sendBuf );

    return ret;
}



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


QCA_CONTEXT_STRUCT * wlan_get_context( void );

/*
 * @brief Network receive callback.
 */
static BaseType_t prvNetworkRecv( void * pvContext,
                                  unsigned char * pucReceiveBuffer,
                                  size_t xReceiveLength )
{
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) pvContext;
    TickType_t xTimeOnEntering = xTaskGetTickCount();

    /* Do not receive data on unconnected socket */
    if( !( pxContext->ulState & nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
    {
        return -1;
    }

    QCA_CONTEXT_STRUCT * enetCtx = wlan_get_context();
    A_STATUS xStatus;

    char * buffLoc = NULL;
    int xRetVal = 0;

    for( ; ; )
    {
        /* Check if there is anything to be received on this socket. */
        xStatus = ( A_STATUS ) t_select( enetCtx,
                                         ( uint32_t ) pxContext->xSocket,
                                         nxpsecuresocketsONE_MILLISECOND );

        if( xStatus == A_OK ) /* Data available. */
        {
            xRetVal = qcom_recv( ( int ) pxContext->xSocket, &buffLoc, xReceiveLength, 0 );

            if( xRetVal > 0 ) /* Data received. */
            {
                memcpy( pucReceiveBuffer, buffLoc, xRetVal );
                break;
            }
            else /* Error occured. */
            {
                /*int errno = t_errno( wlan_get_context(), ( uint32_t ) pxContext->xSocket ); */
                xRetVal = SOCKETS_SOCKET_ERROR;
                break;
            }
        }
        else if( xStatus == A_ERROR ) /* A_ERROR is returned from t_select on timeout. */
        {
            if( ( xTaskGetTickCount() - xTimeOnEntering ) < pxContext->ulRecvTimeout )
            {
                vTaskDelay( nxpsecuresocketsFIVE_MILLISECONDS );
            }
            else
            {
                xRetVal = 0;// return -1;
                break;
            }
        }
        else
        {
            xRetVal = SOCKETS_SOCKET_ERROR;
            break;
        }
    }

    if( buffLoc != NULL )
    {
        zero_copy_free( buffLoc );
    }

    return xRetVal;
}
/*-----------------------------------------------------------*/

/*
 * Interface routines.
 */

int32_t SOCKETS_Close( Socket_t xSocket )
{
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket;
    int32_t lStatus = SOCKETS_ERROR_NONE;

    if( ( NULL != pxContext ) && ( SOCKETS_INVALID_SOCKET != pxContext->xSocket ) )
    {
        pxContext->ulState = 0;

        if( NULL != pxContext->pcDestination )
        {
            vPortFree( pxContext->pcDestination );
        }

        if( NULL != pxContext->pcServerCertificate )
        {
            vPortFree( pxContext->pcServerCertificate );
        }

        if( pdTRUE == pxContext->xRequireTLS )
        {
            TLS_Cleanup( pxContext->pvTLSContext );
        }

        if( NULL != pxContext->ppcAlpnProtocols )
        {
            int i;
            for( i = 0; i < pxContext->ulAlpnProtocolsCount; i++ )
            {
                vPortFree( pxContext->ppcAlpnProtocols[i] );
            }
            vPortFree( pxContext->ppcAlpnProtocols );
            pxContext->ulAlpnProtocolsCount = 0;
        }

        qcom_socket_close( ( int ) pxContext->xSocket );
        vPortFree( pxContext );
    }
    else
    {
        lStatus = SOCKETS_EINVAL;
    }

    return lStatus;
}
/*-----------------------------------------------------------*/

int32_t SOCKETS_Connect( Socket_t xSocket,
                         SocketsSockaddr_t * pxAddress,
                         Socklen_t xAddressLength )
{
    int32_t xStatus = SOCKETS_ERROR_NONE;
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket;
    TLSParams_t xTLSParams = { 0 };
    SOCKADDR_T xTempAddress = { 0 };

    if( ( SOCKETS_INVALID_SOCKET != pxContext->xSocket ) && ( NULL != pxAddress ) && WIFI_IsConnected())
    {
        /* Connect the wrapped socket. */


        /* The driver code expects the endianess of the address and port in host order and then swaps before
         * sending the connection data to the firmware. */
        xTempAddress.sin_addr.s_addr = SOCKETS_ntohl( pxAddress->ulAddress );
        xTempAddress.sin_family = pxAddress->ucSocketDomain;
        xTempAddress.sin_port = SOCKETS_ntohs( pxAddress->usPort );
        xStatus = qcom_connect( ( int ) pxContext->xSocket,
                                ( struct sockaddr * ) &xTempAddress,
                                xAddressLength );

        /* Keep socket state - connected */
        if( SOCKETS_ERROR_NONE == xStatus )
        {
            pxContext->ulState |= nxpsecuresocketsSOCKET_CONNECTED_FLAG;
        }

        /* Negotiate TLS if requested. */
        if( ( SOCKETS_ERROR_NONE == xStatus ) && ( pdTRUE == pxContext->xRequireTLS ) )
        {
            xTLSParams.ulSize = sizeof( xTLSParams );
            xTLSParams.pcDestination = pxContext->pcDestination;
            xTLSParams.pcServerCertificate = pxContext->pcServerCertificate;
            xTLSParams.ulServerCertificateLength = pxContext->ulServerCertificateLength;
            xTLSParams.pvCallerContext = pxContext;
            xTLSParams.pxNetworkRecv = prvNetworkRecv;
            xTLSParams.pxNetworkSend = prvNetworkSend;
            xTLSParams.ppcAlpnProtocols = ( const char ** ) pxContext->ppcAlpnProtocols;
            xTLSParams.ulAlpnProtocolsCount = pxContext->ulAlpnProtocolsCount;
            xStatus = TLS_Init( &pxContext->pvTLSContext, &xTLSParams );

            if( SOCKETS_ERROR_NONE == xStatus )
            {
                xStatus = TLS_Connect( pxContext->pvTLSContext );

                /* Report positive error codes as negative number */
                xStatus = xStatus > 0 ? SOCKETS_TLS_HANDSHAKE_ERROR : xStatus;
            }
        }
    }
    else
    {
        xStatus = SOCKETS_SOCKET_ERROR;
    }

    return xStatus;
}
/*-----------------------------------------------------------*/


/* Convert IP address in uint32_t to comma separated bytes. */
#define UINT32_IPADDR_TO_CSV_BYTES( a )                     \
    ( ( ( a ) >> 24 ) & 0xFF ), ( ( ( a ) >> 16 ) & 0xFF ), \
    ( ( ( a ) >> 8 ) & 0xFF ), ( ( a ) & 0xFF )

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

uint32_t SOCKETS_GetHostByName( const char * pcHostName )
{
    uint32_t ulAddr = 0;

    if( strlen( pcHostName ) <= ( size_t ) securesocketsMAX_DNS_NAME_LENGTH )
    {
        WIFI_GetHostIP( ( char * ) pcHostName, ( uint8_t * ) &ulAddr );
        configPRINTF( ( "Looked up %s as %d.%d.%d.%d\r\n",
                        pcHostName,
                        UINT32_IPADDR_TO_CSV_BYTES( ulAddr ) ) );
    }
    else
    {
        configPRINTF( ( "Malformed URL, Host name: %s is too long.", pcHostName ) );
    }

    /* This api is to return the address in network order. WIFI_GetHostIP returns the host IP
     * in host order.
     */
    ulAddr = SOCKETS_htonl( ulAddr );

    return ulAddr;
}

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

int32_t SOCKETS_Recv( Socket_t xSocket,
                      void * pvBuffer,
                      size_t xBufferLength,
                      uint32_t ulFlags )
{
    int32_t lStatus = SOCKETS_ERROR_NONE;
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket;


    if( ( SOCKETS_INVALID_SOCKET != xSocket ) &&
        ( NULL != pvBuffer ) &&
        ( ( nxpsecuresocketsSOCKET_READ_CLOSED_FLAG & pxContext->xShutdownFlags ) == 0UL ) &&
        ( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
        )
    {
        pxContext->xRecvFlags = ( BaseType_t ) ulFlags;
        if( pdTRUE == pxContext->xRequireTLS )
        {
            /* Receive through TLS pipe, if negotiated. */
            lStatus = TLS_Recv( pxContext->pvTLSContext, pvBuffer, xBufferLength );
        }
        else
        {
            /* Receive unencrypted. */
            lStatus = prvNetworkRecv( pxContext, pvBuffer, xBufferLength );
        }
    }
    else
    {
        lStatus = SOCKETS_SOCKET_ERROR;
    }

    return lStatus;
}
/*-----------------------------------------------------------*/

int32_t SOCKETS_Send( Socket_t xSocket,
                      const void * pvBuffer,
                      size_t xDataLength,
                      uint32_t ulFlags )
{
    /* The WiFi module refuses to send data if it exceeds this threshold defined in
     * atheros_stack_offload.h */
    uint32_t ulSendMaxLength = IPV4_FRAGMENTATION_THRESHOLD;
    int32_t lWritten = 0, lWrittenPerLoop = 0;
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket;

    if( ( SOCKETS_INVALID_SOCKET != pxContext->xSocket ) &&
        ( NULL != pvBuffer ) &&
        ( ( nxpsecuresocketsSOCKET_WRITE_CLOSED_FLAG & pxContext->xShutdownFlags ) == 0UL ) &&
        ( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
        )
    {
        pxContext->xSendFlags = ( BaseType_t ) ulFlags;

        if( pdTRUE == pxContext->xRequireTLS )
        {
            /* In case of TLS, reserve extra space for SSL meta data (header, maclen, ivlen, ... = 45B) */
            ulSendMaxLength = 1531;

            for(
                uint32_t ulToRemain = xDataLength, ulBufferPos = 0, ulToSend = 0;
                ulToRemain;
                ulBufferPos += ulToSend, ulToRemain -= ulToSend
                )
            {
                ulToSend = ulToRemain > ulSendMaxLength ? ulSendMaxLength : ulToRemain;
                /* Send through TLS pipe, if negotiated. */
                lWrittenPerLoop = TLS_Send( pxContext->pvTLSContext,
                                            &( ( unsigned char const * ) pvBuffer )[ ulBufferPos ],
                                            ulToSend );

                /* Error code < 0. */
                if( lWrittenPerLoop < 0 )
                {
                    /* Set the error code to be returned */
                    lWritten = lWrittenPerLoop;
                    break;
                }

                /* Number of lWritten bytes. */
                lWritten += lWrittenPerLoop;

                if( lWrittenPerLoop != ulToSend )
                {
                    break;
                }
            }
        }
        else
        {
            for(
                uint32_t ulToRemain = xDataLength, ulBufferPos = 0, ulToSend = 0;
                ulToRemain;
                ulBufferPos += ulToSend, ulToRemain -= ulToSend
                )
            {
                ulToSend = ulToRemain > ulSendMaxLength ? ulSendMaxLength : ulToRemain;
                lWrittenPerLoop = prvNetworkSend( pxContext,
                                                  &( ( unsigned char const * ) pvBuffer )[ ulBufferPos ],
                                                  ulToSend );

                /* Error code < 0. */
                if( lWrittenPerLoop < 0 )
                {
                    /* Set the error code to be returned */
                    lWritten = lWrittenPerLoop;
                    break;
                }

                /* Number of lWritten bytes. */
                lWritten += lWrittenPerLoop;

                if( lWrittenPerLoop != ulToSend )
                {
                    break;
                }
            }
        }
    }
    else
    {
        lWritten = SOCKETS_SOCKET_ERROR;
    }

    return lWritten;
}
/*-----------------------------------------------------------*/

int32_t SOCKETS_SetSockOpt( Socket_t xSocket,
                            int32_t lLevel,
                            int32_t lOptionName,
                            const void * pvOptionValue,
                            size_t xOptionLength )
{
    int32_t lStatus = SOCKETS_ERROR_NONE;
    TickType_t xTimeout;
    SSOCKETContextPtr_t pxContext = ( SSOCKETContextPtr_t ) xSocket;

    if( ( NULL != pxContext ) && ( SOCKETS_INVALID_SOCKET != pxContext->xSocket ) )
    {
        switch( lOptionName )
        {
            case SOCKETS_SO_SERVER_NAME_INDICATION:

                /* Secure socket option cannot be used on connected socket */
                if( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                    break;
                }

                /* Non-NULL destination string indicates that SNI extension should
                 * be used during TLS negotiation. */
                if( NULL == ( pxContext->pcDestination =
                                  ( char * ) pvPortMalloc( 1 + xOptionLength ) ) )
                {
                    lStatus = SOCKETS_ENOMEM;
                }
                else
                {
                    memcpy( pxContext->pcDestination, pvOptionValue, xOptionLength );
                    pxContext->pcDestination[ xOptionLength ] = '\0';
                }

                break;

            case SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE:

                /* Secure socket option cannot be used on connected socket */
                if( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                    break;
                }

                /* Non-NULL server certificate field indicates that the default trust
                 * list should not be used. */
                if( NULL == ( pxContext->pcServerCertificate =
                                  ( char * ) pvPortMalloc( xOptionLength ) ) )
                {
                    lStatus = SOCKETS_ENOMEM;
                }
                else
                {
                    memcpy( pxContext->pcServerCertificate, pvOptionValue, xOptionLength );
                    pxContext->ulServerCertificateLength = xOptionLength;
                }

                break;

            case SOCKETS_SO_REQUIRE_TLS:

                /* Secure socket option cannot be used on connected socket */
                if( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                    break;
                }

                pxContext->xRequireTLS = pdTRUE;
                break;

            case SOCKETS_SO_NONBLOCK:

                if( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
                {
                    xTimeout = 0;
                    /* TODO: Investigate the NONBLOCK compile time config. */
                    pxContext->ulSendTimeout = 1;
                    pxContext->ulRecvTimeout = 1;
                }
                else
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                }

                break;

            case SOCKETS_SO_RCVTIMEO:
                xTimeout = *( ( const TickType_t * ) pvOptionValue ); /*lint !e9087 pvOptionValue passed should be of TickType_t. */

                if( xTimeout == 0U )
                {
                    pxContext->ulRecvTimeout = portMAX_DELAY;
                }
                else
                {
                    pxContext->ulRecvTimeout = xTimeout;
                }

                break;

            case SOCKETS_SO_SNDTIMEO:
                /* Comply with Berkeley standard - a 0 timeout is wait forever. */
                xTimeout = *( ( const TickType_t * ) pvOptionValue ); /*lint !e9087 pvOptionValue passed should be of TickType_t. */

                if( xTimeout == 0U )
                {
                    pxContext->ulSendTimeout = portMAX_DELAY;
                }
                else
                {
                    pxContext->ulSendTimeout = xTimeout;
                }

                break;

            case SOCKETS_SO_ALPN_PROTOCOLS:

                /* Secure socket option cannot be used on connected socket */
                if( pxContext->ulState & ( nxpsecuresocketsSOCKET_CONNECTED_FLAG ) )
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                    break;
                }

                /* Prepare list of supported protocols, must be NULL-terminated */
                pxContext->ppcAlpnProtocols = (char **)pvPortMalloc((xOptionLength + 1 )*sizeof(char*));
                if( pxContext->ppcAlpnProtocols == NULL )
                {
                    lStatus = SOCKETS_SOCKET_ERROR;
                    break;
                }

                int i;
                for( i = 0; i < xOptionLength; i++ )
                {
                    int len = strlen( ((char **)pvOptionValue)[i] );
                    pxContext->ppcAlpnProtocols[i] = (char *)pvPortMalloc(len + 1);
                    if( pxContext->ppcAlpnProtocols[i] == NULL )
                    {
                        /* Malloc failed - remove created list and set error return value */
                        while(i)
                        {
                            vPortFree( pxContext->ppcAlpnProtocols[i - 1] );
                            i--;
                        }
                        vPortFree( pxContext->ppcAlpnProtocols );
                        pxContext->ppcAlpnProtocols = NULL;
                        pxContext->ulAlpnProtocolsCount = 0;

                        lStatus = SOCKETS_SOCKET_ERROR;
                        break;
                    }
                    memcpy( pxContext->ppcAlpnProtocols[i], ((char **)pvOptionValue)[i], len );
                    pxContext->ppcAlpnProtocols[i][len] = '\0';
                }

                if( lStatus != SOCKETS_SOCKET_ERROR )
                {
                    pxContext->ppcAlpnProtocols[xOptionLength] = NULL;
                    pxContext->ulAlpnProtocolsCount = xOptionLength;
                }

                break;

            default:
                lStatus = qcom_setsockopt( ( int ) pxContext->xSocket,
                                           lLevel,
                                           lOptionName,
                                           ( void * ) pvOptionValue,
                                           xOptionLength );
                break;
        }
    }
    else
    {
        lStatus = SOCKETS_SOCKET_ERROR;
    }

    return lStatus;
}
/*-----------------------------------------------------------*/

int32_t SOCKETS_Shutdown( Socket_t xSocket,
                          uint32_t ulHow )
{
    SSOCKETContextPtr_t pxSecureSocket = ( SSOCKETContextPtr_t ) xSocket;
    int32_t lRetVal = SOCKETS_SOCKET_ERROR;

    /* Ensure that a valid socket was passed. */
    if( ( SOCKETS_INVALID_SOCKET != pxSecureSocket->xSocket ) )
    {
        switch( ulHow )
        {
            case SOCKETS_SHUT_RD:
                /* Further receive calls on this socket should return error. */
                pxSecureSocket->xShutdownFlags |= nxpsecuresocketsSOCKET_READ_CLOSED_FLAG;

                /* Return success to the user. */
                lRetVal = SOCKETS_ERROR_NONE;
                break;

            case SOCKETS_SHUT_WR:
                /* Further send calls on this socket should return error. */
                pxSecureSocket->xShutdownFlags |= nxpsecuresocketsSOCKET_WRITE_CLOSED_FLAG;

                /* Return success to the user. */
                lRetVal = SOCKETS_ERROR_NONE;
                break;

            case SOCKETS_SHUT_RDWR:
                /* Further send or receive calls on this socket should return error. */
                pxSecureSocket->xShutdownFlags |= nxpsecuresocketsSOCKET_READ_CLOSED_FLAG;
                pxSecureSocket->xShutdownFlags |= nxpsecuresocketsSOCKET_WRITE_CLOSED_FLAG;

                /* Return success to the user. */
                lRetVal = SOCKETS_ERROR_NONE;
                break;

            default:
                /* An invalid value was passed for ulHow. */
                lRetVal = SOCKETS_EINVAL;
                break;
        }
    }
    else
    {
        return SOCKETS_EINVAL;
    }

    return lRetVal;
}
/*-----------------------------------------------------------*/

Socket_t SOCKETS_Socket( int32_t lDomain,
                         int32_t lType,
                         int32_t lProtocol )
{
    int32_t lStatus = SOCKETS_ERROR_NONE;
    int32_t xSocket = 0;
    SSOCKETContextPtr_t pxContext = NULL;

    /* Ensure that only supported values are supplied. */
    configASSERT( lDomain == SOCKETS_AF_INET );
    configASSERT( lType == SOCKETS_SOCK_STREAM );
    configASSERT( lProtocol == SOCKETS_IPPROTO_TCP );

    /* Allocate the internal context structure. */
    pxContext = pvPortMalloc( sizeof( SSOCKETContext_t ) );

    if( pxContext != NULL )
    {
        memset( pxContext, 0, sizeof( SSOCKETContext_t ) );

        /* Create the wrapped socket. */
        xSocket = qcom_socket( ATH_AF_INET,
                               SOCK_STREAM_TYPE,
                               0 ); /*xProtocol*/

        if( xSocket != A_ERROR )
        {
            pxContext->xSocket = ( Socket_t ) xSocket;
            /* Set default timeouts. */
            pxContext->ulRecvTimeout = socketsconfigDEFAULT_RECV_TIMEOUT;
            pxContext->ulSendTimeout = socketsconfigDEFAULT_SEND_TIMEOUT;
        }
        else /* Driver could not allocate socket. */
        {
            lStatus = SOCKETS_SOCKET_ERROR;
            vPortFree( pxContext );
        }
    }
    else /* Malloc failed. */
    {
        lStatus = SOCKETS_ENOMEM;
    }

    if( lStatus != SOCKETS_ERROR_NONE )
    {
        pxContext = (SSOCKETContextPtr_t) SOCKETS_INVALID_SOCKET;
    }

    return (Socket_t) pxContext;
}
/*-----------------------------------------------------------*/

BaseType_t SOCKETS_Init( void )
{
    /* Empty initialization for NXP board. */
    return pdPASS;
}
