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