/*
 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
 * 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 NTPDemo.c
 *
 * @brief An example of how to lookup a domain using DNS
 * And also how to send and receive UDP messages to get the NTP time
 *
 */

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

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

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_DNS.h"
#include "FreeRTOS_Stream_Buffer.h"

/* Use the date & time functions from +FAT. */
#if ( USE_PLUS_FAT != 0 )
    #include "ff_time.h"
#endif /* ( USE_PLUS_FAT != 0 ) */

#include "NTPDemo.h"
#include "ntpClient.h"

#include "date_and_time.h"

#if ( ipconfigDNS_USE_CALLBACKS == 0 )
    #error ipconfigDNS_USE_CALLBACKS must be 1
#endif

/* Set time: sets the current time in seconds-after-1/1/1970
 * This function must be provided by the application. */

time_t get_time( time_t * puxTime );
int set_time( const time_t * t );

enum EStatus
{
    EStatusLookup,
    EStatusAsking,
    EStatusPause,
    EStatusFailed,
};

static struct SNtpPacket xNTPPacket;

BaseType_t xNTPHasTime;
uint32_t ulNTPTime;

#if ( ipconfigUSE_CALLBACKS == 0 )
    static char cRecvBuffer[ sizeof( struct SNtpPacket ) + 64 ];
#endif

static enum EStatus xStatus = EStatusLookup;

static const char * pcTimeServers[] =
{
    "0.asia.pool.ntp.org",
    "0.europe.pool.ntp.org",
    "0.id.pool.ntp.org",
    "0.south-america.pool.ntp.org",
    "0.oceania.pool.ntp.org",
    "0.north-america.pool.ntp.org"
};

static SemaphoreHandle_t xNTPWakeupSem = NULL;
static uint32_t ulIPAddressFound;

static struct freertos_sockaddr xIPAddressFound;
static BaseType_t xHasIPAddress = pdFALSE;

static Socket_t xNTP_UDPSocket = NULL;
static TaskHandle_t xNTPTaskhandle = NULL;
static TickType_t uxSendTime;
static BaseType_t xPreferredHostType = FREERTOS_AF_INET4;
static BaseType_t xDNSAsynchronous = pdTRUE;
static BaseType_t xDNSLogging = pdFALSE;

static void prvNTPTask( void * pvParameters );

static void vSignalTask( void )
{
    #if ( ipconfigUSE_CALLBACKS == 0 )
        if( xNTP_UDPSocket != NULL )
        {
            /* Send a signal to the socket so that the
             *  FreeRTOS_recvfrom will get interrupted. */
            FreeRTOS_SignalSocket( xNTP_UDPSocket );
        }
        else
    #endif

    if( xNTPWakeupSem != NULL )
    {
        xSemaphoreGive( xNTPWakeupSem );
    }
}

void vNTPClearCache( void )
{
    ulIPAddressFound = 0U;
    #if ( ipconfigUSE_IPv6 != 0 )
    {
        memset( &( xIPAddressFound ), 0, sizeof xIPAddressFound );
    }
    #endif
    xHasIPAddress = pdFALSE;
}

void vNTPSetNTPType( BaseType_t aIPType,
                     BaseType_t xAsynchronous,
                     BaseType_t xLogging )
{
    switch( aIPType )
    {
        case 4:
            xPreferredHostType = FREERTOS_AF_INET4;
            break;

            #if ( ipconfigUSE_IPv6 != 0 )
                case 6:
                    xPreferredHostType = FREERTOS_AF_INET6;
                    break;
            #endif
        default:
            break;
    }

    xDNSAsynchronous = xAsynchronous;
    xDNSLogging = xLogging;
    FreeRTOS_printf( ( "NTP config: Using IPv%d, %ssynchronous with%s logging\n",
                       ( xPreferredHostType == FREERTOS_AF_INET4 ) ? 4 : 6,
                       xDNSAsynchronous ? "a" : "",
                       xDNSLogging ? "" : "out" ) );
}

BaseType_t xNTPTaskIsRunning()
{
    BaseType_t xResult = ( xNTPTaskhandle != NULL ) ? pdTRUE : pdFALSE;

    return xResult;
}

void vStartNTPTask( uint16_t usTaskStackSize,
                    UBaseType_t uxTaskPriority )
{
    /* The only public function in this module: start a task to contact
     * some NTP server. */

    if( xNTPTaskhandle != NULL )
    {
        switch( xStatus )
        {
            case EStatusPause:
                xStatus = EStatusAsking;
                vSignalTask();
                break;

            case EStatusLookup:
                FreeRTOS_printf( ( "NTP looking up server\n" ) );
                break;

            case EStatusAsking:
                FreeRTOS_printf( ( "NTP still asking\n" ) );
                break;

            case EStatusFailed:
                FreeRTOS_printf( ( "NTP failed somehow\n" ) );
                ulIPAddressFound = 0ul;
                xStatus = EStatusLookup;
                vSignalTask();
                break;
        }
    }
    else
    {
        xNTP_UDPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );

        if( xNTP_UDPSocket != NULL )
        {
            struct freertos_sockaddr xAddress;
            #if ( ipconfigUSE_CALLBACKS != 0 )
                BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 0 );
            #else
                BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 );
            #endif

            xAddress.sin_address.ulIP_IPv4 = 0ul;
            xAddress.sin_port = FreeRTOS_htons( NTP_PORT );
            xAddress.sin_family = FREERTOS_AF_INET;

            FreeRTOS_bind( xNTP_UDPSocket, &xAddress, sizeof( xAddress ) );
            FreeRTOS_setsockopt( xNTP_UDPSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
            xTaskCreate( prvNTPTask,                    /* The function that implements the task. */
                         ( const char * ) "NTP client", /* Just a text name for the task to aid debugging. */
                         usTaskStackSize,               /* The stack size is defined in FreeRTOSIPConfig.h. */
                         NULL,                          /* The task parameter, not used in this case. */
                         uxTaskPriority,                /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
                         &xNTPTaskhandle );             /* The task handle. */
        }
        else
        {
            FreeRTOS_printf( ( "Creating socket failed\n" ) );
        }
    }
}
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_IPv6 != 0 )
    static void vDNS_callback( const char * pcName,
                               void * pvSearchID,
                               struct freertos_addrinfo * pxAddress )
    {
        ( void ) pvSearchID;

        if( pxAddress == NULL )
        {
            FreeRTOS_printf( ( "vDNS_callback: DNS lookup timed out\n" ) );
        }
        else
        {
            if( pxAddress->ai_family == FREERTOS_AF_INET4 )
            {
                char pcBuf[ 16 ];

                /* The DNS lookup has a result, or it has reached the time-out. */
                ulIPAddressFound = pxAddress->ai_addr->sin_address.ulIP_IPv4;
                FreeRTOS_inet_ntoa( ulIPAddressFound, pcBuf );
                FreeRTOS_printf( ( "vDNS_callback: IP address of '%s' found: %s\n", pcName, pcBuf ) );

                if( ulIPAddressFound != 0U )
                {
                    memset( xIPAddressFound.sin_address.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
                    xHasIPAddress = pdTRUE;
                    xStatus = EStatusAsking;
                }
            }
            else if( pxAddress->ai_family == FREERTOS_AF_INET6 )
            {
                /*  struct freertos_sockaddr * ai_addr */
                struct freertos_sockaddr * sockaddr6 = ( struct freertos_sockaddr * ) pxAddress->ai_addr;

                xIPAddressFound.sin_len = sizeof( xIPAddressFound ); /* Ignored, still present for backward compatibility. */
                xIPAddressFound.sin_family = FREERTOS_AF_INET6;      /* Set to FREERTOS_AF_INET6. */
                xIPAddressFound.sin_port = FreeRTOS_htons( NTP_PORT );
                xIPAddressFound.sin_flowinfo = 0;                    /* IPv6 flow information. */
                memcpy( xIPAddressFound.sin_address.xIP_IPv6.ucBytes, sockaddr6->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );

                FreeRTOS_printf( ( "vDNS_callback: using address %pip\n", xIPAddressFound.sin_address.xIP_IPv6.ucBytes ) );
                ulIPAddressFound = 0U;
                xHasIPAddress = pdTRUE;
                xStatus = EStatusAsking;
            }
            else
            {
                FreeRTOS_printf( ( "vDNS_callback: Unknown address family 0x%02x\n", ( unsigned ) pxAddress->ai_family ) );
            }
        }

        vSignalTask();
    }
#else /* if ( ipconfigUSE_IPv6 != 0 ) */
    static void vDNS_callback( const char * pcName,
                               void * pvSearchID,
                               uint32_t ulIPAddress )
    {
        char pcBuf[ 16 ];

        /* The DNS lookup has a result, or it has reached the time-out. */
        FreeRTOS_inet_ntoa( ulIPAddress, pcBuf );
        FreeRTOS_printf( ( "IP address of %s found: %s\n", pcName, pcBuf ) );

        if( ulIPAddressFound == 0U )
        {
            ulIPAddressFound = ulIPAddress;
        }

        /* For testing: in case DNS doesn't respond, still try some NTP server
         * with a known IP-address. */
        if( ulIPAddressFound != 0U )
        {
            xHasIPAddress = pdTRUE;
            xStatus = EStatusAsking;
        }

        vSignalTask();
    }
#endif /* if ( ipconfigUSE_IPv6 != 0 ) */
/*-----------------------------------------------------------*/

static void prvSwapFields( struct SNtpPacket * pxPacket )
{
    /* NTP messages are big-endian */
    pxPacket->rootDelay = FreeRTOS_htonl( pxPacket->rootDelay );
    pxPacket->rootDispersion = FreeRTOS_htonl( pxPacket->rootDispersion );

    pxPacket->referenceTimestamp.seconds = FreeRTOS_htonl( pxPacket->referenceTimestamp.seconds );
    pxPacket->referenceTimestamp.fraction = FreeRTOS_htonl( pxPacket->referenceTimestamp.fraction );

    pxPacket->originateTimestamp.seconds = FreeRTOS_htonl( pxPacket->originateTimestamp.seconds );
    pxPacket->originateTimestamp.fraction = FreeRTOS_htonl( pxPacket->originateTimestamp.fraction );

    pxPacket->receiveTimestamp.seconds = FreeRTOS_htonl( pxPacket->receiveTimestamp.seconds );
    pxPacket->receiveTimestamp.fraction = FreeRTOS_htonl( pxPacket->receiveTimestamp.fraction );

    pxPacket->transmitTimestamp.seconds = FreeRTOS_htonl( pxPacket->transmitTimestamp.seconds );
    pxPacket->transmitTimestamp.fraction = FreeRTOS_htonl( pxPacket->transmitTimestamp.fraction );
}
/*-----------------------------------------------------------*/

static void prvNTPPacketInit()
{
    memset( &xNTPPacket, '\0', sizeof( xNTPPacket ) );

    xNTPPacket.flags = 0xDB;                /* value 0xDB : mode 3 (client), version 3, leap indicator unknown 3 */
    xNTPPacket.poll = 10;                   /* 10 means 1 << 10 = 1024 seconds */
    xNTPPacket.precision = 0xFA;            /* = 250 = 0.015625 seconds */
    xNTPPacket.rootDelay = 0x5D2E;          /* 0x5D2E = 23854 or (23854/65535)= 0.3640 sec */
    xNTPPacket.rootDispersion = 0x0008CAC8; /* 0x0008CAC8 = 8.7912  seconds */

    /* use the recorded NTP time */
    time_t uxSecs = get_time( NULL );               /* apTime may be NULL, returns seconds */

    xNTPPacket.referenceTimestamp.seconds = uxSecs; /* Current time */
    xNTPPacket.transmitTimestamp.seconds = uxSecs + 3;

    /* Transform the contents of the fields from native to big endian. */
    prvSwapFields( &xNTPPacket );
}
/*-----------------------------------------------------------*/

static void prvReadTime( struct SNtpPacket * pxPacket )
{
    #if ( USE_PLUS_FAT != 0 )
        FF_TimeStruct_t xTimeStruct;
    #else
        struct tm xTimeStruct;
    #endif

    time_t uxPreviousSeconds;
    time_t uxPreviousMS;

    time_t uxCurrentSeconds;
    time_t uxCurrentMS;

    const char * pcTimeUnit;
    int32_t ilDiff;
    TickType_t uxTravelTime;

    uxTravelTime = xTaskGetTickCount() - uxSendTime;

    /* Transform the contents of the fields from big to native endian. */
    prvSwapFields( pxPacket );

    uxCurrentSeconds = pxPacket->receiveTimestamp.seconds - TIME1970;
    uxCurrentMS = pxPacket->receiveTimestamp.fraction / 4294967;
    uxCurrentSeconds += uxCurrentMS / 1000;
    uxCurrentMS = uxCurrentMS % 1000;

    /* Get the last time recorded */
    uxPreviousSeconds = FreeRTOS_get_secs_msec( &uxPreviousMS );

    /* Set the new time with precision in msec. * / */
    FreeRTOS_set_secs_msec( &uxCurrentSeconds, &uxCurrentMS );

    if( uxCurrentSeconds >= uxPreviousSeconds )
    {
        ilDiff = ( int32_t ) ( uxCurrentSeconds - uxPreviousSeconds );
    }
    else
    {
        ilDiff = 0 - ( int32_t ) ( uxPreviousSeconds - uxCurrentSeconds );
    }

    if( ( ilDiff < -5 ) || ( ilDiff > 5 ) )
    {
        /* More than 5 seconds difference. */
        pcTimeUnit = "sec";
    }
    else
    {
        /* Less than or equal to 5 second difference. */
        pcTimeUnit = "ms";
        uint32_t ulLowest = ( uxCurrentSeconds <= uxPreviousSeconds ) ? uxCurrentSeconds : uxPreviousSeconds;
        int32_t iCurMS = 1000 * ( uxCurrentSeconds - ulLowest ) + uxCurrentMS;
        int32_t iPrevMS = 1000 * ( uxPreviousSeconds - ulLowest ) + uxPreviousMS;
        ilDiff = iCurMS - iPrevMS;
    }

    /*uxCurrentSeconds -= iTimeZone; */

    #if ( USE_PLUS_FAT != 0 )
        FreeRTOS_gmtime_r( &uxCurrentSeconds, &xTimeStruct );
    #else
        gmtime_r( &uxCurrentSeconds, &xTimeStruct );
    #endif /* ( USE_PLUS_FAT != 0 ) */

    /*
     *  378.067 [NTP client] NTP time: 9/11/2015 16:11:19.559 Difference -20 ms (289 ms)
     *  379.441 [NTP client] NTP time: 9/11/2015 16:11:20.933 Difference 0 ms (263 ms)
     */

    FreeRTOS_printf( ( "NTP time: %u/%u/%02u %2u:%02u:%02u.%03u Difference %d %s (%lu ms)\n",
                       ( unsigned ) xTimeStruct.tm_mday,
                       ( unsigned ) xTimeStruct.tm_mon + 1,
                       ( unsigned ) xTimeStruct.tm_year + 1900,
                       ( unsigned ) xTimeStruct.tm_hour,
                       ( unsigned ) xTimeStruct.tm_min,
                       ( unsigned ) xTimeStruct.tm_sec,
                       ( unsigned ) uxCurrentMS,
                       ( signed ) ilDiff,
                       pcTimeUnit,
                       uxTravelTime ) );

    xNTPHasTime = pdTRUE;
    ulNTPTime = uxCurrentSeconds;
    set_time( &uxCurrentSeconds );

    /* Remove compiler warnings in case FreeRTOS_printf() is not used. */
    ( void ) pcTimeUnit;
    ( void ) uxTravelTime;
}
/*-----------------------------------------------------------*/

#if ( ipconfigUSE_CALLBACKS != 0 )

    static BaseType_t xOnUDPReceive( Socket_t xSocket,
                                     void * pvData,
                                     size_t xLength,
                                     const struct freertos_sockaddr * pxFrom,
                                     const struct freertos_sockaddr * pxDest )
    {
        ( void ) xSocket;
        ( void ) pxFrom;
        ( void ) pxDest;

        if( xLength >= sizeof( xNTPPacket ) )
        {
            prvReadTime( ( struct SNtpPacket * ) pvData );

            if( xStatus != EStatusPause )
            {
                xStatus = EStatusPause;
            }
        }

        vSignalTask();
        /* Tell the driver not to store the RX data */
        return 1;
    }
    /*-----------------------------------------------------------*/

#endif /* ipconfigUSE_CALLBACKS != 0 */

static void prvNTPTask( void * pvParameters )
{
    BaseType_t xServerIndex = 3;
    struct freertos_sockaddr xAddress;

    #if ( ipconfigUSE_CALLBACKS != 0 )
        F_TCP_UDP_Handler_t xHandler;
    #endif /* ipconfigUSE_CALLBACKS != 0 */

    ( void ) pvParameters;

    xStatus = EStatusLookup;
    #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) || ( ipconfigUSE_CALLBACKS != 0 )
    {
        xNTPWakeupSem = xSemaphoreCreateBinary();
    }
    #endif

    #if ( ipconfigUSE_CALLBACKS != 0 )
    {
        memset( &xHandler, '\0', sizeof( xHandler ) );
        xHandler.pxOnUDPReceive = xOnUDPReceive;
        FreeRTOS_setsockopt( xNTP_UDPSocket, 0, FREERTOS_SO_UDP_RECV_HANDLER, ( void * ) &xHandler, sizeof( xHandler ) );
    }
    #endif
    #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 )
    {
        FreeRTOS_setsockopt( xNTP_UDPSocket, 0, FREERTOS_SO_SET_SEMAPHORE, ( void * ) &xNTPWakeupSem, sizeof( xNTPWakeupSem ) );
    }
    #endif

    for( ; ; )
    {
        switch( xStatus )
        {
            case EStatusLookup:

                if( xHasIPAddress == 0 )
                {
                    char pcServerName[ 64 ];

                    if( ++xServerIndex == sizeof( pcTimeServers ) / sizeof( pcTimeServers[ 0 ] ) )
                    {
                        xServerIndex = 0;
                    }

                    snprintf( pcServerName, sizeof pcServerName, "%s", pcTimeServers[ xServerIndex ] );

                    if( ( pcServerName[ 0 ] == '0' ) && ( xPreferredHostType == FREERTOS_AF_INET6 ) )
                    {
                        pcServerName[ 0 ] = '2';
                    }

                    FreeRTOS_printf( ( "Looking up server '%s' IPv%c\n",
                                       pcServerName,
                                       ( xPreferredHostType == FREERTOS_AF_INET4 ) ? '4' : '6' ) );
                    #if ( ipconfigMULTI_INTERFACE != 0 )
                        struct freertos_addrinfo xHints;
                        struct freertos_addrinfo * pxResults = NULL;

                        memset( &( xHints ), 0, sizeof xHints );
                        xHints.ai_family = xPreferredHostType;

                        if( xDNSAsynchronous != 0 )
                        {
                            #if ( ipconfigDNS_USE_CALLBACKS != 0 )
                            {
                                FreeRTOS_getaddrinfo_a( pcServerName,    /* The name of the node or device */
                                                        NULL,            /* Ignored for now. */
                                                        &( xHints ),     /* If not NULL: preferences. */
                                                        &( pxResults ),  /* An allocated struct, containing the results. */
                                                        vDNS_callback,
                                                        ( void * ) NULL, /* An object or a reference. */
                                                        pdMS_TO_TICKS( 2500U ) );
                            }
                            #else
                            {
                                FreeRTOS_printf( ( "ipconfigDNS_USE_CALLBACKS is not defined\n" ) );
                            }
                            #endif /* if ( ipconfigDNS_USE_CALLBACKS != 0 ) */
                        }
                        else
                        {
                            FreeRTOS_getaddrinfo( pcServerName,     /* The name of the node or device */
                                                  NULL,             /* Ignored for now. */
                                                  &( xHints ),      /* If not NULL: preferences. */
                                                  &( pxResults ) ); /* An allocated struct, containing the results. */

                            if( pxResults != NULL )
                            {
                                vDNS_callback( pcServerName, NULL, pxResults );
                            }
                        }
                    #else /* if ( ipconfigMULTI_INTERFACE != 0 ) */
                        #if ( ipconfigDNS_USE_CALLBACKS != 0 )
                            FreeRTOS_gethostbyname_a( pcServerName, vDNS_callback, ( void * ) NULL, 1200U );
                        #else
                            uint32_t ulIPAddress = FreeRTOS_gethostbyname( pcServerName );

                            if( ulIPAddress != 0U )
                            {
                                vDNS_callback( pcServerName, NULL, ulIPAddress );
                            }
                        #endif
                    #endif /* if ( ipconfigMULTI_INTERFACE != 0 ) */
                }
                else
                {
                    xStatus = EStatusAsking;
                }

                break;

            case EStatusAsking:
                prvNTPPacketInit();
                uxSendTime = xTaskGetTickCount();
                #if ( ipconfigUSE_IPv6 != 0 )
                    if( memcmp( xIPAddressFound.sin_address.xIP_IPv6.ucBytes, FreeRTOS_in6addr_any.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) != 0 )
                    {
                        FreeRTOS_printf( ( "Sending UDP message to %pip port %u\n",
                                           xIPAddressFound.sin_address.xIP_IPv6.ucBytes,
                                           FreeRTOS_ntohs( xIPAddressFound.sin_port ) ) );

                        FreeRTOS_sendto( xNTP_UDPSocket,
                                         ( void * ) &xNTPPacket, sizeof( xNTPPacket ),
                                         0,
                                         ( const struct freertos_sockaddr * ) &( xIPAddressFound ),
                                         sizeof( xIPAddressFound ) );
                    }
                    else
                #endif /* ( ipconfigUSE_IPv6 != 0 ) */
                {
                    xAddress.sin_address.ulIP_IPv4 = ulIPAddressFound;
                    xAddress.sin_port = FreeRTOS_htons( NTP_PORT );
                    xAddress.sin_family = FREERTOS_AF_INET;

                    FreeRTOS_printf( ( "Sending UDP message to %xip port %u\n",
                                       ( unsigned ) FreeRTOS_ntohl( xAddress.sin_address.ulIP_IPv4 ),
                                       ( unsigned ) FreeRTOS_ntohs( xAddress.sin_port ) ) );

                    FreeRTOS_sendto( xNTP_UDPSocket,
                                     ( void * ) &xNTPPacket,
                                     sizeof( xNTPPacket ),
                                     0, &( xAddress ),
                                     sizeof( xAddress ) );
                }

                break;

            case EStatusPause:
                break;

            case EStatusFailed:
                break;
        }

        #if ( ipconfigUSE_CALLBACKS != 0 )
        {
            xSemaphoreTake( xNTPWakeupSem, 5000 );
        }
        #else
        {
            uint32_t xAddressSize;
            BaseType_t xReturned;

            xAddressSize = sizeof( xAddress );
            xReturned = FreeRTOS_recvfrom( xNTP_UDPSocket, ( void * ) cRecvBuffer, sizeof( cRecvBuffer ), 0, &xAddress, &xAddressSize );

            switch( xReturned )
            {
                case 0:
                case -pdFREERTOS_ERRNO_EAGAIN:
                case -pdFREERTOS_ERRNO_EINTR:
                    break;

                default:

                    if( xReturned < sizeof( xNTPPacket ) )
                    {
                        FreeRTOS_printf( ( "FreeRTOS_recvfrom: returns %ld\n", xReturned ) );
                    }
                    else
                    {
                        prvReadTime( ( struct SNtpPacket * ) cRecvBuffer );

                        if( xStatus != EStatusPause )
                        {
                            xStatus = EStatusPause;
                        }
                    }

                    break;
            }
        }
        #endif /* if ( ipconfigUSE_CALLBACKS != 0 ) */
    }
}
/*-----------------------------------------------------------*/
