Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_dumbclock.c
blob5158f0a5b038b3105dc693521143552ddd8b7f12
1 /* $NetBSD: refclock_dumbclock.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
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 P((int, struct peer *));
80 static void dumbclock_shutdown P((int, struct peer *));
81 static void dumbclock_receive P((struct recvbuf *));
82 #if 0
83 static void dumbclock_poll P((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 if (up == NULL) {
134 (void) close(fd);
135 return (0);
137 memset((char *)up, 0, sizeof(struct dumbclock_unit));
138 pp = peer->procptr;
139 pp->unitptr = (caddr_t)up;
140 pp->io.clock_recv = dumbclock_receive;
141 pp->io.srcclock = (caddr_t)peer;
142 pp->io.datalen = 0;
143 pp->io.fd = fd;
144 if (!io_addclock(&pp->io)) {
145 (void) close(fd);
146 free(up);
147 return (0);
151 time(&now);
152 #ifdef GET_LOCALTIME
153 tm_time_p = localtime(&now);
154 #else
155 tm_time_p = gmtime(&now);
156 #endif
157 if (tm_time_p)
159 up->ymd = *tm_time_p;
161 else
163 return 0;
167 * Initialize miscellaneous variables
169 peer->precision = PRECISION;
170 pp->clockdesc = DESCRIPTION;
171 memcpy((char *)&pp->refid, REFID, 4);
172 return (1);
177 * dumbclock_shutdown - shut down the clock
179 static void
180 dumbclock_shutdown(
181 int unit,
182 struct peer *peer
185 register struct dumbclock_unit *up;
186 struct refclockproc *pp;
188 pp = peer->procptr;
189 up = (struct dumbclock_unit *)pp->unitptr;
190 io_closeclock(&pp->io);
191 free(up);
196 * dumbclock_receive - receive data from the serial interface
198 static void
199 dumbclock_receive(
200 struct recvbuf *rbufp
203 struct dumbclock_unit *up;
204 struct refclockproc *pp;
205 struct peer *peer;
207 l_fp trtmp; /* arrival timestamp */
208 int hours; /* hour-of-day */
209 int minutes; /* minutes-past-the-hour */
210 int seconds; /* seconds */
211 int temp; /* int temp */
212 int got_good; /* got a good time flag */
215 * Initialize pointers and read the timecode and timestamp
217 peer = (struct peer *)rbufp->recv_srcclock;
218 pp = peer->procptr;
219 up = (struct dumbclock_unit *)pp->unitptr;
220 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
222 if (temp == 0) {
223 if (up->tcswitch == 0) {
224 up->tcswitch = 1;
225 up->laststamp = trtmp;
226 } else
227 up->tcswitch = 0;
228 return;
230 pp->lencode = (u_short)temp;
231 pp->lastrec = up->laststamp;
232 up->laststamp = trtmp;
233 up->tcswitch = 1;
235 #ifdef DEBUG
236 if (debug)
237 printf("dumbclock: timecode %d %s\n",
238 pp->lencode, pp->a_lastcode);
239 #endif
242 * We get down to business. Check the timecode format...
244 got_good=0;
245 if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
246 &hours,&minutes,&seconds) == 3)
248 struct tm *gmtp;
249 struct tm *lt_p;
250 time_t asserted_time; /* the SPM time based on the composite time+date */
251 struct tm asserted_tm; /* the struct tm of the same */
252 int adjyear;
253 int adjmon;
254 int reality_delta;
255 time_t now;
259 * Convert to GMT for sites that distribute localtime. This
260 * means we have to figure out what day it is. Easier said
261 * than done...
264 asserted_tm.tm_year = up->ymd.tm_year;
265 asserted_tm.tm_mon = up->ymd.tm_mon;
266 asserted_tm.tm_mday = up->ymd.tm_mday;
267 asserted_tm.tm_hour = hours;
268 asserted_tm.tm_min = minutes;
269 asserted_tm.tm_sec = seconds;
270 asserted_tm.tm_isdst = -1;
272 #ifdef GET_LOCALTIME
273 asserted_time = mktime (&asserted_tm);
274 time(&now);
275 #else
276 #include "GMT unsupported for dumbclock!"
277 #endif
278 reality_delta = asserted_time - now;
281 * We assume that if the time is grossly wrong, it's because we got the
282 * year/month/day wrong.
284 if (reality_delta > INSANE_SECONDS)
286 asserted_time -= SECSPERDAY; /* local clock behind real time */
288 else if (-reality_delta > INSANE_SECONDS)
290 asserted_time += SECSPERDAY; /* local clock ahead of real time */
292 lt_p = localtime(&asserted_time);
293 if (lt_p)
295 up->ymd = *lt_p;
297 else
299 refclock_report (peer, CEVNT_FAULT);
300 return;
303 if ((gmtp = gmtime (&asserted_time)) == NULL)
305 refclock_report (peer, CEVNT_FAULT);
306 return;
308 adjyear = gmtp->tm_year+1900;
309 adjmon = gmtp->tm_mon+1;
310 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
311 pp->hour = gmtp->tm_hour;
312 pp->minute = gmtp->tm_min;
313 pp->second = gmtp->tm_sec;
314 #ifdef DEBUG
315 if (debug)
316 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
317 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
318 pp->second);
319 #endif
321 got_good=1;
324 if (!got_good)
326 if (up->linect > 0)
327 up->linect--;
328 else
329 refclock_report(peer, CEVNT_BADREPLY);
330 return;
334 * Process the new sample in the median filter and determine the
335 * timecode timestamp.
337 if (!refclock_process(pp)) {
338 refclock_report(peer, CEVNT_BADTIME);
339 return;
341 pp->lastref = pp->lastrec;
342 refclock_receive(peer);
343 record_clock_stats(&peer->srcadr, pp->a_lastcode);
344 up->lasthour = (u_char)pp->hour;
347 #if 0
349 * dumbclock_poll - called by the transmit procedure
351 static void
352 dumbclock_poll(
353 int unit,
354 struct peer *peer
357 register struct dumbclock_unit *up;
358 struct refclockproc *pp;
359 char pollchar;
362 * Time to poll the clock. The Chrono-log clock is supposed to
363 * respond to a 'T' by returning a timecode in the format(s)
364 * specified above. Ours does (can?) not, but this seems to be
365 * an installation-specific problem. This code is dyked out,
366 * but may be re-enabled if anyone ever finds a Chrono-log that
367 * actually listens to this command.
369 #if 0
370 pp = peer->procptr;
371 up = (struct dumbclock_unit *)pp->unitptr;
372 if (peer->reach == 0)
373 refclock_report(peer, CEVNT_TIMEOUT);
374 if (up->linect > 0)
375 pollchar = 'R';
376 else
377 pollchar = 'T';
378 if (write(pp->io.fd, &pollchar, 1) != 1)
379 refclock_report(peer, CEVNT_FAULT);
380 else
381 pp->polls++;
382 #endif
384 #endif
386 #else
387 int refclock_dumbclock_bs;
388 #endif /* REFCLOCK */