1/* 2 * refclock_true - clock driver for the Kinemetrics Truetime receivers 3 * Receiver Version 3.0C - tested plain, with CLKLDISC 4 * Developement work being done: 5 * - Properly handle varying satellite positions (more acurately) 6 * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers 7 */ 8 9#ifdef HAVE_CONFIG_H 10#include <config.h> 11#endif 12 13#if defined(REFCLOCK) && defined(CLOCK_TRUETIME) 14 15#include "ntpd.h" 16#include "ntp_io.h" 17#include "ntp_refclock.h" 18#include "ntp_unixtime.h" 19#include "ntp_stdlib.h" 20 21#include <stdio.h> 22#include <ctype.h> 23 24/* This should be an atom clock but those are very hard to build. 25 * 26 * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch 27 * of TTL input and output pins, all brought out to the back panel. If you 28 * wire a PPS signal (such as the TTL PPS coming out of a GOES or other 29 * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the 30 * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the 31 * number of uSecs since the last PPS upward swing, mediated by reading OUT0 32 * to find out if the counter has wrapped around (this happens if more than 33 * 65535us (65ms) elapses between the PPS event and our being called.) 34 */ 35#ifdef CLOCK_PPS720 36# undef min /* XXX */ 37# undef max /* XXX */ 38# include <machine/inline.h> 39# include <sys/pcl720.h> 40# include <sys/i8253.h> 41# define PCL720_IOB 0x2a0 /* XXX */ 42# define PCL720_CTR 0 /* XXX */ 43#endif 44 45/* 46 * Support for Kinemetrics Truetime Receivers 47 * GOES 48 * GPS/TM-TMD 49 * XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD) 50 * GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module) 51 * OM-DC: getting stale ("OMEGA") 52 * 53 * Most of this code is originally from refclock_wwvb.c with thanks. 54 * It has been so mangled that wwvb is not a recognizable ancestor. 55 * 56 * Timcode format: ADDD:HH:MM:SSQCL 57 * A - control A (this is stripped before we see it) 58 * Q - Quality indication (see below) 59 * C - Carriage return 60 * L - Line feed 61 * 62 * Quality codes indicate possible error of 63 * 468-DC GOES Receiver: 64 * GPS-TM/TMD Receiver: (default quality codes for XL-DC) 65 * ? +/- 1 milliseconds # +/- 100 microseconds 66 * * +/- 10 microseconds . +/- 1 microsecond 67 * space less than 1 microsecond 68 * OM-DC OMEGA Receiver: (default quality codes for OMEGA) 69 * WARNING OMEGA navigation system is no longer existent 70 * > >+- 5 seconds 71 * ? >+/- 500 milliseconds # >+/- 50 milliseconds 72 * * >+/- 5 milliseconds . >+/- 1 millisecond 73 * A-H less than 1 millisecond. Character indicates which station 74 * is being received as follows: 75 * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, 76 * E = La Reunion, F = Argentina, G = Australia, H = Japan. 77 * 78 * The carriage return start bit begins on 0 seconds and extends to 1 bit time. 79 * 80 * Notes on 468-DC and OMEGA receiver: 81 * 82 * Send the clock a 'R' or 'C' and once per second a timestamp will 83 * appear. Send a 'P' to get the satellite position once (GOES only.) 84 * 85 * Notes on the 468-DC receiver: 86 * 87 * Since the old east/west satellite locations are only historical, you can't 88 * set your clock propagation delay settings correctly and still use 89 * automatic mode. The manual says to use a compromise when setting the 90 * switches. This results in significant errors. The solution; use fudge 91 * time1 and time2 to incorporate corrections. If your clock is set for 92 * 50 and it should be 58 for using the west and 46 for using the east, 93 * use the line 94 * 95 * fudge 127.127.5.0 time1 +0.008 time2 -0.004 96 * 97 * This corrects the 4 milliseconds advance and 8 milliseconds retard 98 * needed. The software will ask the clock which satellite it sees. 99 * 100 * Ntp.conf parameters: 101 * time1 - offset applied to samples when reading WEST satellite (default = 0) 102 * time2 - offset applied to samples when reading EAST satellite (default = 0) 103 * val1 - stratum to assign to this clock (default = 0) 104 * val2 - refid assigned to this clock (default = "TRUE", see below) 105 * flag1 - will silence the clock side of ntpd, just reading the clock 106 * without trying to write to it. (default = 0) 107 * flag2 - generate a debug file /tmp/true%d. 108 * flag3 - enable ppsclock streams module 109 * flag4 - use the PCL-720 (BSD/OS only) 110 */ 111 112 113/* 114 * Definitions 115 */ 116#define DEVICE "/dev/true%d" 117#define SPEED232 B9600 /* 9600 baud */ 118 119/* 120 * Radio interface parameters 121 */ 122#define PRECISION (-10) /* precision assumed (about 1 ms) */ 123#define REFID "TRUE" /* reference id */ 124#define DESCRIPTION "Kinemetrics/TrueTime Receiver" 125 126/* 127 * Tags which station (satellite) we see 128 */ 129#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ 130#define GOES_EAST 1 /* until you discover otherwise */ 131 132/* 133 * used by the state machine 134 */ 135enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite, 136 e_Poll, e_Location, e_TS, e_Max}; 137const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite", 138 "Poll", "Location", "TS"}; 139#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") 140 141enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES, 142 s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; 143const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES", 144 "Init", "F18", "F50", "Start", "Auto"}; 145#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") 146 147enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_Max}; 148const char *types[] = {"unknown", "goes", "tm", "tcu", "omega"}; 149#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") 150 151/* 152 * unit control structure 153 */ 154struct true_unit { 155 unsigned int pollcnt; /* poll message counter */ 156 unsigned int station; /* which station we are on */ 157 unsigned int polled; /* Hand in a time sample? */ 158 enum true_state state; /* state machine */ 159 enum true_type type; /* what kind of clock is it? */ 160 int unit; /* save an extra copy of this */ 161 FILE *debug; /* debug logging file */ 162#ifdef CLOCK_PPS720 163 int pcl720init; /* init flag for PCL 720 */ 164#endif 165}; 166 167/* 168 * Function prototypes 169 */ 170static int true_start (int, struct peer *); 171static void true_shutdown (int, struct peer *); 172static void true_receive (struct recvbuf *); 173static void true_poll (int, struct peer *); 174static void true_send (struct peer *, const char *); 175static void true_doevent (struct peer *, enum true_event); 176 177#ifdef CLOCK_PPS720 178static u_long true_sample720 (void); 179#endif 180 181/* 182 * Transfer vector 183 */ 184struct refclock refclock_true = { 185 true_start, /* start up driver */ 186 true_shutdown, /* shut down driver */ 187 true_poll, /* transmit poll message */ 188 noentry, /* not used (old true_control) */ 189 noentry, /* initialize driver (not used) */ 190 noentry, /* not used (old true_buginfo) */ 191 NOFLAGS /* not used */ 192}; 193 194 195#if !defined(__STDC__) 196# define true_debug (void) 197#else 198static void 199true_debug(struct peer *peer, const char *fmt, ...) 200{ 201 va_list ap; 202 int want_debugging, now_debugging; 203 struct refclockproc *pp; 204 struct true_unit *up; 205 206 va_start(ap, fmt); 207 pp = peer->procptr; 208 up = (struct true_unit *)pp->unitptr; 209 210 want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; 211 now_debugging = (up->debug != NULL); 212 if (want_debugging != now_debugging) 213 { 214 if (want_debugging) { 215 char filename[40]; 216 int fd; 217 218 snprintf(filename, sizeof(filename), "/tmp/true%d.debug", up->unit); 219 fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0600); 220 if (fd >= 0 && (up->debug = fdopen(fd, "r+"))) { 221#ifdef HAVE_SETVBUF 222 static char buf[BUFSIZ]; 223 setvbuf(up->debug, buf, _IOLBF, BUFSIZ); 224#else 225 setlinebuf(up->debug); 226#endif 227 } 228 } else { 229 fclose(up->debug); 230 up->debug = NULL; 231 } 232 } 233 234 if (up->debug) { 235 fprintf(up->debug, "true%d: ", up->unit); 236 vfprintf(up->debug, fmt, ap); 237 } 238 va_end(ap); 239} 240#endif /*STDC*/ 241 242/* 243 * true_start - open the devices and initialize data for processing 244 */ 245static int 246true_start( 247 int unit, 248 struct peer *peer 249 ) 250{ 251 register struct true_unit *up; 252 struct refclockproc *pp; 253 char device[40]; 254 int fd; 255 256 /* 257 * Open serial port 258 */ 259 (void)snprintf(device, sizeof(device), DEVICE, unit); 260 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 261 return (0); 262 263 /* 264 * Allocate and initialize unit structure 265 */ 266 if (!(up = (struct true_unit *) 267 emalloc(sizeof(struct true_unit)))) { 268 (void) close(fd); 269 return (0); 270 } 271 memset((char *)up, 0, sizeof(struct true_unit)); 272 pp = peer->procptr; 273 pp->io.clock_recv = true_receive; 274 pp->io.srcclock = (caddr_t)peer; 275 pp->io.datalen = 0; 276 pp->io.fd = fd; 277 if (!io_addclock(&pp->io)) { 278 (void) close(fd); 279 free(up); 280 return (0); 281 } 282 pp->unitptr = (caddr_t)up; 283 284 /* 285 * Initialize miscellaneous variables 286 */ 287 peer->precision = PRECISION; 288 pp->clockdesc = DESCRIPTION; 289 memcpy((char *)&pp->refid, REFID, 4); 290 up->pollcnt = 2; 291 up->type = t_unknown; 292 up->state = s_Base; 293 true_doevent(peer, e_Init); 294 return (1); 295} 296 297/* 298 * true_shutdown - shut down the clock 299 */ 300static void 301true_shutdown( 302 int unit, 303 struct peer *peer 304 ) 305{ 306 register struct true_unit *up; 307 struct refclockproc *pp; 308 309 pp = peer->procptr; 310 up = (struct true_unit *)pp->unitptr; 311 io_closeclock(&pp->io); 312 free(up); 313} 314 315 316/* 317 * true_receive - receive data from the serial interface on a clock 318 */ 319static void 320true_receive( 321 struct recvbuf *rbufp 322 ) 323{ 324 register struct true_unit *up; 325 struct refclockproc *pp; 326 struct peer *peer; 327 u_short new_station; 328 char synced; 329 int i; 330 int lat, lon, off; /* GOES Satellite position */ 331 /* Use these variable to hold data until we decide its worth keeping */ 332 char rd_lastcode[BMAX]; 333 l_fp rd_tmp; 334 u_short rd_lencode; 335 336 /* 337 * Get the clock this applies to and pointers to the data. 338 */ 339 peer = (struct peer *)rbufp->recv_srcclock; 340 pp = peer->procptr; 341 up = (struct true_unit *)pp->unitptr; 342 343 /* 344 * Read clock output. Automatically handles STREAMS, CLKLDISC. 345 */ 346 rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); 347 rd_lastcode[rd_lencode] = '\0'; 348 349 /* 350 * There is a case where <cr><lf> generates 2 timestamps. 351 */ 352 if (rd_lencode == 0) 353 return; 354 pp->lencode = rd_lencode; 355 strcpy(pp->a_lastcode, rd_lastcode); 356 pp->lastrec = rd_tmp; 357 true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode); 358 359 up->pollcnt = 2; 360 record_clock_stats(&peer->srcadr, pp->a_lastcode); 361 362 /* 363 * We get down to business, check the timecode format and decode 364 * its contents. This code decodes a multitude of different 365 * clock messages. Timecodes are processed if needed. All replies 366 * will be run through the state machine to tweak driver options 367 * and program the clock. 368 */ 369 370 /* 371 * Clock misunderstood our last command? 372 */ 373 if (pp->a_lastcode[0] == '?' || 374 strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { 375 true_doevent(peer, e_Huh); 376 return; 377 } 378 379 /* 380 * Timecode: "nnnnn+nnn-nnn" 381 * (from GOES clock when asked about satellite position) 382 */ 383 if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && 384 (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && 385 sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 386 ) { 387 const char *label = "Botch!"; 388 389 /* 390 * This is less than perfect. Call the (satellite) 391 * either EAST or WEST and adjust slop accodingly 392 * Perfectionists would recalculate the exact delay 393 * and adjust accordingly... 394 */ 395 if (lon > 7000 && lon < 14000) { 396 if (lon < 10000) { 397 new_station = GOES_EAST; 398 label = "EAST"; 399 } else { 400 new_station = GOES_WEST; 401 label = "WEST"; 402 } 403 404 if (new_station != up->station) { 405 double dtemp; 406 407 dtemp = pp->fudgetime1; 408 pp->fudgetime1 = pp->fudgetime2; 409 pp->fudgetime2 = dtemp; 410 up->station = new_station; 411 } 412 } 413 else { 414 /*refclock_report(peer, CEVNT_BADREPLY);*/ 415 label = "UNKNOWN"; 416 } 417 true_debug(peer, "GOES: station %s\n", label); 418 true_doevent(peer, e_Satellite); 419 return; 420 } 421 422 /* 423 * Timecode: "Fnn" 424 * (from TM/TMD clock when it wants to tell us what it's up to.) 425 */ 426 if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { 427 switch (i) { 428 case 50: 429 true_doevent(peer, e_F50); 430 break; 431 case 51: 432 true_doevent(peer, e_F51); 433 break; 434 default: 435 true_debug(peer, "got F%02d - ignoring\n", i); 436 break; 437 } 438 return; 439 } 440 441 /* 442 * Timecode: " TRUETIME Mk III" or " TRUETIME XL" 443 * (from a TM/TMD/XL clock during initialization.) 444 */ 445 if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || 446 strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { 447 true_doevent(peer, e_F18); 448 NLOG(NLOG_CLOCKSTATUS) { 449 msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode); 450 } 451 return; 452 } 453 454 /* 455 * Timecode: "N03726428W12209421+000033" 456 * 1 2 457 * 0123456789012345678901234 458 * (from a TCU during initialization) 459 */ 460 if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && 461 (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && 462 pp->a_lastcode[18] == '+') { 463 true_doevent(peer, e_Location); 464 NLOG(NLOG_CLOCKSTATUS) { 465 msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode); 466 } 467 return; 468 } 469 /* 470 * Timecode: "ddd:hh:mm:ssQ" 471 * (from all clocks supported by this driver.) 472 */ 473 if (pp->a_lastcode[3] == ':' && 474 pp->a_lastcode[6] == ':' && 475 pp->a_lastcode[9] == ':' && 476 sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", 477 &pp->day, &pp->hour, &pp->minute, 478 &pp->second, &synced) == 5) { 479 480 /* 481 * Adjust the synchronize indicator according to timecode 482 * say were OK, and then say not if we really are not OK 483 */ 484 if (synced == '>' || synced == '#' || synced == '?') 485 pp->leap = LEAP_NOTINSYNC; 486 else 487 pp->leap = LEAP_NOWARNING; 488 489 true_doevent(peer, e_TS); 490 491#ifdef CLOCK_PPS720 492 /* If it's taken more than 65ms to get here, we'll lose. */ 493 if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { 494 l_fp off; 495 496#ifdef CLOCK_ATOM 497 /* 498 * find out what time it really is. Include 499 * the count from the PCL720 500 */ 501 if (!clocktime(pp->day, pp->hour, pp->minute, 502 pp->second, GMT, pp->lastrec.l_ui, 503 &pp->yearstart, &off.l_ui)) { 504 refclock_report(peer, CEVNT_BADTIME); 505 return; 506 } 507 off.l_uf = 0; 508#endif 509 510 pp->usec = true_sample720(); 511#ifdef CLOCK_ATOM 512 TVUTOTSF(pp->usec, off.l_uf); 513#endif 514 515 /* 516 * Stomp all over the timestamp that was pulled out 517 * of the input stream. It's irrelevant since we've 518 * adjusted the input time to reflect now (via pp->usec) 519 * rather than when the data was collected. 520 */ 521 get_systime(&pp->lastrec); 522#ifdef CLOCK_ATOM 523 /* 524 * Create a true offset for feeding to pps_sample() 525 */ 526 L_SUB(&off, &pp->lastrec); 527 528 pps_sample(peer, &off); 529#endif 530 true_debug(peer, "true_sample720: %luus\n", pp->usec); 531 } 532#endif 533 534 /* 535 * The clock will blurt a timecode every second but we only 536 * want one when polled. If we havn't been polled, bail out. 537 */ 538 if (!up->polled) 539 return; 540 541 true_doevent(peer, e_Poll); 542 if (!refclock_process(pp)) { 543 refclock_report(peer, CEVNT_BADTIME); 544 return; 545 } 546 /* 547 * If clock is good we send a NOMINAL message so that 548 * any previous BAD messages are nullified 549 */ 550 pp->lastref = pp->lastrec; 551 refclock_receive(peer); 552 refclock_report(peer, CEVNT_NOMINAL); 553 554 /* 555 * We have succedded in answering the poll. 556 * Turn off the flag and return 557 */ 558 up->polled = 0; 559 560 return; 561 } 562 563 /* 564 * No match to known timecodes, report failure and return 565 */ 566 refclock_report(peer, CEVNT_BADREPLY); 567 return; 568} 569 570 571/* 572 * true_send - time to send the clock a signal to cough up a time sample 573 */ 574static void 575true_send( 576 struct peer *peer, 577 const char *cmd 578 ) 579{ 580 struct refclockproc *pp; 581 582 pp = peer->procptr; 583 if (!(pp->sloppyclockflag & CLK_FLAG1)) { 584 register int len = strlen(cmd); 585 586 true_debug(peer, "Send '%s'\n", cmd); 587 if (write(pp->io.fd, cmd, (unsigned)len) != len) 588 refclock_report(peer, CEVNT_FAULT); 589 else 590 pp->polls++; 591 } 592} 593 594 595/* 596 * state machine for initializing and controlling a clock 597 */ 598static void 599true_doevent( 600 struct peer *peer, 601 enum true_event event 602 ) 603{ 604 struct true_unit *up; 605 struct refclockproc *pp; 606 607 pp = peer->procptr; 608 up = (struct true_unit *)pp->unitptr; 609 if (event != e_TS) { 610 NLOG(NLOG_CLOCKSTATUS) { 611 msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s", 612 typeStr(up->type), 613 stateStr(up->state), 614 eventStr(event)); 615 } 616 } 617 true_debug(peer, "clock %s, state %s, event %s\n", 618 typeStr(up->type), stateStr(up->state), eventStr(event)); 619 switch (up->type) { 620 case t_goes: 621 switch (event) { 622 case e_Init: /* FALLTHROUGH */ 623 case e_Satellite: 624 /* 625 * Switch back to on-second time codes and return. 626 */ 627 true_send(peer, "C"); 628 up->state = s_Start; 629 break; 630 case e_Poll: 631 /* 632 * After each poll, check the station (satellite). 633 */ 634 true_send(peer, "P"); 635 /* No state change needed. */ 636 break; 637 default: 638 break; 639 } 640 /* FALLTHROUGH */ 641 case t_omega: 642 switch (event) { 643 case e_Init: 644 true_send(peer, "C"); 645 up->state = s_Start; 646 break; 647 case e_TS: 648 if (up->state != s_Start && up->state != s_Auto) { 649 true_send(peer, "\03\r"); 650 break; 651 } 652 up->state = s_Auto; 653 break; 654 default: 655 break; 656 } 657 break; 658 case t_tm: 659 switch (event) { 660 case e_Init: 661 true_send(peer, "F18\r"); 662 up->state = s_Init; 663 break; 664 case e_F18: 665 true_send(peer, "F50\r"); 666 up->state = s_F18; 667 break; 668 case e_F50: 669 true_send(peer, "F51\r"); 670 up->state = s_F50; 671 break; 672 case e_F51: 673 true_send(peer, "F08\r"); 674 up->state = s_Start; 675 break; 676 case e_TS: 677 if (up->state != s_Start && up->state != s_Auto) { 678 true_send(peer, "\03\r"); 679 break; 680 } 681 up->state = s_Auto; 682 break; 683 default: 684 break; 685 } 686 break; 687 case t_tcu: 688 switch (event) { 689 case e_Init: 690 true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ 691 true_send(peer, "TSU\r"); /* UTC, not GPS. */ 692 true_send(peer, "AU\r"); /* Auto Timestamps. */ 693 up->state = s_Start; 694 break; 695 case e_TS: 696 if (up->state != s_Start && up->state != s_Auto) { 697 true_send(peer, "\03\r"); 698 break; 699 } 700 up->state = s_Auto; 701 break; 702 default: 703 break; 704 } 705 break; 706 case t_unknown: 707 switch (up->state) { 708 case s_Base: 709 if (event != e_Init) 710 abort(); 711 true_send(peer, "P\r"); 712 up->state = s_InqGOES; 713 break; 714 case s_InqGOES: 715 switch (event) { 716 case e_Satellite: 717 up->type = t_goes; 718 true_doevent(peer, e_Init); 719 break; 720 case e_Init: /*FALLTHROUGH*/ 721 case e_Huh: /*FALLTHROUGH*/ 722 case e_TS: 723 up->state = s_InqOmega; 724 true_send(peer, "C\r"); 725 break; 726 default: 727 abort(); 728 } 729 break; 730 case s_InqOmega: 731 switch (event) { 732 case e_TS: 733 up->type = t_omega; 734 up->state = s_Auto; /* Inq side-effect. */ 735 break; 736 case e_Init: /*FALLTHROUGH*/ 737 case e_Huh: 738 up->state = s_InqTM; 739 true_send(peer, "F18\r"); 740 break; 741 default: 742 abort(); 743 } 744 break; 745 case s_InqTM: 746 switch (event) { 747 case e_F18: 748 up->type = t_tm; 749 true_doevent(peer, e_Init); 750 break; 751 case e_Init: /*FALLTHROUGH*/ 752 case e_Huh: 753 true_send(peer, "PO\r"); 754 up->state = s_InqTCU; 755 break; 756 default: 757 abort(); 758 } 759 break; 760 case s_InqTCU: 761 switch (event) { 762 case e_Location: 763 up->type = t_tcu; 764 true_doevent(peer, e_Init); 765 break; 766 case e_Init: /*FALLTHROUGH*/ 767 case e_Huh: 768 up->state = s_Base; 769 sleep(1); /* XXX */ 770 break; 771 default: 772 abort(); 773 } 774 break; 775 /* 776 * An expedient hack to prevent lint complaints, 777 * these don't actually need to be used here... 778 */ 779 case s_Init: 780 case s_F18: 781 case s_F50: 782 case s_Start: 783 case s_Auto: 784 case s_Max: 785 msyslog(LOG_INFO, "TRUE: state %s is unexpected!", stateStr(up->state)); 786 } 787 break; 788 default: 789 abort(); 790 /* NOTREACHED */ 791 } 792 793#ifdef CLOCK_PPS720 794 if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { 795 /* Make counter trigger on gate0, count down from 65535. */ 796 pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); 797 /* 798 * (These constants are OK since 799 * they represent hardware maximums.) 800 */ 801 NLOG(NLOG_CLOCKINFO) { 802 msyslog(LOG_NOTICE, "PCL-720 initialized"); 803 } 804 up->pcl720init++; 805 } 806#endif 807 808 809} 810 811/* 812 * true_poll - called by the transmit procedure 813 */ 814static void 815true_poll( 816 int unit, 817 struct peer *peer 818 ) 819{ 820 struct true_unit *up; 821 struct refclockproc *pp; 822 823 /* 824 * You don't need to poll this clock. It puts out timecodes 825 * once per second. If asked for a timestamp, take note. 826 * The next time a timecode comes in, it will be fed back. 827 */ 828 pp = peer->procptr; 829 up = (struct true_unit *)pp->unitptr; 830 if (up->pollcnt > 0) 831 up->pollcnt--; 832 else { 833 true_doevent(peer, e_Init); 834 refclock_report(peer, CEVNT_TIMEOUT); 835 } 836 837 /* 838 * polled every 64 seconds. Ask true_receive to hand in a 839 * timestamp. 840 */ 841 up->polled = 1; 842 pp->polls++; 843} 844 845#ifdef CLOCK_PPS720 846/* 847 * true_sample720 - sample the PCL-720 848 */ 849static u_long 850true_sample720(void) 851{ 852 unsigned long f; 853 854 /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. 855 * If it is not being held low now, we did not get called 856 * within 65535us. 857 */ 858 if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { 859 NLOG(NLOG_CLOCKINFO) { 860 msyslog(LOG_NOTICE, "PCL-720 out of synch"); 861 } 862 return (0); 863 } 864 f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); 865#ifdef PPS720_DEBUG 866 msyslog(LOG_DEBUG, "PCL-720: %luus", f); 867#endif 868 return (f); 869} 870#endif 871 872#else 873int refclock_true_bs; 874#endif /* REFCLOCK */ 875