]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpd/refclock_dumbclock.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / ntp / ntpd / refclock_dumbclock.c
1 /*
2  * refclock_dumbclock - clock driver for a unknown time distribution system
3  * that only provides hh:mm:ss (in local time, yet!).
4  */
5
6 /*
7  * Must interpolate back to local time.  Very annoying.
8  */
9 #define GET_LOCALTIME
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
16
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_refclock.h"
20 #include "ntp_calendar.h"
21 #include "ntp_stdlib.h"
22
23 #include <stdio.h>
24 #include <ctype.h>
25
26 #ifdef SYS_WINNT
27 extern int async_write(int, const void *, unsigned int);
28 #undef write
29 #define write(fd, data, octets) async_write(fd, data, octets)
30 #endif
31
32 /*
33  * This driver supports a generic dumb clock that only outputs hh:mm:ss,
34  * in local time, no less.
35  *
36  * Input format:
37  *
38  *      hh:mm:ss   <cr>
39  *
40  * hh:mm:ss -- what you'd expect, with a 24 hour clock.  (Heck, that's the only
41  * way it could get stupider.)  We take time on the <cr>.
42  *
43  * The original source of this module was the WWVB module.
44  */
45
46 /*
47  * Interface definitions
48  */
49 #define DEVICE          "/dev/dumbclock%d" /* device name and unit */
50 #define SPEED232        B9600   /* uart speed (9600 baud) */
51 #define PRECISION       (-13)   /* precision assumed (about 100 us) */
52 #define REFID           "dumbclock"     /* reference ID */
53 #define DESCRIPTION     "Dumb clock" /* WRU */
54
55
56 /*
57  * Insanity check.  Since the time is local, we need to make sure that during midnight
58  * transitions, we can convert back to Unix time.  If the conversion results in some number
59  * worse than this number of seconds away, assume the next day and retry.
60  */
61 #define INSANE_SECONDS 3600
62
63 /*
64  * Dumb clock control structure
65  */
66 struct dumbclock_unit {
67         u_char    tcswitch;     /* timecode switch */
68         l_fp      laststamp;    /* last receive timestamp */
69         u_char    lasthour;     /* last hour (for monitor) */
70         u_char    linect;       /* count ignored lines (for monitor */
71         struct tm ymd;          /* struct tm for y/m/d only */
72 };
73
74 /*
75  * Function prototypes
76  */
77 static  int     dumbclock_start         (int, struct peer *);
78 static  void    dumbclock_shutdown      (int, struct peer *);
79 static  void    dumbclock_receive       (struct recvbuf *);
80 #if 0
81 static  void    dumbclock_poll          (int, struct peer *);
82 #endif
83
84 /*
85  * Transfer vector
86  */
87 struct  refclock refclock_dumbclock = {
88         dumbclock_start,                     /* start up driver */
89         dumbclock_shutdown,                  /* shut down driver */
90         noentry,                             /* poll the driver -- a nice fabrication */
91         noentry,                             /* not used */
92         noentry,                             /* not used */
93         noentry,                             /* not used */
94         NOFLAGS                              /* not used */
95 };
96
97
98 /*
99  * dumbclock_start - open the devices and initialize data for processing
100  */
101 static int
102 dumbclock_start(
103         int unit,
104         struct peer *peer
105         )
106 {
107         register struct dumbclock_unit *up;
108         struct refclockproc *pp;
109         int fd;
110         char device[20];
111         struct tm *tm_time_p;
112         time_t     now;
113
114         /*
115          * Open serial port. Don't bother with CLK line discipline, since
116          * it's not available.
117          */
118         snprintf(device, sizeof(device), DEVICE, unit);
119 #ifdef DEBUG
120         if (debug)
121                 printf ("starting Dumbclock with device %s\n",device);
122 #endif
123         fd = refclock_open(device, SPEED232, 0);
124         if (fd <= 0)
125                 return (0);
126
127         /*
128          * Allocate and initialize unit structure
129          */
130         up = emalloc_zero(sizeof(*up));
131         pp = peer->procptr;
132         pp->unitptr = up;
133         pp->io.clock_recv = dumbclock_receive;
134         pp->io.srcclock = peer;
135         pp->io.datalen = 0;
136         pp->io.fd = fd;
137         if (!io_addclock(&pp->io)) {
138                 close(fd);
139                 pp->io.fd = -1;
140                 free(up);
141                 pp->unitptr = NULL;
142                 return (0);
143         }
144
145
146         time(&now);
147 #ifdef GET_LOCALTIME
148         tm_time_p = localtime(&now);
149 #else
150         tm_time_p = gmtime(&now);
151 #endif
152         if (tm_time_p)
153                 up->ymd = *tm_time_p;
154         else
155                 return 0;
156
157         /*
158          * Initialize miscellaneous variables
159          */
160         peer->precision = PRECISION;
161         pp->clockdesc = DESCRIPTION;
162         memcpy((char *)&pp->refid, REFID, 4);
163         return (1);
164 }
165
166
167 /*
168  * dumbclock_shutdown - shut down the clock
169  */
170 static void
171 dumbclock_shutdown(
172         int unit,
173         struct peer *peer
174         )
175 {
176         register struct dumbclock_unit *up;
177         struct refclockproc *pp;
178
179         pp = peer->procptr;
180         up = pp->unitptr;
181         if (-1 != pp->io.fd)
182                 io_closeclock(&pp->io);
183         if (NULL != up)
184                 free(up);
185 }
186
187
188 /*
189  * dumbclock_receive - receive data from the serial interface
190  */
191 static void
192 dumbclock_receive(
193         struct recvbuf *rbufp
194         )
195 {
196         struct dumbclock_unit *up;
197         struct refclockproc *pp;
198         struct peer *peer;
199
200         l_fp    trtmp;          /* arrival timestamp */
201         int     hours;          /* hour-of-day */
202         int     minutes;        /* minutes-past-the-hour */
203         int     seconds;        /* seconds */
204         int     temp;           /* int temp */
205         int     got_good;       /* got a good time flag */
206
207         /*
208          * Initialize pointers and read the timecode and timestamp
209          */
210         peer = rbufp->recv_peer;
211         pp = peer->procptr;
212         up = pp->unitptr;
213         temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
214
215         if (temp == 0) {
216                 if (up->tcswitch == 0) {
217                         up->tcswitch = 1;
218                         up->laststamp = trtmp;
219                 } else
220                         up->tcswitch = 0;
221                 return;
222         }
223         pp->lencode = (u_short)temp;
224         pp->lastrec = up->laststamp;
225         up->laststamp = trtmp;
226         up->tcswitch = 1;
227
228 #ifdef DEBUG
229         if (debug)
230                 printf("dumbclock: timecode %d %s\n",
231                        pp->lencode, pp->a_lastcode);
232 #endif
233
234         /*
235          * We get down to business. Check the timecode format...
236          */
237         got_good=0;
238         if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
239                    &hours,&minutes,&seconds) == 3)
240         {
241             struct tm *gmtp;
242             struct tm *lt_p;
243             time_t     asserted_time;        /* the SPM time based on the composite time+date */
244             struct tm  asserted_tm;          /* the struct tm of the same */
245             int        adjyear;
246             int        adjmon;
247             time_t     reality_delta;
248             time_t     now;
249
250
251             /*
252              * Convert to GMT for sites that distribute localtime.  This
253              * means we have to figure out what day it is.  Easier said
254              * than done...
255              */
256
257             memset(&asserted_tm, 0, sizeof(asserted_tm));
258
259             asserted_tm.tm_year  = up->ymd.tm_year;
260             asserted_tm.tm_mon   = up->ymd.tm_mon;
261             asserted_tm.tm_mday  = up->ymd.tm_mday;
262             asserted_tm.tm_hour  = hours;
263             asserted_tm.tm_min   = minutes;
264             asserted_tm.tm_sec   = seconds;
265             asserted_tm.tm_isdst = -1;
266
267 #ifdef GET_LOCALTIME
268             asserted_time = mktime (&asserted_tm);
269             time(&now);
270 #else
271 #include "GMT unsupported for dumbclock!"
272 #endif
273             reality_delta = asserted_time - now;
274
275             /*
276              * We assume that if the time is grossly wrong, it's because we got the
277              * year/month/day wrong.
278              */
279             if (reality_delta > INSANE_SECONDS)
280             {
281                 asserted_time -= SECSPERDAY; /* local clock behind real time */
282             }
283             else if (-reality_delta > INSANE_SECONDS)
284             {
285                 asserted_time += SECSPERDAY; /* local clock ahead of real time */
286             }
287             lt_p = localtime(&asserted_time);
288             if (lt_p)
289             {
290                 up->ymd = *lt_p;
291             }
292             else
293             {
294                 refclock_report (peer, CEVNT_FAULT);
295                 return;
296             }
297
298             if ((gmtp = gmtime (&asserted_time)) == NULL)
299             {
300                 refclock_report (peer, CEVNT_FAULT);
301                 return;
302             }
303             adjyear = gmtp->tm_year+1900;
304             adjmon  = gmtp->tm_mon+1;
305             pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
306             pp->hour   = gmtp->tm_hour;
307             pp->minute = gmtp->tm_min;
308             pp->second = gmtp->tm_sec;
309 #ifdef DEBUG
310             if (debug)
311                 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
312                         adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
313                         pp->second);
314 #endif
315
316             got_good=1;
317         }
318
319         if (!got_good)
320         {
321             if (up->linect > 0)
322                 up->linect--;
323             else
324                 refclock_report(peer, CEVNT_BADREPLY);
325             return;
326         }
327
328         /*
329          * Process the new sample in the median filter and determine the
330          * timecode timestamp.
331          */
332         if (!refclock_process(pp)) {
333                 refclock_report(peer, CEVNT_BADTIME);
334                 return;
335         }
336         pp->lastref = pp->lastrec;
337         refclock_receive(peer);
338         record_clock_stats(&peer->srcadr, pp->a_lastcode);
339         up->lasthour = (u_char)pp->hour;
340 }
341
342 #if 0
343 /*
344  * dumbclock_poll - called by the transmit procedure
345  */
346 static void
347 dumbclock_poll(
348         int unit,
349         struct peer *peer
350         )
351 {
352         register struct dumbclock_unit *up;
353         struct refclockproc *pp;
354         char pollchar;
355
356         /*
357          * Time to poll the clock. The Chrono-log clock is supposed to
358          * respond to a 'T' by returning a timecode in the format(s)
359          * specified above.  Ours does (can?) not, but this seems to be
360          * an installation-specific problem.  This code is dyked out,
361          * but may be re-enabled if anyone ever finds a Chrono-log that
362          * actually listens to this command.
363          */
364 #if 0
365         pp = peer->procptr;
366         up = pp->unitptr;
367         if (peer->reach == 0)
368                 refclock_report(peer, CEVNT_TIMEOUT);
369         if (up->linect > 0)
370                 pollchar = 'R';
371         else
372                 pollchar = 'T';
373         if (write(pp->io.fd, &pollchar, 1) != 1)
374                 refclock_report(peer, CEVNT_FAULT);
375         else
376                 pp->polls++;
377 #endif
378 }
379 #endif
380
381 #else
382 int refclock_dumbclock_bs;
383 #endif  /* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */