diff options
Diffstat (limited to 'ntpd/ntp_refclock.c')
-rw-r--r-- | ntpd/ntp_refclock.c | 443 |
1 files changed, 270 insertions, 173 deletions
diff --git a/ntpd/ntp_refclock.c b/ntpd/ntp_refclock.c index a29ef085e8f2..a4d30e80b118 100644 --- a/ntpd/ntp_refclock.c +++ b/ntpd/ntp_refclock.c @@ -11,6 +11,7 @@ #include "ntp_tty.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" +#include "ntp_assert.h" #include <stdio.h> @@ -34,63 +35,57 @@ #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 + * 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 + * 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 + * clock associations. A set of utility routines is included to open * serial devices, process sample data, edit input lines to extract - * embedded timestamps and to peform various debugging functions. + * embedded timestamps 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 + * 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 containes 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 (for now only - * IPv4 addresses are used, so we need to be sure the address is it), - * where t is the clock type and u the unit. Some legacy drivers derive - * the refclockproc structure pointer from the table - * typeunit[type][unit]. This interface is strongly discouraged and may - * be abandoned in future. + * 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 MAXUNIT 4 /* max units */ #define FUDGEFAC .1 /* fudge correction factor */ #define LF 0x0a /* ASCII LF */ #ifdef PPS int fdpps; /* ppsclock legacy */ #endif /* PPS */ -int cal_enable; /* enable refclock calibrate */ -/* - * Type/unit peer index. Used to find the peer structure for control and - * debugging. When all clock drivers have been converted to new style, - * this dissapears. - */ -static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT]; +int cal_enable; /* enable refclock calibrate */ /* * Forward declarations */ #ifdef QSORT_USES_VOID_P -static int refclock_cmpl_fp P((const void *, const void *)); +static int refclock_cmpl_fp (const void *, const void *); #else -static int refclock_cmpl_fp P((const double *, const double *)); +static int refclock_cmpl_fp (const double *, const double *); #endif /* QSORT_USES_VOID_P */ -static int refclock_sample P((struct refclockproc *)); +static int refclock_sample (struct refclockproc *); /* @@ -113,58 +108,36 @@ refclock_report( return; switch (code) { - case CEVNT_NOMINAL: - break; - case CEVNT_TIMEOUT: - pp->noreply++; - break; - - case CEVNT_BADREPLY: - pp->badformat++; - break; + case CEVNT_TIMEOUT: + pp->noreply++; + break; - case CEVNT_FAULT: - break; + case CEVNT_BADREPLY: + pp->badformat++; + break; - case CEVNT_PROP: - break; + case CEVNT_FAULT: + break; - case CEVNT_BADDATE: - case CEVNT_BADTIME: - pp->baddata++; - break; + case CEVNT_BADDATE: + case CEVNT_BADTIME: + pp->baddata++; + break; - default: - /* shouldn't happen */ - break; + default: + /* ignore others */ + break; } - + if (pp->lastevent < 15) + pp->lastevent++; if (pp->currentstatus != code) { pp->currentstatus = (u_char)code; - - /* RFC1305: copy only iff not CEVNT_NOMINAL */ - if (code != CEVNT_NOMINAL) - pp->lastevent = (u_char)code; - - if (code == CEVNT_FAULT) - msyslog(LOG_ERR, - "clock %s event '%s' (0x%02x)", - refnumtoa(&peer->srcadr), - ceventstr(code), code); - else { - NLOG(NLOG_CLOCKEVENT) - msyslog(LOG_INFO, - "clock %s event '%s' (0x%02x)", - refnumtoa(&peer->srcadr), - ceventstr(code), code); - } - - /* RFC1305: post peer clock event */ - report_event(EVNT_PEERCLOCK, peer); + report_event(PEVNT_CLOCK, peer, ceventstr(code)); } } + /* * init_refclock - initialize the reference clock drivers * @@ -175,14 +148,11 @@ refclock_report( void init_refclock(void) { - int i, j; + int i; - for (i = 0; i < (int)num_refclock_conf; i++) { + for (i = 0; i < (int)num_refclock_conf; i++) if (refclock_conf[i]->clock_init != noentry) (refclock_conf[i]->clock_init)(); - for (j = 0; j < MAXUNIT; j++) - typeunit[i][j] = 0; - } } @@ -211,12 +181,6 @@ refclock_newpeer( * Check for valid clock address. If already running, shut it * down first. */ - if (peer->srcadr.ss_family != AF_INET) { - msyslog(LOG_ERR, - "refclock_newpeer: clock address %s invalid, address family not implemented for refclock", - stoa(&peer->srcadr)); - return (0); - } if (!ISREFCLOCKADR(&peer->srcadr)) { msyslog(LOG_ERR, "refclock_newpeer: clock address %s invalid", @@ -225,7 +189,7 @@ refclock_newpeer( } clktype = (u_char)REFCLOCKTYPE(&peer->srcadr); unit = REFCLOCKUNIT(&peer->srcadr); - if (clktype >= num_refclock_conf || unit >= MAXUNIT || + if (clktype >= num_refclock_conf || refclock_conf[clktype]->clock_start == noentry) { msyslog(LOG_ERR, "refclock_newpeer: clock type %d invalid\n", @@ -236,12 +200,8 @@ refclock_newpeer( /* * Allocate and initialize interface structure */ - pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc)); - if (pp == NULL) - return (0); - - memset((char *)pp, 0, sizeof(struct refclockproc)); - typeunit[clktype][unit] = peer; + pp = emalloc(sizeof(*pp)); + memset(pp, 0, sizeof(*pp)); peer->procptr = pp; /* @@ -249,7 +209,7 @@ refclock_newpeer( */ peer->refclktype = clktype; peer->refclkunit = (u_char)unit; - peer->flags |= FLAG_REFCLOCK | FLAG_FIXPOLL; + peer->flags |= FLAG_REFCLOCK; peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_REFCLOCK; peer->ppoll = peer->maxpoll; @@ -297,7 +257,7 @@ refclock_unpeer( * Wiggle the driver to release its resources, then give back * the interface structure. */ - if (!peer->procptr) + if (NULL == peer->procptr) return; clktype = peer->refclktype; @@ -305,7 +265,7 @@ refclock_unpeer( if (refclock_conf[clktype]->clock_shutdown != noentry) (refclock_conf[clktype]->clock_shutdown)(unit, peer); free(peer->procptr); - peer->procptr = 0; + peer->procptr = NULL; } @@ -365,19 +325,17 @@ refclock_transmit( * Update reachability and poll variables like the * network code. */ - oreach = peer->reach; + 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(EVNT_UNREACH, peer); + report_event(PEVNT_UNREACH, peer, NULL); peer->timereachable = current_time; } } else { - if (!(oreach & 0x07)) { - clock_filter(peer, 0., 0., MAXDISPERSE); - clock_select(); - } if (peer->flags & FLAG_BURST) peer->burst = NSTAGE; } @@ -393,7 +351,6 @@ refclock_transmit( /* * Compare two doubles - used with qsort() */ -#ifdef QSORT_USES_VOID_P static int refclock_cmpl_fp( const void *p1, @@ -404,30 +361,11 @@ refclock_cmpl_fp( const double *dp2 = (const double *)p2; if (*dp1 < *dp2) - return (-1); - - if (*dp1 > *dp2) - return (1); - - return (0); -} - -#else -static int -refclock_cmpl_fp( - const double *dp1, - const double *dp2 - ) -{ - if (*dp1 < *dp2) - return (-1); - + return -1; if (*dp1 > *dp2) - return (1); - - return (0); + return 1; + return 0; } -#endif /* QSORT_USES_VOID_P */ /* @@ -458,6 +396,7 @@ refclock_process_offset( /* * 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, @@ -472,8 +411,9 @@ refclock_process_offset( * zero and the fraction for pp->lastrec is set to the PPS offset. */ int -refclock_process( - struct refclockproc *pp /* refclock structure pointer */ +refclock_process_f( + struct refclockproc *pp, /* refclock structure pointer */ + double fudge ) { l_fp offset, ltemp; @@ -493,12 +433,20 @@ refclock_process( offset.l_uf = 0; DTOLFP(pp->nsec / 1e9, <emp); L_ADD(&offset, <emp); - refclock_process_offset(pp, offset, pp->lastrec, - pp->fudgetime1); + 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 * @@ -514,7 +462,7 @@ refclock_sample( struct refclockproc *pp /* refclock structure pointer */ ) { - int i, j, k, m, n; + size_t i, j, k, m, n; double off[MAXSTAGE]; double offset; @@ -532,13 +480,7 @@ refclock_sample( return (0); if (n > 1) - qsort( -#ifdef QSORT_USES_VOID_P - (void *) -#else - (char *) -#endif - off, (size_t)n, sizeof(double), refclock_cmpl_fp); + qsort((void *)off, n, sizeof(off[0]), refclock_cmpl_fp); /* * Reject the furthest from the median of the samples until @@ -572,7 +514,7 @@ refclock_sample( "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n", n, pp->offset, pp->disp, pp->jitter); #endif - return (n); + return (int)n; } @@ -611,30 +553,23 @@ refclock_receive( peer->received++; peer->timereceived = current_time; if (!peer->reach) { - report_event(EVNT_REACH, peer); + report_event(PEVNT_REACH, peer, NULL); peer->timereachable = current_time; } peer->reach |= 1; peer->reftime = pp->lastref; - peer->org = pp->lastrec; - peer->rootdispersion = pp->disp; - get_systime(&peer->rec); + 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); - record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), - peer->offset, peer->delay, clock_phi * (current_time - - peer->epoch), peer->jitter); - if (cal_enable && last_offset < MINDISPERSE) { -#ifdef KERNEL_PLL - if (peer != sys_peer || pll_status & STA_PPSTIME) -#else - if (peer != sys_peer) -#endif /* KERNEL_PLL */ + 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; - else - pp->fudgetime1 -= pp->fudgetime1 * FUDGEFAC; } } @@ -781,6 +716,9 @@ refclock_open( { int fd; int omode; +#ifdef O_NONBLOCK + char trash[128]; /* litter bin for old input data */ +#endif /* * Open serial port and set default options @@ -798,6 +736,7 @@ refclock_open( msyslog(LOG_ERR, "refclock_open %s: %m", dev); return (0); } + NTP_INSIST(fd != 0); if (!refclock_setup(fd, speed, lflags)) { close(fd); return (0); @@ -806,9 +745,20 @@ refclock_open( close(fd); return (0); } +#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 */ @@ -900,6 +850,14 @@ refclock_setup( "refclock_setup fd %d TCSANOW: %m", fd); return (0); } + + /* + * 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 @@ -1064,7 +1022,7 @@ refclock_ioctl( */ void refclock_control( - struct sockaddr_storage *srcadr, + sockaddr_u *srcadr, struct refclockstat *in, struct refclockstat *out ) @@ -1077,22 +1035,15 @@ refclock_control( /* * Check for valid address and running peer */ - if (srcadr->ss_family != AF_INET) - return; - if (!ISREFCLOCKADR(srcadr)) return; clktype = (u_char)REFCLOCKTYPE(srcadr); unit = REFCLOCKUNIT(srcadr); - if (clktype >= num_refclock_conf || unit >= MAXUNIT) - return; - peer = typeunit[clktype][unit]; - if (peer == NULL) - return; + peer = findexistingpeer(srcadr, NULL, -1, 0); - if (peer->procptr == NULL) + if (NULL == peer || NULL == peer->procptr) return; pp = peer->procptr; @@ -1149,7 +1100,7 @@ refclock_control( out->currentstatus = pp->currentstatus; out->type = pp->type; out->clockdesc = pp->clockdesc; - out->lencode = pp->lencode; + out->lencode = (u_short)pp->lencode; out->p_lastcode = pp->a_lastcode; } @@ -1170,32 +1121,28 @@ refclock_control( */ void refclock_buginfo( - struct sockaddr_storage *srcadr, /* clock address */ + sockaddr_u *srcadr, /* clock address */ struct refclockbug *bug /* output structure */ ) { struct peer *peer; struct refclockproc *pp; - u_char clktype; + int clktype; int unit; - int i; + unsigned u; /* * Check for valid address and peer structure */ - if (srcadr->ss_family != AF_INET) - return; - if (!ISREFCLOCKADR(srcadr)) return; clktype = (u_char) REFCLOCKTYPE(srcadr); unit = REFCLOCKUNIT(srcadr); - if (clktype >= num_refclock_conf || unit >= MAXUNIT) - return; - peer = typeunit[clktype][unit]; - if (peer == NULL) + peer = findexistingpeer(srcadr, NULL, -1, 0); + + if (NULL == peer || NULL == peer->procptr) return; pp = peer->procptr; @@ -1216,8 +1163,8 @@ refclock_buginfo( bug->stimes = 0xfffffffc; bug->times[0] = pp->lastref; bug->times[1] = pp->lastrec; - for (i = 2; i < (int)bug->ntimes; i++) - DTOLFP(pp->filter[i - 2], &bug->times[i]); + for (u = 2; u < bug->ntimes; u++) + DTOLFP(pp->filter[u - 2], &bug->times[u]); /* * Give the stuff to the clock @@ -1226,4 +1173,154 @@ refclock_buginfo( (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 */ + ) +{ + memset(&ap->pps_params, 0, sizeof(pps_params_t)); + 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 (mode & CLK_FLAG3) { + if (time_pps_kcbind(ap->handle, PPS_KC_HARDPPS, + ap->pps_params.mode & ~PPS_TSFMT_TSPEC, + PPS_TSFMT_TSPEC) < 0) { + if (errno != EOPNOTSUPP) { + msyslog(LOG_ERR, + "refclock_params: time_pps_kcbind: %m"); + return (0); + } + } + pps_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; + memset(&pps_info, 0, sizeof(pps_info_t)); + 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); + + /* + * There can be zero, one or two PPS pulses between polls, + * depending on the poll interval relative to the PPS interval. + * The pulse must be newer and within the range gate relative + * to the last pulse. + */ + if (ap->ts.tv_sec <= timeout.tv_sec || abs(ap->ts.tv_nsec - + timeout.tv_nsec) > RANGEGATE) + 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 */ |