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