Annotation of embedaddon/ntp/ntpd/ntp_refclock.c, revision 1.1
1.1 ! misho 1: /*
! 2: * ntp_refclock - processing support for reference clocks
! 3: */
! 4: #ifdef HAVE_CONFIG_H
! 5: # include <config.h>
! 6: #endif
! 7:
! 8: #include "ntpd.h"
! 9: #include "ntp_io.h"
! 10: #include "ntp_unixtime.h"
! 11: #include "ntp_tty.h"
! 12: #include "ntp_refclock.h"
! 13: #include "ntp_stdlib.h"
! 14: #include "ntp_assert.h"
! 15:
! 16: #include <stdio.h>
! 17:
! 18: #ifdef HAVE_SYS_IOCTL_H
! 19: # include <sys/ioctl.h>
! 20: #endif /* HAVE_SYS_IOCTL_H */
! 21:
! 22: #ifdef REFCLOCK
! 23:
! 24: #ifdef TTYCLK
! 25: # ifdef HAVE_SYS_CLKDEFS_H
! 26: # include <sys/clkdefs.h>
! 27: # include <stropts.h>
! 28: # endif
! 29: # ifdef HAVE_SYS_SIO_H
! 30: # include <sys/sio.h>
! 31: # endif
! 32: #endif /* TTYCLK */
! 33:
! 34: #ifdef KERNEL_PLL
! 35: #include "ntp_syscall.h"
! 36: #endif /* KERNEL_PLL */
! 37:
! 38: #ifdef HAVE_PPSAPI
! 39: #include "ppsapi_timepps.h"
! 40: #include "refclock_atom.h"
! 41: #endif /* HAVE_PPSAPI */
! 42:
! 43: /*
! 44: * Reference clock support is provided here by maintaining the fiction
! 45: * that the clock is actually a peer. As no packets are exchanged with
! 46: * a reference clock, however, we replace the transmit, receive and
! 47: * packet procedures with separate code to simulate them. Routines
! 48: * refclock_transmit() and refclock_receive() maintain the peer
! 49: * variables in a state analogous to an actual peer and pass reference
! 50: * clock data on through the filters. Routines refclock_peer() and
! 51: * refclock_unpeer() are called to initialize and terminate reference
! 52: * clock associations. A set of utility routines is included to open
! 53: * serial devices, process sample data, edit input lines to extract
! 54: * embedded timestamps and to perform various debugging functions.
! 55: *
! 56: * The main interface used by these routines is the refclockproc
! 57: * structure, which contains for most drivers the decimal equivalants
! 58: * of the year, day, month, hour, second and millisecond/microsecond
! 59: * decoded from the ASCII timecode. Additional information includes
! 60: * the receive timestamp, exception report, statistics tallies, etc.
! 61: * In addition, there may be a driver-specific unit structure used for
! 62: * local control of the device.
! 63: *
! 64: * The support routines are passed a pointer to the peer structure,
! 65: * which is used for all peer-specific processing and contains a
! 66: * pointer to the refclockproc structure, which in turn contains a
! 67: * pointer to the unit structure, if used. The peer structure is
! 68: * identified by an interface address in the dotted quad form
! 69: * 127.127.t.u, where t is the clock type and u the unit.
! 70: */
! 71: #define FUDGEFAC .1 /* fudge correction factor */
! 72: #define LF 0x0a /* ASCII LF */
! 73:
! 74: #ifdef PPS
! 75: int fdpps; /* ppsclock legacy */
! 76: #endif /* PPS */
! 77:
! 78: int cal_enable; /* enable refclock calibrate */
! 79:
! 80: /*
! 81: * Forward declarations
! 82: */
! 83: #ifdef QSORT_USES_VOID_P
! 84: static int refclock_cmpl_fp (const void *, const void *);
! 85: #else
! 86: static int refclock_cmpl_fp (const double *, const double *);
! 87: #endif /* QSORT_USES_VOID_P */
! 88: static int refclock_sample (struct refclockproc *);
! 89:
! 90:
! 91: /*
! 92: * refclock_report - note the occurance of an event
! 93: *
! 94: * This routine presently just remembers the report and logs it, but
! 95: * does nothing heroic for the trap handler. It tries to be a good
! 96: * citizen and bothers the system log only if things change.
! 97: */
! 98: void
! 99: refclock_report(
! 100: struct peer *peer,
! 101: int code
! 102: )
! 103: {
! 104: struct refclockproc *pp;
! 105:
! 106: pp = peer->procptr;
! 107: if (pp == NULL)
! 108: return;
! 109:
! 110: switch (code) {
! 111:
! 112: case CEVNT_TIMEOUT:
! 113: pp->noreply++;
! 114: break;
! 115:
! 116: case CEVNT_BADREPLY:
! 117: pp->badformat++;
! 118: break;
! 119:
! 120: case CEVNT_FAULT:
! 121: break;
! 122:
! 123: case CEVNT_BADDATE:
! 124: case CEVNT_BADTIME:
! 125: pp->baddata++;
! 126: break;
! 127:
! 128: default:
! 129: /* ignore others */
! 130: break;
! 131: }
! 132: if (pp->lastevent < 15)
! 133: pp->lastevent++;
! 134: if (pp->currentstatus != code) {
! 135: pp->currentstatus = (u_char)code;
! 136: report_event(PEVNT_CLOCK, peer, ceventstr(code));
! 137: }
! 138: }
! 139:
! 140:
! 141: /*
! 142: * init_refclock - initialize the reference clock drivers
! 143: *
! 144: * This routine calls each of the drivers in turn to initialize internal
! 145: * variables, if necessary. Most drivers have nothing to say at this
! 146: * point.
! 147: */
! 148: void
! 149: init_refclock(void)
! 150: {
! 151: int i;
! 152:
! 153: for (i = 0; i < (int)num_refclock_conf; i++)
! 154: if (refclock_conf[i]->clock_init != noentry)
! 155: (refclock_conf[i]->clock_init)();
! 156: }
! 157:
! 158:
! 159: /*
! 160: * refclock_newpeer - initialize and start a reference clock
! 161: *
! 162: * This routine allocates and initializes the interface structure which
! 163: * supports a reference clock in the form of an ordinary NTP peer. A
! 164: * driver-specific support routine completes the initialization, if
! 165: * used. Default peer variables which identify the clock and establish
! 166: * its reference ID and stratum are set here. It returns one if success
! 167: * and zero if the clock address is invalid or already running,
! 168: * insufficient resources are available or the driver declares a bum
! 169: * rap.
! 170: */
! 171: int
! 172: refclock_newpeer(
! 173: struct peer *peer /* peer structure pointer */
! 174: )
! 175: {
! 176: struct refclockproc *pp;
! 177: u_char clktype;
! 178: int unit;
! 179:
! 180: /*
! 181: * Check for valid clock address. If already running, shut it
! 182: * down first.
! 183: */
! 184: if (!ISREFCLOCKADR(&peer->srcadr)) {
! 185: msyslog(LOG_ERR,
! 186: "refclock_newpeer: clock address %s invalid",
! 187: stoa(&peer->srcadr));
! 188: return (0);
! 189: }
! 190: clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);
! 191: unit = REFCLOCKUNIT(&peer->srcadr);
! 192: if (clktype >= num_refclock_conf ||
! 193: refclock_conf[clktype]->clock_start == noentry) {
! 194: msyslog(LOG_ERR,
! 195: "refclock_newpeer: clock type %d invalid\n",
! 196: clktype);
! 197: return (0);
! 198: }
! 199:
! 200: /*
! 201: * Allocate and initialize interface structure
! 202: */
! 203: pp = emalloc(sizeof(*pp));
! 204: memset(pp, 0, sizeof(*pp));
! 205: peer->procptr = pp;
! 206:
! 207: /*
! 208: * Initialize structures
! 209: */
! 210: peer->refclktype = clktype;
! 211: peer->refclkunit = (u_char)unit;
! 212: peer->flags |= FLAG_REFCLOCK;
! 213: peer->leap = LEAP_NOTINSYNC;
! 214: peer->stratum = STRATUM_REFCLOCK;
! 215: peer->ppoll = peer->maxpoll;
! 216: pp->type = clktype;
! 217: pp->timestarted = current_time;
! 218:
! 219: /*
! 220: * Set peer.pmode based on the hmode. For appearances only.
! 221: */
! 222: switch (peer->hmode) {
! 223: case MODE_ACTIVE:
! 224: peer->pmode = MODE_PASSIVE;
! 225: break;
! 226:
! 227: default:
! 228: peer->pmode = MODE_SERVER;
! 229: break;
! 230: }
! 231:
! 232: /*
! 233: * Do driver dependent initialization. The above defaults
! 234: * can be wiggled, then finish up for consistency.
! 235: */
! 236: if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
! 237: refclock_unpeer(peer);
! 238: return (0);
! 239: }
! 240: peer->refid = pp->refid;
! 241: return (1);
! 242: }
! 243:
! 244:
! 245: /*
! 246: * refclock_unpeer - shut down a clock
! 247: */
! 248: void
! 249: refclock_unpeer(
! 250: struct peer *peer /* peer structure pointer */
! 251: )
! 252: {
! 253: u_char clktype;
! 254: int unit;
! 255:
! 256: /*
! 257: * Wiggle the driver to release its resources, then give back
! 258: * the interface structure.
! 259: */
! 260: if (NULL == peer->procptr)
! 261: return;
! 262:
! 263: clktype = peer->refclktype;
! 264: unit = peer->refclkunit;
! 265: if (refclock_conf[clktype]->clock_shutdown != noentry)
! 266: (refclock_conf[clktype]->clock_shutdown)(unit, peer);
! 267: free(peer->procptr);
! 268: peer->procptr = NULL;
! 269: }
! 270:
! 271:
! 272: /*
! 273: * refclock_timer - called once per second for housekeeping.
! 274: */
! 275: void
! 276: refclock_timer(
! 277: struct peer *peer /* peer structure pointer */
! 278: )
! 279: {
! 280: u_char clktype;
! 281: int unit;
! 282:
! 283: clktype = peer->refclktype;
! 284: unit = peer->refclkunit;
! 285: if (refclock_conf[clktype]->clock_timer != noentry)
! 286: (refclock_conf[clktype]->clock_timer)(unit, peer);
! 287: }
! 288:
! 289:
! 290: /*
! 291: * refclock_transmit - simulate the transmit procedure
! 292: *
! 293: * This routine implements the NTP transmit procedure for a reference
! 294: * clock. This provides a mechanism to call the driver at the NTP poll
! 295: * interval, as well as provides a reachability mechanism to detect a
! 296: * broken radio or other madness.
! 297: */
! 298: void
! 299: refclock_transmit(
! 300: struct peer *peer /* peer structure pointer */
! 301: )
! 302: {
! 303: u_char clktype;
! 304: int unit;
! 305:
! 306: clktype = peer->refclktype;
! 307: unit = peer->refclkunit;
! 308: peer->sent++;
! 309: get_systime(&peer->xmt);
! 310:
! 311: /*
! 312: * This is a ripoff of the peer transmit routine, but
! 313: * specialized for reference clocks. We do a little less
! 314: * protocol here and call the driver-specific transmit routine.
! 315: */
! 316: if (peer->burst == 0) {
! 317: u_char oreach;
! 318: #ifdef DEBUG
! 319: if (debug)
! 320: printf("refclock_transmit: at %ld %s\n",
! 321: current_time, stoa(&(peer->srcadr)));
! 322: #endif
! 323:
! 324: /*
! 325: * Update reachability and poll variables like the
! 326: * network code.
! 327: */
! 328: oreach = peer->reach & 0xfe;
! 329: peer->reach <<= 1;
! 330: if (!(peer->reach & 0x0f))
! 331: clock_filter(peer, 0., 0., MAXDISPERSE);
! 332: peer->outdate = current_time;
! 333: if (!peer->reach) {
! 334: if (oreach) {
! 335: report_event(PEVNT_UNREACH, peer, NULL);
! 336: peer->timereachable = current_time;
! 337: }
! 338: } else {
! 339: if (peer->flags & FLAG_BURST)
! 340: peer->burst = NSTAGE;
! 341: }
! 342: } else {
! 343: peer->burst--;
! 344: }
! 345: if (refclock_conf[clktype]->clock_poll != noentry)
! 346: (refclock_conf[clktype]->clock_poll)(unit, peer);
! 347: poll_update(peer, peer->hpoll);
! 348: }
! 349:
! 350:
! 351: /*
! 352: * Compare two doubles - used with qsort()
! 353: */
! 354: static int
! 355: refclock_cmpl_fp(
! 356: const void *p1,
! 357: const void *p2
! 358: )
! 359: {
! 360: const double *dp1 = (const double *)p1;
! 361: const double *dp2 = (const double *)p2;
! 362:
! 363: if (*dp1 < *dp2)
! 364: return -1;
! 365: if (*dp1 > *dp2)
! 366: return 1;
! 367: return 0;
! 368: }
! 369:
! 370:
! 371: /*
! 372: * refclock_process_offset - update median filter
! 373: *
! 374: * This routine uses the given offset and timestamps to construct a new
! 375: * entry in the median filter circular buffer. Samples that overflow the
! 376: * filter are quietly discarded.
! 377: */
! 378: void
! 379: refclock_process_offset(
! 380: struct refclockproc *pp, /* refclock structure pointer */
! 381: l_fp lasttim, /* last timecode timestamp */
! 382: l_fp lastrec, /* last receive timestamp */
! 383: double fudge
! 384: )
! 385: {
! 386: l_fp lftemp;
! 387: double doffset;
! 388:
! 389: pp->lastrec = lastrec;
! 390: lftemp = lasttim;
! 391: L_SUB(&lftemp, &lastrec);
! 392: LFPTOD(&lftemp, doffset);
! 393: SAMPLE(doffset + fudge);
! 394: }
! 395:
! 396:
! 397: /*
! 398: * refclock_process - process a sample from the clock
! 399: * refclock_process_f - refclock_process with other than time1 fudge
! 400: *
! 401: * This routine converts the timecode in the form days, hours, minutes,
! 402: * seconds and milliseconds/microseconds to internal timestamp format,
! 403: * then constructs a new entry in the median filter circular buffer.
! 404: * Return success (1) if the data are correct and consistent with the
! 405: * converntional calendar.
! 406: *
! 407: * Important for PPS users: Normally, the pp->lastrec is set to the
! 408: * system time when the on-time character is received and the pp->year,
! 409: * ..., pp->second decoded and the seconds fraction pp->nsec in
! 410: * nanoseconds). When a PPS offset is available, pp->nsec is forced to
! 411: * zero and the fraction for pp->lastrec is set to the PPS offset.
! 412: */
! 413: int
! 414: refclock_process_f(
! 415: struct refclockproc *pp, /* refclock structure pointer */
! 416: double fudge
! 417: )
! 418: {
! 419: l_fp offset, ltemp;
! 420:
! 421: /*
! 422: * Compute the timecode timestamp from the days, hours, minutes,
! 423: * seconds and milliseconds/microseconds of the timecode. Use
! 424: * clocktime() for the aggregate seconds and the msec/usec for
! 425: * the fraction, when present. Note that this code relies on the
! 426: * filesystem time for the years and does not use the years of
! 427: * the timecode.
! 428: */
! 429: if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
! 430: pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))
! 431: return (0);
! 432:
! 433: offset.l_uf = 0;
! 434: DTOLFP(pp->nsec / 1e9, <emp);
! 435: L_ADD(&offset, <emp);
! 436: refclock_process_offset(pp, offset, pp->lastrec, fudge);
! 437: return (1);
! 438: }
! 439:
! 440:
! 441: int
! 442: refclock_process(
! 443: struct refclockproc *pp /* refclock structure pointer */
! 444: )
! 445: {
! 446: return refclock_process_f(pp, pp->fudgetime1);
! 447: }
! 448:
! 449:
! 450: /*
! 451: * refclock_sample - process a pile of samples from the clock
! 452: *
! 453: * This routine implements a recursive median filter to suppress spikes
! 454: * in the data, as well as determine a performance statistic. It
! 455: * calculates the mean offset and RMS jitter. A time adjustment
! 456: * fudgetime1 can be added to the final offset to compensate for various
! 457: * systematic errors. The routine returns the number of samples
! 458: * processed, which could be zero.
! 459: */
! 460: static int
! 461: refclock_sample(
! 462: struct refclockproc *pp /* refclock structure pointer */
! 463: )
! 464: {
! 465: size_t i, j, k, m, n;
! 466: double off[MAXSTAGE];
! 467: double offset;
! 468:
! 469: /*
! 470: * Copy the raw offsets and sort into ascending order. Don't do
! 471: * anything if the buffer is empty.
! 472: */
! 473: n = 0;
! 474: while (pp->codeproc != pp->coderecv) {
! 475: pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;
! 476: off[n] = pp->filter[pp->codeproc];
! 477: n++;
! 478: }
! 479: if (n == 0)
! 480: return (0);
! 481:
! 482: if (n > 1)
! 483: qsort((void *)off, n, sizeof(off[0]), refclock_cmpl_fp);
! 484:
! 485: /*
! 486: * Reject the furthest from the median of the samples until
! 487: * approximately 60 percent of the samples remain.
! 488: */
! 489: i = 0; j = n;
! 490: m = n - (n * 4) / 10;
! 491: while ((j - i) > m) {
! 492: offset = off[(j + i) / 2];
! 493: if (off[j - 1] - offset < offset - off[i])
! 494: i++; /* reject low end */
! 495: else
! 496: j--; /* reject high end */
! 497: }
! 498:
! 499: /*
! 500: * Determine the offset and jitter.
! 501: */
! 502: pp->offset = 0;
! 503: pp->jitter = 0;
! 504: for (k = i; k < j; k++) {
! 505: pp->offset += off[k];
! 506: if (k > i)
! 507: pp->jitter += SQUARE(off[k] - off[k - 1]);
! 508: }
! 509: pp->offset /= m;
! 510: pp->jitter = max(SQRT(pp->jitter / m), LOGTOD(sys_precision));
! 511: #ifdef DEBUG
! 512: if (debug)
! 513: printf(
! 514: "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",
! 515: n, pp->offset, pp->disp, pp->jitter);
! 516: #endif
! 517: return (int)n;
! 518: }
! 519:
! 520:
! 521: /*
! 522: * refclock_receive - simulate the receive and packet procedures
! 523: *
! 524: * This routine simulates the NTP receive and packet procedures for a
! 525: * reference clock. This provides a mechanism in which the ordinary NTP
! 526: * filter, selection and combining algorithms can be used to suppress
! 527: * misbehaving radios and to mitigate between them when more than one is
! 528: * available for backup.
! 529: */
! 530: void
! 531: refclock_receive(
! 532: struct peer *peer /* peer structure pointer */
! 533: )
! 534: {
! 535: struct refclockproc *pp;
! 536:
! 537: #ifdef DEBUG
! 538: if (debug)
! 539: printf("refclock_receive: at %lu %s\n",
! 540: current_time, stoa(&peer->srcadr));
! 541: #endif
! 542:
! 543: /*
! 544: * Do a little sanity dance and update the peer structure. Groom
! 545: * the median filter samples and give the data to the clock
! 546: * filter.
! 547: */
! 548: pp = peer->procptr;
! 549: peer->leap = pp->leap;
! 550: if (peer->leap == LEAP_NOTINSYNC)
! 551: return;
! 552:
! 553: peer->received++;
! 554: peer->timereceived = current_time;
! 555: if (!peer->reach) {
! 556: report_event(PEVNT_REACH, peer, NULL);
! 557: peer->timereachable = current_time;
! 558: }
! 559: peer->reach |= 1;
! 560: peer->reftime = pp->lastref;
! 561: peer->aorg = pp->lastrec;
! 562: peer->rootdisp = pp->disp;
! 563: get_systime(&peer->dst);
! 564: if (!refclock_sample(pp))
! 565: return;
! 566:
! 567: clock_filter(peer, pp->offset, 0., pp->jitter);
! 568: if (cal_enable && fabs(last_offset) < sys_mindisp && sys_peer !=
! 569: NULL) {
! 570: if (sys_peer->refclktype == REFCLK_ATOM_PPS &&
! 571: peer->refclktype != REFCLK_ATOM_PPS)
! 572: pp->fudgetime1 -= pp->offset * FUDGEFAC;
! 573: }
! 574: }
! 575:
! 576:
! 577: /*
! 578: * refclock_gtlin - groom next input line and extract timestamp
! 579: *
! 580: * This routine processes the timecode received from the clock and
! 581: * strips the parity bit and control characters. It returns the number
! 582: * of characters in the line followed by a NULL character ('\0'), which
! 583: * is not included in the count. In case of an empty line, the previous
! 584: * line is preserved.
! 585: */
! 586: int
! 587: refclock_gtlin(
! 588: struct recvbuf *rbufp, /* receive buffer pointer */
! 589: char *lineptr, /* current line pointer */
! 590: int bmax, /* remaining characters in line */
! 591: l_fp *tsptr /* pointer to timestamp returned */
! 592: )
! 593: {
! 594: char s[BMAX];
! 595: char *dpt, *dpend, *dp;
! 596:
! 597: dpt = s;
! 598: dpend = s + refclock_gtraw(rbufp, s, BMAX - 1, tsptr);
! 599: if (dpend - dpt > bmax - 1)
! 600: dpend = dpt + bmax - 1;
! 601: for (dp = lineptr; dpt < dpend; dpt++) {
! 602: char c;
! 603:
! 604: c = *dpt & 0x7f;
! 605: if (c >= 0x20 && c < 0x7f)
! 606: *dp++ = c;
! 607: }
! 608: if (dp == lineptr)
! 609: return (0);
! 610:
! 611: *dp = '\0';
! 612: return (dp - lineptr);
! 613: }
! 614:
! 615:
! 616: /*
! 617: * refclock_gtraw - get next line/chunk of data
! 618: *
! 619: * This routine returns the raw data received from the clock in both
! 620: * canonical or raw modes. The terminal interface routines map CR to LF.
! 621: * In canonical mode this results in two lines, one containing data
! 622: * followed by LF and another containing only LF. In raw mode the
! 623: * interface routines can deliver arbitraty chunks of data from one
! 624: * character to a maximum specified by the calling routine. In either
! 625: * mode the routine returns the number of characters in the line
! 626: * followed by a NULL character ('\0'), which is not included in the
! 627: * count.
! 628: *
! 629: * If a timestamp is present in the timecode, as produced by the tty_clk
! 630: * STREAMS module, it returns that as the timestamp; otherwise, it
! 631: * returns the buffer timestamp.
! 632: */
! 633: int
! 634: refclock_gtraw(
! 635: struct recvbuf *rbufp, /* receive buffer pointer */
! 636: char *lineptr, /* current line pointer */
! 637: int bmax, /* remaining characters in line */
! 638: l_fp *tsptr /* pointer to timestamp returned */
! 639: )
! 640: {
! 641: char *dpt, *dpend, *dp;
! 642: l_fp trtmp, tstmp;
! 643: int i;
! 644:
! 645: /*
! 646: * Check for the presence of a timestamp left by the tty_clock
! 647: * module and, if present, use that instead of the buffer
! 648: * timestamp captured by the I/O routines. We recognize a
! 649: * timestamp by noting its value is earlier than the buffer
! 650: * timestamp, but not more than one second earlier.
! 651: */
! 652: dpt = (char *)rbufp->recv_buffer;
! 653: dpend = dpt + rbufp->recv_length;
! 654: trtmp = rbufp->recv_time;
! 655: if (dpend >= dpt + 8) {
! 656: if (buftvtots(dpend - 8, &tstmp)) {
! 657: L_SUB(&trtmp, &tstmp);
! 658: if (trtmp.l_ui == 0) {
! 659: #ifdef DEBUG
! 660: if (debug > 1) {
! 661: printf(
! 662: "refclock_gtlin: fd %d ldisc %s",
! 663: rbufp->fd, lfptoa(&trtmp,
! 664: 6));
! 665: get_systime(&trtmp);
! 666: L_SUB(&trtmp, &tstmp);
! 667: printf(" sigio %s\n",
! 668: lfptoa(&trtmp, 6));
! 669: }
! 670: #endif
! 671: dpend -= 8;
! 672: trtmp = tstmp;
! 673: } else
! 674: trtmp = rbufp->recv_time;
! 675: }
! 676: }
! 677:
! 678: /*
! 679: * Copy the raw buffer to the user string. The string is padded
! 680: * with a NULL, which is not included in the character count.
! 681: */
! 682: if (dpend - dpt > bmax - 1)
! 683: dpend = dpt + bmax - 1;
! 684: for (dp = lineptr; dpt < dpend; dpt++)
! 685: *dp++ = *dpt;
! 686: *dp = '\0';
! 687: i = dp - lineptr;
! 688: #ifdef DEBUG
! 689: if (debug > 1)
! 690: printf("refclock_gtraw: fd %d time %s timecode %d %s\n",
! 691: rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
! 692: #endif
! 693: *tsptr = trtmp;
! 694: return (i);
! 695: }
! 696:
! 697:
! 698: /*
! 699: * The following code does not apply to WINNT & VMS ...
! 700: */
! 701: #if !defined SYS_VXWORKS && !defined SYS_WINNT
! 702: #if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
! 703:
! 704: /*
! 705: * refclock_open - open serial port for reference clock
! 706: *
! 707: * This routine opens a serial port for I/O and sets default options. It
! 708: * returns the file descriptor if success and zero if failure.
! 709: */
! 710: int
! 711: refclock_open(
! 712: char *dev, /* device name pointer */
! 713: u_int speed, /* serial port speed (code) */
! 714: u_int lflags /* line discipline flags */
! 715: )
! 716: {
! 717: int fd;
! 718: int omode;
! 719: #ifdef O_NONBLOCK
! 720: char trash[128]; /* litter bin for old input data */
! 721: #endif
! 722:
! 723: /*
! 724: * Open serial port and set default options
! 725: */
! 726: omode = O_RDWR;
! 727: #ifdef O_NONBLOCK
! 728: omode |= O_NONBLOCK;
! 729: #endif
! 730: #ifdef O_NOCTTY
! 731: omode |= O_NOCTTY;
! 732: #endif
! 733:
! 734: fd = open(dev, omode, 0777);
! 735: if (fd < 0) {
! 736: msyslog(LOG_ERR, "refclock_open %s: %m", dev);
! 737: return (0);
! 738: }
! 739: NTP_INSIST(fd != 0);
! 740: if (!refclock_setup(fd, speed, lflags)) {
! 741: close(fd);
! 742: return (0);
! 743: }
! 744: if (!refclock_ioctl(fd, lflags)) {
! 745: close(fd);
! 746: return (0);
! 747: }
! 748: #ifdef O_NONBLOCK
! 749: /*
! 750: * We want to make sure there is no pending trash in the input
! 751: * buffer. Since we have non-blocking IO available, this is a
! 752: * good moment to read and dump all available outdated stuff
! 753: * that might have become toxic for the driver.
! 754: */
! 755: while (read(fd, trash, sizeof(trash)) > 0 || errno == EINTR)
! 756: /*NOP*/;
! 757: #endif
! 758: return (fd);
! 759: }
! 760:
! 761:
! 762: /*
! 763: * refclock_setup - initialize terminal interface structure
! 764: */
! 765: int
! 766: refclock_setup(
! 767: int fd, /* file descriptor */
! 768: u_int speed, /* serial port speed (code) */
! 769: u_int lflags /* line discipline flags */
! 770: )
! 771: {
! 772: int i;
! 773: TTY ttyb, *ttyp;
! 774: #ifdef PPS
! 775: fdpps = fd; /* ppsclock legacy */
! 776: #endif /* PPS */
! 777:
! 778: /*
! 779: * By default, the serial line port is initialized in canonical
! 780: * (line-oriented) mode at specified line speed, 8 bits and no
! 781: * parity. LF ends the line and CR is mapped to LF. The break,
! 782: * erase and kill functions are disabled. There is a different
! 783: * section for each terminal interface, as selected at compile
! 784: * time. The flag bits can be used to set raw mode and echo.
! 785: */
! 786: ttyp = &ttyb;
! 787: #ifdef HAVE_TERMIOS
! 788:
! 789: /*
! 790: * POSIX serial line parameters (termios interface)
! 791: */
! 792: if (tcgetattr(fd, ttyp) < 0) {
! 793: msyslog(LOG_ERR,
! 794: "refclock_setup fd %d tcgetattr: %m", fd);
! 795: return (0);
! 796: }
! 797:
! 798: /*
! 799: * Set canonical mode and local connection; set specified speed,
! 800: * 8 bits and no parity; map CR to NL; ignore break.
! 801: */
! 802: if (speed) {
! 803: u_int ltemp = 0;
! 804:
! 805: ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
! 806: ttyp->c_oflag = 0;
! 807: ttyp->c_cflag = CS8 | CLOCAL | CREAD;
! 808: if (lflags & LDISC_7O1) {
! 809: /* HP Z3801A needs 7-bit, odd parity */
! 810: ttyp->c_cflag = CS7 | PARENB | PARODD | CLOCAL | CREAD;
! 811: }
! 812: cfsetispeed(&ttyb, speed);
! 813: cfsetospeed(&ttyb, speed);
! 814: for (i = 0; i < NCCS; ++i)
! 815: ttyp->c_cc[i] = '\0';
! 816:
! 817: #if defined(TIOCMGET) && !defined(SCO5_CLOCK)
! 818:
! 819: /*
! 820: * If we have modem control, check to see if modem leads
! 821: * are active; if so, set remote connection. This is
! 822: * necessary for the kernel pps mods to work.
! 823: */
! 824: if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
! 825: msyslog(LOG_ERR,
! 826: "refclock_setup fd %d TIOCMGET: %m", fd);
! 827: #ifdef DEBUG
! 828: if (debug)
! 829: printf("refclock_setup fd %d modem status: 0x%x\n",
! 830: fd, ltemp);
! 831: #endif
! 832: if (ltemp & TIOCM_DSR && lflags & LDISC_REMOTE)
! 833: ttyp->c_cflag &= ~CLOCAL;
! 834: #endif /* TIOCMGET */
! 835: }
! 836:
! 837: /*
! 838: * Set raw and echo modes. These can be changed on-fly.
! 839: */
! 840: ttyp->c_lflag = ICANON;
! 841: if (lflags & LDISC_RAW) {
! 842: ttyp->c_lflag = 0;
! 843: ttyp->c_iflag = 0;
! 844: ttyp->c_cc[VMIN] = 1;
! 845: }
! 846: if (lflags & LDISC_ECHO)
! 847: ttyp->c_lflag |= ECHO;
! 848: if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
! 849: msyslog(LOG_ERR,
! 850: "refclock_setup fd %d TCSANOW: %m", fd);
! 851: return (0);
! 852: }
! 853:
! 854: /*
! 855: * flush input and output buffers to discard any outdated stuff
! 856: * that might have become toxic for the driver. Failing to do so
! 857: * is logged, but we keep our fingers crossed otherwise.
! 858: */
! 859: if (tcflush(fd, TCIOFLUSH) < 0)
! 860: msyslog(LOG_ERR, "refclock_setup fd %d tcflush(): %m", fd);
! 861: #endif /* HAVE_TERMIOS */
! 862:
! 863: #ifdef HAVE_SYSV_TTYS
! 864:
! 865: /*
! 866: * System V serial line parameters (termio interface)
! 867: *
! 868: */
! 869: if (ioctl(fd, TCGETA, ttyp) < 0) {
! 870: msyslog(LOG_ERR,
! 871: "refclock_setup fd %d TCGETA: %m", fd);
! 872: return (0);
! 873: }
! 874:
! 875: /*
! 876: * Set canonical mode and local connection; set specified speed,
! 877: * 8 bits and no parity; map CR to NL; ignore break.
! 878: */
! 879: if (speed) {
! 880: u_int ltemp = 0;
! 881:
! 882: ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
! 883: ttyp->c_oflag = 0;
! 884: ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
! 885: for (i = 0; i < NCCS; ++i)
! 886: ttyp->c_cc[i] = '\0';
! 887:
! 888: #if defined(TIOCMGET) && !defined(SCO5_CLOCK)
! 889:
! 890: /*
! 891: * If we have modem control, check to see if modem leads
! 892: * are active; if so, set remote connection. This is
! 893: * necessary for the kernel pps mods to work.
! 894: */
! 895: if (ioctl(fd, TIOCMGET, (char *)<emp) < 0)
! 896: msyslog(LOG_ERR,
! 897: "refclock_setup fd %d TIOCMGET: %m", fd);
! 898: #ifdef DEBUG
! 899: if (debug)
! 900: printf("refclock_setup fd %d modem status: %x\n",
! 901: fd, ltemp);
! 902: #endif
! 903: if (ltemp & TIOCM_DSR)
! 904: ttyp->c_cflag &= ~CLOCAL;
! 905: #endif /* TIOCMGET */
! 906: }
! 907:
! 908: /*
! 909: * Set raw and echo modes. These can be changed on-fly.
! 910: */
! 911: ttyp->c_lflag = ICANON;
! 912: if (lflags & LDISC_RAW) {
! 913: ttyp->c_lflag = 0;
! 914: ttyp->c_iflag = 0;
! 915: ttyp->c_cc[VMIN] = 1;
! 916: }
! 917: if (ioctl(fd, TCSETA, ttyp) < 0) {
! 918: msyslog(LOG_ERR,
! 919: "refclock_setup fd %d TCSETA: %m", fd);
! 920: return (0);
! 921: }
! 922: #endif /* HAVE_SYSV_TTYS */
! 923:
! 924: #ifdef HAVE_BSD_TTYS
! 925:
! 926: /*
! 927: * 4.3bsd serial line parameters (sgttyb interface)
! 928: */
! 929: if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
! 930: msyslog(LOG_ERR,
! 931: "refclock_setup fd %d TIOCGETP: %m", fd);
! 932: return (0);
! 933: }
! 934: if (speed)
! 935: ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
! 936: ttyp->sg_flags = EVENP | ODDP | CRMOD;
! 937: if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
! 938: msyslog(LOG_ERR,
! 939: "refclock_setup TIOCSETP: %m");
! 940: return (0);
! 941: }
! 942: #endif /* HAVE_BSD_TTYS */
! 943: return(1);
! 944: }
! 945: #endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
! 946: #endif /* SYS_VXWORKS SYS_WINNT */
! 947:
! 948:
! 949: /*
! 950: * refclock_ioctl - set serial port control functions
! 951: *
! 952: * This routine attempts to hide the internal, system-specific details
! 953: * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
! 954: * (sgtty) interfaces with varying degrees of success. The routine sets
! 955: * up optional features such as tty_clk. The routine returns 1 if
! 956: * success and 0 if failure.
! 957: */
! 958: int
! 959: refclock_ioctl(
! 960: int fd, /* file descriptor */
! 961: u_int lflags /* line discipline flags */
! 962: )
! 963: {
! 964: /*
! 965: * simply return 1 if no UNIX line discipline is supported
! 966: */
! 967: #if !defined SYS_VXWORKS && !defined SYS_WINNT
! 968: #if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
! 969:
! 970: #ifdef DEBUG
! 971: if (debug)
! 972: printf("refclock_ioctl: fd %d flags 0x%x\n", fd,
! 973: lflags);
! 974: #endif
! 975: #ifdef TTYCLK
! 976:
! 977: /*
! 978: * The TTYCLK option provides timestamping at the driver level.
! 979: * It requires the tty_clk streams module and System V STREAMS
! 980: * support. If not available, don't complain.
! 981: */
! 982: if (lflags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
! 983: int rval = 0;
! 984:
! 985: if (ioctl(fd, I_PUSH, "clk") < 0) {
! 986: msyslog(LOG_NOTICE,
! 987: "refclock_ioctl fd %d I_PUSH: %m", fd);
! 988: return (0);
! 989: #ifdef CLK_SETSTR
! 990: } else {
! 991: char *str;
! 992:
! 993: if (lflags & LDISC_CLKPPS)
! 994: str = "\377";
! 995: else if (lflags & LDISC_ACTS)
! 996: str = "*";
! 997: else
! 998: str = "\n";
! 999: if (ioctl(fd, CLK_SETSTR, str) < 0) {
! 1000: msyslog(LOG_ERR,
! 1001: "refclock_ioctl fd %d CLK_SETSTR: %m", fd);
! 1002: return (0);
! 1003: }
! 1004: #endif /*CLK_SETSTR */
! 1005: }
! 1006: }
! 1007: #endif /* TTYCLK */
! 1008: #endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
! 1009: #endif /* SYS_VXWORKS SYS_WINNT */
! 1010: return (1);
! 1011: }
! 1012:
! 1013:
! 1014: /*
! 1015: * refclock_control - set and/or return clock values
! 1016: *
! 1017: * This routine is used mainly for debugging. It returns designated
! 1018: * values from the interface structure that can be displayed using
! 1019: * ntpdc and the clockstat command. It can also be used to initialize
! 1020: * configuration variables, such as fudgetimes, fudgevalues, reference
! 1021: * ID and stratum.
! 1022: */
! 1023: void
! 1024: refclock_control(
! 1025: sockaddr_u *srcadr,
! 1026: struct refclockstat *in,
! 1027: struct refclockstat *out
! 1028: )
! 1029: {
! 1030: struct peer *peer;
! 1031: struct refclockproc *pp;
! 1032: u_char clktype;
! 1033: int unit;
! 1034:
! 1035: /*
! 1036: * Check for valid address and running peer
! 1037: */
! 1038: if (!ISREFCLOCKADR(srcadr))
! 1039: return;
! 1040:
! 1041: clktype = (u_char)REFCLOCKTYPE(srcadr);
! 1042: unit = REFCLOCKUNIT(srcadr);
! 1043:
! 1044: peer = findexistingpeer(srcadr, NULL, -1, 0);
! 1045:
! 1046: if (NULL == peer || NULL == peer->procptr)
! 1047: return;
! 1048:
! 1049: pp = peer->procptr;
! 1050:
! 1051: /*
! 1052: * Initialize requested data
! 1053: */
! 1054: if (in != 0) {
! 1055: if (in->haveflags & CLK_HAVETIME1)
! 1056: pp->fudgetime1 = in->fudgetime1;
! 1057: if (in->haveflags & CLK_HAVETIME2)
! 1058: pp->fudgetime2 = in->fudgetime2;
! 1059: if (in->haveflags & CLK_HAVEVAL1)
! 1060: peer->stratum = pp->stratum = (u_char)in->fudgeval1;
! 1061: if (in->haveflags & CLK_HAVEVAL2)
! 1062: peer->refid = pp->refid = in->fudgeval2;
! 1063: if (in->haveflags & CLK_HAVEFLAG1) {
! 1064: pp->sloppyclockflag &= ~CLK_FLAG1;
! 1065: pp->sloppyclockflag |= in->flags & CLK_FLAG1;
! 1066: }
! 1067: if (in->haveflags & CLK_HAVEFLAG2) {
! 1068: pp->sloppyclockflag &= ~CLK_FLAG2;
! 1069: pp->sloppyclockflag |= in->flags & CLK_FLAG2;
! 1070: }
! 1071: if (in->haveflags & CLK_HAVEFLAG3) {
! 1072: pp->sloppyclockflag &= ~CLK_FLAG3;
! 1073: pp->sloppyclockflag |= in->flags & CLK_FLAG3;
! 1074: }
! 1075: if (in->haveflags & CLK_HAVEFLAG4) {
! 1076: pp->sloppyclockflag &= ~CLK_FLAG4;
! 1077: pp->sloppyclockflag |= in->flags & CLK_FLAG4;
! 1078: }
! 1079: }
! 1080:
! 1081: /*
! 1082: * Readback requested data
! 1083: */
! 1084: if (out != 0) {
! 1085: out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 |
! 1086: CLK_HAVEVAL2 | CLK_HAVEFLAG4;
! 1087: out->fudgetime1 = pp->fudgetime1;
! 1088: out->fudgetime2 = pp->fudgetime2;
! 1089: out->fudgeval1 = pp->stratum;
! 1090: out->fudgeval2 = pp->refid;
! 1091: out->flags = (u_char) pp->sloppyclockflag;
! 1092:
! 1093: out->timereset = current_time - pp->timestarted;
! 1094: out->polls = pp->polls;
! 1095: out->noresponse = pp->noreply;
! 1096: out->badformat = pp->badformat;
! 1097: out->baddata = pp->baddata;
! 1098:
! 1099: out->lastevent = pp->lastevent;
! 1100: out->currentstatus = pp->currentstatus;
! 1101: out->type = pp->type;
! 1102: out->clockdesc = pp->clockdesc;
! 1103: out->lencode = (u_short)pp->lencode;
! 1104: out->p_lastcode = pp->a_lastcode;
! 1105: }
! 1106:
! 1107: /*
! 1108: * Give the stuff to the clock
! 1109: */
! 1110: if (refclock_conf[clktype]->clock_control != noentry)
! 1111: (refclock_conf[clktype]->clock_control)(unit, in, out, peer);
! 1112: }
! 1113:
! 1114:
! 1115: /*
! 1116: * refclock_buginfo - return debugging info
! 1117: *
! 1118: * This routine is used mainly for debugging. It returns designated
! 1119: * values from the interface structure that can be displayed using
! 1120: * ntpdc and the clkbug command.
! 1121: */
! 1122: void
! 1123: refclock_buginfo(
! 1124: sockaddr_u *srcadr, /* clock address */
! 1125: struct refclockbug *bug /* output structure */
! 1126: )
! 1127: {
! 1128: struct peer *peer;
! 1129: struct refclockproc *pp;
! 1130: int clktype;
! 1131: int unit;
! 1132: unsigned u;
! 1133:
! 1134: /*
! 1135: * Check for valid address and peer structure
! 1136: */
! 1137: if (!ISREFCLOCKADR(srcadr))
! 1138: return;
! 1139:
! 1140: clktype = (u_char) REFCLOCKTYPE(srcadr);
! 1141: unit = REFCLOCKUNIT(srcadr);
! 1142:
! 1143: peer = findexistingpeer(srcadr, NULL, -1, 0);
! 1144:
! 1145: if (NULL == peer || NULL == peer->procptr)
! 1146: return;
! 1147:
! 1148: pp = peer->procptr;
! 1149:
! 1150: /*
! 1151: * Copy structure values
! 1152: */
! 1153: bug->nvalues = 8;
! 1154: bug->svalues = 0x0000003f;
! 1155: bug->values[0] = pp->year;
! 1156: bug->values[1] = pp->day;
! 1157: bug->values[2] = pp->hour;
! 1158: bug->values[3] = pp->minute;
! 1159: bug->values[4] = pp->second;
! 1160: bug->values[5] = pp->nsec;
! 1161: bug->values[6] = pp->yearstart;
! 1162: bug->values[7] = pp->coderecv;
! 1163: bug->stimes = 0xfffffffc;
! 1164: bug->times[0] = pp->lastref;
! 1165: bug->times[1] = pp->lastrec;
! 1166: for (u = 2; u < bug->ntimes; u++)
! 1167: DTOLFP(pp->filter[u - 2], &bug->times[u]);
! 1168:
! 1169: /*
! 1170: * Give the stuff to the clock
! 1171: */
! 1172: if (refclock_conf[clktype]->clock_buginfo != noentry)
! 1173: (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer);
! 1174: }
! 1175:
! 1176:
! 1177: #ifdef HAVE_PPSAPI
! 1178: /*
! 1179: * refclock_ppsapi - initialize/update ppsapi
! 1180: *
! 1181: * This routine is called after the fudge command to open the PPSAPI
! 1182: * interface for later parameter setting after the fudge command.
! 1183: */
! 1184: int
! 1185: refclock_ppsapi(
! 1186: int fddev, /* fd device */
! 1187: struct refclock_atom *ap /* atom structure pointer */
! 1188: )
! 1189: {
! 1190: if (ap->handle == 0) {
! 1191: if (time_pps_create(fddev, &ap->handle) < 0) {
! 1192: msyslog(LOG_ERR,
! 1193: "refclock_ppsapi: time_pps_create: %m");
! 1194: return (0);
! 1195: }
! 1196: }
! 1197: return (1);
! 1198: }
! 1199:
! 1200:
! 1201: /*
! 1202: * refclock_params - set ppsapi parameters
! 1203: *
! 1204: * This routine is called to set the PPSAPI parameters after the fudge
! 1205: * command.
! 1206: */
! 1207: int
! 1208: refclock_params(
! 1209: int mode, /* mode bits */
! 1210: struct refclock_atom *ap /* atom structure pointer */
! 1211: )
! 1212: {
! 1213: memset(&ap->pps_params, 0, sizeof(pps_params_t));
! 1214: ap->pps_params.api_version = PPS_API_VERS_1;
! 1215:
! 1216: /*
! 1217: * Solaris serial ports provide PPS pulse capture only on the
! 1218: * assert edge. FreeBSD serial ports provide capture on the
! 1219: * clear edge, while FreeBSD parallel ports provide capture
! 1220: * on the assert edge. Your mileage may vary.
! 1221: */
! 1222: if (mode & CLK_FLAG2)
! 1223: ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTURECLEAR;
! 1224: else
! 1225: ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT;
! 1226: if (time_pps_setparams(ap->handle, &ap->pps_params) < 0) {
! 1227: msyslog(LOG_ERR,
! 1228: "refclock_params: time_pps_setparams: %m");
! 1229: return (0);
! 1230: }
! 1231:
! 1232: /*
! 1233: * If flag3 is lit, select the kernel PPS.
! 1234: */
! 1235: if (mode & CLK_FLAG3) {
! 1236: if (time_pps_kcbind(ap->handle, PPS_KC_HARDPPS,
! 1237: ap->pps_params.mode & ~PPS_TSFMT_TSPEC,
! 1238: PPS_TSFMT_TSPEC) < 0) {
! 1239: if (errno != EOPNOTSUPP) {
! 1240: msyslog(LOG_ERR,
! 1241: "refclock_params: time_pps_kcbind: %m");
! 1242: return (0);
! 1243: }
! 1244: }
! 1245: pps_enable = 1;
! 1246: }
! 1247: return (1);
! 1248: }
! 1249:
! 1250:
! 1251: /*
! 1252: * refclock_pps - called once per second
! 1253: *
! 1254: * This routine is called once per second. It snatches the PPS
! 1255: * timestamp from the kernel and saves the sign-extended fraction in
! 1256: * a circular buffer for processing at the next poll event.
! 1257: */
! 1258: int
! 1259: refclock_pps(
! 1260: struct peer *peer, /* peer structure pointer */
! 1261: struct refclock_atom *ap, /* atom structure pointer */
! 1262: int mode /* mode bits */
! 1263: )
! 1264: {
! 1265: struct refclockproc *pp;
! 1266: pps_info_t pps_info;
! 1267: struct timespec timeout;
! 1268: double dtemp;
! 1269:
! 1270: /*
! 1271: * We require the clock to be synchronized before setting the
! 1272: * parameters. When the parameters have been set, fetch the
! 1273: * most recent PPS timestamp.
! 1274: */
! 1275: pp = peer->procptr;
! 1276: if (ap->handle == 0)
! 1277: return (0);
! 1278:
! 1279: if (ap->pps_params.mode == 0 && sys_leap != LEAP_NOTINSYNC) {
! 1280: if (refclock_params(pp->sloppyclockflag, ap) < 1)
! 1281: return (0);
! 1282: }
! 1283: timeout.tv_sec = 0;
! 1284: timeout.tv_nsec = 0;
! 1285: memset(&pps_info, 0, sizeof(pps_info_t));
! 1286: if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info,
! 1287: &timeout) < 0) {
! 1288: refclock_report(peer, CEVNT_FAULT);
! 1289: return (0);
! 1290: }
! 1291: timeout = ap->ts;
! 1292: if (ap->pps_params.mode & PPS_CAPTUREASSERT)
! 1293: ap->ts = pps_info.assert_timestamp;
! 1294: else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
! 1295: ap->ts = pps_info.clear_timestamp;
! 1296: else
! 1297: return (0);
! 1298:
! 1299: /*
! 1300: * There can be zero, one or two PPS pulses between polls,
! 1301: * depending on the poll interval relative to the PPS interval.
! 1302: * The pulse must be newer and within the range gate relative
! 1303: * to the last pulse.
! 1304: */
! 1305: if (ap->ts.tv_sec <= timeout.tv_sec || abs(ap->ts.tv_nsec -
! 1306: timeout.tv_nsec) > RANGEGATE)
! 1307: return (0);
! 1308:
! 1309: /*
! 1310: * Convert to signed fraction offset and stuff in median filter.
! 1311: */
! 1312: pp->lastrec.l_ui = (u_int32)ap->ts.tv_sec + JAN_1970;
! 1313: dtemp = ap->ts.tv_nsec / 1e9;
! 1314: pp->lastrec.l_uf = (u_int32)(dtemp * FRAC);
! 1315: if (dtemp > .5)
! 1316: dtemp -= 1.;
! 1317: SAMPLE(-dtemp + pp->fudgetime1);
! 1318: #ifdef DEBUG
! 1319: if (debug > 1)
! 1320: printf("refclock_pps: %lu %f %f\n", current_time,
! 1321: dtemp, pp->fudgetime1);
! 1322: #endif
! 1323: return (1);
! 1324: }
! 1325: #endif /* HAVE_PPSAPI */
! 1326: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>