/*
 * 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_IP_Utils.c
 * @brief Implements the basic functionality for the FreeRTOS+TCP network stack.
 */

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IP_Utils.h"
#include "FreeRTOS_IP_Timers.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_ARP.h"
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_DHCP.h"
#include "NetworkInterface.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_DNS.h"

/* Used to ensure the structure packing is having the desired effect.  The
 * 'volatile' is used to prevent compiler warnings about comparing a constant with
 * a constant. */
#ifndef _lint
    #define ipEXPECTED_EthernetHeader_t_SIZE    ( ( size_t ) 14 ) /**< Ethernet Header size in bytes. */
    #define ipEXPECTED_ARPHeader_t_SIZE         ( ( size_t ) 28 ) /**< ARP header size in bytes. */
    #define ipEXPECTED_IPHeader_t_SIZE          ( ( size_t ) 20 ) /**< IP header size in bytes. */
    #define ipEXPECTED_IGMPHeader_t_SIZE        ( ( size_t ) 8 )  /**< IGMP header size in bytes. */
    #define ipEXPECTED_ICMPHeader_t_SIZE        ( ( size_t ) 8 )  /**< ICMP header size in bytes. */
    #define ipEXPECTED_UDPHeader_t_SIZE         ( ( size_t ) 8 )  /**< UDP header size in bytes. */
    #define ipEXPECTED_TCPHeader_t_SIZE         ( ( size_t ) 20 ) /**< TCP header size in bytes. */
#endif

/** @brief Time delay between repeated attempts to initialise the network hardware. */
#ifndef ipINITIALISATION_RETRY_DELAY
    #define ipINITIALISATION_RETRY_DELAY    ( pdMS_TO_TICKS( 3000U ) )
#endif

#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
    /* used for unit testing */

/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
/* coverity[single_use] */
    static BaseType_t xCallEventHook = pdFALSE;
#endif

#if ( ipconfigHAS_PRINTF != 0 )
    /** @brief Last value of minimum buffer count. */
    static UBaseType_t uxLastMinBufferCount = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;

/** @brief Last value of minimum size. Used in printing resource stats. */
    static size_t uxMinLastSize = 0u;
#endif

#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) && ( ipconfigHAS_PRINTF != 0 )
    static UBaseType_t uxLastMinQueueSpace = 0;
#endif

/**
 * Used in checksum calculation.
 */
typedef union _xUnion32
{
    uint32_t u32;      /**< The 32-bit member of the union. */
    uint16_t u16[ 2 ]; /**< The array of 2 16-bit members of the union. */
    uint8_t u8[ 4 ];   /**< The array of 4 8-bit members of the union. */
} xUnion32;

/**
 * Used in checksum calculation.
 */
typedef union _xUnionPtr
{
    const uint32_t * u32ptr; /**< The pointer member to a 32-bit variable. */
    const uint16_t * u16ptr; /**< The pointer member to a 16-bit variable. */
    const uint8_t * u8ptr;   /**< The pointer member to an 8-bit variable. */
} xUnionPtr;

/*
 * Returns the network buffer descriptor that owns a given packet buffer.
 */
static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
                                                                     size_t uxOffset );

#if ( ipconfigUSE_DHCP != 0 )

/**
 * @brief Create a DHCP event.
 *
 * @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask()
 *         succeeded.
 */
    BaseType_t xSendDHCPEvent( void )
    {
        IPStackEvent_t xEventMessage;
        const TickType_t uxDontBlock = 0U;
        uintptr_t uxOption = ( uintptr_t ) eGetDHCPState();

        xEventMessage.eEventType = eDHCPEvent;

        /* MISRA Ref 11.6.1 [DHCP events and conversion to void] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
        /* coverity[misra_c_2012_rule_11_6_violation] */
        xEventMessage.pvData = ( void * ) uxOption;

        return xSendEventStructToIPTask( &xEventMessage, uxDontBlock );
    }
/*-----------------------------------------------------------*/
#endif /* ( ipconfigUSE_DHCP != 0 ) */

/**
 * @brief Set multicast MAC address.
 *
 * @param[in] ulIPAddress: IP address.
 * @param[out] pxMACAddress: Pointer to MAC address.
 */
void vSetMultiCastIPv4MacAddress( uint32_t ulIPAddress,
                                  MACAddress_t * pxMACAddress )
{
    uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress );

    pxMACAddress->ucBytes[ 0 ] = ( uint8_t ) 0x01U;
    pxMACAddress->ucBytes[ 1 ] = ( uint8_t ) 0x00U;
    pxMACAddress->ucBytes[ 2 ] = ( uint8_t ) 0x5EU;
    pxMACAddress->ucBytes[ 3 ] = ( uint8_t ) ( ( ulIP >> 16 ) & 0x7fU ); /* Use 7 bits. */
    pxMACAddress->ucBytes[ 4 ] = ( uint8_t ) ( ( ulIP >> 8 ) & 0xffU );  /* Use 8 bits. */
    pxMACAddress->ucBytes[ 5 ] = ( uint8_t ) ( ( ulIP ) & 0xffU );       /* Use 8 bits. */
}
/*-----------------------------------------------------------*/


/**
 * @brief Duplicate the given network buffer descriptor with a modified length.
 *
 * @param[in] pxNetworkBuffer: The network buffer to be duplicated.
 * @param[in] uxNewLength: The length for the new buffer.
 *
 * @return If properly duplicated, then the duplicate network buffer or else, NULL.
 */
NetworkBufferDescriptor_t * pxDuplicateNetworkBufferWithDescriptor( const NetworkBufferDescriptor_t * const pxNetworkBuffer,
                                                                    size_t uxNewLength )
{
    NetworkBufferDescriptor_t * pxNewBuffer;
    size_t uxLengthToCopy = uxNewLength;

    /* This function is only used when 'ipconfigZERO_COPY_TX_DRIVER' is set to 1.
     * The transmit routine wants to have ownership of the network buffer
     * descriptor, because it will pass the buffer straight to DMA. */
    pxNewBuffer = pxGetNetworkBufferWithDescriptor( uxNewLength, ( TickType_t ) 0 );

    if( pxNewBuffer != NULL )
    {
        /* Get the minimum of both values to copy the data. */
        if( uxLengthToCopy > pxNetworkBuffer->xDataLength )
        {
            uxLengthToCopy = pxNetworkBuffer->xDataLength;
        }

        /* Set the actual packet size in case a bigger buffer than requested
         * was returned. */
        pxNewBuffer->xDataLength = uxNewLength;

        /* Copy the original packet information. */
        pxNewBuffer->ulIPAddress = pxNetworkBuffer->ulIPAddress;
        pxNewBuffer->usPort = pxNetworkBuffer->usPort;
        pxNewBuffer->usBoundPort = pxNetworkBuffer->usBoundPort;
        ( void ) memcpy( pxNewBuffer->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, uxLengthToCopy );
    }

    return pxNewBuffer;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the network buffer descriptor from the packet buffer.
 *
 * @param[in] pvBuffer: The pointer to packet buffer.
 * @param[in] uxOffset: Additional offset (such as the packet length of UDP packet etc.).
 *
 * @return The network buffer descriptor if the alignment is correct. Else a NULL is returned.
 */
static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
                                                                     size_t uxOffset )
{
    uintptr_t uxBuffer;
    NetworkBufferDescriptor_t * pxResult;

    if( pvBuffer == NULL )
    {
        pxResult = NULL;
    }
    else
    {
        /* Obtain the network buffer from the zero copy pointer. */

        /* MISRA Ref 11.6.2 [Pointer arithmetic and hidden pointer] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
        /* coverity[misra_c_2012_rule_11_6_violation] */
        uxBuffer = ( uintptr_t ) pvBuffer;

        /* The input here is a pointer to a packet buffer plus some offset.  Subtract
         * this offset, and also the size of the header in the network buffer, usually
         * 8 + 2 bytes. */
        uxBuffer -= ( uxOffset + ipBUFFER_PADDING );

        /* Here a pointer was placed to the network descriptor.  As a
         * pointer is dereferenced, make sure it is well aligned. */
        if( ( uxBuffer & ( ( ( uintptr_t ) sizeof( uxBuffer ) ) - 1U ) ) == ( uintptr_t ) 0U )
        {
            /* MISRA Ref 11.4.2 [Validation of pointer alignment] */
            /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
            /* coverity[misra_c_2012_rule_11_4_violation] */
            pxResult = *( ( NetworkBufferDescriptor_t ** ) uxBuffer );
        }
        else
        {
            pxResult = NULL;
        }
    }

    return pxResult;
}
/*-----------------------------------------------------------*/

#if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 )

/**
 * @brief Get the network buffer from the packet buffer.
 *
 * @param[in] pvBuffer: Pointer to the packet buffer.
 *
 * @return The network buffer if the alignment is correct. Else a NULL is returned.
 */
    NetworkBufferDescriptor_t * pxPacketBuffer_to_NetworkBuffer( const void * pvBuffer )
    {
        return prvPacketBuffer_to_NetworkBuffer( pvBuffer, 0U );
    }

#endif /* ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
/*-----------------------------------------------------------*/

/**
 * @brief Get the network buffer from the UDP Payload buffer.
 *
 * @param[in] pvBuffer: Pointer to the UDP payload buffer.
 *
 * @return The network buffer if the alignment is correct. Else a NULL is returned.
 */
NetworkBufferDescriptor_t * pxUDPPayloadBuffer_to_NetworkBuffer( const void * pvBuffer )
{
    return prvPacketBuffer_to_NetworkBuffer( pvBuffer, sizeof( UDPPacket_t ) );
}
/*-----------------------------------------------------------*/

/**
 * @brief Function to check whether the current context belongs to
 *        the IP-task.
 *
 * @return If the current context belongs to the IP-task, then pdTRUE is
 *         returned. Else pdFALSE is returned.
 *
 * @note Very important: the IP-task is not allowed to call its own API's,
 *        because it would easily get into a dead-lock.
 */
BaseType_t xIsCallingFromIPTask( void )
{
    BaseType_t xReturn;
    const struct tskTaskControlBlock * const xCurrentHandle = xTaskGetCurrentTaskHandle();
    const struct tskTaskControlBlock * const xCurrentIPTaskHandle = FreeRTOS_GetIPTaskHandle();

    if( xCurrentHandle == xCurrentIPTaskHandle )
    {
        xReturn = pdTRUE;
    }
    else
    {
        xReturn = pdFALSE;
    }

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

/**
 * @brief Process a 'Network down' event and complete required processing.
 */
/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
/* coverity[single_use] */
void prvProcessNetworkDownEvent( void )
{
    /* Stop the ARP timer while there is no network. */
    vIPSetARPTimerEnableState( pdFALSE );

    #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
        {
            /* The first network down event is generated by the IP stack itself to
             * initialise the network hardware, so do not call the network down event
             * the first time through. */
            if( xCallEventHook == pdTRUE )
            {
                vApplicationIPNetworkEventHook( eNetworkDown );
            }

            xCallEventHook = pdTRUE;
        }
    #endif /* if ipconfigUSE_NETWORK_EVENT_HOOK == 1 */

    /* Per the ARP Cache Validation section of https://tools.ietf.org/html/rfc1122,
     * treat network down as a "delivery problem" and flush the ARP cache for this
     * interface. */
    FreeRTOS_ClearARP();

    /* The network has been disconnected (or is being initialised for the first
     * time).  Perform whatever hardware processing is necessary to bring it up
     * again, or wait for it to be available again.  This is hardware dependent. */
    if( xNetworkInterfaceInitialise() != pdPASS )
    {
        /* Ideally the network interface initialisation function will only
         * return when the network is available.  In case this is not the case,
         * wait a while before retrying the initialisation. */
        vTaskDelay( ipINITIALISATION_RETRY_DELAY );
        FreeRTOS_NetworkDown();
    }
    else
    {
        /* Set remaining time to 0 so it will become active immediately. */
        #if ipconfigUSE_DHCP == 1
            {
                /* The network is not up until DHCP has completed. */
                vDHCPProcess( pdTRUE, eInitialWait );
            }
        #else
            {
                /* Perform any necessary 'network up' processing. */
                vIPNetworkUpCalls();
            }
        #endif
    }
}
/*-----------------------------------------------------------*/

/**
 * @brief Check the values of configuration options and assert on it. Also verify that the IP-task
 *        has not already been initialized.
 */
void vPreCheckConfigs( void )
{
    /* This function should only be called once. */
    configASSERT( xIPIsNetworkTaskReady() == pdFALSE );
    configASSERT( xNetworkEventQueue == NULL );
    configASSERT( FreeRTOS_GetIPTaskHandle() == NULL );

    #if ( configASSERT_DEFINED == 1 )
        {
            volatile size_t uxSize = sizeof( uintptr_t );

            if( uxSize == 8U )
            {
                /* This is a 64-bit platform, make sure there is enough space in
                 * pucEthernetBuffer to store a pointer. */
                configASSERT( ipconfigBUFFER_PADDING >= 14 );
                /* But it must have this strange alignment: */
                configASSERT( ( ( ( ipconfigBUFFER_PADDING ) + 2 ) % 4 ) == 0 );
            }

            /* LCOV_EXCL_BR_START */
            uxSize = ipconfigNETWORK_MTU;
            /* Check if MTU is big enough. */
            configASSERT( uxSize >= ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + ipconfigTCP_MSS ) );

            uxSize = sizeof( EthernetHeader_t );
            /* Check structure packing is correct. */
            configASSERT( uxSize == ipEXPECTED_EthernetHeader_t_SIZE );

            uxSize = sizeof( ARPHeader_t );
            configASSERT( uxSize == ipEXPECTED_ARPHeader_t_SIZE );

            uxSize = sizeof( IPHeader_t );
            configASSERT( uxSize == ipEXPECTED_IPHeader_t_SIZE );

            uxSize = sizeof( ICMPHeader_t );
            configASSERT( uxSize == ipEXPECTED_ICMPHeader_t_SIZE );

            uxSize = sizeof( UDPHeader_t );
            configASSERT( uxSize == ipEXPECTED_UDPHeader_t_SIZE );
            /* LCOV_EXCL_BR_STOP */
        }
    #endif /* if ( configASSERT_DEFINED == 1 ) */
}

/**
 * @brief Generate or check the protocol checksum of the data sent in the first parameter.
 *        At the same time, the length of the packet and the length of the different layers
 *        will be checked.
 *
 * @param[in] pucEthernetBuffer: The Ethernet buffer for which the checksum is to be calculated
 *                               or checked.
 * @param[in] uxBufferLength: the total number of bytes received, or the number of bytes written
 *                            in the packet buffer.
 * @param[in] xOutgoingPacket: Whether this is an outgoing packet or not.
 *
 * @return When xOutgoingPacket is false: the error code can be either: ipINVALID_LENGTH,
 *         ipUNHANDLED_PROTOCOL, ipWRONG_CRC, or ipCORRECT_CRC.
 *         When xOutgoingPacket is true: either ipINVALID_LENGTH, ipUNHANDLED_PROTOCOL,
 *         or ipCORRECT_CRC.
 */
uint16_t usGenerateProtocolChecksum( uint8_t * pucEthernetBuffer,
                                     size_t uxBufferLength,
                                     BaseType_t xOutgoingPacket )
{
    uint32_t ulLength;
    uint16_t usChecksum;           /* The checksum as calculated. */
    uint16_t usChecksumFound = 0U; /* The checksum as found in the incoming packet. */
    const IPPacket_t * pxIPPacket;
    UBaseType_t uxIPHeaderLength;
    ProtocolPacket_t * pxProtPack;
    uint8_t ucProtocol;

    #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
        const char * pcType;
    #endif
    uint16_t usLength;
    uint16_t ucVersionHeaderLength;
    DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 );

    /* Introduce a do-while loop to allow use of break statements.
     * Note: MISRA prohibits use of 'goto', thus replaced with breaks. */
    do
    {
        /* Check for minimum packet size. */
        if( uxBufferLength < sizeof( IPPacket_t ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 1 );
            break;
        }

        /* Parse the packet length. */

        /* 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] */
        pxIPPacket = ( ( const IPPacket_t * ) pucEthernetBuffer );

        /* Per https://tools.ietf.org/html/rfc791, the four-bit Internet Header
         * Length field contains the length of the internet header in 32-bit words. */
        ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength;
        ucVersionHeaderLength = ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2;
        uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength;

        /* Check for minimum packet size. */
        if( uxBufferLength < ( sizeof( IPPacket_t ) + ( uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ) ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 2 );
            break;
        }

        usLength = pxIPPacket->xIPHeader.usLength;
        usLength = FreeRTOS_ntohs( usLength );

        if( usLength < uxIPHeaderLength )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 3 );
            break;
        }

        if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 4 );
            break;
        }

        /* Identify the next protocol. */
        ucProtocol = pxIPPacket->xIPHeader.ucProtocol;

        /* N.B., if this IP packet header includes Options, then the following
         * assignment results in a pointer into the protocol packet with the Ethernet
         * and IP headers incorrectly aligned. However, either way, the "third"
         * protocol (Layer 3 or 4) header will be aligned, which is the convenience
         * of this calculation. */

        /* 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] */
        pxProtPack = ( ( ProtocolPacket_t * ) &( pucEthernetBuffer[ uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ] ) );

        /* Switch on the Layer 3/4 protocol. */
        if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 5 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the UDP checksum field before calculating it. */
                pxProtPack->xUDPPacket.xUDPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xUDPPacket.xUDPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    pcType = "UDP";
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 6 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the TCP checksum field before calculating it. */
                pxProtPack->xTCPPacket.xTCPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xTCPPacket.xTCPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    pcType = "TCP";
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
                 ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 7 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the ICMP/IGMP checksum field before calculating it. */
                pxProtPack->xICMPPacket.xICMPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xICMPPacket.xICMPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    if( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
                    {
                        pcType = "ICMP";
                    }
                    else
                    {
                        pcType = "IGMP";
                    }
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else
        {
            /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
            usChecksum = ipUNHANDLED_PROTOCOL;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 8 );
            break;
        }

        /* The protocol and checksum field have been identified. Check the direction
         * of the packet. */
        if( xOutgoingPacket != pdFALSE )
        {
            /* This is an outgoing packet. The CRC-field has been cleared. */
        }
        else if( ( usChecksumFound == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
        {
            #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 )
                {
                    /* Sender hasn't set the checksum, drop the packet because
                     * ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is not set. */
                    usChecksum = ipWRONG_CRC;
                    #if ( ipconfigHAS_PRINTF != 0 )
                        {
                            static BaseType_t xCount = 0;

                            /* Exclude this from branch coverage as this is only used for debugging. */
                            if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */
                            {
                                FreeRTOS_printf( ( "usGenerateProtocolChecksum: UDP packet from %xip without CRC dropped\n",
                                                   FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) );
                                xCount++;
                            }
                        }
                    #endif /* ( ipconfigHAS_PRINTF != 0 ) */
                }
            #else /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
                {
                    /* Sender hasn't set the checksum, no use to calculate it. */
                    usChecksum = ipCORRECT_CRC;
                }
            #endif /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
            DEBUG_SET_TRACE_VARIABLE( xLocation, 9 );
            break;
        }
        else
        {
            /* Other incoming packet than UDP. */
        }

        usLength = pxIPPacket->xIPHeader.usLength;
        usLength = FreeRTOS_ntohs( usLength );
        ulLength = ( uint32_t ) usLength;
        ulLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally minus 20 */

        if( ( ulLength < ( ( uint32_t ) sizeof( pxProtPack->xUDPPacket.xUDPHeader ) ) ) ||
            ( ulLength > ( ( uint32_t ) ipconfigNETWORK_MTU - ( uint32_t ) uxIPHeaderLength ) ) )
        {
            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len invalid: %lu\n", pcType, ulLength ) );
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */

            /* Again, in a 16-bit return value there is no space to indicate an
             * error.  For incoming packets, 0x1234 will cause dropping of the packet.
             * For outgoing packets, there is a serious problem with the
             * format/length */
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 10 );
            break;
        }

        if( ucProtocol <= ( uint8_t ) ipPROTOCOL_IGMP )
        {
            /* ICMP/IGMP do not have a pseudo header for CRC-calculation. */
            usChecksum = ( uint16_t )
                         ( ~usGenerateChecksum( 0U,
                                                ( const uint8_t * ) &( pxProtPack->xICMPPacket.xICMPHeader ), ( size_t ) ulLength ) );
        }
        else
        {
            /* For UDP and TCP, sum the pseudo header, i.e. IP protocol + length
             * fields */
            usChecksum = ( uint16_t ) ( ulLength + ( ( uint16_t ) ucProtocol ) );

            /* And then continue at the IPv4 source and destination addresses. */
            usChecksum = ( uint16_t )
                         ( ~usGenerateChecksum( usChecksum,
                                                ( const uint8_t * ) &( pxIPPacket->xIPHeader.ulSourceIPAddress ),
                                                ( size_t ) ( ( 2U * ( size_t ) ipSIZE_OF_IPv4_ADDRESS ) + ulLength ) ) );
            /* Sum TCP header and data. */
        }

        if( xOutgoingPacket == pdFALSE )
        {
            /* This is in incoming packet. If the CRC is correct, it should be zero. */
            if( usChecksum == 0U )
            {
                usChecksum = ( uint16_t ) ipCORRECT_CRC;
            }
            else
            {
                usChecksum = ( uint16_t ) ipWRONG_CRC;
            }
        }
        else
        {
            if( ( usChecksum == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
            {
                /* In case of UDP, a calculated checksum of 0x0000 is transmitted
                 * as 0xffff. A value of zero would mean that the checksum is not used. */
                usChecksum = ( uint16_t ) 0xffffu;
            }
        }

        usChecksum = FreeRTOS_htons( usChecksum );

        if( xOutgoingPacket != pdFALSE )
        {
            switch( ucProtocol )
            {
                case ipPROTOCOL_UDP:
                    pxProtPack->xUDPPacket.xUDPHeader.usChecksum = usChecksum;
                    break;

                case ipPROTOCOL_TCP:
                    pxProtPack->xTCPPacket.xTCPHeader.usChecksum = usChecksum;
                    break;

                case ipPROTOCOL_ICMP:
                    pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum;
                    break;

                default: /*  ipPROTOCOL_IGMP */
                    pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum;
                    break;
            }

            usChecksum = ( uint16_t ) ipCORRECT_CRC;
        }

        #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
            else if( ( xOutgoingPacket == pdFALSE ) && ( usChecksum != ipCORRECT_CRC ) )
            {
                FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: ID %04X: from %lxip to %lxip bad crc: %04X\n",
                                         pcType,
                                         FreeRTOS_ntohs( pxIPPacket->xIPHeader.usIdentification ),
                                         FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ),
                                         FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulDestinationIPAddress ),
                                         FreeRTOS_ntohs( usChecksumFound ) ) );
            }
            else
            {
                /* Nothing. */
            }
        #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
    } while( ipFALSE_BOOL );

    if( ( usChecksum == ipUNHANDLED_PROTOCOL ) ||
        ( usChecksum == ipINVALID_LENGTH ) )
    {
        /* NOP if ipconfigHAS_PRINTF != 0 */
        FreeRTOS_printf( ( "CRC error: %04x location %ld\n", usChecksum, xLocation ) );
    }

    return usChecksum;
}
/*-----------------------------------------------------------*/

/**
 * This method generates a checksum for a given IPv4 header, per RFC791 (page 14).
 * The checksum algorithm is described as:
 *   "[T]he 16 bit one's complement of the one's complement sum of all 16 bit words in the
 *   header.  For purposes of computing the checksum, the value of the checksum field is zero."
 *
 * In a nutshell, that means that each 16-bit 'word' must be summed, after which
 * the number of 'carries' (overflows) is added to the result. If that addition
 * produces an overflow, that 'carry' must also be added to the final result. The final checksum
 * should be the bitwise 'not' (ones-complement) of the result if the packet is
 * meant to be transmitted, but this method simply returns the raw value, probably
 * because when a packet is received, the checksum is verified by checking that
 * ((received & calculated) == 0) without applying a bitwise 'not' to the 'calculated' checksum.
 *
 * This logic is optimized for microcontrollers which have limited resources, so the logic looks odd.
 * It iterates over the full range of 16-bit words, but it does so by processing several 32-bit
 * words at once whenever possible. Its first step is to align the memory pointer to a 32-bit boundary,
 * after which it runs a fast loop to process multiple 32-bit words at once and adding their 'carries'.
 * Finally, it finishes up by processing any remaining 16-bit words, and adding up all of the 'carries'.
 * With 32-bit arithmetic, the number of 16-bit 'carries' produced by sequential additions can be found
 * by looking at the 16 most-significant bits of the 32-bit integer, since a 32-bit int will continue
 * counting up instead of overflowing after 16 bits. That is why the actual checksum calculations look like:
 *   union.u32 = ( uint32_t ) union.u16[ 0 ] + union.u16[ 1 ];
 *
 * Arguments:
 *   ulSum: This argument provides a value to initialise the progressive summation
 *   of the header's values to. It is often 0, but protocols like TCP or UDP
 *   can have pseudo-header fields which need to be included in the checksum.
 *   pucNextData: This argument contains the address of the first byte which this
 *   method should process. The method's memory iterator is initialised to this value.
 *   uxDataLengthBytes: This argument contains the number of bytes that this method
 *   should process.
 */

/**
 * @brief Calculates the 16-bit checksum of an array of bytes
 *
 * @param[in] usSum: The initial sum, obtained from earlier data.
 * @param[in] pucNextData: The actual data.
 * @param[in] uxByteCount: The number of bytes.
 *
 * @return The 16-bit one's complement of the one's complement sum of all 16-bit
 *         words in the header
 */
uint16_t usGenerateChecksum( uint16_t usSum,
                             const uint8_t * pucNextData,
                             size_t uxByteCount )
{
/* MISRA/PC-lint doesn't like the use of unions. Here, they are a great
 * aid though to optimise the calculations. */
    xUnion32 xSum2;
    xUnion32 xSum;
    xUnion32 xTerm;
    xUnionPtr xSource;
    uintptr_t uxAlignBits;
    uint32_t ulCarry = 0U;
    uint16_t usTemp;
    size_t uxDataLengthBytes = uxByteCount;
    size_t uxSize;
    uintptr_t ulX;

    /* Small MCUs often spend up to 30% of the time doing checksum calculations
    * This function is optimised for 32-bit CPUs; Each time it will try to fetch
    * 32-bits, sums it with an accumulator and counts the number of carries. */

    /* Swap the input (little endian platform only). */
    usTemp = FreeRTOS_ntohs( usSum );
    xSum.u32 = ( uint32_t ) usTemp;
    xTerm.u32 = 0U;

    xSource.u8ptr = pucNextData;

    /* MISRA Ref 11.4.3 [Casting pointer to int for verification] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
    /* coverity[misra_c_2012_rule_11_4_violation] */
    uxAlignBits = ( ( ( uintptr_t ) pucNextData ) & 0x03U );

    /*
     * If pucNextData is non-aligned then the checksum is starting at an
     * odd position and we need to make sure the usSum value now in xSum is
     * as if it had been "aligned" in the same way.
     */
    if( ( uxAlignBits & 1U ) != 0U )
    {
        xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 );
    }

    /* If byte (8-bit) aligned... */
    if( ( ( uxAlignBits & 1U ) != 0U ) && ( uxDataLengthBytes >= ( size_t ) 1U ) )
    {
        xTerm.u8[ 1 ] = *( xSource.u8ptr );
        xSource.u8ptr++;
        uxDataLengthBytes--;
        /* Now xSource is word (16-bit) aligned. */
    }

    /* If half-word (16-bit) aligned... */
    if( ( ( uxAlignBits == 1U ) || ( uxAlignBits == 2U ) ) && ( uxDataLengthBytes >= 2U ) )
    {
        xSum.u32 += *( xSource.u16ptr );
        xSource.u16ptr++;
        uxDataLengthBytes -= 2U;
        /* Now xSource is word (32-bit) aligned. */
    }

    /* Word (32-bit) aligned, do the most part. */

    uxSize = ( size_t ) ( ( uxDataLengthBytes / 4U ) * 4U );

    if( uxSize >= ( 3U * sizeof( uint32_t ) ) )
    {
        uxSize -= ( 3U * sizeof( uint32_t ) );
    }
    else
    {
        uxSize = 0U;
    }

    /* In this loop, four 32-bit additions will be done, in total 16 bytes.
     * Indexing with constants (0,1,2,3) gives faster code than using
     * post-increments. */
    for( ulX = 0U; ulX < uxSize; ulX += 4U * sizeof( uint32_t ) )
    {
        /* Use a secondary Sum2, just to see if the addition produced an
         * overflow. */
        xSum2.u32 = xSum.u32 + xSource.u32ptr[ 0 ];

        if( xSum2.u32 < xSum.u32 )
        {
            ulCarry++;
        }

        /* Now add the secondary sum to the major sum, and remember if there was
         * a carry. */
        xSum.u32 = xSum2.u32 + xSource.u32ptr[ 1 ];

        if( xSum2.u32 > xSum.u32 )
        {
            ulCarry++;
        }

        /* And do the same trick once again for indexes 2 and 3 */
        xSum2.u32 = xSum.u32 + xSource.u32ptr[ 2 ];

        if( xSum2.u32 < xSum.u32 )
        {
            ulCarry++;
        }

        xSum.u32 = xSum2.u32 + xSource.u32ptr[ 3 ];

        if( xSum2.u32 > xSum.u32 )
        {
            ulCarry++;
        }

        /* And finally advance the pointer 4 * 4 = 16 bytes. */
        xSource.u32ptr = &( xSource.u32ptr[ 4 ] );
    }

    /* Now add all carries. */
    xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ] + ulCarry;

    uxDataLengthBytes %= 16U;

    /* Half-word aligned. */
    uxSize = ( ( uxDataLengthBytes & ~( ( size_t ) 1U ) ) );

    for( ulX = 0U; ulX < uxSize; ulX += 1U * sizeof( uint16_t ) )
    {
        /* At least one more short. */
        xSum.u32 += xSource.u16ptr[ 0 ];
        xSource.u16ptr = &xSource.u16ptr[ 1 ];
    }

    if( ( uxDataLengthBytes & ( size_t ) 1U ) != 0U ) /* Maybe one more ? */
    {
        xTerm.u8[ 0 ] = xSource.u8ptr[ 0 ];
    }

    /* MISRA Ref 2.2.1 [Unions and dead code] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
    /* coverity[misra_c_2012_rule_2_2_violation] */
    /* coverity[assigned_value] */
    xSum.u32 += xTerm.u32;

    /* Now add all carries again. */

    /* Assigning value from "xTerm.u32" to "xSum.u32" here, but that stored value is overwritten before it can be used. */
    /* MISRA Ref 2.2.1 [Unions and dead code] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
    /* coverity[misra_c_2012_rule_2_2_violation] */
    /* coverity[value_overwrite] */
    xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ];

    /* MISRA Ref 2.2.1 [Unions and dead code] */
    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
    /* coverity[misra_c_2012_rule_2_2_violation] */
    /* coverity[value_overwrite] */
    xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ];

    if( ( uxAlignBits & 1U ) != 0U )
    {
        /* Quite unlikely, but pucNextData might be non-aligned, which would
        * mean that a checksum is calculated starting at an odd position. */
        xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 );
    }

    /* swap the output (little endian platform only). */
    return FreeRTOS_htons( ( ( uint16_t ) xSum.u32 ) );
}
/*-----------------------------------------------------------*/

#if ( ipconfigHAS_PRINTF != 0 )

    #ifndef ipMONITOR_MAX_HEAP

/* As long as the heap has more space than e.g. 1 MB, there
 * will be no messages. */
        #define ipMONITOR_MAX_HEAP    ( 1024U * 1024U )
    #endif /* ipMONITOR_MAX_HEAP */

    #ifndef ipMONITOR_PERCENTAGE_90
        /* Make this number lower to get less logging messages. */
        #define ipMONITOR_PERCENTAGE_90    ( 90U )
    #endif

    #define ipMONITOR_PERCENTAGE_100       ( 100U )

/**
 * @brief A function that monitors a three resources: the heap, the space in the message
 *        queue of the IP-task, the number of available network buffer descriptors.
 */
    void vPrintResourceStats( void )
    {
        UBaseType_t uxCurrentBufferCount;
        size_t uxMinSize;

        /* When setting up and testing a project with FreeRTOS+TCP, it is
         * can be helpful to monitor a few resources: the number of network
         * buffers and the amount of available heap.
         * This function will issue some logging when a minimum value has
         * changed. */
        uxCurrentBufferCount = uxGetMinimumFreeNetworkBuffers();

        if( uxLastMinBufferCount > uxCurrentBufferCount )
        {
            /* The logging produced below may be helpful
             * while tuning +TCP: see how many buffers are in use. */
            uxLastMinBufferCount = uxCurrentBufferCount;
            FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
                               uxGetNumberOfFreeNetworkBuffers(),
                               uxCurrentBufferCount ) );
        }

        uxMinSize = xPortGetMinimumEverFreeHeapSize();

        if( uxMinLastSize == 0U )
        {
            /* Probably the first time this function is called. */
            uxMinLastSize = uxMinSize;
        }
        else if( uxMinSize >= ipMONITOR_MAX_HEAP )
        {
            /* There is more than enough heap space. No need for logging. */
        }
        /* Write logging if there is a 10% decrease since the last time logging was written. */
        else if( ( uxMinLastSize * ipMONITOR_PERCENTAGE_90 ) > ( uxMinSize * ipMONITOR_PERCENTAGE_100 ) )
        {
            uxMinLastSize = uxMinSize;
            FreeRTOS_printf( ( "Heap: current %lu lowest %lu\n", xPortGetFreeHeapSize(), uxMinSize ) );
        }
        else
        {
            /* Nothing to log. */
        }

        #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
            {
                UBaseType_t uxCurrentCount = 0u;

                uxCurrentCount = uxGetMinimumIPQueueSpace();

                if( uxLastMinQueueSpace != uxCurrentCount )
                {
                    /* The logging produced below may be helpful
                     * while tuning +TCP: see how many buffers are in use. */
                    uxLastMinQueueSpace = uxCurrentCount;
                    FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
                }
            }
        #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
    }
#endif /* ( ipconfigHAS_PRINTF != 0 ) */
/*-----------------------------------------------------------*/

/**
 * @brief Utility function: Convert error number to a human readable
 *        string. Declaration in FreeRTOS_errno_TCP.h.
 *
 * @param[in] xErrnum: The error number.
 * @param[in] pcBuffer: Buffer big enough to be filled with the human readable message.
 * @param[in] uxLength: Maximum length of the buffer.
 *
 * @return The buffer filled with human readable error string.
 */
const char * FreeRTOS_strerror_r( BaseType_t xErrnum,
                                  char * pcBuffer,
                                  size_t uxLength )
{
    const char * pcName;

    switch( xErrnum )
    {
        case pdFREERTOS_ERRNO_EADDRINUSE:
            pcName = "EADDRINUSE";
            break;

        case pdFREERTOS_ERRNO_ENOMEM:
            pcName = "ENOMEM";
            break;

        case pdFREERTOS_ERRNO_EADDRNOTAVAIL:
            pcName = "EADDRNOTAVAIL";
            break;

        case pdFREERTOS_ERRNO_ENOPROTOOPT:
            pcName = "ENOPROTOOPT";
            break;

        case pdFREERTOS_ERRNO_EBADF:
            pcName = "EBADF";
            break;

        case pdFREERTOS_ERRNO_ENOSPC:
            pcName = "ENOSPC";
            break;

        case pdFREERTOS_ERRNO_ECANCELED:
            pcName = "ECANCELED";
            break;

        case pdFREERTOS_ERRNO_ENOTCONN:
            pcName = "ENOTCONN";
            break;

        case pdFREERTOS_ERRNO_EINPROGRESS:
            pcName = "EINPROGRESS";
            break;

        case pdFREERTOS_ERRNO_EOPNOTSUPP:
            pcName = "EOPNOTSUPP";
            break;

        case pdFREERTOS_ERRNO_EINTR:
            pcName = "EINTR";
            break;

        case pdFREERTOS_ERRNO_ETIMEDOUT:
            pcName = "ETIMEDOUT";
            break;

        case pdFREERTOS_ERRNO_EINVAL:
            pcName = "EINVAL";
            break;

        case pdFREERTOS_ERRNO_EWOULDBLOCK:
            pcName = "EWOULDBLOCK";
            break; /* same as EAGAIN */

        case pdFREERTOS_ERRNO_EISCONN:
            pcName = "EISCONN";
            break;

        default:
            /* MISRA Ref 21.6.1 [snprintf and logging] */
            /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */
            /* coverity[misra_c_2012_rule_21_6_violation] */
            ( void ) snprintf( pcBuffer, uxLength, "Errno %d", ( int ) xErrnum );
            pcName = NULL;
            break;
    }

    if( pcName != NULL )
    {
        /* MISRA Ref 21.6.1 [snprintf and logging] */
        /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */
        /* coverity[misra_c_2012_rule_21_6_violation] */
        ( void ) snprintf( pcBuffer, uxLength, "%s", pcName );
    }

    if( uxLength > 0U )
    {
        pcBuffer[ uxLength - 1U ] = '\0';
    }

    return pcBuffer;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the highest value of two int32's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The highest of the two values.
 */
int32_t FreeRTOS_max_int32( int32_t a,
                            int32_t b )
{
    return ( a >= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the highest value of two uint32_t's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The highest of the two values.
 */
uint32_t FreeRTOS_max_uint32( uint32_t a,
                              uint32_t b )
{
    return ( a >= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the highest value of two size_t's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The highest of the two values.
 */
size_t FreeRTOS_max_size_t( size_t a,
                            size_t b )
{
    return ( a >= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the lowest value of two int32_t's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The lowest of the two values.
 */
int32_t FreeRTOS_min_int32( int32_t a,
                            int32_t b )
{
    return ( a <= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the lowest value of two uint32_t's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The lowest of the two values.
 */
uint32_t FreeRTOS_min_uint32( uint32_t a,
                              uint32_t b )
{
    return ( a <= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Get the lowest value of two size_t's.
 * @param[in] a: the first value.
 * @param[in] b: the second value.
 * @return The lowest of the two values.
 */
size_t FreeRTOS_min_size_t( size_t a,
                            size_t b )
{
    return ( a <= b ) ? a : b;
}
/*-----------------------------------------------------------*/

/**
 * @brief Round-up a number to a multiple of 'd'.
 * @param[in] a: the first value.
 * @param[in] d: the second value.
 * @return A multiple of d.
 */
uint32_t FreeRTOS_round_up( uint32_t a,
                            uint32_t d )
{
    uint32_t ulResult = a;

    configASSERT( d != 0U );

    if( d != 0U )
    {
        ulResult = d * ( ( a + d - 1U ) / d );
    }

    return ulResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Round-down a number to a multiple of 'd'.
 * @param[in] a: the first value.
 * @param[in] d: the second value.
 * @return A multiple of d.
 */
uint32_t FreeRTOS_round_down( uint32_t a,
                              uint32_t d )
{
    uint32_t ulResult = 0;

    configASSERT( d != 0U );

    if( d != 0U )
    {
        ulResult = d * ( a / d );
    }

    return ulResult;
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert character array (of size 4) to equivalent 32-bit value.
 * @param[in] pucPtr: The character array.
 * @return 32-bit equivalent value extracted from the character array.
 *
 * @note Going by MISRA rules, these utility functions should not be defined
 *        if they are not being used anywhere. But their use depends on the
 *        application and hence these functions are defined unconditionally.
 */
uint32_t ulChar2u32( const uint8_t * pucPtr )
{
    return ( ( ( uint32_t ) pucPtr[ 0 ] ) << 24 ) |
           ( ( ( uint32_t ) pucPtr[ 1 ] ) << 16 ) |
           ( ( ( uint32_t ) pucPtr[ 2 ] ) << 8 ) |
           ( ( ( uint32_t ) pucPtr[ 3 ] ) );
}
/*-----------------------------------------------------------*/

/**
 * @brief Convert character array (of size 2) to equivalent 16-bit value.
 * @param[in] pucPtr: The character array.
 * @return 16-bit equivalent value extracted from the character array.
 *
 * @note Going by MISRA rules, these utility functions should not be defined
 *        if they are not being used anywhere. But their use depends on the
 *        application and hence these functions are defined unconditionally.
 */
uint16_t usChar2u16( const uint8_t * pucPtr )
{
    return ( uint16_t )
           ( ( ( ( uint32_t ) pucPtr[ 0 ] ) << 8 ) |
             ( ( ( uint32_t ) pucPtr[ 1 ] ) ) );
}
/*-----------------------------------------------------------*/