xref: /freebsd/contrib/ntp/ntpd/refclock_true.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
22b15cb3dSCy Schubert  * refclock_true - clock driver for the Kinemetrics/TrueTime receivers
3c0b746e5SOllivier Robert  *	Receiver Version 3.0C - tested plain, with CLKLDISC
42b15cb3dSCy Schubert  *	Development work being done:
52b15cb3dSCy Schubert  *      - Support TL-3 WWV TOD receiver
6c0b746e5SOllivier Robert  */
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
9c0b746e5SOllivier Robert #include <config.h>
10c0b746e5SOllivier Robert #endif
11c0b746e5SOllivier Robert 
12c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
13c0b746e5SOllivier Robert 
142b15cb3dSCy Schubert #include <stdio.h>
152b15cb3dSCy Schubert #include <ctype.h>
162b15cb3dSCy Schubert 
17c0b746e5SOllivier Robert #include "ntpd.h"
18c0b746e5SOllivier Robert #include "ntp_io.h"
19c0b746e5SOllivier Robert #include "ntp_refclock.h"
20c0b746e5SOllivier Robert #include "ntp_unixtime.h"
21c0b746e5SOllivier Robert #include "ntp_stdlib.h"
22c0b746e5SOllivier Robert 
23c0b746e5SOllivier Robert /* This should be an atom clock but those are very hard to build.
24c0b746e5SOllivier Robert  *
25c0b746e5SOllivier Robert  * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
26c0b746e5SOllivier Robert  * of TTL input and output pins, all brought out to the back panel.  If you
27c0b746e5SOllivier Robert  * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
28c0b746e5SOllivier Robert  * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
29c0b746e5SOllivier Robert  * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
30c0b746e5SOllivier Robert  * number of uSecs since the last PPS upward swing, mediated by reading OUT0
31c0b746e5SOllivier Robert  * to find out if the counter has wrapped around (this happens if more than
32c0b746e5SOllivier Robert  * 65535us (65ms) elapses between the PPS event and our being called.)
33c0b746e5SOllivier Robert  */
34c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
35c0b746e5SOllivier Robert # undef min	/* XXX */
36c0b746e5SOllivier Robert # undef max	/* XXX */
37c0b746e5SOllivier Robert # include <machine/inline.h>
38c0b746e5SOllivier Robert # include <sys/pcl720.h>
39c0b746e5SOllivier Robert # include <sys/i8253.h>
40c0b746e5SOllivier Robert # define PCL720_IOB 0x2a0	/* XXX */
41c0b746e5SOllivier Robert # define PCL720_CTR 0		/* XXX */
42c0b746e5SOllivier Robert #endif
43c0b746e5SOllivier Robert 
44c0b746e5SOllivier Robert /*
45c0b746e5SOllivier Robert  * Support for Kinemetrics Truetime Receivers
462b15cb3dSCy Schubert  *	GOES:           (468-DC, usable with GPS->GOES converting antenna)
472b15cb3dSCy Schubert  *	GPS/TM-TMD:
482b15cb3dSCy Schubert  *	XL-DC:		(a 151-602-210, reported by the driver as a GPS/TM-TMD)
492b15cb3dSCy Schubert  *	GPS-800 TCU:	(an 805-957 with the RS232 Talker/Listener module)
502b15cb3dSCy Schubert  *      TL-3:           3 channel WWV/H receiver w/ IRIG and RS-232 outputs
51c0b746e5SOllivier Robert  *	OM-DC:		getting stale ("OMEGA")
52c0b746e5SOllivier Robert  *
53c0b746e5SOllivier Robert  * Most of this code is originally from refclock_wwvb.c with thanks.
54c0b746e5SOllivier Robert  * It has been so mangled that wwvb is not a recognizable ancestor.
55c0b746e5SOllivier Robert  *
56c0b746e5SOllivier Robert  * Timcode format: ADDD:HH:MM:SSQCL
57c0b746e5SOllivier Robert  *	A - control A		(this is stripped before we see it)
58c0b746e5SOllivier Robert  *	Q - Quality indication	(see below)
59c0b746e5SOllivier Robert  *	C - Carriage return
60c0b746e5SOllivier Robert  *	L - Line feed
61c0b746e5SOllivier Robert  *
62c0b746e5SOllivier Robert  * Quality codes indicate possible error of
63c0b746e5SOllivier Robert  *   468-DC GOES Receiver:
64224ba2bdSOllivier Robert  *   GPS-TM/TMD Receiver: (default quality codes for XL-DC)
65224ba2bdSOllivier Robert  *       ?     +/- 1  milliseconds	#     +/- 100 microseconds
66224ba2bdSOllivier Robert  *       *     +/- 10 microseconds	.     +/- 1   microsecond
67224ba2bdSOllivier Robert  *     space   less than 1 microsecond
682b15cb3dSCy Schubert  *   TL-3 Receiver: (default quality codes for TL-3)
692b15cb3dSCy Schubert  *       ?     unknown quality (receiver is unlocked)
702b15cb3dSCy Schubert  *     space   +/- 5 milliseconds
71224ba2bdSOllivier Robert  *   OM-DC OMEGA Receiver: (default quality codes for OMEGA)
72224ba2bdSOllivier Robert  *   WARNING OMEGA navigation system is no longer existent
73c0b746e5SOllivier Robert  *       >     >+- 5 seconds
74c0b746e5SOllivier Robert  *       ?     >+/- 500 milliseconds    #     >+/- 50 milliseconds
75c0b746e5SOllivier Robert  *       *     >+/- 5 milliseconds      .     >+/- 1 millisecond
76c0b746e5SOllivier Robert  *      A-H    less than 1 millisecond.  Character indicates which station
77c0b746e5SOllivier Robert  *	       is being received as follows:
78c0b746e5SOllivier Robert  *	       A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
79c0b746e5SOllivier Robert  *	       E = La Reunion, F = Argentina, G = Australia, H = Japan.
80c0b746e5SOllivier Robert  *
81c0b746e5SOllivier Robert  * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
82c0b746e5SOllivier Robert  *
83c0b746e5SOllivier Robert  * Notes on 468-DC and OMEGA receiver:
84c0b746e5SOllivier Robert  *
85c0b746e5SOllivier Robert  * Send the clock a 'R' or 'C' and once per second a timestamp will
86c0b746e5SOllivier Robert  * appear.  Send a 'P' to get the satellite position once (GOES only.)
87c0b746e5SOllivier Robert  *
88c0b746e5SOllivier Robert  * Notes on the 468-DC receiver:
89c0b746e5SOllivier Robert  *
90c0b746e5SOllivier Robert  * Since the old east/west satellite locations are only historical, you can't
91c0b746e5SOllivier Robert  * set your clock propagation delay settings correctly and still use
92c0b746e5SOllivier Robert  * automatic mode. The manual says to use a compromise when setting the
93c0b746e5SOllivier Robert  * switches. This results in significant errors. The solution; use fudge
94c0b746e5SOllivier Robert  * time1 and time2 to incorporate corrections. If your clock is set for
95c0b746e5SOllivier Robert  * 50 and it should be 58 for using the west and 46 for using the east,
96c0b746e5SOllivier Robert  * use the line
97c0b746e5SOllivier Robert  *
98c0b746e5SOllivier Robert  * fudge 127.127.5.0 time1 +0.008 time2 -0.004
99c0b746e5SOllivier Robert  *
100c0b746e5SOllivier Robert  * This corrects the 4 milliseconds advance and 8 milliseconds retard
101c0b746e5SOllivier Robert  * needed. The software will ask the clock which satellite it sees.
102c0b746e5SOllivier Robert  *
1032b15cb3dSCy Schubert  * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver:
1042b15cb3dSCy Schubert  *
1052b15cb3dSCy Schubert  * This clock may be polled, or send one timecode per second.
1062b15cb3dSCy Schubert  * That mode may be toggled via the front panel ("C" mode), or controlled
1072b15cb3dSCy Schubert  * from the RS-232 port.  Send the receiver "ST1" to turn it on, and
1082b15cb3dSCy Schubert  * "ST0" to turn it off.  Send "QV" to get the firmware revision (useful
1092b15cb3dSCy Schubert  * for identifying this model.)
1102b15cb3dSCy Schubert  *
1112b15cb3dSCy Schubert  * Note that it can take several polling cycles, especially if the receiver
1122b15cb3dSCy Schubert  * was in the continuous timecode mode.  (It can be slow to leave that mode.)
1132b15cb3dSCy Schubert  *
1142b15cb3dSCy Schubert  * ntp.conf parameters:
115c0b746e5SOllivier Robert  * time1   - offset applied to samples when reading WEST satellite (default = 0)
116c0b746e5SOllivier Robert  * time2   - offset applied to samples when reading EAST satellite (default = 0)
1172b15cb3dSCy Schubert  * stratum - stratum to assign to this clock (default = 0)
1182b15cb3dSCy Schubert  * refid   - refid assigned to this clock (default = "TRUE", see below)
119c0b746e5SOllivier Robert  * flag1   - will silence the clock side of ntpd, just reading the clock
120c0b746e5SOllivier Robert  *	     without trying to write to it.  (default = 0)
121c0b746e5SOllivier Robert  * flag2   - generate a debug file /tmp/true%d.
122c0b746e5SOllivier Robert  * flag3   - enable ppsclock streams module
123c0b746e5SOllivier Robert  * flag4   - use the PCL-720 (BSD/OS only)
124c0b746e5SOllivier Robert  */
125c0b746e5SOllivier Robert 
126224ba2bdSOllivier Robert 
127c0b746e5SOllivier Robert /*
128c0b746e5SOllivier Robert  * Definitions
129c0b746e5SOllivier Robert  */
130c0b746e5SOllivier Robert #define	DEVICE		"/dev/true%d"
131c0b746e5SOllivier Robert #define	SPEED232	B9600	/* 9600 baud */
132c0b746e5SOllivier Robert 
133c0b746e5SOllivier Robert /*
134c0b746e5SOllivier Robert  * Radio interface parameters
135c0b746e5SOllivier Robert  */
136c0b746e5SOllivier Robert #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
137c0b746e5SOllivier Robert #define	REFID		"TRUE"	/* reference id */
138c0b746e5SOllivier Robert #define	DESCRIPTION	"Kinemetrics/TrueTime Receiver"
139c0b746e5SOllivier Robert 
140c0b746e5SOllivier Robert /*
141c0b746e5SOllivier Robert  * Tags which station (satellite) we see
142c0b746e5SOllivier Robert  */
143c0b746e5SOllivier Robert #define GOES_WEST	0	/* Default to WEST satellite and apply time1 */
144c0b746e5SOllivier Robert #define GOES_EAST	1	/* until you discover otherwise */
145c0b746e5SOllivier Robert 
146c0b746e5SOllivier Robert /*
147c0b746e5SOllivier Robert  * used by the state machine
148c0b746e5SOllivier Robert  */
149c0b746e5SOllivier Robert enum true_event	{e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
1502b15cb3dSCy Schubert 		 e_TL3, e_Poll, e_Location, e_TS, e_Max};
151c0b746e5SOllivier Robert const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
1522b15cb3dSCy Schubert 			"TL3", "Poll", "Location", "TS"};
153c0b746e5SOllivier Robert #define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
154c0b746e5SOllivier Robert 
155c0b746e5SOllivier Robert enum true_state	{s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
1562b15cb3dSCy Schubert 		 s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
157c0b746e5SOllivier Robert const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
1582b15cb3dSCy Schubert 			"InqTL3", "Init", "F18", "F50", "Start", "Auto"};
159c0b746e5SOllivier Robert #define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
160c0b746e5SOllivier Robert 
1612b15cb3dSCy Schubert enum true_type	{t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max};
1622b15cb3dSCy Schubert const char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"};
163c0b746e5SOllivier Robert #define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
164c0b746e5SOllivier Robert 
165c0b746e5SOllivier Robert /*
166c0b746e5SOllivier Robert  * unit control structure
167c0b746e5SOllivier Robert  */
168c0b746e5SOllivier Robert struct true_unit {
169c0b746e5SOllivier Robert 	unsigned int	pollcnt;	/* poll message counter */
170c0b746e5SOllivier Robert 	unsigned int	station;	/* which station we are on */
171c0b746e5SOllivier Robert 	unsigned int	polled;		/* Hand in a time sample? */
172c0b746e5SOllivier Robert 	enum true_state	state;		/* state machine */
173c0b746e5SOllivier Robert 	enum true_type	type;		/* what kind of clock is it? */
174c0b746e5SOllivier Robert 	int		unit;		/* save an extra copy of this */
175c0b746e5SOllivier Robert 	FILE		*debug;		/* debug logging file */
176c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
177c0b746e5SOllivier Robert 	int		pcl720init;	/* init flag for PCL 720 */
178c0b746e5SOllivier Robert #endif
179c0b746e5SOllivier Robert };
180c0b746e5SOllivier Robert 
181c0b746e5SOllivier Robert /*
182c0b746e5SOllivier Robert  * Function prototypes
183c0b746e5SOllivier Robert  */
1842b15cb3dSCy Schubert static	int	true_start	(int, struct peer *);
1852b15cb3dSCy Schubert static	void	true_shutdown	(int, struct peer *);
1862b15cb3dSCy Schubert static	void	true_receive	(struct recvbuf *);
1872b15cb3dSCy Schubert static	void	true_poll	(int, struct peer *);
1882b15cb3dSCy Schubert static	void	true_send	(struct peer *, const char *);
1892b15cb3dSCy Schubert static	void	true_doevent	(struct peer *, enum true_event);
190c0b746e5SOllivier Robert 
191c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
1922b15cb3dSCy Schubert static	u_long	true_sample720	(void);
193c0b746e5SOllivier Robert #endif
194c0b746e5SOllivier Robert 
195c0b746e5SOllivier Robert /*
196c0b746e5SOllivier Robert  * Transfer vector
197c0b746e5SOllivier Robert  */
198c0b746e5SOllivier Robert struct	refclock refclock_true = {
199c0b746e5SOllivier Robert 	true_start,		/* start up driver */
200c0b746e5SOllivier Robert 	true_shutdown,		/* shut down driver */
201c0b746e5SOllivier Robert 	true_poll,		/* transmit poll message */
202c0b746e5SOllivier Robert 	noentry,		/* not used (old true_control) */
203c0b746e5SOllivier Robert 	noentry,		/* initialize driver (not used) */
204c0b746e5SOllivier Robert 	noentry,		/* not used (old true_buginfo) */
205c0b746e5SOllivier Robert 	NOFLAGS			/* not used */
206c0b746e5SOllivier Robert };
207c0b746e5SOllivier Robert 
208c0b746e5SOllivier Robert 
209c0b746e5SOllivier Robert #if !defined(__STDC__)
210c0b746e5SOllivier Robert # define true_debug (void)
211c0b746e5SOllivier Robert #else
2122b15cb3dSCy Schubert NTP_PRINTF(2, 3)
213c0b746e5SOllivier Robert static void
214c0b746e5SOllivier Robert true_debug(struct peer *peer, const char *fmt, ...)
215c0b746e5SOllivier Robert {
216c0b746e5SOllivier Robert 	va_list ap;
217c0b746e5SOllivier Robert 	int want_debugging, now_debugging;
218c0b746e5SOllivier Robert 	struct refclockproc *pp;
219c0b746e5SOllivier Robert 	struct true_unit *up;
220c0b746e5SOllivier Robert 
221c0b746e5SOllivier Robert 	va_start(ap, fmt);
222c0b746e5SOllivier Robert 	pp = peer->procptr;
2232b15cb3dSCy Schubert 	up = pp->unitptr;
224c0b746e5SOllivier Robert 
225c0b746e5SOllivier Robert 	want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
226c0b746e5SOllivier Robert 	now_debugging = (up->debug != NULL);
227c0b746e5SOllivier Robert 	if (want_debugging != now_debugging)
228c0b746e5SOllivier Robert 	{
229c0b746e5SOllivier Robert 		if (want_debugging) {
230224ba2bdSOllivier Robert 			char filename[40];
231224ba2bdSOllivier Robert 			int fd;
232c0b746e5SOllivier Robert 
2332b15cb3dSCy Schubert 			snprintf(filename, sizeof(filename),
2342b15cb3dSCy Schubert 				 "/tmp/true%d.debug", up->unit);
2352b15cb3dSCy Schubert 			fd = open(filename, O_CREAT | O_WRONLY | O_EXCL,
2362b15cb3dSCy Schubert 				  0600);
237276da39aSCy Schubert 			if (fd >= 0 && (up->debug = fdopen(fd, "w"))) {
238c0b746e5SOllivier Robert #ifdef HAVE_SETVBUF
239c0b746e5SOllivier Robert 				static char buf[BUFSIZ];
2402b15cb3dSCy Schubert 
241c0b746e5SOllivier Robert 				setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
242c0b746e5SOllivier Robert #else
243c0b746e5SOllivier Robert 				setlinebuf(up->debug);
244c0b746e5SOllivier Robert #endif
245c0b746e5SOllivier Robert 			}
246c0b746e5SOllivier Robert 		} else {
247c0b746e5SOllivier Robert 			fclose(up->debug);
248c0b746e5SOllivier Robert 			up->debug = NULL;
249c0b746e5SOllivier Robert 		}
250c0b746e5SOllivier Robert 	}
251c0b746e5SOllivier Robert 
252c0b746e5SOllivier Robert 	if (up->debug) {
253c0b746e5SOllivier Robert 		fprintf(up->debug, "true%d: ", up->unit);
254c0b746e5SOllivier Robert 		vfprintf(up->debug, fmt, ap);
255c0b746e5SOllivier Robert 	}
2562b15cb3dSCy Schubert 	va_end(ap);
257c0b746e5SOllivier Robert }
258c0b746e5SOllivier Robert #endif /*STDC*/
259c0b746e5SOllivier Robert 
260c0b746e5SOllivier Robert /*
261c0b746e5SOllivier Robert  * true_start - open the devices and initialize data for processing
262c0b746e5SOllivier Robert  */
263c0b746e5SOllivier Robert static int
264c0b746e5SOllivier Robert true_start(
265c0b746e5SOllivier Robert 	int unit,
266c0b746e5SOllivier Robert 	struct peer *peer
267c0b746e5SOllivier Robert 	)
268c0b746e5SOllivier Robert {
269c0b746e5SOllivier Robert 	register struct true_unit *up;
270c0b746e5SOllivier Robert 	struct refclockproc *pp;
271224ba2bdSOllivier Robert 	char device[40];
272c0b746e5SOllivier Robert 	int fd;
273c0b746e5SOllivier Robert 
274c0b746e5SOllivier Robert 	/*
275c0b746e5SOllivier Robert 	 * Open serial port
276c0b746e5SOllivier Robert 	 */
2772b15cb3dSCy Schubert 	snprintf(device, sizeof(device), DEVICE, unit);
278a466cc55SCy Schubert 	fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK);
2792b15cb3dSCy Schubert 	if (fd <= 0)
2802b15cb3dSCy Schubert 		return 0;
281c0b746e5SOllivier Robert 
282c0b746e5SOllivier Robert 	/*
283c0b746e5SOllivier Robert 	 * Allocate and initialize unit structure
284c0b746e5SOllivier Robert 	 */
2852b15cb3dSCy Schubert 	up = emalloc_zero(sizeof(*up));
286c0b746e5SOllivier Robert 	pp = peer->procptr;
287c0b746e5SOllivier Robert 	pp->io.clock_recv = true_receive;
2882b15cb3dSCy Schubert 	pp->io.srcclock = peer;
289c0b746e5SOllivier Robert 	pp->io.datalen = 0;
290c0b746e5SOllivier Robert 	pp->io.fd = fd;
291c0b746e5SOllivier Robert 	if (!io_addclock(&pp->io)) {
2922b15cb3dSCy Schubert 		close(fd);
2932b15cb3dSCy Schubert 		pp->io.fd = -1;
294c0b746e5SOllivier Robert 		free(up);
295c0b746e5SOllivier Robert 		return (0);
296c0b746e5SOllivier Robert 	}
2972b15cb3dSCy Schubert 	pp->unitptr = up;
298c0b746e5SOllivier Robert 
299c0b746e5SOllivier Robert 	/*
300c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
301c0b746e5SOllivier Robert 	 */
302c0b746e5SOllivier Robert 	peer->precision = PRECISION;
303c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
3042b15cb3dSCy Schubert 	memcpy(&pp->refid, REFID, 4);
305c0b746e5SOllivier Robert 	up->pollcnt = 2;
306c0b746e5SOllivier Robert 	up->type = t_unknown;
307c0b746e5SOllivier Robert 	up->state = s_Base;
3082b15cb3dSCy Schubert 
3092b15cb3dSCy Schubert 	/*
3102b15cb3dSCy Schubert 	 * Send a CTRL-C character at the start,
3112b15cb3dSCy Schubert 	 * just in case the clock is already
3122b15cb3dSCy Schubert 	 * sending timecodes
3132b15cb3dSCy Schubert 	 */
3142b15cb3dSCy Schubert 	true_send(peer, "\03\r");
3152b15cb3dSCy Schubert 
316c0b746e5SOllivier Robert 	true_doevent(peer, e_Init);
3172b15cb3dSCy Schubert 
318c0b746e5SOllivier Robert 	return (1);
319c0b746e5SOllivier Robert }
320c0b746e5SOllivier Robert 
3212b15cb3dSCy Schubert 
322c0b746e5SOllivier Robert /*
323c0b746e5SOllivier Robert  * true_shutdown - shut down the clock
324c0b746e5SOllivier Robert  */
325c0b746e5SOllivier Robert static void
326c0b746e5SOllivier Robert true_shutdown(
327c0b746e5SOllivier Robert 	int unit,
328c0b746e5SOllivier Robert 	struct peer *peer
329c0b746e5SOllivier Robert 	)
330c0b746e5SOllivier Robert {
331c0b746e5SOllivier Robert 	register struct true_unit *up;
332c0b746e5SOllivier Robert 	struct refclockproc *pp;
333c0b746e5SOllivier Robert 
334c0b746e5SOllivier Robert 	pp = peer->procptr;
3352b15cb3dSCy Schubert 	up = pp->unitptr;
3362b15cb3dSCy Schubert 	if (pp->io.fd != -1)
337c0b746e5SOllivier Robert 		io_closeclock(&pp->io);
3382b15cb3dSCy Schubert 	if (up != NULL)
339c0b746e5SOllivier Robert 		free(up);
340c0b746e5SOllivier Robert }
341c0b746e5SOllivier Robert 
342c0b746e5SOllivier Robert 
343c0b746e5SOllivier Robert /*
344c0b746e5SOllivier Robert  * true_receive - receive data from the serial interface on a clock
345c0b746e5SOllivier Robert  */
346c0b746e5SOllivier Robert static void
347c0b746e5SOllivier Robert true_receive(
348c0b746e5SOllivier Robert 	struct recvbuf *rbufp
349c0b746e5SOllivier Robert 	)
350c0b746e5SOllivier Robert {
351c0b746e5SOllivier Robert 	register struct true_unit *up;
352c0b746e5SOllivier Robert 	struct refclockproc *pp;
353c0b746e5SOllivier Robert 	struct peer *peer;
354c0b746e5SOllivier Robert 	u_short new_station;
355c0b746e5SOllivier Robert 	char synced;
356c0b746e5SOllivier Robert 	int i;
357c0b746e5SOllivier Robert 	int lat, lon, off;	/* GOES Satellite position */
3582b15cb3dSCy Schubert 	/* These variables hold data until we decide to keep it */
359224ba2bdSOllivier Robert 	char	rd_lastcode[BMAX];
360224ba2bdSOllivier Robert 	l_fp	rd_tmp;
361224ba2bdSOllivier Robert 	u_short	rd_lencode;
362c0b746e5SOllivier Robert 
363c0b746e5SOllivier Robert 	/*
364c0b746e5SOllivier Robert 	 * Get the clock this applies to and pointers to the data.
365c0b746e5SOllivier Robert 	 */
3662b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
367c0b746e5SOllivier Robert 	pp = peer->procptr;
3682b15cb3dSCy Schubert 	up = pp->unitptr;
369c0b746e5SOllivier Robert 
370c0b746e5SOllivier Robert 	/*
371c0b746e5SOllivier Robert 	 * Read clock output.  Automatically handles STREAMS, CLKLDISC.
372c0b746e5SOllivier Robert 	 */
373224ba2bdSOllivier Robert 	rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
374224ba2bdSOllivier Robert 	rd_lastcode[rd_lencode] = '\0';
375c0b746e5SOllivier Robert 
376c0b746e5SOllivier Robert 	/*
377c0b746e5SOllivier Robert 	 * There is a case where <cr><lf> generates 2 timestamps.
378c0b746e5SOllivier Robert 	 */
379224ba2bdSOllivier Robert 	if (rd_lencode == 0)
380c0b746e5SOllivier Robert 		return;
381224ba2bdSOllivier Robert 	pp->lencode = rd_lencode;
3822b15cb3dSCy Schubert 	strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode));
383224ba2bdSOllivier Robert 	pp->lastrec = rd_tmp;
3842b15cb3dSCy Schubert 	true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode,
3852b15cb3dSCy Schubert 		   pp->lencode);
386c0b746e5SOllivier Robert 
387c0b746e5SOllivier Robert 	up->pollcnt = 2;
388c0b746e5SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
389c0b746e5SOllivier Robert 
390c0b746e5SOllivier Robert 	/*
391c0b746e5SOllivier Robert 	 * We get down to business, check the timecode format and decode
392c0b746e5SOllivier Robert 	 * its contents. This code decodes a multitude of different
393c0b746e5SOllivier Robert 	 * clock messages. Timecodes are processed if needed. All replies
394c0b746e5SOllivier Robert 	 * will be run through the state machine to tweak driver options
395c0b746e5SOllivier Robert 	 * and program the clock.
396c0b746e5SOllivier Robert 	 */
397c0b746e5SOllivier Robert 
398c0b746e5SOllivier Robert 	/*
399c0b746e5SOllivier Robert 	 * Clock misunderstood our last command?
400c0b746e5SOllivier Robert 	 */
401224ba2bdSOllivier Robert 	if (pp->a_lastcode[0] == '?' ||
402224ba2bdSOllivier Robert 	    strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
403c0b746e5SOllivier Robert 		true_doevent(peer, e_Huh);
404c0b746e5SOllivier Robert 		return;
405c0b746e5SOllivier Robert 	}
406c0b746e5SOllivier Robert 
407c0b746e5SOllivier Robert 	/*
408c0b746e5SOllivier Robert 	 * Timecode: "nnnnn+nnn-nnn"
409c0b746e5SOllivier Robert 	 * (from GOES clock when asked about satellite position)
410c0b746e5SOllivier Robert 	 */
411c0b746e5SOllivier Robert 	if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
412c0b746e5SOllivier Robert 	    (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
413c0b746e5SOllivier Robert 	    sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
414c0b746e5SOllivier Robert 	    ) {
415c0b746e5SOllivier Robert 		const char *label = "Botch!";
416c0b746e5SOllivier Robert 
417c0b746e5SOllivier Robert 		/*
418c0b746e5SOllivier Robert 		 * This is less than perfect.  Call the (satellite)
419c0b746e5SOllivier Robert 		 * either EAST or WEST and adjust slop accodingly
420c0b746e5SOllivier Robert 		 * Perfectionists would recalculate the exact delay
421c0b746e5SOllivier Robert 		 * and adjust accordingly...
422c0b746e5SOllivier Robert 		 */
423c0b746e5SOllivier Robert 		if (lon > 7000 && lon < 14000) {
424c0b746e5SOllivier Robert 			if (lon < 10000) {
425c0b746e5SOllivier Robert 				new_station = GOES_EAST;
426c0b746e5SOllivier Robert 				label = "EAST";
427c0b746e5SOllivier Robert 			} else {
428c0b746e5SOllivier Robert 				new_station = GOES_WEST;
429c0b746e5SOllivier Robert 				label = "WEST";
430c0b746e5SOllivier Robert 			}
431c0b746e5SOllivier Robert 
432c0b746e5SOllivier Robert 			if (new_station != up->station) {
433c0b746e5SOllivier Robert 				double dtemp;
434c0b746e5SOllivier Robert 
435c0b746e5SOllivier Robert 				dtemp = pp->fudgetime1;
436c0b746e5SOllivier Robert 				pp->fudgetime1 = pp->fudgetime2;
437c0b746e5SOllivier Robert 				pp->fudgetime2 = dtemp;
438c0b746e5SOllivier Robert 				up->station = new_station;
439c0b746e5SOllivier Robert 			}
440c0b746e5SOllivier Robert 		}
441c0b746e5SOllivier Robert 		else {
442ce265a54SOllivier Robert 			/*refclock_report(peer, CEVNT_BADREPLY);*/
443c0b746e5SOllivier Robert 			label = "UNKNOWN";
444c0b746e5SOllivier Robert 		}
445c0b746e5SOllivier Robert 		true_debug(peer, "GOES: station %s\n", label);
446c0b746e5SOllivier Robert 		true_doevent(peer, e_Satellite);
447c0b746e5SOllivier Robert 		return;
448c0b746e5SOllivier Robert 	}
449c0b746e5SOllivier Robert 
450c0b746e5SOllivier Robert 	/*
451c0b746e5SOllivier Robert 	 * Timecode: "Fnn"
452c0b746e5SOllivier Robert 	 * (from TM/TMD clock when it wants to tell us what it's up to.)
453c0b746e5SOllivier Robert 	 */
454c0b746e5SOllivier Robert 	if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
455c0b746e5SOllivier Robert 		switch (i) {
456c0b746e5SOllivier Robert 		case 50:
457c0b746e5SOllivier Robert 			true_doevent(peer, e_F50);
458c0b746e5SOllivier Robert 			break;
459c0b746e5SOllivier Robert 		case 51:
460c0b746e5SOllivier Robert 			true_doevent(peer, e_F51);
461c0b746e5SOllivier Robert 			break;
462c0b746e5SOllivier Robert 		default:
463c0b746e5SOllivier Robert 			true_debug(peer, "got F%02d - ignoring\n", i);
464c0b746e5SOllivier Robert 			break;
465c0b746e5SOllivier Robert 		}
466c0b746e5SOllivier Robert 		return;
467c0b746e5SOllivier Robert 	}
468c0b746e5SOllivier Robert 
469c0b746e5SOllivier Robert         /*
4702b15cb3dSCy Schubert          * Timecode: "VER xx.xx"
4712b15cb3dSCy Schubert          * (from a TL3 when sent "QV", so id's it during initialization.)
4722b15cb3dSCy Schubert          */
4732b15cb3dSCy Schubert         if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' &&
4742b15cb3dSCy Schubert             pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') {
4752b15cb3dSCy Schubert                 true_doevent(peer, e_TL3);
4762b15cb3dSCy Schubert                 NLOG(NLOG_CLOCKSTATUS) {
4772b15cb3dSCy Schubert                         msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode);
4782b15cb3dSCy Schubert                 }
4792b15cb3dSCy Schubert                 return;
4802b15cb3dSCy Schubert         }
4812b15cb3dSCy Schubert 
4822b15cb3dSCy Schubert 	/*
483224ba2bdSOllivier Robert 	 * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
484224ba2bdSOllivier Robert 	 * (from a TM/TMD/XL clock during initialization.)
485c0b746e5SOllivier Robert 	 */
4862b15cb3dSCy Schubert 	if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 ||
487224ba2bdSOllivier Robert 	    strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
488c0b746e5SOllivier Robert 		true_doevent(peer, e_F18);
489c0b746e5SOllivier Robert 		NLOG(NLOG_CLOCKSTATUS) {
490224ba2bdSOllivier Robert 			msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
491c0b746e5SOllivier Robert 		}
492c0b746e5SOllivier Robert 		return;
493c0b746e5SOllivier Robert 	}
494c0b746e5SOllivier Robert 
495c0b746e5SOllivier Robert 	/*
496c0b746e5SOllivier Robert 	 * Timecode: "N03726428W12209421+000033"
497c0b746e5SOllivier Robert 	 *			1	   2
4982b15cb3dSCy Schubert 	 * index      0123456789012345678901234
499c0b746e5SOllivier Robert 	 * (from a TCU during initialization)
500c0b746e5SOllivier Robert 	 */
501c0b746e5SOllivier Robert 	if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
502c0b746e5SOllivier Robert 	    (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
503c0b746e5SOllivier Robert 	    pp->a_lastcode[18] == '+') {
504c0b746e5SOllivier Robert 		true_doevent(peer, e_Location);
505c0b746e5SOllivier Robert 		NLOG(NLOG_CLOCKSTATUS) {
506c0b746e5SOllivier Robert 			msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
507c0b746e5SOllivier Robert 		}
508c0b746e5SOllivier Robert 		return;
509c0b746e5SOllivier Robert 	}
510c0b746e5SOllivier Robert 	/*
511c0b746e5SOllivier Robert 	 * Timecode: "ddd:hh:mm:ssQ"
5122b15cb3dSCy Schubert 	 *			1	   2
5132b15cb3dSCy Schubert 	 * index      0123456789012345678901234
514c0b746e5SOllivier Robert 	 * (from all clocks supported by this driver.)
515c0b746e5SOllivier Robert 	 */
516c0b746e5SOllivier Robert 	if (pp->a_lastcode[3] == ':' &&
517c0b746e5SOllivier Robert 	    pp->a_lastcode[6] == ':' &&
518c0b746e5SOllivier Robert 	    pp->a_lastcode[9] == ':' &&
519c0b746e5SOllivier Robert 	    sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
520c0b746e5SOllivier Robert 		   &pp->day, &pp->hour, &pp->minute,
521c0b746e5SOllivier Robert 		   &pp->second, &synced) == 5) {
522c0b746e5SOllivier Robert 
523c0b746e5SOllivier Robert 		/*
524c0b746e5SOllivier Robert 		 * Adjust the synchronize indicator according to timecode
525224ba2bdSOllivier Robert 		 * say were OK, and then say not if we really are not OK
526c0b746e5SOllivier Robert 		 */
5272b15cb3dSCy Schubert 		if (synced == '>' || synced == '#' || synced == '?'
5282b15cb3dSCy Schubert 		    || synced == 'X')
529c0b746e5SOllivier Robert 			pp->leap = LEAP_NOTINSYNC;
530224ba2bdSOllivier Robert 		else
531224ba2bdSOllivier Robert 			pp->leap = LEAP_NOWARNING;
532c0b746e5SOllivier Robert 
533c0b746e5SOllivier Robert 		true_doevent(peer, e_TS);
534c0b746e5SOllivier Robert 
535c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
536c0b746e5SOllivier Robert 		/* If it's taken more than 65ms to get here, we'll lose. */
537c0b746e5SOllivier Robert 		if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
538c0b746e5SOllivier Robert 			l_fp   off;
539c0b746e5SOllivier Robert 
540c0b746e5SOllivier Robert #ifdef CLOCK_ATOM
541c0b746e5SOllivier Robert 			/*
542c0b746e5SOllivier Robert 			 * find out what time it really is. Include
543c0b746e5SOllivier Robert 			 * the count from the PCL720
544c0b746e5SOllivier Robert 			 */
545c0b746e5SOllivier Robert 			if (!clocktime(pp->day, pp->hour, pp->minute,
546c0b746e5SOllivier Robert 				       pp->second, GMT, pp->lastrec.l_ui,
547c0b746e5SOllivier Robert 				       &pp->yearstart, &off.l_ui)) {
548c0b746e5SOllivier Robert 				refclock_report(peer, CEVNT_BADTIME);
549c0b746e5SOllivier Robert 				return;
550c0b746e5SOllivier Robert 			}
5519c2daa00SOllivier Robert 			off.l_uf = 0;
552c0b746e5SOllivier Robert #endif
553c0b746e5SOllivier Robert 
554c0b746e5SOllivier Robert 			pp->usec = true_sample720();
555c0b746e5SOllivier Robert #ifdef CLOCK_ATOM
556c0b746e5SOllivier Robert 			TVUTOTSF(pp->usec, off.l_uf);
557c0b746e5SOllivier Robert #endif
558c0b746e5SOllivier Robert 
559c0b746e5SOllivier Robert 			/*
560c0b746e5SOllivier Robert 			 * Stomp all over the timestamp that was pulled out
561c0b746e5SOllivier Robert 			 * of the input stream. It's irrelevant since we've
562c0b746e5SOllivier Robert 			 * adjusted the input time to reflect now (via pp->usec)
563c0b746e5SOllivier Robert 			 * rather than when the data was collected.
564c0b746e5SOllivier Robert 			 */
565c0b746e5SOllivier Robert 			get_systime(&pp->lastrec);
566c0b746e5SOllivier Robert #ifdef CLOCK_ATOM
567c0b746e5SOllivier Robert 			/*
568c0b746e5SOllivier Robert 			 * Create a true offset for feeding to pps_sample()
569c0b746e5SOllivier Robert 			 */
570c0b746e5SOllivier Robert 			L_SUB(&off, &pp->lastrec);
571c0b746e5SOllivier Robert 
572c0b746e5SOllivier Robert 			pps_sample(peer, &off);
573c0b746e5SOllivier Robert #endif
574c0b746e5SOllivier Robert 			true_debug(peer, "true_sample720: %luus\n", pp->usec);
575c0b746e5SOllivier Robert 		}
576c0b746e5SOllivier Robert #endif
577c0b746e5SOllivier Robert 
578c0b746e5SOllivier Robert 		/*
579c0b746e5SOllivier Robert 		 * The clock will blurt a timecode every second but we only
580c0b746e5SOllivier Robert 		 * want one when polled.  If we havn't been polled, bail out.
581c0b746e5SOllivier Robert 		 */
582c0b746e5SOllivier Robert 		if (!up->polled)
583c0b746e5SOllivier Robert 			return;
584c0b746e5SOllivier Robert 
5852b15cb3dSCy Schubert                 /* We only call doevent if additional things need be done
5862b15cb3dSCy Schubert                  * at poll interval.  Currently, its only for GOES.  We also
5872b15cb3dSCy Schubert                  * call it for clock unknown so that it gets logged.
5882b15cb3dSCy Schubert                  */
5892b15cb3dSCy Schubert                 if (up->type == t_goes || up->type == t_unknown)
590c0b746e5SOllivier Robert                     true_doevent(peer, e_Poll);
5912b15cb3dSCy Schubert 
592c0b746e5SOllivier Robert 		if (!refclock_process(pp)) {
593c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADTIME);
594c0b746e5SOllivier Robert 			return;
595c0b746e5SOllivier Robert 		}
596224ba2bdSOllivier Robert 		/*
597224ba2bdSOllivier Robert 		 * If clock is good we send a NOMINAL message so that
598224ba2bdSOllivier Robert 		 * any previous BAD messages are nullified
599224ba2bdSOllivier Robert 		 */
6009c2daa00SOllivier Robert 		pp->lastref = pp->lastrec;
601c0b746e5SOllivier Robert 		refclock_receive(peer);
6029c2daa00SOllivier Robert 		refclock_report(peer, CEVNT_NOMINAL);
603c0b746e5SOllivier Robert 
604c0b746e5SOllivier Robert 		/*
605c0b746e5SOllivier Robert 		 * We have succedded in answering the poll.
606c0b746e5SOllivier Robert 		 * Turn off the flag and return
607c0b746e5SOllivier Robert 		 */
608c0b746e5SOllivier Robert 		up->polled = 0;
609c0b746e5SOllivier Robert 
610c0b746e5SOllivier Robert 		return;
611c0b746e5SOllivier Robert 	}
612c0b746e5SOllivier Robert 
613c0b746e5SOllivier Robert 	/*
614c0b746e5SOllivier Robert 	 * No match to known timecodes, report failure and return
615c0b746e5SOllivier Robert 	 */
616c0b746e5SOllivier Robert 	refclock_report(peer, CEVNT_BADREPLY);
617c0b746e5SOllivier Robert 	return;
618c0b746e5SOllivier Robert }
619c0b746e5SOllivier Robert 
620c0b746e5SOllivier Robert 
621c0b746e5SOllivier Robert /*
622c0b746e5SOllivier Robert  * true_send - time to send the clock a signal to cough up a time sample
623c0b746e5SOllivier Robert  */
624c0b746e5SOllivier Robert static void
625c0b746e5SOllivier Robert true_send(
626c0b746e5SOllivier Robert 	struct peer *peer,
627c0b746e5SOllivier Robert 	const char *cmd
628c0b746e5SOllivier Robert 	)
629c0b746e5SOllivier Robert {
630c0b746e5SOllivier Robert 	struct refclockproc *pp;
631c0b746e5SOllivier Robert 
632c0b746e5SOllivier Robert 	pp = peer->procptr;
633c0b746e5SOllivier Robert 	if (!(pp->sloppyclockflag & CLK_FLAG1)) {
6343311ff84SXin LI 		size_t len = strlen(cmd);
635c0b746e5SOllivier Robert 
636c0b746e5SOllivier Robert 		true_debug(peer, "Send '%s'\n", cmd);
637a466cc55SCy Schubert 		if (refclock_write(peer, cmd, len, NULL) != (ssize_t)len)
638c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_FAULT);
639c0b746e5SOllivier Robert 		else
640c0b746e5SOllivier Robert 			pp->polls++;
641c0b746e5SOllivier Robert 	}
642c0b746e5SOllivier Robert }
643c0b746e5SOllivier Robert 
644c0b746e5SOllivier Robert 
645c0b746e5SOllivier Robert /*
646c0b746e5SOllivier Robert  * state machine for initializing and controlling a clock
647c0b746e5SOllivier Robert  */
648c0b746e5SOllivier Robert static void
649c0b746e5SOllivier Robert true_doevent(
650c0b746e5SOllivier Robert 	struct peer *peer,
651c0b746e5SOllivier Robert 	enum true_event event
652c0b746e5SOllivier Robert 	)
653c0b746e5SOllivier Robert {
654c0b746e5SOllivier Robert 	struct true_unit *up;
655c0b746e5SOllivier Robert 	struct refclockproc *pp;
656c0b746e5SOllivier Robert 
657c0b746e5SOllivier Robert 	pp = peer->procptr;
6582b15cb3dSCy Schubert 	up = pp->unitptr;
659c0b746e5SOllivier Robert 	if (event != e_TS) {
660c0b746e5SOllivier Robert 		NLOG(NLOG_CLOCKSTATUS) {
661c0b746e5SOllivier Robert 			msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
662c0b746e5SOllivier Robert 				typeStr(up->type),
663c0b746e5SOllivier Robert 				stateStr(up->state),
664c0b746e5SOllivier Robert 				eventStr(event));
665c0b746e5SOllivier Robert 		}
666c0b746e5SOllivier Robert 	}
667c0b746e5SOllivier Robert 	true_debug(peer, "clock %s, state %s, event %s\n",
668c0b746e5SOllivier Robert 		   typeStr(up->type), stateStr(up->state), eventStr(event));
669c0b746e5SOllivier Robert 	switch (up->type) {
670c0b746e5SOllivier Robert 	case t_goes:
671c0b746e5SOllivier Robert 		switch (event) {
672c0b746e5SOllivier Robert 		case e_Init:	/* FALLTHROUGH */
673c0b746e5SOllivier Robert 		case e_Satellite:
674c0b746e5SOllivier Robert 			/*
675c0b746e5SOllivier Robert 			 * Switch back to on-second time codes and return.
676c0b746e5SOllivier Robert 			 */
677c0b746e5SOllivier Robert 			true_send(peer, "C");
678c0b746e5SOllivier Robert 			up->state = s_Start;
679c0b746e5SOllivier Robert 			break;
680c0b746e5SOllivier Robert 		case e_Poll:
681c0b746e5SOllivier Robert 			/*
682c0b746e5SOllivier Robert 			 * After each poll, check the station (satellite).
683c0b746e5SOllivier Robert 			 */
684c0b746e5SOllivier Robert 			true_send(peer, "P");
685c0b746e5SOllivier Robert 			/* No state change needed. */
686c0b746e5SOllivier Robert 			break;
687c0b746e5SOllivier Robert 		default:
688c0b746e5SOllivier Robert 			break;
689c0b746e5SOllivier Robert 		}
690c0b746e5SOllivier Robert 		/* FALLTHROUGH */
691c0b746e5SOllivier Robert 	case t_omega:
692c0b746e5SOllivier Robert 		switch (event) {
693c0b746e5SOllivier Robert 		case e_Init:
694c0b746e5SOllivier Robert 			true_send(peer, "C");
695c0b746e5SOllivier Robert 			up->state = s_Start;
696c0b746e5SOllivier Robert 			break;
697c0b746e5SOllivier Robert 		case e_TS:
698c0b746e5SOllivier Robert 			if (up->state != s_Start && up->state != s_Auto) {
699c0b746e5SOllivier Robert 				true_send(peer, "\03\r");
700c0b746e5SOllivier Robert 				break;
701c0b746e5SOllivier Robert 			}
702c0b746e5SOllivier Robert 			up->state = s_Auto;
703c0b746e5SOllivier Robert 			break;
704c0b746e5SOllivier Robert 		default:
705c0b746e5SOllivier Robert 			break;
706c0b746e5SOllivier Robert 		}
707c0b746e5SOllivier Robert 		break;
708c0b746e5SOllivier Robert 	case t_tm:
709c0b746e5SOllivier Robert 		switch (event) {
710c0b746e5SOllivier Robert 		case e_Init:
711c0b746e5SOllivier Robert 			true_send(peer, "F18\r");
712c0b746e5SOllivier Robert 			up->state = s_Init;
713c0b746e5SOllivier Robert 			break;
714c0b746e5SOllivier Robert 		case e_F18:
715c0b746e5SOllivier Robert 			true_send(peer, "F50\r");
7162b15cb3dSCy Schubert                         /*
7172b15cb3dSCy Schubert                          * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
7182b15cb3dSCy Schubert                          * (from a TM/TMD/XL clock during initialization.)
7192b15cb3dSCy Schubert                          */
7202b15cb3dSCy Schubert                         if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
7212b15cb3dSCy Schubert                             strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
7222b15cb3dSCy Schubert                                 true_doevent(peer, e_F18);
7232b15cb3dSCy Schubert                                 NLOG(NLOG_CLOCKSTATUS) {
7242b15cb3dSCy Schubert                                     msyslog(LOG_INFO, "TM/TMD/XL: %s",
7252b15cb3dSCy Schubert                                             pp->a_lastcode);
7262b15cb3dSCy Schubert                                 }
7272b15cb3dSCy Schubert                                 return;
7282b15cb3dSCy Schubert                         }
729c0b746e5SOllivier Robert 			up->state = s_F18;
730c0b746e5SOllivier Robert 			break;
731c0b746e5SOllivier Robert 		case e_F50:
732c0b746e5SOllivier Robert 			true_send(peer, "F51\r");
733c0b746e5SOllivier Robert 			up->state = s_F50;
734c0b746e5SOllivier Robert 			break;
735c0b746e5SOllivier Robert 		case e_F51:
736c0b746e5SOllivier Robert 			true_send(peer, "F08\r");
737c0b746e5SOllivier Robert 			up->state = s_Start;
738c0b746e5SOllivier Robert 			break;
739c0b746e5SOllivier Robert 		case e_TS:
740c0b746e5SOllivier Robert 			if (up->state != s_Start && up->state != s_Auto) {
741c0b746e5SOllivier Robert 				true_send(peer, "\03\r");
742c0b746e5SOllivier Robert 				break;
743c0b746e5SOllivier Robert 			}
744c0b746e5SOllivier Robert 			up->state = s_Auto;
745c0b746e5SOllivier Robert 			break;
746c0b746e5SOllivier Robert 		default:
747c0b746e5SOllivier Robert 			break;
748c0b746e5SOllivier Robert 		}
749c0b746e5SOllivier Robert 		break;
750c0b746e5SOllivier Robert 	case t_tcu:
751c0b746e5SOllivier Robert 		switch (event) {
752c0b746e5SOllivier Robert 		case e_Init:
753c0b746e5SOllivier Robert 			true_send(peer, "MD3\r");	/* GPS Synch'd Gen. */
754c0b746e5SOllivier Robert 			true_send(peer, "TSU\r");	/* UTC, not GPS. */
755c0b746e5SOllivier Robert 			true_send(peer, "AU\r");	/* Auto Timestamps. */
756c0b746e5SOllivier Robert 			up->state = s_Start;
757c0b746e5SOllivier Robert 			break;
758c0b746e5SOllivier Robert 		case e_TS:
759c0b746e5SOllivier Robert 			if (up->state != s_Start && up->state != s_Auto) {
760c0b746e5SOllivier Robert 				true_send(peer, "\03\r");
761c0b746e5SOllivier Robert 				break;
762c0b746e5SOllivier Robert 			}
763c0b746e5SOllivier Robert 			up->state = s_Auto;
764c0b746e5SOllivier Robert 			break;
765c0b746e5SOllivier Robert 		default:
766c0b746e5SOllivier Robert 			break;
767c0b746e5SOllivier Robert 		}
768c0b746e5SOllivier Robert 		break;
7692b15cb3dSCy Schubert 	case t_tl3:
7702b15cb3dSCy Schubert                 switch (event) {
7712b15cb3dSCy Schubert                     case e_Init:
7722b15cb3dSCy Schubert                         true_send(peer, "ST1"); /* Turn on continuous stream */
7732b15cb3dSCy Schubert                         break;
7742b15cb3dSCy Schubert                     case e_TS:
7752b15cb3dSCy Schubert                         up->state = s_Auto;
7762b15cb3dSCy Schubert                         break;
7772b15cb3dSCy Schubert                     default:
7782b15cb3dSCy Schubert                         break;
7792b15cb3dSCy Schubert                 }
7802b15cb3dSCy Schubert                 break;
781c0b746e5SOllivier Robert 	case t_unknown:
7822b15cb3dSCy Schubert                if (event == e_Poll)
7832b15cb3dSCy Schubert                    break;
784c0b746e5SOllivier Robert 		switch (up->state) {
785c0b746e5SOllivier Robert 		case s_Base:
786c0b746e5SOllivier Robert 			if (event != e_Init)
787c0b746e5SOllivier Robert 			    abort();
788c0b746e5SOllivier Robert 			true_send(peer, "P\r");
789c0b746e5SOllivier Robert 			up->state = s_InqGOES;
790c0b746e5SOllivier Robert 			break;
791c0b746e5SOllivier Robert 		case s_InqGOES:
792c0b746e5SOllivier Robert 			switch (event) {
793c0b746e5SOllivier Robert 			case e_Satellite:
794c0b746e5SOllivier Robert 				up->type = t_goes;
795c0b746e5SOllivier Robert 				true_doevent(peer, e_Init);
796c0b746e5SOllivier Robert 				break;
797c0b746e5SOllivier Robert 			case e_Init:	/*FALLTHROUGH*/
7982b15cb3dSCy Schubert 			case e_Huh:
799c0b746e5SOllivier Robert 			case e_TS:
8002b15cb3dSCy Schubert                                 true_send(peer, "ST0"); /* turn off TL3 auto */
8012b15cb3dSCy Schubert                                 sleep(1);               /* wait for it */
8022b15cb3dSCy Schubert                                 up->state = s_InqTL3;
8032b15cb3dSCy Schubert                                 true_send(peer, "QV");  /* see if its a TL3 */
804c0b746e5SOllivier Robert                                 break;
805c0b746e5SOllivier Robert                             default:
806c0b746e5SOllivier Robert                                 abort();
807c0b746e5SOllivier Robert                         }
808c0b746e5SOllivier Robert                         break;
8092b15cb3dSCy Schubert                     case s_InqTL3:
8102b15cb3dSCy Schubert                         switch (event) {
8112b15cb3dSCy Schubert                             case e_TL3:
8122b15cb3dSCy Schubert                                 up->type = t_tl3;
8132b15cb3dSCy Schubert                                 up->state = s_Auto;     /* Inq side-effect. */
8142b15cb3dSCy Schubert                                 true_send(peer, "ST1"); /* Turn on 1/sec data */
8152b15cb3dSCy Schubert                                 break;
8162b15cb3dSCy Schubert                             case e_Init:        /*FALLTHROUGH*/
8172b15cb3dSCy Schubert                             case e_Huh:
8182b15cb3dSCy Schubert 				up->state = s_InqOmega;
8192b15cb3dSCy Schubert 				true_send(peer, "C\r");
8202b15cb3dSCy Schubert 				break;
8212b15cb3dSCy Schubert                             case e_TS:
8222b15cb3dSCy Schubert                                  up->type = t_tl3;    /* Already sending data */
8232b15cb3dSCy Schubert                                  up->state = s_Auto;
8242b15cb3dSCy Schubert                                  break;
8252b15cb3dSCy Schubert 			    default:
8262b15cb3dSCy Schubert                                 msyslog(LOG_INFO,
8272b15cb3dSCy Schubert                                         "TRUE: TL3 init fellthrough! (%d)", event);
8282b15cb3dSCy Schubert                                 break;
8292b15cb3dSCy Schubert 			}
8302b15cb3dSCy Schubert 			break;
831c0b746e5SOllivier Robert 		case s_InqOmega:
832c0b746e5SOllivier Robert 			switch (event) {
833c0b746e5SOllivier Robert 			case e_TS:
834c0b746e5SOllivier Robert 				up->type = t_omega;
835c0b746e5SOllivier Robert 				up->state = s_Auto;	/* Inq side-effect. */
836c0b746e5SOllivier Robert 				break;
837c0b746e5SOllivier Robert 			case e_Init:	/*FALLTHROUGH*/
838c0b746e5SOllivier Robert 			case e_Huh:
839c0b746e5SOllivier Robert 				up->state = s_InqTM;
840c0b746e5SOllivier Robert 				true_send(peer, "F18\r");
841c0b746e5SOllivier Robert 				break;
842c0b746e5SOllivier Robert 			default:
843c0b746e5SOllivier Robert 				abort();
844c0b746e5SOllivier Robert 			}
845c0b746e5SOllivier Robert 			break;
846c0b746e5SOllivier Robert 		case s_InqTM:
847c0b746e5SOllivier Robert 			switch (event) {
848c0b746e5SOllivier Robert 			case e_F18:
849c0b746e5SOllivier Robert 				up->type = t_tm;
850c0b746e5SOllivier Robert 				true_doevent(peer, e_Init);
851c0b746e5SOllivier Robert 				break;
852c0b746e5SOllivier Robert 			case e_Init:	/*FALLTHROUGH*/
853c0b746e5SOllivier Robert 			case e_Huh:
854c0b746e5SOllivier Robert 				true_send(peer, "PO\r");
855c0b746e5SOllivier Robert 				up->state = s_InqTCU;
856c0b746e5SOllivier Robert 				break;
857c0b746e5SOllivier Robert 			default:
8582b15cb3dSCy Schubert                                 msyslog(LOG_INFO,
8592b15cb3dSCy Schubert                                         "TRUE: TM/TMD init fellthrough!");
8602b15cb3dSCy Schubert 			        break;
861c0b746e5SOllivier Robert 			}
862c0b746e5SOllivier Robert 			break;
863c0b746e5SOllivier Robert 		case s_InqTCU:
864c0b746e5SOllivier Robert 			switch (event) {
865c0b746e5SOllivier Robert 			case e_Location:
866c0b746e5SOllivier Robert 				up->type = t_tcu;
867c0b746e5SOllivier Robert 				true_doevent(peer, e_Init);
868c0b746e5SOllivier Robert 				break;
869c0b746e5SOllivier Robert 			case e_Init:	/*FALLTHROUGH*/
870c0b746e5SOllivier Robert 			case e_Huh:
871c0b746e5SOllivier Robert 				up->state = s_Base;
872c0b746e5SOllivier Robert 				sleep(1);	/* XXX */
873c0b746e5SOllivier Robert 				break;
874c0b746e5SOllivier Robert 			default:
8752b15cb3dSCy Schubert                                 msyslog(LOG_INFO,
8762b15cb3dSCy Schubert                                         "TRUE: TCU init fellthrough!");
8772b15cb3dSCy Schubert                                 break;
878c0b746e5SOllivier Robert 			}
879c0b746e5SOllivier Robert 			break;
880c0b746e5SOllivier Robert 			/*
881c0b746e5SOllivier Robert 			 * An expedient hack to prevent lint complaints,
882c0b746e5SOllivier Robert 			 * these don't actually need to be used here...
883c0b746e5SOllivier Robert 			 */
884c0b746e5SOllivier Robert 		case s_Init:
885c0b746e5SOllivier Robert 		case s_F18:
886c0b746e5SOllivier Robert 		case s_F50:
887c0b746e5SOllivier Robert 		case s_Start:
888c0b746e5SOllivier Robert 		case s_Auto:
889c0b746e5SOllivier Robert 		case s_Max:
8902b15cb3dSCy Schubert 			msyslog(LOG_INFO, "TRUE: state %s is unexpected!",
8912b15cb3dSCy Schubert 				stateStr(up->state));
892c0b746e5SOllivier Robert 		}
893c0b746e5SOllivier Robert 		break;
894c0b746e5SOllivier Robert 	default:
8952b15cb3dSCy Schubert                 msyslog(LOG_INFO, "TRUE: cannot identify refclock!");
896c0b746e5SOllivier Robert 		abort();
897c0b746e5SOllivier Robert 		/* NOTREACHED */
898c0b746e5SOllivier Robert 	}
899c0b746e5SOllivier Robert 
900c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
901c0b746e5SOllivier Robert 	if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
902c0b746e5SOllivier Robert 		/* Make counter trigger on gate0, count down from 65535. */
903c0b746e5SOllivier Robert 		pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
904c0b746e5SOllivier Robert 		/*
905c0b746e5SOllivier Robert 		 * (These constants are OK since
906c0b746e5SOllivier Robert 		 * they represent hardware maximums.)
907c0b746e5SOllivier Robert 		 */
908c0b746e5SOllivier Robert 		NLOG(NLOG_CLOCKINFO) {
909c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "PCL-720 initialized");
910c0b746e5SOllivier Robert 		}
911c0b746e5SOllivier Robert 		up->pcl720init++;
912c0b746e5SOllivier Robert 	}
913c0b746e5SOllivier Robert #endif
914c0b746e5SOllivier Robert 
915c0b746e5SOllivier Robert 
916c0b746e5SOllivier Robert }
917c0b746e5SOllivier Robert 
918c0b746e5SOllivier Robert /*
919c0b746e5SOllivier Robert  * true_poll - called by the transmit procedure
920c0b746e5SOllivier Robert  */
921c0b746e5SOllivier Robert static void
922c0b746e5SOllivier Robert true_poll(
923c0b746e5SOllivier Robert 	int unit,
924c0b746e5SOllivier Robert 	struct peer *peer
925c0b746e5SOllivier Robert 	)
926c0b746e5SOllivier Robert {
927c0b746e5SOllivier Robert 	struct true_unit *up;
928c0b746e5SOllivier Robert 	struct refclockproc *pp;
929c0b746e5SOllivier Robert 
930c0b746e5SOllivier Robert 	/*
931c0b746e5SOllivier Robert 	 * You don't need to poll this clock.  It puts out timecodes
932c0b746e5SOllivier Robert 	 * once per second.  If asked for a timestamp, take note.
933c0b746e5SOllivier Robert 	 * The next time a timecode comes in, it will be fed back.
934c0b746e5SOllivier Robert 	 */
935c0b746e5SOllivier Robert 	pp = peer->procptr;
9362b15cb3dSCy Schubert 	up = pp->unitptr;
9372b15cb3dSCy Schubert 	if (up->pollcnt > 0) {
938c0b746e5SOllivier Robert 		up->pollcnt--;
9392b15cb3dSCy Schubert 	} else {
940c0b746e5SOllivier Robert 		true_doevent(peer, e_Init);
941c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_TIMEOUT);
942c0b746e5SOllivier Robert 	}
943c0b746e5SOllivier Robert 
944c0b746e5SOllivier Robert 	/*
945c0b746e5SOllivier Robert 	 * polled every 64 seconds. Ask true_receive to hand in a
946c0b746e5SOllivier Robert 	 * timestamp.
947c0b746e5SOllivier Robert 	 */
948c0b746e5SOllivier Robert 	up->polled = 1;
949c0b746e5SOllivier Robert 	pp->polls++;
950c0b746e5SOllivier Robert }
951c0b746e5SOllivier Robert 
952c0b746e5SOllivier Robert #ifdef CLOCK_PPS720
953c0b746e5SOllivier Robert /*
954c0b746e5SOllivier Robert  * true_sample720 - sample the PCL-720
955c0b746e5SOllivier Robert  */
956c0b746e5SOllivier Robert static u_long
957c0b746e5SOllivier Robert true_sample720(void)
958c0b746e5SOllivier Robert {
959c0b746e5SOllivier Robert 	unsigned long f;
960c0b746e5SOllivier Robert 
961c0b746e5SOllivier Robert 	/* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
962c0b746e5SOllivier Robert 	 * If it is not being held low now, we did not get called
963c0b746e5SOllivier Robert 	 * within 65535us.
964c0b746e5SOllivier Robert 	 */
965c0b746e5SOllivier Robert 	if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
966c0b746e5SOllivier Robert 		NLOG(NLOG_CLOCKINFO) {
967c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "PCL-720 out of synch");
968c0b746e5SOllivier Robert 		}
969c0b746e5SOllivier Robert 		return (0);
970c0b746e5SOllivier Robert 	}
971c0b746e5SOllivier Robert 	f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
972c0b746e5SOllivier Robert #ifdef PPS720_DEBUG
973c0b746e5SOllivier Robert 	msyslog(LOG_DEBUG, "PCL-720: %luus", f);
974c0b746e5SOllivier Robert #endif
975c0b746e5SOllivier Robert 	return (f);
976c0b746e5SOllivier Robert }
977c0b746e5SOllivier Robert #endif
978c0b746e5SOllivier Robert 
979c0b746e5SOllivier Robert #else
980*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
981c0b746e5SOllivier Robert #endif /* REFCLOCK */
982