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