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 
00074 #include <cfg/sntp.h>
00075 
00076 #include <pro/sntp.h>
00077 #include <sys/socket.h>
00078 #include <sys/heap.h>
00079 #include <string.h>
00080 #include "../crt/ctime.h"
00081 #include <stdio.h>
00082 #include <sys/thread.h>
00083 #include <sys/timer.h>
00084 #include <sys/types.h>
00085 #include <netinet/in.h>
00086 
00091 
00092 #ifndef NUT_THREAD_SNTPSTACK
00093 #define NUT_THREAD_SNTPSTACK    256
00094 #endif
00095 
00096 typedef struct _sntpframe sntpframe;
00097 struct _sntpframe {
00098     uint8_t mode;
00099     uint8_t stratum;
00100     uint8_t poll;
00101     uint8_t precision;
00102     uint32_t root_delay;
00103     uint32_t root_dispersion;
00104     uint32_t reference_identifier;
00105     uint32_t reference_ts_sec;
00106     uint32_t reference_ts_frac;
00107     uint32_t originate_ts_sec;
00108     uint32_t originate_ts_frac;
00109     uint32_t receive_ts_sec;
00110     uint32_t receive_ts_frac;
00111     uint32_t transmit_ts_sec;
00112     uint32_t transmit_ts_frac;
00113 };
00114 
00115 
00116 #define NTP_PORT    123
00117 #define SNTP_PORT NTP_PORT
00118 
00119 struct SNTP_resync_args {
00120     uint32_t server_addr;
00121     uint32_t interval;
00122 };
00123 
00124 THREAD(SNTP_resync, arg)
00125 {
00126     uint32_t server_addr = ((struct SNTP_resync_args *) arg)->server_addr;
00127     uint32_t interval = ((struct SNTP_resync_args *) arg)->interval;
00128     uint32_t cur_server_addr = server_addr;
00129     int retry = 0;
00130     time_t t;
00131 
00132     NutHeapFree(arg);
00133 
00134     NutThreadSetPriority(63);
00135     for (;;) {
00136         if (NutSNTPGetTime(&cur_server_addr, &t)) {     /* if any error retry */
00137             if (cur_server_addr != server_addr && server_addr == 0xFFFFFFFF) {
00138                 cur_server_addr = server_addr;
00139                 continue;
00140             }
00141 
00142             if (retry++ >= 3) { /* if numer of retries >= 3 wait 30 secs before next retry sequence ... */
00143                 retry = 0;
00144                 NutSleep(30000);
00145             } else              /* ... else wait 5 secs for next retry */
00146                 NutSleep(5000);
00147         } else {                /* no error */
00148             stime(&t);          /* so set the time */
00149             retry = 0;
00150             NutSleep(interval); /* and wait the interval time */
00151         }
00152     }
00153 }
00154 
00155 int NutSNTPGetTime(uint32_t * server_adr, time_t * t)
00156 {
00157     /*first check the pointers */
00158     uint32_t rec_addr;
00159     UDPSOCKET *sock = NULL;     /* the udp socket */
00160     sntpframe *data;            /* we're using the heap to save stack space */
00161     uint16_t port;               /* source port from incoming packet */
00162     int len;
00163     int result = -1;
00164     /* Set UDP input buffer to 256 bytes */
00165     uint16_t bufsize = 256;
00166 
00167 
00168     if (t == NULL)
00169         return -1;
00170     if (server_adr == NULL)
00171         return -1;
00172 
00173     if ((data = NutHeapAllocClear(sizeof(*data))) == NULL)
00174         goto error;
00175 
00176     sock = NutUdpCreateSocket(0);       /* allocate new udp socket */
00177     if (sock == NULL)
00178         goto error;
00179 
00180     NutUdpSetSockOpt(sock, SO_RCVBUF, &bufsize, sizeof(bufsize));
00181 
00182     data->mode = 0x1B;          /* LI, VN and Mode bit fields (all in u_char mode); */
00183     if (NutUdpSendTo(sock, *server_adr, SNTP_PORT, data, sizeof(*data)))        /* Send packet to server */
00184         goto error;             /* on error return -1 */
00185   retry:
00186     rec_addr = 0;
00187     len = NutUdpReceiveFrom(sock, &rec_addr, &port, data, sizeof(*data), 5000); /* Receive packet with timeout of 5s */
00188     if (len <= 0) {
00189         goto error;             /* error or timeout occured */
00190     } 
00191 
00192     if (port != SNTP_PORT || (data->mode & 0xc0) == 0xc0)       /* if source port is not SNTP_PORT or server is not in sync return */
00193     {
00194         if (*server_adr == 0xFFFFFFFF)
00195             goto retry;         /*  unusable packets will be just ignored. */
00196         else
00197             goto error;
00198     }
00199 
00200     *t = ntohl(data->transmit_ts_sec) - (70 * 365 + _LEAP_YEAR_ADJUST) * _DAY_SEC;
00201     *server_adr = rec_addr;
00202     result = 0;
00203   error:
00204     if (sock)
00205         NutUdpDestroySocket(sock);
00206     if (data)
00207         NutHeapFree(data);
00208     return result;
00209 }
00210 
00211 int NutSNTPStartThread(uint32_t server_addr, uint32_t interval)
00212 {
00213     struct SNTP_resync_args *arg = NutHeapAlloc(sizeof(struct SNTP_resync_args));
00214     if (!arg)
00215         return -1;
00216     arg->server_addr = server_addr;
00217     arg->interval = interval;
00218     if (NutThreadCreate("sntpc", SNTP_resync, arg, NUT_THREAD_SNTPSTACK))
00219         return 0;
00220     else {
00221         NutHeapFree(arg);
00222         return -1;
00223     }
00224 }
00225 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/