]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_dumbclock.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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         P((int, struct peer *));
78 static  void    dumbclock_shutdown      P((int, struct peer *));
79 static  void    dumbclock_receive       P((struct recvbuf *));
80 #if 0
81 static  void    dumbclock_poll          P((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         (void)sprintf(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 = (struct dumbclock_unit *)emalloc(sizeof(struct dumbclock_unit));
131         if (up == NULL) {
132                 (void) close(fd);
133                 return (0);
134         }
135         memset((char *)up, 0, sizeof(struct dumbclock_unit));
136         pp = peer->procptr;
137         pp->unitptr = (caddr_t)up;
138         pp->io.clock_recv = dumbclock_receive;
139         pp->io.srcclock = (caddr_t)peer;
140         pp->io.datalen = 0;
141         pp->io.fd = fd;
142         if (!io_addclock(&pp->io)) {
143                 (void) close(fd);
144                 free(up);
145                 return (0);
146         }
147
148
149         time(&now);
150 #ifdef GET_LOCALTIME
151         tm_time_p = localtime(&now);
152 #else
153         tm_time_p = gmtime(&now);
154 #endif
155         if (tm_time_p)
156         {
157             up->ymd = *tm_time_p;
158         }
159         else
160         {
161             return 0;
162         }
163             
164         /*
165          * Initialize miscellaneous variables
166          */
167         peer->precision = PRECISION;
168         pp->clockdesc = DESCRIPTION;
169         memcpy((char *)&pp->refid, REFID, 4);
170         return (1);
171 }
172
173
174 /*
175  * dumbclock_shutdown - shut down the clock
176  */
177 static void
178 dumbclock_shutdown(
179         int unit,
180         struct peer *peer
181         )
182 {
183         register struct dumbclock_unit *up;
184         struct refclockproc *pp;
185
186         pp = peer->procptr;
187         up = (struct dumbclock_unit *)pp->unitptr;
188         io_closeclock(&pp->io);
189         free(up);
190 }
191
192
193 /*
194  * dumbclock_receive - receive data from the serial interface
195  */
196 static void
197 dumbclock_receive(
198         struct recvbuf *rbufp
199         )
200 {
201         struct dumbclock_unit *up;
202         struct refclockproc *pp;
203         struct peer *peer;
204
205         l_fp         trtmp;     /* arrival timestamp */
206         int          hours;     /* hour-of-day */
207         int          minutes;   /* minutes-past-the-hour */
208         int          seconds;   /* seconds */
209         int          temp;      /* int temp */
210         int          got_good;  /* got a good time flag */
211
212         /*
213          * Initialize pointers and read the timecode and timestamp
214          */
215         peer = (struct peer *)rbufp->recv_srcclock;
216         pp = peer->procptr;
217         up = (struct dumbclock_unit *)pp->unitptr;
218         temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
219
220         if (temp == 0) {
221                 if (up->tcswitch == 0) {
222                         up->tcswitch = 1;
223                         up->laststamp = trtmp;
224                 } else
225                     up->tcswitch = 0;
226                 return;
227         }
228         pp->lencode = (u_short)temp;
229         pp->lastrec = up->laststamp;
230         up->laststamp = trtmp;
231         up->tcswitch = 1;
232
233 #ifdef DEBUG
234         if (debug)
235                 printf("dumbclock: timecode %d %s\n",
236                        pp->lencode, pp->a_lastcode);
237 #endif
238
239         /*
240          * We get down to business. Check the timecode format...
241          */
242         got_good=0;
243         if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
244                    &hours,&minutes,&seconds) == 3)
245         {
246             struct tm *gmtp;
247             struct tm *lt_p;
248             time_t     asserted_time;        /* the SPM time based on the composite time+date */
249             struct tm  asserted_tm;          /* the struct tm of the same */
250             int        adjyear;
251             int        adjmon;
252             int        reality_delta;
253             time_t     now;
254
255
256             /*
257              * Convert to GMT for sites that distribute localtime.  This
258              * means we have to figure out what day it is.  Easier said
259              * than done...
260              */
261
262             asserted_tm.tm_year  = up->ymd.tm_year;
263             asserted_tm.tm_mon   = up->ymd.tm_mon;
264             asserted_tm.tm_mday  = up->ymd.tm_mday;
265             asserted_tm.tm_hour  = hours;
266             asserted_tm.tm_min   = minutes;
267             asserted_tm.tm_sec   = seconds;
268             asserted_tm.tm_isdst = -1;
269
270 #ifdef GET_LOCALTIME
271             asserted_time = mktime (&asserted_tm);
272             time(&now);
273 #else
274 #include "GMT unsupported for dumbclock!"
275 #endif
276             reality_delta = asserted_time - now;
277
278             /*
279              * We assume that if the time is grossly wrong, it's because we got the
280              * year/month/day wrong.
281              */
282             if (reality_delta > INSANE_SECONDS)
283             {
284                 asserted_time -= SECSPERDAY; /* local clock behind real time */
285             }
286             else if (-reality_delta > INSANE_SECONDS)
287             {
288                 asserted_time += SECSPERDAY; /* local clock ahead of real time */
289             }
290             lt_p = localtime(&asserted_time);
291             if (lt_p)
292             {
293                 up->ymd = *lt_p;
294             }
295             else
296             {
297                 refclock_report (peer, CEVNT_FAULT);
298                 return;
299             }
300
301             if ((gmtp = gmtime (&asserted_time)) == NULL)
302             {
303                 refclock_report (peer, CEVNT_FAULT);
304                 return;
305             }
306             adjyear = gmtp->tm_year+1900;
307             adjmon  = gmtp->tm_mon+1;
308             pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
309             pp->hour   = gmtp->tm_hour;
310             pp->minute = gmtp->tm_min;
311             pp->second = gmtp->tm_sec;
312 #ifdef DEBUG
313             if (debug)
314                 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
315                         adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
316                         pp->second);
317 #endif
318
319             got_good=1;
320         }
321
322         if (!got_good)
323         {
324             if (up->linect > 0)
325                 up->linect--;
326             else
327                 refclock_report(peer, CEVNT_BADREPLY);
328             return;
329         }
330
331         /*
332          * Process the new sample in the median filter and determine the
333          * timecode timestamp.
334          */
335         if (!refclock_process(pp)) {
336                 refclock_report(peer, CEVNT_BADTIME);
337                 return;
338         }
339         pp->lastref = pp->lastrec;
340         refclock_receive(peer);
341         record_clock_stats(&peer->srcadr, pp->a_lastcode);
342         up->lasthour = (u_char)pp->hour;
343 }
344
345 #if 0
346 /*
347  * dumbclock_poll - called by the transmit procedure
348  */
349 static void
350 dumbclock_poll(
351         int unit,
352         struct peer *peer
353         )
354 {
355         register struct dumbclock_unit *up;
356         struct refclockproc *pp;
357         char pollchar;
358
359         /*
360          * Time to poll the clock. The Chrono-log clock is supposed to
361          * respond to a 'T' by returning a timecode in the format(s)
362          * specified above.  Ours does (can?) not, but this seems to be
363          * an installation-specific problem.  This code is dyked out,
364          * but may be re-enabled if anyone ever finds a Chrono-log that
365          * actually listens to this command.
366          */
367 #if 0
368         pp = peer->procptr;
369         up = (struct dumbclock_unit *)pp->unitptr;
370         if (peer->reach == 0)
371                 refclock_report(peer, CEVNT_TIMEOUT);
372         if (up->linect > 0)
373                 pollchar = 'R';
374         else
375                 pollchar = 'T';
376         if (write(pp->io.fd, &pollchar, 1) != 1)
377                 refclock_report(peer, CEVNT_FAULT);
378         else
379                 pp->polls++;
380 #endif
381 }
382 #endif
383
384 #else
385 int refclock_dumbclock_bs;
386 #endif /* REFCLOCK */