Nut/OS  4.10.3
API Reference
sntp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  * Thanks to Lars H. Andersson, who submitted the first idea of this simple function
00033  */
00034 
00035 
00082 #include <cfg/sntp.h>
00083 
00084 #include <pro/sntp.h>
00085 #include <sys/socket.h>
00086 #include <sys/heap.h>
00087 #include <stdlib.h>
00088 #include <string.h>
00089 #include "../crt/ctime.h"
00090 #include <stdio.h>
00091 #include <memdebug.h>
00092 #include <sys/thread.h>
00093 #include <sys/timer.h>
00094 #include <sys/types.h>
00095 #include <netinet/in.h>
00096 
00101 
00102 #ifndef NUT_THREAD_SNTPSTACK
00103 #define NUT_THREAD_SNTPSTACK    256
00104 #endif
00105 
00106 typedef struct _sntpframe sntpframe;
00107 struct _sntpframe {
00108     uint8_t mode;
00109     uint8_t stratum;
00110     uint8_t poll;
00111     uint8_t precision;
00112     uint32_t root_delay;
00113     uint32_t root_dispersion;
00114     uint32_t reference_identifier;
00115     uint32_t reference_ts_sec;
00116     uint32_t reference_ts_frac;
00117     uint32_t originate_ts_sec;
00118     uint32_t originate_ts_frac;
00119     uint32_t receive_ts_sec;
00120     uint32_t receive_ts_frac;
00121     uint32_t transmit_ts_sec;
00122     uint32_t transmit_ts_frac;
00123 };
00124 
00125 
00126 #define NTP_PORT        123
00127 #define SNTP_PORT NTP_PORT
00128 
00129 struct SNTP_resync_args {
00130     uint32_t server_addr;
00131     uint32_t interval;
00132 };
00133 
00134 THREAD(SNTP_resync, arg)
00135 {
00136     uint32_t server_addr = ((struct SNTP_resync_args *) arg)->server_addr;
00137     uint32_t interval = ((struct SNTP_resync_args *) arg)->interval;
00138     uint32_t cur_server_addr = server_addr;
00139     int retry = 0;
00140     time_t t;
00141 
00142     free(arg);
00143 
00144     NutThreadSetPriority(63);
00145     for (;;) {
00146         if (NutSNTPGetTime(&cur_server_addr, &t)) {     /* if any error retry */
00147             if (cur_server_addr != server_addr && server_addr == 0xFFFFFFFF) {
00148                 cur_server_addr = server_addr;
00149                 continue;
00150             }
00151 
00152             if (retry++ >= 3) { /* if numer of retries >= 3 wait 30 secs before next retry sequence ... */
00153                 retry = 0;
00154                 NutSleep(30000);
00155             } else              /* ... else wait 5 secs for next retry */
00156                 NutSleep(5000);
00157         } else {                /* no error */
00158             stime(&t);          /* so set the time */
00159             retry = 0;
00160             NutSleep(interval); /* and wait the interval time */
00161         }
00162     }
00163 }
00164 
00165 int NutSNTPGetTime(uint32_t * server_adr, time_t * t)
00166 {
00167     /*first check the pointers */
00168     uint32_t rec_addr;
00169     UDPSOCKET *sock = NULL;     /* the udp socket */
00170     sntpframe *data;            /* we're using the heap to save stack space */
00171     uint16_t port;               /* source port from incoming packet */
00172     int len;
00173     int result = -1;
00174     /* Set UDP input buffer to 256 bytes */
00175     uint16_t bufsize = 256;
00176 
00177 
00178     if (t == NULL)
00179         return -1;
00180     if (server_adr == NULL)
00181         return -1;
00182 
00183     if ((data = calloc(1, sizeof(*data))) == NULL)
00184         goto error;
00185 
00186     sock = NutUdpCreateSocket(0);       /* allocate new udp socket */
00187     if (sock == NULL)
00188         goto error;
00189 
00190     NutUdpSetSockOpt(sock, SO_RCVBUF, &bufsize, sizeof(bufsize));
00191 
00192     data->mode = 0x1B;          /* LI, VN and Mode bit fields (all in u_char mode); */
00193     if (NutUdpSendTo(sock, *server_adr, SNTP_PORT, data, sizeof(*data)))        /* Send packet to server */
00194         goto error;             /* on error return -1 */
00195   retry:
00196     rec_addr = 0;
00197     len = NutUdpReceiveFrom(sock, &rec_addr, &port, data, sizeof(*data), 5000); /* Receive packet with timeout of 5s */
00198     if (len <= 0) {
00199         goto error;             /* error or timeout occured */
00200     } 
00201 
00202     if (port != SNTP_PORT || (data->mode & 0xc0) == 0xc0)       /* if source port is not SNTP_PORT or server is not in sync return */
00203     {
00204         if (*server_adr == 0xFFFFFFFF)
00205             goto retry;         /*  unusable packets will be just ignored. */
00206         else
00207             goto error;
00208     }
00209 
00210     *t = ntohl(data->transmit_ts_sec) - (70 * 365 + _LEAP_YEAR_ADJUST) * _DAY_SEC;
00211     *server_adr = rec_addr;
00212     result = 0;
00213   error:
00214     if (sock)
00215         NutUdpDestroySocket(sock);
00216     if (data)
00217         free(data);
00218     return result;
00219 }
00220 
00221 int NutSNTPStartThread(uint32_t server_addr, uint32_t interval)
00222 {
00223     struct SNTP_resync_args *arg = malloc(sizeof(struct SNTP_resync_args));
00224     if (!arg)
00225         return -1;
00226     arg->server_addr = server_addr;
00227     arg->interval = interval;
00228     if (NutThreadCreate("sntpc", SNTP_resync, arg, 
00229         (NUT_THREAD_SNTPSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD))
00230         return 0;
00231     else {
00232         free(arg);
00233         return -1;
00234     }
00235 }
00236