summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/refclock_usno.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/refclock_usno.c')
-rw-r--r--contrib/ntp/ntpd/refclock_usno.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_usno.c b/contrib/ntp/ntpd/refclock_usno.c
new file mode 100644
index 0000000..cf404e7
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_usno.c
@@ -0,0 +1,674 @@
+/*
+ * refclock_usno - clock driver for the Naval Observatory dialup
+ * Michael Shields <shields@tembel.org> 1995/02/25
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_USNO)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+
+/*
+ * This driver supports the Naval Observatory dialup at +1 202 653 0351.
+ * It is a hacked-up version of the ACTS driver.
+ *
+ * This driver does not support the `phone' configuration because that
+ * is needlessly global; it would clash with the ACTS driver.
+ *
+ * The Naval Observatory does not support the echo-delay measurement scheme.
+ *
+ * However, this driver *does* support UUCP port locking, allowing the
+ * line to be shared with other processes when not actually dialing
+ * for time.
+ */
+
+/*
+ * Interface definitions
+ */
+
+#define DEVICE "/dev/cua%d" /* device name and unit */
+#define LOCKFILE "/var/lock/LCK..cua%d"
+/* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */
+
+#define PHONE "atdt 202 653 0351"
+/* #define PHONE "atdt 1 202 653 0351" */
+
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "USNO" /* reference ID */
+#define DESCRIPTION "Naval Observatory dialup"
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define MSGCNT 10 /* we need this many time messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 20 /* length of valid timecode string */
+#define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max before USNO kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE (MSGCNT+16) /* timecode timeout (s) */
+
+/*
+ * Unit control structure
+ */
+struct usnounit {
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of time messages received */
+ long redial; /* interval to next automatic call */
+ int unit; /* unit number (= port) */
+};
+
+/*
+ * Function prototypes
+ */
+static int usno_start P((int, struct peer *));
+static void usno_shutdown P((int, struct peer *));
+static void usno_poll P((int, struct peer *));
+static void usno_disc P((struct peer *));
+#if 0
+static void usno_timeout P((struct peer *));
+static void usno_receive P((struct recvbuf *));
+static int usno_write P((struct peer *, const char *));
+#endif /* 0 */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_usno = {
+ usno_start, /* start up driver */
+ usno_shutdown, /* shut down driver */
+ usno_poll, /* transmit poll message */
+ noentry, /* not used (usno_control) */
+ noentry, /* not used (usno_init) */
+ noentry, /* not used (usno_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * usno_start - open the devices and initialize data for processing
+ */
+static int
+usno_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp = peer->procptr;
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = USNO_MINPOLL;
+ peer->maxpoll = USNO_MAXPOLL;
+ peer->sstclktype = CTL_SST_TS_TELEPHONE;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct usnounit *)
+ emalloc(sizeof(struct usnounit))))
+ return (0);
+ memset((char *)up, 0, sizeof(struct usnounit));
+ up->unit = unit;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Set up the driver timeout
+ */
+ peer->nextdate = current_time + WAIT;
+ return (1);
+}
+
+
+/*
+ * usno_shutdown - shut down the clock
+ */
+static void
+usno_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
+#endif
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ usno_disc(peer);
+ free(up);
+}
+
+
+#if 0
+/*
+ * usno_receive - receive data from the serial interface
+ */
+static void
+usno_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ u_long mjd; /* Modified Julian Day */
+ static int day, hour, minute, second;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->a_lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("usno: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are about to dial. Just drop it.
+ */
+ return;
+
+ case 2:
+
+ /*
+ * State 2. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 3.
+ */
+ (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO modem status %s",
+ ntoa(&peer->srcadr), pp->a_lastcode);
+ usno_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ peer->nextdate = current_time + CONNECT;
+ up->msgcnt = 0;
+ up->state++;
+ } else {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_WARNING,
+ "clock %s USNO unknown modem status %s",
+ ntoa(&peer->srcadr), pp->a_lastcode);
+ }
+ return;
+
+ case 3:
+
+ /*
+ * State 3. The call has been answered and we are
+ * waiting for the first message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or they are
+ * down.
+ */
+ peer->nextdate = current_time + TIMECODE;
+ up->state++;
+ return;
+
+ case 4:
+
+ /*
+ * State 4. We are reading a timecode. It's an actual
+ * timecode, or it's the `*' OTM.
+ *
+ * jjjjj nnn hhmmss UTC
+ */
+ if (pp->lencode == LENCODE) {
+ if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
+ &mjd, &day, &hour, &minute, &second) != 5) {
+#ifdef DEBUG
+ if (debug)
+ printf("usno: bad timecode format\n");
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ } else
+ up->msgcnt++;
+ return;
+ } else if (pp->lencode != 1 || !up->msgcnt)
+ return;
+ /* else, OTM; drop out of switch */
+ }
+
+ pp->leap = LEAP_NOWARNING;
+ pp->day = day;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp->second = second;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received.
+ */
+ if (!refclock_process(pp)) {
+#ifdef DEBUG
+ if (debug)
+ printf("usno: time rejected\n");
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ up->state = 0;
+ usno_disc(peer);
+}
+#endif /* 0 */
+
+
+/*
+ * usno_poll - called by the transmit routine
+ */
+static void
+usno_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call. If not, the enable flag can be set using
+ * ntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+#if 0
+/*
+ * usno_timeout - called by the timer interrupt
+ */
+static void
+usno_timeout(
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ char lockfile[128], pidbuf[8];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ if (up->state) {
+ if (up->state != 1) {
+ usno_disc(peer);
+ return;
+ }
+ /*
+ * Call, and start the answer timeout. We think it
+ * strange if the OK status has not been received from
+ * the modem, but plow ahead anyway.
+ *
+ * This code is *here* because we had to stick in a brief
+ * delay to let the modem settle down after raising DTR,
+ * and for the OK to be received. State machines are
+ * contorted.
+ */
+ if (strcmp(pp->a_lastcode, "OK") != 0)
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s USNO no modem status",
+ ntoa(&peer->srcadr));
+ (void)usno_write(peer, PHONE);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
+ ntoa(&peer->srcadr), PHONE);
+ up->state = 2;
+ up->pollcnt++;
+ pp->polls++;
+ peer->nextdate = current_time + ANSWER;
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the calling program is activated
+ * by the ntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "clock %s USNO invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via ntpdc, the calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ peer->nextdate = current_time + IDLE;
+ return;
+ }
+
+ /*
+ * Lock the port.
+ */
+ (void)sprintf(lockfile, LOCKFILE, up->unit);
+ fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "clock %s USNO port busy",
+ ntoa(&peer->srcadr));
+ return;
+ }
+ sprintf(pidbuf, "%d\n", (int) getpid());
+ write(fd, pidbuf, strlen(pidbuf));
+ close(fd);
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, up->unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
+ unlink(lockfile);
+ return;
+ }
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
+ msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
+ ntoa(&peer->srcadr));
+
+ pp->io.clock_recv = usno_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ unlink(lockfile);
+ free(up);
+ return;
+ }
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!usno_write(peer, MODEM_SETUP)) {
+ msyslog(LOG_ERR, "clock %s USNO couldn't write",
+ ntoa(&peer->srcadr));
+ io_closeclock(&pp->io);
+ unlink(lockfile);
+ free(up);
+ return;
+ }
+
+ /*
+ * Initiate a call to the Observatory. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds.
+ */
+ if (up->pollcnt) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+#ifdef DEBUG
+ if (debug)
+ printf("usno: calling program terminated\n");
+#endif
+ usno_disc(peer);
+ return;
+ }
+
+ /*
+ * Raise DTR, and let the modem settle. Then we'll dial.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
+ msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
+ ntoa(&peer->srcadr));
+ up->state = 1;
+ peer->nextdate = current_time + WAIT;
+}
+#endif /* 0 */
+
+
+/*
+ * usno_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+usno_disc(
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+ char lockfile[128];
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while the modem is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
+ msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
+ ntoa(&peer->srcadr));
+
+ if (up->state > 0) {
+ up->state = 0;
+ msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("usno: call failed %d\n", up->state);
+#endif
+ }
+
+ io_closeclock(&pp->io);
+ sprintf(lockfile, LOCKFILE, up->unit);
+ unlink(lockfile);
+
+ peer->nextdate = current_time + WAIT;
+}
+
+
+#if 0
+/*
+ * usno_write - write a message to the serial port
+ */
+static int
+usno_write(
+ struct peer *peer,
+ const char *str
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("usno: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, (unsigned)len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+#endif /* 0 */
+
+#else
+int refclock_usno_bs;
+#endif /* REFCLOCK */
OpenPOWER on IntegriCloud