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)
14 #include "ntp_unixtime.h"
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_control.h"
21 #ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
23 #endif /* HAVE_SYS_IOCTL_H */
26 * This driver supports the Naval Observatory dialup at +1 202 653 0351.
27 * It is a hacked-up version of the ACTS driver.
29 * This driver does not support the `phone' configuration because that
30 * is needlessly global; it would clash with the ACTS driver.
32 * The Naval Observatory does not support the echo-delay measurement scheme.
34 * However, this driver *does* support UUCP port locking, allowing the
35 * line to be shared with other processes when not actually dialing
40 * Interface definitions
43 #define DEVICE "/dev/cua%d" /* device name and unit */
44 #define LOCKFILE "/var/lock/LCK..cua%d"
45 /* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */
47 #define PHONE "atdt 202 653 0351"
48 /* #define PHONE "atdt 1 202 653 0351" */
50 #define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
51 #define PRECISION (-10) /* precision assumed (about 1 ms) */
52 #define REFID "USNO" /* reference ID */
53 #define DESCRIPTION "Naval Observatory dialup"
55 #define MODE_AUTO 0 /* automatic mode */
56 #define MODE_BACKUP 1 /* backup mode */
57 #define MODE_MANUAL 2 /* manual mode */
59 #define MSGCNT 10 /* we need this many time messages */
60 #define SMAX 80 /* max token string length */
61 #define LENCODE 20 /* length of valid timecode string */
62 #define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */
63 #define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */
64 #define MAXOUTAGE 3600 /* max before USNO kicks in (s) */
67 * Modem control strings. These may have to be changed for some modems.
70 * B1 initiate call negotiation using Bell 212A
71 * &C1 enable carrier detect
72 * &D2 hang up and return to command mode on DTR transition
73 * E0 modem command echo disabled
74 * l1 set modem speaker volume to low level
75 * M1 speaker enabled untill carrier detect
76 * Q0 return result codes
77 * V1 return result codes as English words
79 #define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
80 #define MODEM_HANGUP "ATH" /* modem disconnect */
85 #define IDLE 60 /* idle timeout (s) */
86 #define WAIT 2 /* wait timeout (s) */
87 #define ANSWER 30 /* answer timeout (s) */
88 #define CONNECT 10 /* connect timeout (s) */
89 #define TIMECODE (MSGCNT+16) /* timecode timeout (s) */
92 * Unit control structure
95 int pollcnt; /* poll message counter */
97 int state; /* the first one was Delaware */
98 int run; /* call program run switch */
99 int msgcnt; /* count of time messages received */
100 long redial; /* interval to next automatic call */
101 int unit; /* unit number (= port) */
105 * Function prototypes
107 static int usno_start P((int, struct peer *));
108 static void usno_shutdown P((int, struct peer *));
109 static void usno_poll P((int, struct peer *));
110 static void usno_disc P((struct peer *));
112 static void usno_timeout P((struct peer *));
113 static void usno_receive P((struct recvbuf *));
114 static int usno_write P((struct peer *, const char *));
120 struct refclock refclock_usno = {
121 usno_start, /* start up driver */
122 usno_shutdown, /* shut down driver */
123 usno_poll, /* transmit poll message */
124 noentry, /* not used (usno_control) */
125 noentry, /* not used (usno_init) */
126 noentry, /* not used (usno_buginfo) */
127 NOFLAGS /* not used */
132 * usno_start - open the devices and initialize data for processing
140 register struct usnounit *up;
141 struct refclockproc *pp;
144 * Initialize miscellaneous variables
147 peer->precision = PRECISION;
148 pp->clockdesc = DESCRIPTION;
149 memcpy((char *)&pp->refid, REFID, 4);
150 peer->minpoll = USNO_MINPOLL;
151 peer->maxpoll = USNO_MAXPOLL;
152 peer->sstclktype = CTL_SST_TS_TELEPHONE;
155 * Allocate and initialize unit structure
157 if (!(up = (struct usnounit *)
158 emalloc(sizeof(struct usnounit))))
160 memset((char *)up, 0, sizeof(struct usnounit));
162 pp->unitptr = (caddr_t)up;
165 * Set up the driver timeout
167 peer->nextdate = current_time + WAIT;
173 * usno_shutdown - shut down the clock
181 register struct usnounit *up;
182 struct refclockproc *pp;
186 printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
189 up = (struct usnounit *)pp->unitptr;
197 * usno_receive - receive data from the serial interface
201 struct recvbuf *rbufp
204 register struct usnounit *up;
205 struct refclockproc *pp;
208 u_long mjd; /* Modified Julian Day */
209 static int day, hour, minute, second;
212 * Initialize pointers and read the timecode and timestamp. If
213 * the OK modem status code, leave it where folks can find it.
215 peer = (struct peer *)rbufp->recv_srcclock;
217 up = (struct usnounit *)pp->unitptr;
218 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
220 if (pp->lencode == 0) {
221 if (strcmp(pp->a_lastcode, "OK") == 0)
227 printf("usno: timecode %d %s\n", pp->lencode,
236 * State 0. We are not expecting anything. Probably
237 * modem disconnect noise. Go back to sleep.
244 * State 1. We are about to dial. Just drop it.
251 * State 2. We are waiting for the call to be answered.
252 * All we care about here is CONNECT as the first token
253 * in the string. If the modem signals BUSY, ERROR, NO
254 * ANSWER, NO CARRIER or NO DIALTONE, we immediately
255 * hang up the phone. If CONNECT doesn't happen after
256 * ANSWER seconds, hang up the phone. If everything is
257 * okay, start the connect timeout and slide into state
260 (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
261 if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
262 0 || strcmp(str, "NO") == 0) {
263 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
265 "clock %s USNO modem status %s",
266 ntoa(&peer->srcadr), pp->a_lastcode);
268 } else if (strcmp(str, "CONNECT") == 0) {
269 peer->nextdate = current_time + CONNECT;
273 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
275 "clock %s USNO unknown modem status %s",
276 ntoa(&peer->srcadr), pp->a_lastcode);
283 * State 3. The call has been answered and we are
284 * waiting for the first message. If this doesn't
285 * happen within the timecode timeout, hang up the
286 * phone. We probably got a wrong number or they are
289 peer->nextdate = current_time + TIMECODE;
296 * State 4. We are reading a timecode. It's an actual
297 * timecode, or it's the `*' OTM.
299 * jjjjj nnn hhmmss UTC
301 if (pp->lencode == LENCODE) {
302 if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
303 &mjd, &day, &hour, &minute, &second) != 5) {
306 printf("usno: bad timecode format\n");
308 refclock_report(peer, CEVNT_BADREPLY);
312 } else if (pp->lencode != 1 || !up->msgcnt)
314 /* else, OTM; drop out of switch */
317 pp->leap = LEAP_NOWARNING;
324 * Colossal hack here. We process each sample in a trimmed-mean
325 * filter and determine the reference clock offset and
326 * dispersion. The fudge time1 value is added to each sample as
329 if (!refclock_process(pp)) {
332 printf("usno: time rejected\n");
334 refclock_report(peer, CEVNT_BADTIME);
336 } else if (up->msgcnt < MSGCNT)
340 * We have a filtered sample offset ready for peer processing.
341 * We use lastrec as both the reference time and receive time in
342 * order to avoid being cute, like setting the reference time
343 * later than the receive time, which may cause a paranoid
344 * protocol module to chuck out the data. Finaly, we unhook the
345 * timeout, arm for the next call, fold the tent and go home.
347 record_clock_stats(&peer->srcadr, pp->a_lastcode);
348 refclock_receive(peer);
349 pp->sloppyclockflag &= ~CLK_FLAG1;
358 * usno_poll - called by the transmit routine
366 register struct usnounit *up;
367 struct refclockproc *pp;
370 * If the driver is running, we set the enable flag (fudge
371 * flag1), which causes the driver timeout routine to initiate a
372 * call. If not, the enable flag can be set using
373 * ntpdc. If this is the sustem peer, then follow the system
377 up = (struct usnounit *)pp->unitptr;
379 pp->sloppyclockflag |= CLK_FLAG1;
380 if (peer == sys_peer)
381 peer->hpoll = sys_poll;
383 peer->hpoll = peer->minpoll;
390 * usno_timeout - called by the timer interrupt
397 register struct usnounit *up;
398 struct refclockproc *pp;
401 char lockfile[128], pidbuf[8];
405 * If a timeout occurs in other than state 0, the call has
406 * failed. If in state 0, we just see if there is other work to
410 up = (struct usnounit *)pp->unitptr;
412 if (up->state != 1) {
417 * Call, and start the answer timeout. We think it
418 * strange if the OK status has not been received from
419 * the modem, but plow ahead anyway.
421 * This code is *here* because we had to stick in a brief
422 * delay to let the modem settle down after raising DTR,
423 * and for the OK to be received. State machines are
426 if (strcmp(pp->a_lastcode, "OK") != 0)
427 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
428 msyslog(LOG_NOTICE, "clock %s USNO no modem status",
429 ntoa(&peer->srcadr));
430 (void)usno_write(peer, PHONE);
431 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
432 msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
433 ntoa(&peer->srcadr), PHONE);
437 peer->nextdate = current_time + ANSWER;
440 switch (peer->ttlmax) {
443 * In manual mode the calling program is activated
444 * by the ntpdc program using the enable flag (fudge
445 * flag1), either manually or by a cron job.
452 * In automatic mode the calling program runs
453 * continuously at intervals determined by the sys_poll
458 pp->sloppyclockflag |= CLK_FLAG1;
463 * In backup mode the calling program is disabled,
464 * unless no system peer has been selected for MAXOUTAGE
465 * (3600 s). Once enabled, it runs until some other NTP
469 if (!up->run && sys_peer == 0) {
470 if (current_time - last_time > MAXOUTAGE) {
472 peer->hpoll = peer->minpoll;
473 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
475 "clock %s USNO backup started ",
476 ntoa(&peer->srcadr));
478 } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
479 peer->hpoll = peer->minpoll;
481 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
483 "clock %s USNO backup stopped",
484 ntoa(&peer->srcadr));
490 "clock %s USNO invalid mode", ntoa(&peer->srcadr));
495 * The fudge flag1 is used as an enable/disable; if set either
496 * by the code or via ntpdc, the calling program is
497 * started; if reset, the phones stop ringing.
499 if (!(pp->sloppyclockflag & CLK_FLAG1)) {
501 peer->nextdate = current_time + IDLE;
508 (void)sprintf(lockfile, LOCKFILE, up->unit);
509 fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
511 msyslog(LOG_ERR, "clock %s USNO port busy",
512 ntoa(&peer->srcadr));
515 sprintf(pidbuf, "%d\n", (int) getpid());
516 write(fd, pidbuf, strlen(pidbuf));
520 * Open serial port. Use ACTS line discipline, if available. It
521 * pumps a timestamp into the data stream at every on-time
522 * character '*' found. Note: the port must have modem control
523 * or deep pockets for the phone bill. HP-UX 9.03 users should
524 * have very deep pockets.
526 (void)sprintf(device, DEVICE, up->unit);
527 if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
531 if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
532 msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
533 ntoa(&peer->srcadr));
535 pp->io.clock_recv = usno_receive;
536 pp->io.srcclock = (caddr_t)peer;
539 if (!io_addclock(&pp->io)) {
547 * Initialize modem and kill DTR. We skedaddle if this comes
550 if (!usno_write(peer, MODEM_SETUP)) {
551 msyslog(LOG_ERR, "clock %s USNO couldn't write",
552 ntoa(&peer->srcadr));
553 io_closeclock(&pp->io);
560 * Initiate a call to the Observatory. If we wind up here in
561 * other than state 0, a successful call could not be completed
562 * within minpoll seconds.
565 refclock_report(peer, CEVNT_TIMEOUT);
566 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
568 "clock %s USNO calling program terminated",
569 ntoa(&peer->srcadr));
570 pp->sloppyclockflag &= ~CLK_FLAG1;
574 printf("usno: calling program terminated\n");
581 * Raise DTR, and let the modem settle. Then we'll dial.
583 if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
584 msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
585 ntoa(&peer->srcadr));
587 peer->nextdate = current_time + WAIT;
593 * usno_disc - disconnect the call and wait for the ruckus to cool
600 register struct usnounit *up;
601 struct refclockproc *pp;
606 * We should never get here other than in state 0, unless a call
607 * has timed out. We drop DTR, which will reliably get the modem
608 * off the air, even while the modem is hammering away full tilt.
611 up = (struct usnounit *)pp->unitptr;
613 if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
614 msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
615 ntoa(&peer->srcadr));
619 msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
620 ntoa(&peer->srcadr), up->state);
623 printf("usno: call failed %d\n", up->state);
627 io_closeclock(&pp->io);
628 sprintf(lockfile, LOCKFILE, up->unit);
631 peer->nextdate = current_time + WAIT;
637 * usno_write - write a message to the serial port
645 register struct usnounit *up;
646 struct refclockproc *pp;
652 * Not much to do here, other than send the message, handle
653 * debug and report faults.
656 up = (struct usnounit *)pp->unitptr;
660 printf("usno: state %d send %d %s\n", up->state, len,
663 code = write(pp->io.fd, str, (unsigned)len) == len;
664 code |= write(pp->io.fd, &cr, 1) == 1;
666 refclock_report(peer, CEVNT_FAULT);
672 int refclock_usno_bs;
673 #endif /* REFCLOCK */