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 15 #include <stdio.h> 16 17 #ifdef HAVE_SYS_IOCTL_H 18 # include <sys/ioctl.h> 19 #endif /* HAVE_SYS_IOCTL_H */ 20 21 #ifdef REFCLOCK 22 23 #ifdef TTYCLK 24 # ifdef HAVE_SYS_CLKDEFS_H 25 # include <sys/clkdefs.h> 26 # include <stropts.h> 27 # endif 28 # ifdef HAVE_SYS_SIO_H 29 # include <sys/sio.h> 30 # endif 31 #endif /* TTYCLK */ 32 33 #ifdef HAVE_PPSCLOCK_H 34 #include <sys/ppsclock.h> 35 #endif /* HAVE_PPSCLOCK_H */ 36 37 #ifdef KERNEL_PLL 38 #include "ntp_syscall.h" 39 #endif /* KERNEL_PLL */ 40 41 /* 42 * Reference clock support is provided here by maintaining the fiction 43 * that the clock is actually a peer. As no packets are exchanged with a 44 * reference clock, however, we replace the transmit, receive and packet 45 * procedures with separate code to simulate them. Routines 46 * refclock_transmit() and refclock_receive() maintain the peer 47 * variables in a state analogous to an actual peer and pass reference 48 * clock data on through the filters. Routines refclock_peer() and 49 * refclock_unpeer() are called to initialize and terminate reference 50 * clock associations. A set of utility routines is included to open 51 * serial devices, process sample data, edit input lines to extract 52 * embedded timestamps and to peform various debugging functions. 53 * 54 * The main interface used by these routines is the refclockproc 55 * structure, which contains for most drivers the decimal equivalants of 56 * the year, day, month, hour, second and millisecond/microsecond 57 * decoded from the ASCII timecode. Additional information includes the 58 * receive timestamp, exception report, statistics tallies, etc. In 59 * addition, there may be a driver-specific unit structure used for 60 * local control of the device. 61 * 62 * The support routines are passed a pointer to the peer structure, 63 * which is used for all peer-specific processing and contains a pointer 64 * to the refclockproc structure, which in turn containes a pointer to 65 * the unit structure, if used. The peer structure is identified by an 66 * interface address in the dotted quad form 127.127.t.u, where t is the 67 * clock type and u the unit. Some legacy drivers derive the 68 * refclockproc structure pointer from the table typeunit[type][unit]. 69 * This interface is strongly discouraged and may be abandoned in 70 * future. 71 */ 72 #define MAXUNIT 4 /* max units */ 73 #define FUDGEFAC .1 /* fudge correction factor */ 74 75 int fdpps; /* pps file descriptor */ 76 int cal_enable; /* enable refclock calibrate */ 77 78 /* 79 * Type/unit peer index. Used to find the peer structure for control and 80 * debugging. When all clock drivers have been converted to new style, 81 * this dissapears. 82 */ 83 static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT]; 84 85 /* 86 * Forward declarations 87 */ 88 #ifdef QSORT_USES_VOID_P 89 static int refclock_cmpl_fp P((const void *, const void *)); 90 #else 91 static int refclock_cmpl_fp P((const double *, const double *)); 92 #endif /* QSORT_USES_VOID_P */ 93 static int refclock_sample P((struct refclockproc *)); 94 95 /* 96 * refclock_report - note the occurance of an event 97 * 98 * This routine presently just remembers the report and logs it, but 99 * does nothing heroic for the trap handler. It tries to be a good 100 * citizen and bothers the system log only if things change. 101 */ 102 void 103 refclock_report( 104 struct peer *peer, 105 int code 106 ) 107 { 108 struct refclockproc *pp; 109 110 if (!(pp = peer->procptr)) 111 return; 112 if (code == CEVNT_BADREPLY) 113 pp->badformat++; 114 if (code == CEVNT_BADTIME) 115 pp->baddata++; 116 if (code == CEVNT_TIMEOUT) 117 pp->noreply++; 118 if (pp->currentstatus != code) { 119 pp->currentstatus = code; 120 pp->lastevent = code; 121 if (code == CEVNT_FAULT) 122 msyslog(LOG_ERR, 123 "clock %s event '%s' (0x%02x)", 124 refnumtoa(peer->srcadr.sin_addr.s_addr), 125 ceventstr(code), code); 126 else { 127 NLOG(NLOG_CLOCKEVENT) 128 msyslog(LOG_INFO, 129 "clock %s event '%s' (0x%02x)", 130 refnumtoa(peer->srcadr.sin_addr.s_addr), 131 ceventstr(code), code); 132 } 133 } 134 #ifdef DEBUG 135 if (debug) 136 printf("clock %s event '%s' (0x%02x)\n", 137 refnumtoa(peer->srcadr.sin_addr.s_addr), 138 ceventstr(code), code); 139 #endif 140 } 141 142 143 /* 144 * init_refclock - initialize the reference clock drivers 145 * 146 * This routine calls each of the drivers in turn to initialize internal 147 * variables, if necessary. Most drivers have nothing to say at this 148 * point. 149 */ 150 void 151 init_refclock(void) 152 { 153 int i, j; 154 155 for (i = 0; i < (int)num_refclock_conf; i++) { 156 if (refclock_conf[i]->clock_init != noentry) 157 (refclock_conf[i]->clock_init)(); 158 for (j = 0; j < MAXUNIT; j++) 159 typeunit[i][j] = 0; 160 } 161 } 162 163 164 /* 165 * refclock_newpeer - initialize and start a reference clock 166 * 167 * This routine allocates and initializes the interface structure which 168 * supports a reference clock in the form of an ordinary NTP peer. A 169 * driver-specific support routine completes the initialization, if 170 * used. Default peer variables which identify the clock and establish 171 * its reference ID and stratum are set here. It returns one if success 172 * and zero if the clock address is invalid or already running, 173 * insufficient resources are available or the driver declares a bum 174 * rap. 175 */ 176 int 177 refclock_newpeer( 178 struct peer *peer /* peer structure pointer */ 179 ) 180 { 181 struct refclockproc *pp; 182 u_char clktype; 183 int unit; 184 185 /* 186 * Check for valid clock address. If already running, shut it 187 * down first. 188 */ 189 if (!ISREFCLOCKADR(&peer->srcadr)) { 190 msyslog(LOG_ERR, 191 "refclock_newpeer: clock address %s invalid", 192 ntoa(&peer->srcadr)); 193 return (0); 194 } 195 clktype = (u_char)REFCLOCKTYPE(&peer->srcadr); 196 unit = REFCLOCKUNIT(&peer->srcadr); 197 if (clktype >= num_refclock_conf || unit >= MAXUNIT || 198 refclock_conf[clktype]->clock_start == noentry) { 199 msyslog(LOG_ERR, 200 "refclock_newpeer: clock type %d invalid\n", 201 clktype); 202 return (0); 203 } 204 205 /* 206 * Allocate and initialize interface structure 207 */ 208 if (!(pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc)))) 209 return (0); 210 memset((char *)pp, 0, sizeof(struct refclockproc)); 211 typeunit[clktype][unit] = peer; 212 peer->procptr = pp; 213 214 /* 215 * Initialize structures 216 */ 217 peer->refclktype = clktype; 218 peer->refclkunit = unit; 219 peer->flags |= FLAG_REFCLOCK; 220 peer->stratum = STRATUM_REFCLOCK; 221 peer->refid = peer->srcadr.sin_addr.s_addr; 222 peer->maxpoll = peer->minpoll; 223 224 pp->type = clktype; 225 pp->timestarted = current_time; 226 227 /* 228 * Set peer.pmode based on the hmode. For appearances only. 229 */ 230 switch (peer->hmode) { 231 232 case MODE_ACTIVE: 233 peer->pmode = MODE_PASSIVE; 234 break; 235 236 default: 237 peer->pmode = MODE_SERVER; 238 break; 239 } 240 241 /* 242 * Do driver dependent initialization. The above defaults 243 * can be wiggled, then finish up for consistency. 244 */ 245 if (!((refclock_conf[clktype]->clock_start)(unit, peer))) { 246 refclock_unpeer(peer); 247 return (0); 248 } 249 peer->hpoll = peer->minpoll; 250 peer->ppoll = peer->maxpoll; 251 if (peer->stratum <= 1) 252 peer->refid = pp->refid; 253 else 254 peer->refid = peer->srcadr.sin_addr.s_addr; 255 return (1); 256 } 257 258 259 /* 260 * refclock_unpeer - shut down a clock 261 */ 262 void 263 refclock_unpeer( 264 struct peer *peer /* peer structure pointer */ 265 ) 266 { 267 u_char clktype; 268 int unit; 269 270 /* 271 * Wiggle the driver to release its resources, then give back 272 * the interface structure. 273 */ 274 if (!peer->procptr) 275 return; 276 clktype = peer->refclktype; 277 unit = peer->refclkunit; 278 if (refclock_conf[clktype]->clock_shutdown != noentry) 279 (refclock_conf[clktype]->clock_shutdown)(unit, peer); 280 free(peer->procptr); 281 peer->procptr = 0; 282 } 283 284 285 /* 286 * refclock_transmit - simulate the transmit procedure 287 * 288 * This routine implements the NTP transmit procedure for a reference 289 * clock. This provides a mechanism to call the driver at the NTP poll 290 * interval, as well as provides a reachability mechanism to detect a 291 * broken radio or other madness. 292 */ 293 void 294 refclock_transmit( 295 struct peer *peer /* peer structure pointer */ 296 ) 297 { 298 u_char clktype; 299 int unit; 300 int hpoll; 301 u_long next; 302 303 clktype = peer->refclktype; 304 unit = peer->refclkunit; 305 peer->sent++; 306 307 /* 308 * This is a ripoff of the peer transmit routine, but 309 * specialized for reference clocks. We do a little less 310 * protocol here and call the driver-specific transmit routine. 311 */ 312 hpoll = peer->hpoll; 313 next = peer->outdate; 314 if (peer->burst == 0) { 315 u_char oreach; 316 #ifdef DEBUG 317 if (debug) 318 printf("refclock_transmit: at %ld %s\n", 319 current_time, ntoa(&(peer->srcadr))); 320 #endif 321 322 /* 323 * Update reachability and poll variables like the 324 * network code. 325 */ 326 oreach = peer->reach; 327 peer->reach <<= 1; 328 if (!peer->reach) { 329 if (oreach) { 330 report_event(EVNT_UNREACH, peer); 331 peer->timereachable = current_time; 332 peer_clear(peer); 333 } 334 } else { 335 if (!(oreach & 0x03)) { 336 clock_filter(peer, 0., 0., MAXDISPERSE); 337 clock_select(); 338 } 339 if (!(oreach & 0x0f)) { 340 hpoll--; 341 } else if ((oreach & 0x0f) == 0x0f) 342 hpoll++; 343 if (peer->flags & FLAG_BURST) 344 peer->burst = NSTAGE; 345 } 346 next = current_time; 347 } 348 get_systime(&peer->xmt); 349 if (refclock_conf[clktype]->clock_poll != noentry) 350 (refclock_conf[clktype]->clock_poll)(unit, peer); 351 peer->outdate = next; 352 if (peer->burst > 0) 353 peer->burst--; 354 poll_update(peer, hpoll); 355 } 356 357 358 /* 359 * Compare two doubles - used with qsort() 360 */ 361 #ifdef QSORT_USES_VOID_P 362 static int 363 refclock_cmpl_fp( 364 const void *p1, 365 const void *p2 366 ) 367 { 368 const double *dp1 = (const double *)p1; 369 const double *dp2 = (const double *)p2; 370 371 if (*dp1 < *dp2) 372 return (-1); 373 if (*dp1 > *dp2) 374 return (1); 375 return (0); 376 } 377 #else 378 static int 379 refclock_cmpl_fp( 380 const double *dp1, 381 const double *dp2 382 ) 383 { 384 if (*dp1 < *dp2) 385 return (-1); 386 if (*dp1 > *dp2) 387 return (1); 388 return (0); 389 } 390 #endif /* QSORT_USES_VOID_P */ 391 392 393 /* 394 * refclock_process_offset - update median filter 395 * 396 * This routine uses the given offset and timestamps to construct a new 397 * entry in the median filter circular buffer. Samples that overflow the 398 * filter are quietly discarded. 399 */ 400 void 401 refclock_process_offset( 402 struct refclockproc *pp, 403 l_fp offset, 404 l_fp lastrec, 405 double fudge 406 ) 407 { 408 double doffset; 409 410 pp->lastref = offset; 411 pp->lastrec = lastrec; 412 L_SUB(&offset, &lastrec); 413 LFPTOD(&offset, doffset); 414 SAMPLE(doffset + fudge); 415 } 416 417 /* 418 * refclock_process - process a sample from the clock 419 * 420 * This routine converts the timecode in the form days, hours, minutes, 421 * seconds and milliseconds/microseconds to internal timestamp format, 422 * then constructs a new entry in the median filter circular buffer. 423 * Return success (1) if the data are correct and consistent with the 424 * converntional calendar. 425 */ 426 int 427 refclock_process( 428 struct refclockproc *pp 429 ) 430 { 431 l_fp offset; 432 433 /* 434 * Compute the timecode timestamp from the days, hours, minutes, 435 * seconds and milliseconds/microseconds of the timecode. Use 436 * clocktime() for the aggregate seconds and the msec/usec for 437 * the fraction, when present. Note that this code relies on the 438 * filesystem time for the years and does not use the years of 439 * the timecode. 440 */ 441 if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, 442 pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui)) 443 return (0); 444 if (pp->usec) { 445 TVUTOTSF(pp->usec, offset.l_uf); 446 } else { 447 MSUTOTSF(pp->msec, offset.l_uf); 448 } 449 refclock_process_offset(pp, offset, pp->lastrec, 450 pp->fudgetime1); 451 return (1); 452 } 453 454 /* 455 * refclock_sample - process a pile of samples from the clock 456 * 457 * This routine implements a recursive median filter to suppress spikes 458 * in the data, as well as determine a performance statistic. It 459 * calculates the mean offset and jitter (squares). A time adjustment 460 * fudgetime1 can be added to the final offset to compensate for various 461 * systematic errors. The routine returns the number of samples 462 * processed, which could be zero. 463 */ 464 static int 465 refclock_sample( 466 struct refclockproc *pp 467 ) 468 { 469 int i, j, k, m, n; 470 double offset; 471 double off[MAXSTAGE]; 472 473 /* 474 * Copy the raw offsets and sort into ascending order. Don't do 475 * anything if the buffer is empty. 476 */ 477 if (pp->codeproc == pp->coderecv) 478 return (0); 479 n = 0; 480 while (pp->codeproc != pp->coderecv) 481 off[n++] = pp->filter[pp->codeproc++ % MAXSTAGE]; 482 if (n > 1) 483 qsort((char *)off, (size_t)n, sizeof(double), 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 * 2) / NSTAGE; 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 offset = 0; 503 for (k = i; k < j; k++) 504 offset += off[k]; 505 pp->offset = offset / m; 506 if (m > 1) 507 pp->jitter = SQUARE(off[i] - off[j - 1]); 508 else 509 pp->jitter = 0; 510 #ifdef DEBUG 511 if (debug) 512 printf( 513 "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n", 514 n, pp->offset, pp->disp, SQRT(pp->jitter)); 515 #endif 516 return (n); 517 } 518 519 520 /* 521 * refclock_receive - simulate the receive and packet procedures 522 * 523 * This routine simulates the NTP receive and packet procedures for a 524 * reference clock. This provides a mechanism in which the ordinary NTP 525 * filter, selection and combining algorithms can be used to suppress 526 * misbehaving radios and to mitigate between them when more than one is 527 * available for backup. 528 */ 529 void 530 refclock_receive( 531 struct peer *peer /* peer structure pointer */ 532 ) 533 { 534 struct refclockproc *pp; 535 536 #ifdef DEBUG 537 if (debug) 538 printf("refclock_receive: at %lu %s\n", 539 current_time, ntoa(&peer->srcadr)); 540 #endif 541 542 /* 543 * Do a little sanity dance and update the peer structure. Groom 544 * the median filter samples and give the data to the clock 545 * filter. 546 */ 547 peer->received++; 548 pp = peer->procptr; 549 peer->processed++; 550 peer->timereceived = current_time; 551 peer->leap = pp->leap; 552 if (peer->leap == LEAP_NOTINSYNC) { 553 refclock_report(peer, CEVNT_FAULT); 554 return; 555 } 556 if (!peer->reach) 557 report_event(EVNT_REACH, peer); 558 peer->reach |= 1; 559 peer->reftime = peer->org = pp->lastrec; 560 peer->rootdispersion = pp->disp + SQRT(pp->jitter); 561 get_systime(&peer->rec); 562 if (!refclock_sample(pp)) 563 return; 564 clock_filter(peer, pp->offset, 0., pp->jitter); 565 clock_select(); 566 record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), 567 peer->offset, peer->delay, clock_phi * (current_time - 568 peer->epoch), SQRT(peer->jitter)); 569 if (cal_enable && last_offset < MINDISPERSE) { 570 #ifdef KERNEL_PLL 571 if (peer != sys_peer || pll_status & STA_PPSTIME) 572 #else 573 if (peer != sys_peer) 574 #endif /* KERNEL_PLL */ 575 pp->fudgetime1 -= pp->offset * FUDGEFAC; 576 else 577 pp->fudgetime1 -= pp->fudgetime1 * FUDGEFAC; 578 } 579 } 580 581 /* 582 * refclock_gtlin - groom next input line and extract timestamp 583 * 584 * This routine processes the timecode received from the clock and 585 * removes the parity bit and control characters. If a timestamp is 586 * present in the timecode, as produced by the tty_clk STREAMS module, 587 * it returns that as the timestamp; otherwise, it returns the buffer 588 * timestamp. The routine return code is the number of characters in 589 * the line. 590 */ 591 int 592 refclock_gtlin( 593 struct recvbuf *rbufp, /* receive buffer pointer */ 594 char *lineptr, /* current line pointer */ 595 int bmax, /* remaining characters in line */ 596 l_fp *tsptr /* pointer to timestamp returned */ 597 ) 598 { 599 char *dpt, *dpend, *dp; 600 int i; 601 l_fp trtmp, tstmp; 602 char c; 603 604 /* 605 * Check for the presence of a timestamp left by the tty_clock 606 * module and, if present, use that instead of the buffer 607 * timestamp captured by the I/O routines. We recognize a 608 * timestamp by noting its value is earlier than the buffer 609 * timestamp, but not more than one second earlier. 610 */ 611 dpt = (char *)&rbufp->recv_space; 612 dpend = dpt + rbufp->recv_length; 613 trtmp = rbufp->recv_time; 614 615 if (dpend >= dpt + 8) { 616 if (buftvtots(dpend - 8, &tstmp)) { 617 L_SUB(&trtmp, &tstmp); 618 if (trtmp.l_ui == 0) { 619 #ifdef DEBUG 620 if (debug > 1) { 621 printf( 622 "refclock_gtlin: fd %d ldisc %s", 623 rbufp->fd, lfptoa(&trtmp, 6)); 624 get_systime(&trtmp); 625 L_SUB(&trtmp, &tstmp); 626 printf(" sigio %s\n", lfptoa(&trtmp, 6)); 627 } 628 #endif 629 dpend -= 8; 630 trtmp = tstmp; 631 } else 632 trtmp = rbufp->recv_time; 633 } 634 } 635 636 /* 637 * Edit timecode to remove control chars. Don't monkey with the 638 * line buffer if the input buffer contains no ASCII printing 639 * characters. 640 */ 641 if (dpend - dpt > bmax - 1) 642 dpend = dpt + bmax - 1; 643 for (dp = lineptr; dpt < dpend; dpt++) { 644 c = *dpt & 0x7f; 645 if (c >= ' ') 646 *dp++ = c; 647 } 648 i = dp - lineptr; 649 if (i > 0) 650 *dp = '\0'; 651 #ifdef DEBUG 652 if (debug > 1 && i > 0) 653 printf("refclock_gtlin: fd %d time %s timecode %d %s\n", 654 rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr); 655 #endif 656 *tsptr = trtmp; 657 return (i); 658 } 659 660 /* 661 * The following code does not apply to WINNT & VMS ... 662 */ 663 #if !defined SYS_VXWORKS && !defined SYS_WINNT 664 #if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS) 665 666 /* 667 * refclock_open - open serial port for reference clock 668 * 669 * This routine opens a serial port for I/O and sets default options. It 670 * returns the file descriptor if success and zero if failure. 671 */ 672 int 673 refclock_open( 674 char *dev, /* device name pointer */ 675 int speed, /* serial port speed (code) */ 676 int lflags /* line discipline flags */ 677 ) 678 { 679 int fd, i; 680 int flags; 681 TTY ttyb, *ttyp; 682 #ifdef TIOCMGET 683 u_long ltemp; 684 #endif /* TIOCMGET */ 685 int omode; 686 687 /* 688 * Open serial port and set default options 689 */ 690 flags = lflags; 691 692 omode = O_RDWR; 693 #ifdef O_NONBLOCK 694 omode |= O_NONBLOCK; 695 #endif 696 #ifdef O_NOCTTY 697 omode |= O_NOCTTY; 698 #endif 699 700 fd = open(dev, omode, 0777); 701 702 if (fd < 0) { 703 msyslog(LOG_ERR, "refclock_open: %s: %m", dev); 704 return (0); 705 } 706 707 /* 708 * This little jewel lights up the PPS file descriptor if the 709 * device name matches the name in the pps line in the 710 * configuration file. This is so the atom driver can glom onto 711 * the right device. Very silly. 712 */ 713 if (strcmp(dev, pps_device) == 0) 714 fdpps = fd; 715 716 /* 717 * The following sections initialize the serial line port in 718 * canonical (line-oriented) mode and set the specified line 719 * speed, 8 bits and no parity. The modem control, break, erase 720 * and kill functions are normally disabled. There is a 721 * different section for each terminal interface, as selected at 722 * compile time. 723 */ 724 ttyp = &ttyb; 725 726 #ifdef HAVE_TERMIOS 727 /* 728 * POSIX serial line parameters (termios interface) 729 */ 730 if (tcgetattr(fd, ttyp) < 0) { 731 msyslog(LOG_ERR, 732 "refclock_open: fd %d tcgetattr: %m", fd); 733 return (0); 734 } 735 736 /* 737 * Set canonical mode and local connection; set specified speed, 738 * 8 bits and no parity; map CR to NL; ignore break. 739 */ 740 ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; 741 ttyp->c_oflag = 0; 742 ttyp->c_cflag = CS8 | CLOCAL | CREAD; 743 (void)cfsetispeed(&ttyb, (u_int)speed); 744 (void)cfsetospeed(&ttyb, (u_int)speed); 745 ttyp->c_lflag = ICANON; 746 for (i = 0; i < NCCS; ++i) 747 { 748 ttyp->c_cc[i] = '\0'; 749 } 750 751 /* 752 * Some special cases 753 */ 754 if (flags & LDISC_RAW) { 755 ttyp->c_iflag = 0; 756 ttyp->c_lflag = 0; 757 ttyp->c_cc[VMIN] = 1; 758 } 759 #if defined(TIOCMGET) && !defined(SCO5_CLOCK) 760 /* 761 * If we have modem control, check to see if modem leads are 762 * active; if so, set remote connection. This is necessary for 763 * the kernel pps mods to work. 764 */ 765 ltemp = 0; 766 if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) 767 msyslog(LOG_ERR, 768 "refclock_open: fd %d TIOCMGET failed: %m", fd); 769 #ifdef DEBUG 770 if (debug) 771 printf("refclock_open: fd %d modem status 0x%lx\n", 772 fd, ltemp); 773 #endif 774 if (ltemp & TIOCM_DSR) 775 ttyp->c_cflag &= ~CLOCAL; 776 #endif /* TIOCMGET */ 777 if (tcsetattr(fd, TCSANOW, ttyp) < 0) { 778 msyslog(LOG_ERR, 779 "refclock_open: fd %d TCSANOW failed: %m", fd); 780 return (0); 781 } 782 if (tcflush(fd, TCIOFLUSH) < 0) { 783 msyslog(LOG_ERR, 784 "refclock_open: fd %d TCIOFLUSH failed: %m", fd); 785 return (0); 786 } 787 #endif /* HAVE_TERMIOS */ 788 789 #ifdef HAVE_SYSV_TTYS 790 791 /* 792 * System V serial line parameters (termio interface) 793 * 794 */ 795 if (ioctl(fd, TCGETA, ttyp) < 0) { 796 msyslog(LOG_ERR, 797 "refclock_open: fd %d TCGETA failed: %m", fd); 798 return (0); 799 } 800 801 /* 802 * Set canonical mode and local connection; set specified speed, 803 * 8 bits and no parity; map CR to NL; ignore break. 804 */ 805 ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; 806 ttyp->c_oflag = 0; 807 ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD; 808 ttyp->c_lflag = ICANON; 809 ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; 810 811 /* 812 * Some special cases 813 */ 814 if (flags & LDISC_RAW) { 815 ttyp->c_iflag = 0; 816 ttyp->c_lflag = 0; 817 } 818 #ifdef TIOCMGET 819 /* 820 * If we have modem control, check to see if modem leads are 821 * active; if so, set remote connection. This is necessary for 822 * the kernel pps mods to work. 823 */ 824 ltemp = 0; 825 if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) 826 msyslog(LOG_ERR, 827 "refclock_open: fd %d TIOCMGET failed: %m", fd); 828 #ifdef DEBUG 829 if (debug) 830 printf("refclock_open: fd %d modem status %lx\n", 831 fd, ltemp); 832 #endif 833 if (ltemp & TIOCM_DSR) 834 ttyp->c_cflag &= ~CLOCAL; 835 #endif /* TIOCMGET */ 836 if (ioctl(fd, TCSETA, ttyp) < 0) { 837 msyslog(LOG_ERR, 838 "refclock_open: fd %d TCSETA failed: %m", fd); 839 return (0); 840 } 841 #endif /* HAVE_SYSV_TTYS */ 842 843 #ifdef HAVE_BSD_TTYS 844 845 /* 846 * 4.3bsd serial line parameters (sgttyb interface) 847 */ 848 if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) { 849 msyslog(LOG_ERR, 850 "refclock_open: fd %d TIOCGETP %m", fd); 851 return (0); 852 } 853 ttyp->sg_ispeed = ttyp->sg_ospeed = speed; 854 ttyp->sg_flags = EVENP | ODDP | CRMOD; 855 if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) { 856 msyslog(LOG_ERR, 857 "refclock_open: TIOCSETP failed: %m"); 858 return (0); 859 } 860 #endif /* HAVE_BSD_TTYS */ 861 if (!refclock_ioctl(fd, flags)) { 862 (void)close(fd); 863 msyslog(LOG_ERR, 864 "refclock_open: fd %d ioctl failed: %m", fd); 865 return (0); 866 } 867 return (fd); 868 } 869 #endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */ 870 #endif /* SYS_VXWORKS SYS_WINNT */ 871 872 /* 873 * refclock_ioctl - set serial port control functions 874 * 875 * This routine attempts to hide the internal, system-specific details 876 * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD 877 * (sgtty) interfaces with varying degrees of success. The routine sets 878 * up optional features such as tty_clk. The routine returns 1 if 879 * success and 0 if failure. 880 */ 881 int 882 refclock_ioctl( 883 int fd, /* file descriptor */ 884 int flags /* line discipline flags */ 885 ) 886 { 887 /* simply return 1 if no UNIX line discipline is supported */ 888 #if !defined SYS_VXWORKS && !defined SYS_WINNT 889 #if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS) 890 891 #ifdef TTYCLK 892 TTY ttyb, *ttyp; 893 #endif /* TTYCLK */ 894 895 #ifdef DEBUG 896 if (debug) 897 printf("refclock_ioctl: fd %d flags 0x%x\n", fd, flags); 898 #endif 899 if (flags == 0) 900 return (1); 901 #if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS)) 902 if (flags & (LDISC_CLK | LDISC_PPS | LDISC_ACTS)) { 903 msyslog(LOG_ERR, 904 "refclock_ioctl: unsupported terminal interface"); 905 return (0); 906 } 907 #endif /* HAVE_TERMIOS HAVE_BSD_TTYS */ 908 #ifdef TTYCLK 909 ttyp = &ttyb; 910 #endif /* TTYCLK */ 911 912 /* 913 * The following features may or may not require System V 914 * STREAMS support, depending on the particular implementation. 915 */ 916 #if defined(TTYCLK) 917 /* 918 * The TTYCLK option provides timestamping at the driver level. 919 * It requires the tty_clk streams module and System V STREAMS 920 * support. If not available, don't complain. 921 */ 922 if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) { 923 int rval = 0; 924 925 if (ioctl(fd, I_PUSH, "clk") < 0) { 926 msyslog(LOG_NOTICE, 927 "refclock_ioctl: I_PUSH clk failed: %m"); 928 } else { 929 char *str; 930 931 if (flags & LDISC_CLKPPS) 932 str = "\377"; 933 else if (flags & LDISC_ACTS) 934 str = "*"; 935 else 936 str = "\n"; 937 #ifdef CLK_SETSTR 938 if ((rval = ioctl(fd, CLK_SETSTR, str)) < 0) 939 msyslog(LOG_ERR, 940 "refclock_ioctl: CLK_SETSTR failed: %m"); 941 if (debug) 942 printf("refclock_ioctl: fd %d CLK_SETSTR %d str %s\n", 943 fd, rval, str); 944 #endif 945 } 946 } 947 #endif /* TTYCLK */ 948 #endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */ 949 #endif /* SYS_VXWORKS SYS_WINNT */ 950 return (1); 951 } 952 953 /* 954 * refclock_control - set and/or return clock values 955 * 956 * This routine is used mainly for debugging. It returns designated 957 * values from the interface structure that can be displayed using 958 * ntpdc and the clockstat command. It can also be used to initialize 959 * configuration variables, such as fudgetimes, fudgevalues, reference 960 * ID and stratum. 961 */ 962 void 963 refclock_control( 964 struct sockaddr_in *srcadr, 965 struct refclockstat *in, 966 struct refclockstat *out 967 ) 968 { 969 struct peer *peer; 970 struct refclockproc *pp; 971 u_char clktype; 972 int unit; 973 974 /* 975 * Check for valid address and running peer 976 */ 977 if (!ISREFCLOCKADR(srcadr)) 978 return; 979 clktype = (u_char)REFCLOCKTYPE(srcadr); 980 unit = REFCLOCKUNIT(srcadr); 981 if (clktype >= num_refclock_conf || unit >= MAXUNIT) 982 return; 983 if (!(peer = typeunit[clktype][unit])) 984 return; 985 if (peer->procptr == NULL) 986 return; 987 pp = peer->procptr; 988 989 /* 990 * Initialize requested data 991 */ 992 if (in != 0) { 993 if (in->haveflags & CLK_HAVETIME1) 994 pp->fudgetime1 = in->fudgetime1; 995 if (in->haveflags & CLK_HAVETIME2) 996 pp->fudgetime2 = in->fudgetime2; 997 if (in->haveflags & CLK_HAVEVAL1) 998 peer->stratum = (u_char) in->fudgeval1; 999 if (in->haveflags & CLK_HAVEVAL2) 1000 pp->refid = in->fudgeval2; 1001 if (peer->stratum <= 1) 1002 peer->refid = pp->refid; 1003 else 1004 peer->refid = peer->srcadr.sin_addr.s_addr; 1005 if (in->haveflags & CLK_HAVEFLAG1) { 1006 pp->sloppyclockflag &= ~CLK_FLAG1; 1007 pp->sloppyclockflag |= in->flags & CLK_FLAG1; 1008 } 1009 if (in->haveflags & CLK_HAVEFLAG2) { 1010 pp->sloppyclockflag &= ~CLK_FLAG2; 1011 pp->sloppyclockflag |= in->flags & CLK_FLAG2; 1012 } 1013 if (in->haveflags & CLK_HAVEFLAG3) { 1014 pp->sloppyclockflag &= ~CLK_FLAG3; 1015 pp->sloppyclockflag |= in->flags & CLK_FLAG3; 1016 } 1017 if (in->haveflags & CLK_HAVEFLAG4) { 1018 pp->sloppyclockflag &= ~CLK_FLAG4; 1019 pp->sloppyclockflag |= in->flags & CLK_FLAG4; 1020 } 1021 } 1022 1023 /* 1024 * Readback requested data 1025 */ 1026 if (out != 0) { 1027 out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 | 1028 CLK_HAVEVAL2 | CLK_HAVEFLAG4; 1029 out->fudgetime1 = pp->fudgetime1; 1030 out->fudgetime2 = pp->fudgetime2; 1031 out->fudgeval1 = peer->stratum; 1032 out->fudgeval2 = pp->refid; 1033 out->flags = (u_char) pp->sloppyclockflag; 1034 1035 out->timereset = current_time - pp->timestarted; 1036 out->polls = pp->polls; 1037 out->noresponse = pp->noreply; 1038 out->badformat = pp->badformat; 1039 out->baddata = pp->baddata; 1040 1041 out->lastevent = pp->lastevent; 1042 out->currentstatus = pp->currentstatus; 1043 out->type = pp->type; 1044 out->clockdesc = pp->clockdesc; 1045 out->lencode = pp->lencode; 1046 out->p_lastcode = pp->a_lastcode; 1047 } 1048 1049 /* 1050 * Give the stuff to the clock 1051 */ 1052 if (refclock_conf[clktype]->clock_control != noentry) 1053 (refclock_conf[clktype]->clock_control)(unit, in, out, peer); 1054 } 1055 1056 1057 /* 1058 * refclock_buginfo - return debugging info 1059 * 1060 * This routine is used mainly for debugging. It returns designated 1061 * values from the interface structure that can be displayed using 1062 * ntpdc and the clkbug command. 1063 */ 1064 void 1065 refclock_buginfo( 1066 struct sockaddr_in *srcadr, /* clock address */ 1067 struct refclockbug *bug /* output structure */ 1068 ) 1069 { 1070 struct peer *peer; 1071 struct refclockproc *pp; 1072 u_char clktype; 1073 int unit; 1074 int i; 1075 1076 /* 1077 * Check for valid address and peer structure 1078 */ 1079 if (!ISREFCLOCKADR(srcadr)) 1080 return; 1081 clktype = (u_char) REFCLOCKTYPE(srcadr); 1082 unit = REFCLOCKUNIT(srcadr); 1083 if (clktype >= num_refclock_conf || unit >= MAXUNIT) 1084 return; 1085 if (!(peer = typeunit[clktype][unit])) 1086 return; 1087 pp = peer->procptr; 1088 1089 /* 1090 * Copy structure values 1091 */ 1092 bug->nvalues = 8; 1093 bug->svalues = 0x0000003f; 1094 bug->values[0] = pp->year; 1095 bug->values[1] = pp->day; 1096 bug->values[2] = pp->hour; 1097 bug->values[3] = pp->minute; 1098 bug->values[4] = pp->second; 1099 bug->values[5] = pp->msec; 1100 bug->values[6] = pp->yearstart; 1101 bug->values[7] = pp->coderecv; 1102 bug->stimes = 0xfffffffc; 1103 bug->times[0] = pp->lastref; 1104 bug->times[1] = pp->lastrec; 1105 for (i = 2; i < (int)bug->ntimes; i++) 1106 DTOLFP(pp->filter[i - 2], &bug->times[i]); 1107 1108 /* 1109 * Give the stuff to the clock 1110 */ 1111 if (refclock_conf[clktype]->clock_buginfo != noentry) 1112 (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer); 1113 } 1114 1115 #endif /* REFCLOCK */ 1116