summaryrefslogtreecommitdiff
path: root/ntpd/ntp_refclock.c
diff options
context:
space:
mode:
Diffstat (limited to 'ntpd/ntp_refclock.c')
-rw-r--r--ntpd/ntp_refclock.c1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/ntpd/ntp_refclock.c b/ntpd/ntp_refclock.c
new file mode 100644
index 0000000..f0e9b9e
--- /dev/null
+++ b/ntpd/ntp_refclock.c
@@ -0,0 +1,1332 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#ifdef REFCLOCK
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+#ifdef HAVE_PPSAPI
+#include "ppsapi_timepps.h"
+#include "refclock_atom.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with
+ * a reference clock, however, we replace the transmit, receive and
+ * packet procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, and to perform various debugging
+ * functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants
+ * of the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes
+ * the receive timestamp, exception report, statistics tallies, etc.
+ * In addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a
+ * pointer to the refclockproc structure, which in turn contains a
+ * pointer to the unit structure, if used. The peer structure is
+ * identified by an interface address in the dotted quad form
+ * 127.127.t.u, where t is the clock type and u the unit.
+ */
+#define FUDGEFAC .1 /* fudge correction factor */
+#define LF 0x0a /* ASCII LF */
+
+int cal_enable; /* enable refclock calibrate */
+
+/*
+ * Forward declarations
+ */
+static int refclock_cmpl_fp (const void *, const void *);
+static int refclock_sample (struct refclockproc *);
+static int refclock_ioctl(int, u_int);
+
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(
+ struct peer *peer,
+ int code
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (pp == NULL)
+ return;
+
+ switch (code) {
+
+ case CEVNT_TIMEOUT:
+ pp->noreply++;
+ break;
+
+ case CEVNT_BADREPLY:
+ pp->badformat++;
+ break;
+
+ case CEVNT_FAULT:
+ break;
+
+ case CEVNT_BADDATE:
+ case CEVNT_BADTIME:
+ pp->baddata++;
+ break;
+
+ default:
+ /* ignore others */
+ break;
+ }
+ if (pp->lastevent < 15)
+ pp->lastevent++;
+ if (pp->currentstatus != code) {
+ pp->currentstatus = (u_char)code;
+ report_event(PEVNT_CLOCK, peer, ceventstr(code));
+ }
+}
+
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock(void)
+{
+ int i;
+
+ for (i = 0; i < (int)num_refclock_conf; i++)
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it
+ * down first.
+ */
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ pp = emalloc_zero(sizeof(*pp));
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = (u_char)unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->ppoll = peer->maxpoll;
+ pp->type = clktype;
+ pp->conf = refclock_conf[clktype];
+ pp->timestarted = current_time;
+ pp->io.fd = -1;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Do driver dependent initialization. The above defaults
+ * can be wiggled, then finish up for consistency.
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ refclock_unpeer(peer);
+ return (0);
+ }
+ peer->refid = pp->refid;
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (NULL == peer->procptr)
+ return;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = NULL;
+}
+
+
+/*
+ * refclock_timer - called once per second for housekeeping.
+ */
+void
+refclock_timer(
+ struct peer *p
+ )
+{
+ struct refclockproc * pp;
+ int unit;
+
+ unit = p->refclkunit;
+ pp = p->procptr;
+ if (pp->conf->clock_timer != noentry)
+ (*pp->conf->clock_timer)(unit, p);
+ if (pp->action != NULL && pp->nextaction <= current_time)
+ (*pp->action)(p);
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+ get_systime(&peer->xmt);
+
+ /*
+ * This is a ripoff of the peer transmit routine, but
+ * specialized for reference clocks. We do a little less
+ * protocol here and call the driver-specific transmit routine.
+ */
+ if (peer->burst == 0) {
+ u_char oreach;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_transmit: at %ld %s\n",
+ current_time, stoa(&(peer->srcadr)));
+#endif
+
+ /*
+ * Update reachability and poll variables like the
+ * network code.
+ */
+ oreach = peer->reach & 0xfe;
+ peer->reach <<= 1;
+ if (!(peer->reach & 0x0f))
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ peer->outdate = current_time;
+ if (!peer->reach) {
+ if (oreach) {
+ report_event(PEVNT_UNREACH, peer, NULL);
+ peer->timereachable = current_time;
+ }
+ } else {
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NSTAGE;
+ }
+ } else {
+ peer->burst--;
+ }
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+ poll_update(peer, peer->hpoll);
+}
+
+
+/*
+ * Compare two doubles - used with qsort()
+ */
+static int
+refclock_cmpl_fp(
+ const void *p1,
+ const void *p2
+ )
+{
+ const double *dp1 = (const double *)p1;
+ const double *dp2 = (const double *)p2;
+
+ if (*dp1 < *dp2)
+ return -1;
+ if (*dp1 > *dp2)
+ return 1;
+ return 0;
+}
+
+
+/*
+ * refclock_process_offset - update median filter
+ *
+ * This routine uses the given offset and timestamps to construct a new
+ * entry in the median filter circular buffer. Samples that overflow the
+ * filter are quietly discarded.
+ */
+void
+refclock_process_offset(
+ struct refclockproc *pp, /* refclock structure pointer */
+ l_fp lasttim, /* last timecode timestamp */
+ l_fp lastrec, /* last receive timestamp */
+ double fudge
+ )
+{
+ l_fp lftemp;
+ double doffset;
+
+ pp->lastrec = lastrec;
+ lftemp = lasttim;
+ L_SUB(&lftemp, &lastrec);
+ LFPTOD(&lftemp, doffset);
+ SAMPLE(doffset + fudge);
+}
+
+
+/*
+ * refclock_process - process a sample from the clock
+ * refclock_process_f - refclock_process with other than time1 fudge
+ *
+ * This routine converts the timecode in the form days, hours, minutes,
+ * seconds and milliseconds/microseconds to internal timestamp format,
+ * then constructs a new entry in the median filter circular buffer.
+ * Return success (1) if the data are correct and consistent with the
+ * converntional calendar.
+ *
+ * Important for PPS users: Normally, the pp->lastrec is set to the
+ * system time when the on-time character is received and the pp->year,
+ * ..., pp->second decoded and the seconds fraction pp->nsec in
+ * nanoseconds). When a PPS offset is available, pp->nsec is forced to
+ * zero and the fraction for pp->lastrec is set to the PPS offset.
+ */
+int
+refclock_process_f(
+ struct refclockproc *pp, /* refclock structure pointer */
+ double fudge
+ )
+{
+ l_fp offset, ltemp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))
+ return (0);
+
+ offset.l_uf = 0;
+ DTOLFP(pp->nsec / 1e9, &ltemp);
+ L_ADD(&offset, &ltemp);
+ refclock_process_offset(pp, offset, pp->lastrec, fudge);
+ return (1);
+}
+
+
+int
+refclock_process(
+ struct refclockproc *pp /* refclock structure pointer */
+)
+{
+ return refclock_process_f(pp, pp->fudgetime1);
+}
+
+
+/*
+ * refclock_sample - process a pile of samples from the clock
+ *
+ * This routine implements a recursive median filter to suppress spikes
+ * in the data, as well as determine a performance statistic. It
+ * calculates the mean offset and RMS jitter. A time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns the number of samples
+ * processed, which could be zero.
+ */
+static int
+refclock_sample(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ size_t i, j, k, m, n;
+ double off[MAXSTAGE];
+ double offset;
+
+ /*
+ * Copy the raw offsets and sort into ascending order. Don't do
+ * anything if the buffer is empty.
+ */
+ n = 0;
+ while (pp->codeproc != pp->coderecv) {
+ pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;
+ off[n] = pp->filter[pp->codeproc];
+ n++;
+ }
+ if (n == 0)
+ return (0);
+
+ if (n > 1)
+ qsort(off, n, sizeof(off[0]), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of the samples until
+ * approximately 60 percent of the samples remain.
+ */
+ i = 0; j = n;
+ m = n - (n * 4) / 10;
+ while ((j - i) > m) {
+ offset = off[(j + i) / 2];
+ if (off[j - 1] - offset < offset - off[i])
+ i++; /* reject low end */
+ else
+ j--; /* reject high end */
+ }
+
+ /*
+ * Determine the offset and jitter.
+ */
+ pp->offset = 0;
+ pp->jitter = 0;
+ for (k = i; k < j; k++) {
+ pp->offset += off[k];
+ if (k > i)
+ pp->jitter += SQUARE(off[k] - off[k - 1]);
+ }
+ pp->offset /= m;
+ pp->jitter = max(SQRT(pp->jitter / m), LOGTOD(sys_precision));
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",
+ (int)n, pp->offset, pp->disp, pp->jitter);
+#endif
+ return (int)n;
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: at %lu %s\n",
+ current_time, stoa(&peer->srcadr));
+#endif
+
+ /*
+ * Do a little sanity dance and update the peer structure. Groom
+ * the median filter samples and give the data to the clock
+ * filter.
+ */
+ pp = peer->procptr;
+ peer->leap = pp->leap;
+ if (peer->leap == LEAP_NOTINSYNC)
+ return;
+
+ peer->received++;
+ peer->timereceived = current_time;
+ if (!peer->reach) {
+ report_event(PEVNT_REACH, peer, NULL);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+ peer->reftime = pp->lastref;
+ peer->aorg = pp->lastrec;
+ peer->rootdisp = pp->disp;
+ get_systime(&peer->dst);
+ if (!refclock_sample(pp))
+ return;
+
+ clock_filter(peer, pp->offset, 0., pp->jitter);
+ if (cal_enable && fabs(last_offset) < sys_mindisp && sys_peer !=
+ NULL) {
+ if (sys_peer->refclktype == REFCLK_ATOM_PPS &&
+ peer->refclktype != REFCLK_ATOM_PPS)
+ pp->fudgetime1 -= pp->offset * FUDGEFAC;
+ }
+}
+
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * strips the parity bit and control characters. It returns the number
+ * of characters in the line followed by a NULL character ('\0'), which
+ * is not included in the count. In case of an empty line, the previous
+ * line is preserved.
+ */
+int
+refclock_gtlin(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ const char *sp, *spend;
+ char *dp, *dpend;
+ int dlen;
+
+ if (bmax <= 0)
+ return (0);
+
+ dp = lineptr;
+ dpend = dp + bmax - 1; /* leave room for NUL pad */
+ sp = (const char *)rbufp->recv_buffer;
+ spend = sp + rbufp->recv_length;
+
+ while (sp != spend && dp != dpend) {
+ char c;
+
+ c = *sp++ & 0x7f;
+ if (c >= 0x20 && c < 0x7f)
+ *dp++ = c;
+ }
+ /* Get length of data written to the destination buffer. If
+ * zero, do *not* place a NUL byte to preserve the previous
+ * buffer content.
+ */
+ dlen = dp - lineptr;
+ if (dlen)
+ *dp = '\0';
+ *tsptr = rbufp->recv_time;
+ DPRINTF(2, ("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&rbufp->recv_time, 6), dlen,
+ (dlen != 0)
+ ? lineptr
+ : ""));
+ return (dlen);
+}
+
+
+/*
+ * refclock_gtraw - get next line/chunk of data
+ *
+ * This routine returns the raw data received from the clock in both
+ * canonical or raw modes. The terminal interface routines map CR to LF.
+ * In canonical mode this results in two lines, one containing data
+ * followed by LF and another containing only LF. In raw mode the
+ * interface routines can deliver arbitraty chunks of data from one
+ * character to a maximum specified by the calling routine. In either
+ * mode the routine returns the number of characters in the line
+ * followed by a NULL character ('\0'), which is not included in the
+ * count.
+ *
+ * *tsptr receives a copy of the buffer timestamp.
+ */
+int
+refclock_gtraw(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ if (bmax <= 0)
+ return (0);
+ bmax -= 1; /* leave room for trailing NUL */
+ if (bmax > rbufp->recv_length)
+ bmax = rbufp->recv_length;
+ memcpy(lineptr, rbufp->recv_buffer, bmax);
+ lineptr[bmax] = '\0';
+
+ *tsptr = rbufp->recv_time;
+ DPRINTF(2, ("refclock_gtraw: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&rbufp->recv_time, 6), bmax,
+ lineptr));
+ return (bmax);
+}
+
+
+/*
+ * indicate_refclock_packet()
+ *
+ * Passes a fragment of refclock input read from the device to the
+ * driver direct input routine, which may consume it (batch it for
+ * queuing once a logical unit is assembled). If it is not so
+ * consumed, queue it for the driver's receive entrypoint.
+ *
+ * The return value is TRUE if the data has been consumed as a fragment
+ * and should not be counted as a received packet.
+ */
+int
+indicate_refclock_packet(
+ struct refclockio * rio,
+ struct recvbuf * rb
+ )
+{
+ /* Does this refclock use direct input routine? */
+ if (rio->io_input != NULL && (*rio->io_input)(rb) == 0) {
+ /*
+ * data was consumed - nothing to pass up
+ * into block input machine
+ */
+ freerecvbuf(rb);
+
+ return TRUE;
+ }
+ add_full_recv_buffer(rb);
+
+ return FALSE;
+}
+
+
+/*
+ * process_refclock_packet()
+ *
+ * Used for deferred processing of 'io_input' on systems where threading
+ * is used (notably Windows). This is acting as a trampoline to make the
+ * real calls to the refclock functions.
+ */
+#ifdef HAVE_IO_COMPLETION_PORT
+void
+process_refclock_packet(
+ struct recvbuf * rb
+ )
+{
+ struct refclockio * rio;
+
+ /* get the refclockio structure from the receive buffer */
+ rio = &rb->recv_peer->procptr->io;
+
+ /* call 'clock_recv' if either there is no input function or the
+ * raw input function tells us to feed the packet to the
+ * receiver.
+ */
+ if (rio->io_input == NULL || (*rio->io_input)(rb) != 0) {
+ rio->recvcount++;
+ packets_received++;
+ handler_pkts++;
+ (*rio->clock_recv)(rb);
+ }
+}
+#endif /* HAVE_IO_COMPLETION_PORT */
+
+
+/*
+ * The following code does not apply to WINNT & VMS ...
+ */
+#if !defined(SYS_VXWORKS) && !defined(SYS_WINNT)
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if successful, or logs an error and
+ * returns -1.
+ */
+int
+refclock_open(
+ char *dev, /* device name pointer */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int fd;
+ int omode;
+#ifdef O_NONBLOCK
+ char trash[128]; /* litter bin for old input data */
+#endif
+
+ /*
+ * Open serial port and set default options
+ */
+ omode = O_RDWR;
+#ifdef O_NONBLOCK
+ omode |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ omode |= O_NOCTTY;
+#endif
+
+ fd = open(dev, omode, 0777);
+ /* refclock_open() long returned 0 on failure, avoid it. */
+ if (0 == fd) {
+ fd = dup(0);
+ SAVE_ERRNO(
+ close(0);
+ )
+ }
+ if (fd < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "refclock_open %s: %m", dev);
+ )
+ return -1;
+ }
+ if (!refclock_setup(fd, speed, lflags)) {
+ close(fd);
+ return -1;
+ }
+ if (!refclock_ioctl(fd, lflags)) {
+ close(fd);
+ return -1;
+ }
+#ifdef O_NONBLOCK
+ /*
+ * We want to make sure there is no pending trash in the input
+ * buffer. Since we have non-blocking IO available, this is a
+ * good moment to read and dump all available outdated stuff
+ * that might have become toxic for the driver.
+ */
+ while (read(fd, trash, sizeof(trash)) > 0 || errno == EINTR)
+ /*NOP*/;
+#endif
+ return fd;
+}
+
+
+/*
+ * refclock_setup - initialize terminal interface structure
+ */
+int
+refclock_setup(
+ int fd, /* file descriptor */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int i;
+ TTY ttyb, *ttyp;
+
+ /*
+ * By default, the serial line port is initialized in canonical
+ * (line-oriented) mode at specified line speed, 8 bits and no
+ * parity. LF ends the line and CR is mapped to LF. The break,
+ * erase and kill functions are disabled. There is a different
+ * section for each terminal interface, as selected at compile
+ * time. The flag bits can be used to set raw mode and echo.
+ */
+ ttyp = &ttyb;
+#ifdef HAVE_TERMIOS
+
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d tcgetattr: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ if (lflags & LDISC_7O1) {
+ /* HP Z3801A needs 7-bit, odd parity */
+ ttyp->c_cflag = CS7 | PARENB | PARODD | CLOCAL | CREAD;
+ }
+ cfsetispeed(&ttyb, speed);
+ cfsetospeed(&ttyb, speed);
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: 0x%x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR && lflags & LDISC_REMOTE)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (lflags & LDISC_ECHO)
+ ttyp->c_lflag |= ECHO;
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSANOW: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * flush input and output buffers to discard any outdated stuff
+ * that might have become toxic for the driver. Failing to do so
+ * is logged, but we keep our fingers crossed otherwise.
+ */
+ if (tcflush(fd, TCIOFLUSH) < 0)
+ msyslog(LOG_ERR, "refclock_setup fd %d tcflush(): %m",
+ fd);
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCGETA: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: %x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSETA: %m", fd);
+ )
+ return FALSE;
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCGETP: %m",
+ fd);
+ )
+ return FALSE;
+ }
+ if (speed)
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "refclock_setup TIOCSETP: %m");
+ )
+ return FALSE;
+ }
+#endif /* HAVE_BSD_TTYS */
+ return(1);
+}
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up optional features such as tty_clk. The routine returns TRUE if
+ * successful.
+ */
+int
+refclock_ioctl(
+ int fd, /* file descriptor */
+ u_int lflags /* line discipline flags */
+ )
+{
+ /*
+ * simply return TRUE if no UNIX line discipline is supported
+ */
+ DPRINTF(1, ("refclock_ioctl: fd %d flags 0x%x\n", fd, lflags));
+
+ return TRUE;
+}
+#endif /* !defined(SYS_VXWORKS) && !defined(SYS_WINNT) */
+
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(
+ sockaddr_u *srcadr,
+ const struct refclockstat *in,
+ struct refclockstat *out
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char)REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+
+ peer = findexistingpeer(srcadr, NULL, NULL, -1, 0);
+
+ if (NULL == peer)
+ return;
+
+ NTP_INSIST(peer->procptr != NULL);
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != NULL) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ peer->stratum = pp->stratum = (u_char)in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ peer->refid = pp->refid = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != NULL) {
+ out->fudgeval1 = pp->stratum;
+ out->fudgeval2 = pp->refid;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgetime1 = pp->fudgetime1;
+ if (0.0 != out->fudgetime1)
+ out->haveflags |= CLK_HAVETIME1;
+ out->fudgetime2 = pp->fudgetime2;
+ if (0.0 != out->fudgetime2)
+ out->haveflags |= CLK_HAVETIME2;
+ out->flags = (u_char) pp->sloppyclockflag;
+ if (CLK_FLAG1 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG1;
+ if (CLK_FLAG2 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG2;
+ if (CLK_FLAG3 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG3;
+ if (CLK_FLAG4 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG4;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = (u_short)pp->lencode;
+ out->p_lastcode = pp->a_lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out, peer);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(
+ sockaddr_u *srcadr, /* clock address */
+ struct refclockbug *bug /* output structure */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ int clktype;
+ int unit;
+ unsigned u;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char) REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+
+ peer = findexistingpeer(srcadr, NULL, NULL, -1, 0);
+
+ if (NULL == peer || NULL == peer->procptr)
+ return;
+
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->svalues = 0x0000003f;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->nsec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ for (u = 2; u < bug->ntimes; u++)
+ DTOLFP(pp->filter[u - 2], &bug->times[u]);
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer);
+}
+
+
+#ifdef HAVE_PPSAPI
+/*
+ * refclock_ppsapi - initialize/update ppsapi
+ *
+ * This routine is called after the fudge command to open the PPSAPI
+ * interface for later parameter setting after the fudge command.
+ */
+int
+refclock_ppsapi(
+ int fddev, /* fd device */
+ struct refclock_atom *ap /* atom structure pointer */
+ )
+{
+ if (ap->handle == 0) {
+ if (time_pps_create(fddev, &ap->handle) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ppsapi: time_pps_create: %m");
+ return (0);
+ }
+ }
+ return (1);
+}
+
+
+/*
+ * refclock_params - set ppsapi parameters
+ *
+ * This routine is called to set the PPSAPI parameters after the fudge
+ * command.
+ */
+int
+refclock_params(
+ int mode, /* mode bits */
+ struct refclock_atom *ap /* atom structure pointer */
+ )
+{
+ ZERO(ap->pps_params);
+ ap->pps_params.api_version = PPS_API_VERS_1;
+
+ /*
+ * Solaris serial ports provide PPS pulse capture only on the
+ * assert edge. FreeBSD serial ports provide capture on the
+ * clear edge, while FreeBSD parallel ports provide capture
+ * on the assert edge. Your mileage may vary.
+ */
+ if (mode & CLK_FLAG2)
+ ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTURECLEAR;
+ else
+ ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT;
+ if (time_pps_setparams(ap->handle, &ap->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_params: time_pps_setparams: %m");
+ return (0);
+ }
+
+ /*
+ * If flag3 is lit, select the kernel PPS if we can.
+ */
+ if (mode & CLK_FLAG3) {
+ if (time_pps_kcbind(ap->handle, PPS_KC_HARDPPS,
+ ap->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_params: time_pps_kcbind: %m");
+ return (0);
+ }
+ hardpps_enable = 1;
+ }
+ return (1);
+}
+
+
+/*
+ * refclock_pps - called once per second
+ *
+ * This routine is called once per second. It snatches the PPS
+ * timestamp from the kernel and saves the sign-extended fraction in
+ * a circular buffer for processing at the next poll event.
+ */
+int
+refclock_pps(
+ struct peer *peer, /* peer structure pointer */
+ struct refclock_atom *ap, /* atom structure pointer */
+ int mode /* mode bits */
+ )
+{
+ struct refclockproc *pp;
+ pps_info_t pps_info;
+ struct timespec timeout;
+ double dtemp;
+
+ /*
+ * We require the clock to be synchronized before setting the
+ * parameters. When the parameters have been set, fetch the
+ * most recent PPS timestamp.
+ */
+ pp = peer->procptr;
+ if (ap->handle == 0)
+ return (0);
+
+ if (ap->pps_params.mode == 0 && sys_leap != LEAP_NOTINSYNC) {
+ if (refclock_params(pp->sloppyclockflag, ap) < 1)
+ return (0);
+ }
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ ZERO(pps_info);
+ if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info,
+ &timeout) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return (0);
+ }
+ timeout = ap->ts;
+ if (ap->pps_params.mode & PPS_CAPTUREASSERT)
+ ap->ts = pps_info.assert_timestamp;
+ else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
+ ap->ts = pps_info.clear_timestamp;
+ else
+ return (0);
+
+ if (0 == memcmp(&timeout, &ap->ts, sizeof(timeout)))
+ return (0);
+
+ /*
+ * Convert to signed fraction offset and stuff in median filter.
+ */
+ pp->lastrec.l_ui = (u_int32)ap->ts.tv_sec + JAN_1970;
+ dtemp = ap->ts.tv_nsec / 1e9;
+ pp->lastrec.l_uf = (u_int32)(dtemp * FRAC);
+ if (dtemp > .5)
+ dtemp -= 1.;
+ SAMPLE(-dtemp + pp->fudgetime1);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("refclock_pps: %lu %f %f\n", current_time,
+ dtemp, pp->fudgetime1);
+#endif
+ return (1);
+}
+#endif /* HAVE_PPSAPI */
+#endif /* REFCLOCK */