xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_true.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
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