2 * refclock_usno - clock driver for the Naval Observatory dialup
3 * Michael Shields <shields@tembel.org> 1995/02/25
10 #if defined(REFCLOCK) && defined(CLOCK_USNO)
15 #ifdef HAVE_SYS_IOCTL_H
16 # include <sys/ioctl.h>
17 #endif /* HAVE_SYS_IOCTL_H */
21 #include "ntp_unixtime.h"
22 #include "ntp_refclock.h"
23 #include "ntp_stdlib.h"
24 #include "ntp_control.h"
27 * This driver supports the Naval Observatory dialup at +1 202 653 0351.
28 * It is a hacked-up version of the ACTS driver.
30 * This driver does not support the `phone' configuration because that
31 * is needlessly global; it would clash with the ACTS driver.
33 * The Naval Observatory does not support the echo-delay measurement scheme.
35 * However, this driver *does* support UUCP port locking, allowing the
36 * line to be shared with other processes when not actually dialing
41 * Interface definitions
44 #define DEVICE "/dev/cua%d" /* device name and unit */
45 #define LOCKFILE "/var/lock/LCK..cua%d"
46 /* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */
48 #define PHONE "atdt 202 653 0351"
49 /* #define PHONE "atdt 1 202 653 0351" */
51 #define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
52 #define PRECISION (-10) /* precision assumed (about 1 ms) */
53 #define REFID "USNO" /* reference ID */
54 #define DESCRIPTION "Naval Observatory dialup"
56 #define MODE_AUTO 0 /* automatic mode */
57 #define MODE_BACKUP 1 /* backup mode */
58 #define MODE_MANUAL 2 /* manual mode */
60 #define MSGCNT 10 /* we need this many time messages */
61 #define SMAX 80 /* max token string length */
62 #define LENCODE 20 /* length of valid timecode string */
63 #define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */
64 #define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */
65 #define MAXOUTAGE 3600 /* max before USNO kicks in (s) */
68 * Modem control strings. These may have to be changed for some modems.
71 * B1 initiate call negotiation using Bell 212A
72 * &C1 enable carrier detect
73 * &D2 hang up and return to command mode on DTR transition
74 * E0 modem command echo disabled
75 * l1 set modem speaker volume to low level
76 * M1 speaker enabled untill carrier detect
77 * Q0 return result codes
78 * V1 return result codes as English words
80 #define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
81 #define MODEM_HANGUP "ATH" /* modem disconnect */
86 #define IDLE 60 /* idle timeout (s) */
87 #define WAIT 2 /* wait timeout (s) */
88 #define ANSWER 30 /* answer timeout (s) */
89 #define CONNECT 10 /* connect timeout (s) */
90 #define TIMECODE (MSGCNT+16) /* timecode timeout (s) */
93 * Unit control structure
96 int pollcnt; /* poll message counter */
98 int state; /* the first one was Delaware */
99 int run; /* call program run switch */
100 int msgcnt; /* count of time messages received */
101 long redial; /* interval to next automatic call */
102 int unit; /* unit number (= port) */
106 * Function prototypes
108 static int usno_start P((int, struct peer *));
109 static void usno_shutdown P((int, struct peer *));
110 static void usno_poll P((int, struct peer *));
111 static void usno_disc P((struct peer *));
113 static void usno_timeout P((struct peer *));
114 static void usno_receive P((struct recvbuf *));
115 static int usno_write P((struct peer *, const char *));
121 struct refclock refclock_usno = {
122 usno_start, /* start up driver */
123 usno_shutdown, /* shut down driver */
124 usno_poll, /* transmit poll message */
125 noentry, /* not used (usno_control) */
126 noentry, /* not used (usno_init) */
127 noentry, /* not used (usno_buginfo) */
128 NOFLAGS /* not used */
133 * usno_start - open the devices and initialize data for processing
141 register struct usnounit *up;
142 struct refclockproc *pp;
145 * Initialize miscellaneous variables
148 peer->precision = PRECISION;
149 pp->clockdesc = DESCRIPTION;
150 memcpy((char *)&pp->refid, REFID, 4);
151 peer->minpoll = USNO_MINPOLL;
152 peer->maxpoll = USNO_MAXPOLL;
153 peer->sstclktype = CTL_SST_TS_TELEPHONE;
156 * Allocate and initialize unit structure
158 if (!(up = (struct usnounit *)
159 emalloc(sizeof(struct usnounit))))
161 memset((char *)up, 0, sizeof(struct usnounit));
163 pp->unitptr = (caddr_t)up;
166 * Set up the driver timeout
168 peer->nextdate = current_time + WAIT;
174 * usno_shutdown - shut down the clock
182 register struct usnounit *up;
183 struct refclockproc *pp;
187 printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
190 up = (struct usnounit *)pp->unitptr;
198 * usno_receive - receive data from the serial interface
202 struct recvbuf *rbufp
205 register struct usnounit *up;
206 struct refclockproc *pp;
209 u_long mjd; /* Modified Julian Day */
210 static int day, hour, minute, second;
213 * Initialize pointers and read the timecode and timestamp. If
214 * the OK modem status code, leave it where folks can find it.
216 peer = (struct peer *)rbufp->recv_srcclock;
218 up = (struct usnounit *)pp->unitptr;
219 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
221 if (pp->lencode == 0) {
222 if (strcmp(pp->a_lastcode, "OK") == 0)
228 printf("usno: timecode %d %s\n", pp->lencode,
237 * State 0. We are not expecting anything. Probably
238 * modem disconnect noise. Go back to sleep.
245 * State 1. We are about to dial. Just drop it.
252 * State 2. We are waiting for the call to be answered.
253 * All we care about here is CONNECT as the first token
254 * in the string. If the modem signals BUSY, ERROR, NO
255 * ANSWER, NO CARRIER or NO DIALTONE, we immediately
256 * hang up the phone. If CONNECT doesn't happen after
257 * ANSWER seconds, hang up the phone. If everything is
258 * okay, start the connect timeout and slide into state
261 (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
262 if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
263 0 || strcmp(str, "NO") == 0) {
264 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
266 "clock %s USNO modem status %s",
267 ntoa(&peer->srcadr), pp->a_lastcode);
269 } else if (strcmp(str, "CONNECT") == 0) {
270 peer->nextdate = current_time + CONNECT;
274 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
276 "clock %s USNO unknown modem status %s",
277 ntoa(&peer->srcadr), pp->a_lastcode);
284 * State 3. The call has been answered and we are
285 * waiting for the first message. If this doesn't
286 * happen within the timecode timeout, hang up the
287 * phone. We probably got a wrong number or they are
290 peer->nextdate = current_time + TIMECODE;
297 * State 4. We are reading a timecode. It's an actual
298 * timecode, or it's the `*' OTM.
300 * jjjjj nnn hhmmss UTC
302 if (pp->lencode == LENCODE) {
303 if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
304 &mjd, &day, &hour, &minute, &second) != 5) {
307 printf("usno: bad timecode format\n");
309 refclock_report(peer, CEVNT_BADREPLY);
313 } else if (pp->lencode != 1 || !up->msgcnt)
315 /* else, OTM; drop out of switch */
318 pp->leap = LEAP_NOWARNING;
325 * Colossal hack here. We process each sample in a trimmed-mean
326 * filter and determine the reference clock offset and
327 * dispersion. The fudge time1 value is added to each sample as
330 if (!refclock_process(pp)) {
333 printf("usno: time rejected\n");
335 refclock_report(peer, CEVNT_BADTIME);
337 } else if (up->msgcnt < MSGCNT)
341 * We have a filtered sample offset ready for peer processing.
342 * We use lastrec as both the reference time and receive time in
343 * order to avoid being cute, like setting the reference time
344 * later than the receive time, which may cause a paranoid
345 * protocol module to chuck out the data. Finaly, we unhook the
346 * timeout, arm for the next call, fold the tent and go home.
348 record_clock_stats(&peer->srcadr, pp->a_lastcode);
349 refclock_receive(peer);
350 pp->sloppyclockflag &= ~CLK_FLAG1;
359 * usno_poll - called by the transmit routine
367 register struct usnounit *up;
368 struct refclockproc *pp;
371 * If the driver is running, we set the enable flag (fudge
372 * flag1), which causes the driver timeout routine to initiate a
373 * call. If not, the enable flag can be set using
374 * ntpdc. If this is the sustem peer, then follow the system
378 up = (struct usnounit *)pp->unitptr;
380 pp->sloppyclockflag |= CLK_FLAG1;
381 if (peer == sys_peer)
382 peer->hpoll = sys_poll;
384 peer->hpoll = peer->minpoll;
391 * usno_timeout - called by the timer interrupt
398 register struct usnounit *up;
399 struct refclockproc *pp;
402 char lockfile[128], pidbuf[8];
406 * If a timeout occurs in other than state 0, the call has
407 * failed. If in state 0, we just see if there is other work to
411 up = (struct usnounit *)pp->unitptr;
413 if (up->state != 1) {
418 * Call, and start the answer timeout. We think it
419 * strange if the OK status has not been received from
420 * the modem, but plow ahead anyway.
422 * This code is *here* because we had to stick in a brief
423 * delay to let the modem settle down after raising DTR,
424 * and for the OK to be received. State machines are
427 if (strcmp(pp->a_lastcode, "OK") != 0)
428 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
429 msyslog(LOG_NOTICE, "clock %s USNO no modem status",
430 ntoa(&peer->srcadr));
431 (void)usno_write(peer, PHONE);
432 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
433 msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
434 ntoa(&peer->srcadr), PHONE);
438 peer->nextdate = current_time + ANSWER;
444 * In manual mode the calling program is activated
445 * by the ntpdc program using the enable flag (fudge
446 * flag1), either manually or by a cron job.
453 * In automatic mode the calling program runs
454 * continuously at intervals determined by the sys_poll
459 pp->sloppyclockflag |= CLK_FLAG1;
464 * In backup mode the calling program is disabled,
465 * unless no system peer has been selected for MAXOUTAGE
466 * (3600 s). Once enabled, it runs until some other NTP
470 if (!up->run && sys_peer == 0) {
471 if (current_time - last_time > MAXOUTAGE) {
473 peer->hpoll = peer->minpoll;
474 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
476 "clock %s USNO backup started ",
477 ntoa(&peer->srcadr));
479 } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
480 peer->hpoll = peer->minpoll;
482 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
484 "clock %s USNO backup stopped",
485 ntoa(&peer->srcadr));
491 "clock %s USNO invalid mode", ntoa(&peer->srcadr));
496 * The fudge flag1 is used as an enable/disable; if set either
497 * by the code or via ntpdc, the calling program is
498 * started; if reset, the phones stop ringing.
500 if (!(pp->sloppyclockflag & CLK_FLAG1)) {
502 peer->nextdate = current_time + IDLE;
509 (void)sprintf(lockfile, LOCKFILE, up->unit);
510 fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
512 msyslog(LOG_ERR, "clock %s USNO port busy",
513 ntoa(&peer->srcadr));
516 sprintf(pidbuf, "%d\n", (int) getpid());
517 write(fd, pidbuf, strlen(pidbuf));
521 * Open serial port. Use ACTS line discipline, if available. It
522 * pumps a timestamp into the data stream at every on-time
523 * character '*' found. Note: the port must have modem control
524 * or deep pockets for the phone bill. HP-UX 9.03 users should
525 * have very deep pockets.
527 (void)sprintf(device, DEVICE, up->unit);
528 if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
532 if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
533 msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
534 ntoa(&peer->srcadr));
536 pp->io.clock_recv = usno_receive;
537 pp->io.srcclock = (caddr_t)peer;
540 if (!io_addclock(&pp->io)) {
548 * Initialize modem and kill DTR. We skedaddle if this comes
551 if (!usno_write(peer, MODEM_SETUP)) {
552 msyslog(LOG_ERR, "clock %s USNO couldn't write",
553 ntoa(&peer->srcadr));
554 io_closeclock(&pp->io);
561 * Initiate a call to the Observatory. If we wind up here in
562 * other than state 0, a successful call could not be completed
563 * within minpoll seconds.
566 refclock_report(peer, CEVNT_TIMEOUT);
567 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
569 "clock %s USNO calling program terminated",
570 ntoa(&peer->srcadr));
571 pp->sloppyclockflag &= ~CLK_FLAG1;
575 printf("usno: calling program terminated\n");
582 * Raise DTR, and let the modem settle. Then we'll dial.
584 if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
585 msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
586 ntoa(&peer->srcadr));
588 peer->nextdate = current_time + WAIT;
594 * usno_disc - disconnect the call and wait for the ruckus to cool
601 register struct usnounit *up;
602 struct refclockproc *pp;
607 * We should never get here other than in state 0, unless a call
608 * has timed out. We drop DTR, which will reliably get the modem
609 * off the air, even while the modem is hammering away full tilt.
612 up = (struct usnounit *)pp->unitptr;
614 if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
615 msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
616 ntoa(&peer->srcadr));
620 msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
621 ntoa(&peer->srcadr), up->state);
624 printf("usno: call failed %d\n", up->state);
628 io_closeclock(&pp->io);
629 sprintf(lockfile, LOCKFILE, up->unit);
632 peer->nextdate = current_time + WAIT;
638 * usno_write - write a message to the serial port
646 register struct usnounit *up;
647 struct refclockproc *pp;
653 * Not much to do here, other than send the message, handle
654 * debug and report faults.
657 up = (struct usnounit *)pp->unitptr;
661 printf("usno: state %d send %d %s\n", up->state, len,
664 code = write(pp->io.fd, str, (unsigned)len) == len;
665 code |= write(pp->io.fd, &cr, 1) == 1;
667 refclock_report(peer, CEVNT_FAULT);
673 int refclock_usno_bs;
674 #endif /* REFCLOCK */