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