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
pp->lastref = pp->lastrec;
348
refclock_receive(peer);
349
record_clock_stats(&peer->srcadr, pp->a_lastcode);
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 */