154359Sroberto/*
2285612Sdelphij * refclock_true - clock driver for the Kinemetrics/TrueTime receivers
354359Sroberto *	Receiver Version 3.0C - tested plain, with CLKLDISC
4285612Sdelphij *	Development work being done:
5285612Sdelphij *      - Support TL-3 WWV TOD receiver
654359Sroberto */
754359Sroberto
854359Sroberto#ifdef HAVE_CONFIG_H
954359Sroberto#include <config.h>
1054359Sroberto#endif
1154359Sroberto
1254359Sroberto#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
1354359Sroberto
14285612Sdelphij#include <stdio.h>
15285612Sdelphij#include <ctype.h>
16285612Sdelphij
1754359Sroberto#include "ntpd.h"
1854359Sroberto#include "ntp_io.h"
1954359Sroberto#include "ntp_refclock.h"
2054359Sroberto#include "ntp_unixtime.h"
2154359Sroberto#include "ntp_stdlib.h"
2254359Sroberto
23285612Sdelphij#ifdef SYS_WINNT
24285612Sdelphijextern int async_write(int, const void *, unsigned int);
25285612Sdelphij#undef write
26285612Sdelphij#define write(fd, data, octets)	async_write(fd, data, octets)
27285612Sdelphij#endif
2882498Sroberto
2954359Sroberto/* This should be an atom clock but those are very hard to build.
3054359Sroberto *
3154359Sroberto * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
3254359Sroberto * of TTL input and output pins, all brought out to the back panel.  If you
3354359Sroberto * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
3454359Sroberto * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
3554359Sroberto * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
3654359Sroberto * number of uSecs since the last PPS upward swing, mediated by reading OUT0
3754359Sroberto * to find out if the counter has wrapped around (this happens if more than
3854359Sroberto * 65535us (65ms) elapses between the PPS event and our being called.)
3954359Sroberto */
4054359Sroberto#ifdef CLOCK_PPS720
4154359Sroberto# undef min	/* XXX */
4254359Sroberto# undef max	/* XXX */
4354359Sroberto# include <machine/inline.h>
4454359Sroberto# include <sys/pcl720.h>
4554359Sroberto# include <sys/i8253.h>
4654359Sroberto# define PCL720_IOB 0x2a0	/* XXX */
4754359Sroberto# define PCL720_CTR 0		/* XXX */
4854359Sroberto#endif
4954359Sroberto
5054359Sroberto/*
5154359Sroberto * Support for Kinemetrics Truetime Receivers
52285612Sdelphij *	GOES:           (468-DC, usable with GPS->GOES converting antenna)
53285612Sdelphij *	GPS/TM-TMD:
54285612Sdelphij *	XL-DC:		(a 151-602-210, reported by the driver as a GPS/TM-TMD)
55285612Sdelphij *	GPS-800 TCU:	(an 805-957 with the RS232 Talker/Listener module)
56285612Sdelphij *      TL-3:           3 channel WWV/H receiver w/ IRIG and RS-232 outputs
5754359Sroberto *	OM-DC:		getting stale ("OMEGA")
5854359Sroberto *
5954359Sroberto * Most of this code is originally from refclock_wwvb.c with thanks.
6054359Sroberto * It has been so mangled that wwvb is not a recognizable ancestor.
6154359Sroberto *
6254359Sroberto * Timcode format: ADDD:HH:MM:SSQCL
6354359Sroberto *	A - control A		(this is stripped before we see it)
6454359Sroberto *	Q - Quality indication	(see below)
6554359Sroberto *	C - Carriage return
6654359Sroberto *	L - Line feed
6754359Sroberto *
6854359Sroberto * Quality codes indicate possible error of
6954359Sroberto *   468-DC GOES Receiver:
7082498Sroberto *   GPS-TM/TMD Receiver: (default quality codes for XL-DC)
7182498Sroberto *       ?     +/- 1  milliseconds	#     +/- 100 microseconds
7282498Sroberto *       *     +/- 10 microseconds	.     +/- 1   microsecond
7382498Sroberto *     space   less than 1 microsecond
74285612Sdelphij *   TL-3 Receiver: (default quality codes for TL-3)
75285612Sdelphij *       ?     unknown quality (receiver is unlocked)
76285612Sdelphij *     space   +/- 5 milliseconds
7782498Sroberto *   OM-DC OMEGA Receiver: (default quality codes for OMEGA)
7882498Sroberto *   WARNING OMEGA navigation system is no longer existent
7954359Sroberto *       >     >+- 5 seconds
8054359Sroberto *       ?     >+/- 500 milliseconds    #     >+/- 50 milliseconds
8154359Sroberto *       *     >+/- 5 milliseconds      .     >+/- 1 millisecond
8254359Sroberto *      A-H    less than 1 millisecond.  Character indicates which station
83285612Sdelphij *	       is being received as follows:
84285612Sdelphij *	       A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
85285612Sdelphij *	       E = La Reunion, F = Argentina, G = Australia, H = Japan.
8654359Sroberto *
8754359Sroberto * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
8854359Sroberto *
8954359Sroberto * Notes on 468-DC and OMEGA receiver:
9054359Sroberto *
9154359Sroberto * Send the clock a 'R' or 'C' and once per second a timestamp will
9254359Sroberto * appear.  Send a 'P' to get the satellite position once (GOES only.)
9354359Sroberto *
9454359Sroberto * Notes on the 468-DC receiver:
9554359Sroberto *
9654359Sroberto * Since the old east/west satellite locations are only historical, you can't
9754359Sroberto * set your clock propagation delay settings correctly and still use
9854359Sroberto * automatic mode. The manual says to use a compromise when setting the
9954359Sroberto * switches. This results in significant errors. The solution; use fudge
10054359Sroberto * time1 and time2 to incorporate corrections. If your clock is set for
10154359Sroberto * 50 and it should be 58 for using the west and 46 for using the east,
10254359Sroberto * use the line
10354359Sroberto *
10454359Sroberto * fudge 127.127.5.0 time1 +0.008 time2 -0.004
10554359Sroberto *
10654359Sroberto * This corrects the 4 milliseconds advance and 8 milliseconds retard
10754359Sroberto * needed. The software will ask the clock which satellite it sees.
10854359Sroberto *
109285612Sdelphij * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver:
110285612Sdelphij *
111285612Sdelphij * This clock may be polled, or send one timecode per second.
112285612Sdelphij * That mode may be toggled via the front panel ("C" mode), or controlled
113285612Sdelphij * from the RS-232 port.  Send the receiver "ST1" to turn it on, and
114285612Sdelphij * "ST0" to turn it off.  Send "QV" to get the firmware revision (useful
115285612Sdelphij * for identifying this model.)
116285612Sdelphij *
117285612Sdelphij * Note that it can take several polling cycles, especially if the receiver
118285612Sdelphij * was in the continuous timecode mode.  (It can be slow to leave that mode.)
119285612Sdelphij *
120285612Sdelphij * ntp.conf parameters:
121285612Sdelphij * time1   - offset applied to samples when reading WEST satellite (default = 0)
122285612Sdelphij * time2   - offset applied to samples when reading EAST satellite (default = 0)
123285612Sdelphij * stratum - stratum to assign to this clock (default = 0)
124285612Sdelphij * refid   - refid assigned to this clock (default = "TRUE", see below)
125285612Sdelphij * flag1   - will silence the clock side of ntpd, just reading the clock
126285612Sdelphij *	     without trying to write to it.  (default = 0)
127285612Sdelphij * flag2   - generate a debug file /tmp/true%d.
128285612Sdelphij * flag3   - enable ppsclock streams module
129285612Sdelphij * flag4   - use the PCL-720 (BSD/OS only)
13054359Sroberto */
13154359Sroberto
13282498Sroberto
13354359Sroberto/*
13454359Sroberto * Definitions
13554359Sroberto */
13654359Sroberto#define	DEVICE		"/dev/true%d"
13754359Sroberto#define	SPEED232	B9600	/* 9600 baud */
13854359Sroberto
13954359Sroberto/*
14054359Sroberto * Radio interface parameters
14154359Sroberto */
14254359Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
14354359Sroberto#define	REFID		"TRUE"	/* reference id */
14454359Sroberto#define	DESCRIPTION	"Kinemetrics/TrueTime Receiver"
14554359Sroberto
14654359Sroberto/*
14754359Sroberto * Tags which station (satellite) we see
14854359Sroberto */
14954359Sroberto#define GOES_WEST	0	/* Default to WEST satellite and apply time1 */
15054359Sroberto#define GOES_EAST	1	/* until you discover otherwise */
15154359Sroberto
15254359Sroberto/*
15354359Sroberto * used by the state machine
15454359Sroberto */
15554359Srobertoenum true_event	{e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
156285612Sdelphij		 e_TL3, e_Poll, e_Location, e_TS, e_Max};
15754359Srobertoconst char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
158285612Sdelphij			"TL3", "Poll", "Location", "TS"};
15954359Sroberto#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
16054359Sroberto
16154359Srobertoenum true_state	{s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
162285612Sdelphij		 s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
16354359Srobertoconst char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
164285612Sdelphij			"InqTL3", "Init", "F18", "F50", "Start", "Auto"};
16554359Sroberto#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
16654359Sroberto
167285612Sdelphijenum true_type	{t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max};
168285612Sdelphijconst char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"};
16954359Sroberto#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
17054359Sroberto
17154359Sroberto/*
17254359Sroberto * unit control structure
17354359Sroberto */
17454359Srobertostruct true_unit {
17554359Sroberto	unsigned int	pollcnt;	/* poll message counter */
17654359Sroberto	unsigned int	station;	/* which station we are on */
17754359Sroberto	unsigned int	polled;		/* Hand in a time sample? */
17854359Sroberto	enum true_state	state;		/* state machine */
17954359Sroberto	enum true_type	type;		/* what kind of clock is it? */
18054359Sroberto	int		unit;		/* save an extra copy of this */
18154359Sroberto	FILE		*debug;		/* debug logging file */
18254359Sroberto#ifdef CLOCK_PPS720
18354359Sroberto	int		pcl720init;	/* init flag for PCL 720 */
18454359Sroberto#endif
18554359Sroberto};
18654359Sroberto
18754359Sroberto/*
18854359Sroberto * Function prototypes
18954359Sroberto */
190285612Sdelphijstatic	int	true_start	(int, struct peer *);
191285612Sdelphijstatic	void	true_shutdown	(int, struct peer *);
192285612Sdelphijstatic	void	true_receive	(struct recvbuf *);
193285612Sdelphijstatic	void	true_poll	(int, struct peer *);
194285612Sdelphijstatic	void	true_send	(struct peer *, const char *);
195285612Sdelphijstatic	void	true_doevent	(struct peer *, enum true_event);
19654359Sroberto
19754359Sroberto#ifdef CLOCK_PPS720
198285612Sdelphijstatic	u_long	true_sample720	(void);
19954359Sroberto#endif
20054359Sroberto
20154359Sroberto/*
20254359Sroberto * Transfer vector
20354359Sroberto */
20454359Srobertostruct	refclock refclock_true = {
20554359Sroberto	true_start,		/* start up driver */
20654359Sroberto	true_shutdown,		/* shut down driver */
20754359Sroberto	true_poll,		/* transmit poll message */
20854359Sroberto	noentry,		/* not used (old true_control) */
20954359Sroberto	noentry,		/* initialize driver (not used) */
21054359Sroberto	noentry,		/* not used (old true_buginfo) */
21154359Sroberto	NOFLAGS			/* not used */
21254359Sroberto};
21354359Sroberto
21454359Sroberto
21554359Sroberto#if !defined(__STDC__)
21654359Sroberto# define true_debug (void)
21754359Sroberto#else
218285612SdelphijNTP_PRINTF(2, 3)
21954359Srobertostatic void
22054359Srobertotrue_debug(struct peer *peer, const char *fmt, ...)
22154359Sroberto{
22254359Sroberto	va_list ap;
22354359Sroberto	int want_debugging, now_debugging;
22454359Sroberto	struct refclockproc *pp;
22554359Sroberto	struct true_unit *up;
22654359Sroberto
22754359Sroberto	va_start(ap, fmt);
22854359Sroberto	pp = peer->procptr;
229285612Sdelphij	up = pp->unitptr;
23054359Sroberto
23154359Sroberto	want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
23254359Sroberto	now_debugging = (up->debug != NULL);
23354359Sroberto	if (want_debugging != now_debugging)
23454359Sroberto	{
23554359Sroberto		if (want_debugging) {
236285612Sdelphij			char filename[40];
237285612Sdelphij			int fd;
23854359Sroberto
239285612Sdelphij			snprintf(filename, sizeof(filename),
240285612Sdelphij				 "/tmp/true%d.debug", up->unit);
241285612Sdelphij			fd = open(filename, O_CREAT | O_WRONLY | O_EXCL,
242285612Sdelphij				  0600);
243285612Sdelphij			if (fd >= 0 && (up->debug = fdopen(fd, "w"))) {
24454359Sroberto#ifdef HAVE_SETVBUF
245285612Sdelphij				static char buf[BUFSIZ];
246285612Sdelphij
247285612Sdelphij				setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
24854359Sroberto#else
249285612Sdelphij				setlinebuf(up->debug);
25054359Sroberto#endif
251285612Sdelphij			}
252285612Sdelphij		} else {
253285612Sdelphij			fclose(up->debug);
254285612Sdelphij			up->debug = NULL;
255285612Sdelphij		}
25654359Sroberto	}
25754359Sroberto
25854359Sroberto	if (up->debug) {
25954359Sroberto		fprintf(up->debug, "true%d: ", up->unit);
26054359Sroberto		vfprintf(up->debug, fmt, ap);
26154359Sroberto	}
262285612Sdelphij	va_end(ap);
26354359Sroberto}
26454359Sroberto#endif /*STDC*/
26554359Sroberto
26654359Sroberto/*
26754359Sroberto * true_start - open the devices and initialize data for processing
26854359Sroberto */
26954359Srobertostatic int
27054359Srobertotrue_start(
27154359Sroberto	int unit,
27254359Sroberto	struct peer *peer
27354359Sroberto	)
27454359Sroberto{
27554359Sroberto	register struct true_unit *up;
27654359Sroberto	struct refclockproc *pp;
27782498Sroberto	char device[40];
27854359Sroberto	int fd;
27954359Sroberto
28054359Sroberto	/*
28154359Sroberto	 * Open serial port
28254359Sroberto	 */
283285612Sdelphij	snprintf(device, sizeof(device), DEVICE, unit);
284285612Sdelphij	fd = refclock_open(device, SPEED232, LDISC_CLK);
285285612Sdelphij	if (fd <= 0)
286285612Sdelphij		return 0;
28754359Sroberto
28854359Sroberto	/*
28954359Sroberto	 * Allocate and initialize unit structure
29054359Sroberto	 */
291285612Sdelphij	up = emalloc_zero(sizeof(*up));
29254359Sroberto	pp = peer->procptr;
29354359Sroberto	pp->io.clock_recv = true_receive;
294285612Sdelphij	pp->io.srcclock = peer;
29554359Sroberto	pp->io.datalen = 0;
29654359Sroberto	pp->io.fd = fd;
29754359Sroberto	if (!io_addclock(&pp->io)) {
298285612Sdelphij		close(fd);
299285612Sdelphij		pp->io.fd = -1;
30054359Sroberto		free(up);
30154359Sroberto		return (0);
30254359Sroberto	}
303285612Sdelphij	pp->unitptr = up;
30454359Sroberto
30554359Sroberto	/*
30654359Sroberto	 * Initialize miscellaneous variables
30754359Sroberto	 */
30854359Sroberto	peer->precision = PRECISION;
30954359Sroberto	pp->clockdesc = DESCRIPTION;
310285612Sdelphij	memcpy(&pp->refid, REFID, 4);
31154359Sroberto	up->pollcnt = 2;
31254359Sroberto	up->type = t_unknown;
31354359Sroberto	up->state = s_Base;
314285612Sdelphij
315285612Sdelphij	/*
316285612Sdelphij	 * Send a CTRL-C character at the start,
317285612Sdelphij	 * just in case the clock is already
318285612Sdelphij	 * sending timecodes
319285612Sdelphij	 */
320285612Sdelphij	true_send(peer, "\03\r");
321285612Sdelphij
32254359Sroberto	true_doevent(peer, e_Init);
323285612Sdelphij
32454359Sroberto	return (1);
32554359Sroberto}
32654359Sroberto
327285612Sdelphij
32854359Sroberto/*
32954359Sroberto * true_shutdown - shut down the clock
33054359Sroberto */
33154359Srobertostatic void
33254359Srobertotrue_shutdown(
33354359Sroberto	int unit,
33454359Sroberto	struct peer *peer
33554359Sroberto	)
33654359Sroberto{
33754359Sroberto	register struct true_unit *up;
33854359Sroberto	struct refclockproc *pp;
33954359Sroberto
34054359Sroberto	pp = peer->procptr;
341285612Sdelphij	up = pp->unitptr;
342285612Sdelphij	if (pp->io.fd != -1)
343285612Sdelphij		io_closeclock(&pp->io);
344285612Sdelphij	if (up != NULL)
345285612Sdelphij		free(up);
34654359Sroberto}
34754359Sroberto
34854359Sroberto
34954359Sroberto/*
35054359Sroberto * true_receive - receive data from the serial interface on a clock
35154359Sroberto */
35254359Srobertostatic void
35354359Srobertotrue_receive(
35454359Sroberto	struct recvbuf *rbufp
35554359Sroberto	)
35654359Sroberto{
35754359Sroberto	register struct true_unit *up;
35854359Sroberto	struct refclockproc *pp;
35954359Sroberto	struct peer *peer;
36054359Sroberto	u_short new_station;
36154359Sroberto	char synced;
36254359Sroberto	int i;
36354359Sroberto	int lat, lon, off;	/* GOES Satellite position */
364285612Sdelphij	/* These variables hold data until we decide to keep it */
365285612Sdelphij	char	rd_lastcode[BMAX];
366285612Sdelphij	l_fp	rd_tmp;
367285612Sdelphij	u_short	rd_lencode;
36854359Sroberto
36954359Sroberto	/*
37054359Sroberto	 * Get the clock this applies to and pointers to the data.
37154359Sroberto	 */
372285612Sdelphij	peer = rbufp->recv_peer;
37354359Sroberto	pp = peer->procptr;
374285612Sdelphij	up = pp->unitptr;
37554359Sroberto
37654359Sroberto	/*
37754359Sroberto	 * Read clock output.  Automatically handles STREAMS, CLKLDISC.
37854359Sroberto	 */
379285612Sdelphij	rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
380285612Sdelphij	rd_lastcode[rd_lencode] = '\0';
38154359Sroberto
38254359Sroberto	/*
38354359Sroberto	 * There is a case where <cr><lf> generates 2 timestamps.
38454359Sroberto	 */
385285612Sdelphij	if (rd_lencode == 0)
386285612Sdelphij		return;
387285612Sdelphij	pp->lencode = rd_lencode;
388285612Sdelphij	strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode));
389285612Sdelphij	pp->lastrec = rd_tmp;
390285612Sdelphij	true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode,
391285612Sdelphij		   pp->lencode);
39254359Sroberto
39354359Sroberto	up->pollcnt = 2;
39454359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
39554359Sroberto
39654359Sroberto	/*
39754359Sroberto	 * We get down to business, check the timecode format and decode
39854359Sroberto	 * its contents. This code decodes a multitude of different
39954359Sroberto	 * clock messages. Timecodes are processed if needed. All replies
40054359Sroberto	 * will be run through the state machine to tweak driver options
40154359Sroberto	 * and program the clock.
40254359Sroberto	 */
40354359Sroberto
40454359Sroberto	/*
40554359Sroberto	 * Clock misunderstood our last command?
40654359Sroberto	 */
40782498Sroberto	if (pp->a_lastcode[0] == '?' ||
40882498Sroberto	    strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
40954359Sroberto		true_doevent(peer, e_Huh);
41054359Sroberto		return;
41154359Sroberto	}
41254359Sroberto
41354359Sroberto	/*
41454359Sroberto	 * Timecode: "nnnnn+nnn-nnn"
41554359Sroberto	 * (from GOES clock when asked about satellite position)
41654359Sroberto	 */
41754359Sroberto	if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
41854359Sroberto	    (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
41954359Sroberto	    sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
42054359Sroberto	    ) {
42154359Sroberto		const char *label = "Botch!";
42254359Sroberto
42354359Sroberto		/*
42454359Sroberto		 * This is less than perfect.  Call the (satellite)
42554359Sroberto		 * either EAST or WEST and adjust slop accodingly
42654359Sroberto		 * Perfectionists would recalculate the exact delay
42754359Sroberto		 * and adjust accordingly...
42854359Sroberto		 */
42954359Sroberto		if (lon > 7000 && lon < 14000) {
43054359Sroberto			if (lon < 10000) {
43154359Sroberto				new_station = GOES_EAST;
43254359Sroberto				label = "EAST";
43354359Sroberto			} else {
43454359Sroberto				new_station = GOES_WEST;
43554359Sroberto				label = "WEST";
43654359Sroberto			}
43754359Sroberto
43854359Sroberto			if (new_station != up->station) {
43954359Sroberto				double dtemp;
44054359Sroberto
44154359Sroberto				dtemp = pp->fudgetime1;
44254359Sroberto				pp->fudgetime1 = pp->fudgetime2;
44354359Sroberto				pp->fudgetime2 = dtemp;
44454359Sroberto				up->station = new_station;
44554359Sroberto			}
44654359Sroberto		}
44754359Sroberto		else {
448106163Sroberto			/*refclock_report(peer, CEVNT_BADREPLY);*/
44954359Sroberto			label = "UNKNOWN";
45054359Sroberto		}
45154359Sroberto		true_debug(peer, "GOES: station %s\n", label);
45254359Sroberto		true_doevent(peer, e_Satellite);
45354359Sroberto		return;
45454359Sroberto	}
45554359Sroberto
45654359Sroberto	/*
45754359Sroberto	 * Timecode: "Fnn"
45854359Sroberto	 * (from TM/TMD clock when it wants to tell us what it's up to.)
45954359Sroberto	 */
46054359Sroberto	if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
46154359Sroberto		switch (i) {
462285612Sdelphij		case 50:
46354359Sroberto			true_doevent(peer, e_F50);
46454359Sroberto			break;
465285612Sdelphij		case 51:
46654359Sroberto			true_doevent(peer, e_F51);
46754359Sroberto			break;
468285612Sdelphij		default:
46954359Sroberto			true_debug(peer, "got F%02d - ignoring\n", i);
47054359Sroberto			break;
47154359Sroberto		}
47254359Sroberto		return;
47354359Sroberto	}
47454359Sroberto
475285612Sdelphij        /*
476285612Sdelphij         * Timecode: "VER xx.xx"
477285612Sdelphij         * (from a TL3 when sent "QV", so id's it during initialization.)
478285612Sdelphij         */
479285612Sdelphij        if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' &&
480285612Sdelphij            pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') {
481285612Sdelphij                true_doevent(peer, e_TL3);
482285612Sdelphij                NLOG(NLOG_CLOCKSTATUS) {
483285612Sdelphij                        msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode);
484285612Sdelphij                }
485285612Sdelphij                return;
486285612Sdelphij        }
487285612Sdelphij
48854359Sroberto	/*
48982498Sroberto	 * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
49082498Sroberto	 * (from a TM/TMD/XL clock during initialization.)
49154359Sroberto	 */
492285612Sdelphij	if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 ||
49382498Sroberto	    strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
49454359Sroberto		true_doevent(peer, e_F18);
49554359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
49682498Sroberto			msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
49754359Sroberto		}
49854359Sroberto		return;
49954359Sroberto	}
50054359Sroberto
50154359Sroberto	/*
50254359Sroberto	 * Timecode: "N03726428W12209421+000033"
503285612Sdelphij	 *			1	   2
504285612Sdelphij	 * index      0123456789012345678901234
50554359Sroberto	 * (from a TCU during initialization)
50654359Sroberto	 */
50754359Sroberto	if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
50854359Sroberto	    (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
50954359Sroberto	    pp->a_lastcode[18] == '+') {
51054359Sroberto		true_doevent(peer, e_Location);
51154359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
51254359Sroberto			msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
51354359Sroberto		}
51454359Sroberto		return;
51554359Sroberto	}
51654359Sroberto	/*
51754359Sroberto	 * Timecode: "ddd:hh:mm:ssQ"
518285612Sdelphij	 *			1	   2
519285612Sdelphij	 * index      0123456789012345678901234
52054359Sroberto	 * (from all clocks supported by this driver.)
52154359Sroberto	 */
52254359Sroberto	if (pp->a_lastcode[3] == ':' &&
52354359Sroberto	    pp->a_lastcode[6] == ':' &&
52454359Sroberto	    pp->a_lastcode[9] == ':' &&
52554359Sroberto	    sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
52654359Sroberto		   &pp->day, &pp->hour, &pp->minute,
52754359Sroberto		   &pp->second, &synced) == 5) {
52854359Sroberto
52954359Sroberto		/*
53054359Sroberto		 * Adjust the synchronize indicator according to timecode
53182498Sroberto		 * say were OK, and then say not if we really are not OK
53254359Sroberto		 */
533285612Sdelphij		if (synced == '>' || synced == '#' || synced == '?'
534285612Sdelphij		    || synced == 'X')
535285612Sdelphij			pp->leap = LEAP_NOTINSYNC;
53682498Sroberto		else
537285612Sdelphij			pp->leap = LEAP_NOWARNING;
53854359Sroberto
53954359Sroberto		true_doevent(peer, e_TS);
54054359Sroberto
54154359Sroberto#ifdef CLOCK_PPS720
54254359Sroberto		/* If it's taken more than 65ms to get here, we'll lose. */
54354359Sroberto		if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
54454359Sroberto			l_fp   off;
54554359Sroberto
54654359Sroberto#ifdef CLOCK_ATOM
54754359Sroberto			/*
54854359Sroberto			 * find out what time it really is. Include
54954359Sroberto			 * the count from the PCL720
55054359Sroberto			 */
55154359Sroberto			if (!clocktime(pp->day, pp->hour, pp->minute,
55254359Sroberto				       pp->second, GMT, pp->lastrec.l_ui,
55354359Sroberto				       &pp->yearstart, &off.l_ui)) {
55454359Sroberto				refclock_report(peer, CEVNT_BADTIME);
55554359Sroberto				return;
55654359Sroberto			}
557132451Sroberto			off.l_uf = 0;
55854359Sroberto#endif
55954359Sroberto
56054359Sroberto			pp->usec = true_sample720();
56154359Sroberto#ifdef CLOCK_ATOM
56254359Sroberto			TVUTOTSF(pp->usec, off.l_uf);
56354359Sroberto#endif
56454359Sroberto
56554359Sroberto			/*
56654359Sroberto			 * Stomp all over the timestamp that was pulled out
56754359Sroberto			 * of the input stream. It's irrelevant since we've
56854359Sroberto			 * adjusted the input time to reflect now (via pp->usec)
56954359Sroberto			 * rather than when the data was collected.
57054359Sroberto			 */
57154359Sroberto			get_systime(&pp->lastrec);
57254359Sroberto#ifdef CLOCK_ATOM
57354359Sroberto			/*
57454359Sroberto			 * Create a true offset for feeding to pps_sample()
57554359Sroberto			 */
57654359Sroberto			L_SUB(&off, &pp->lastrec);
57754359Sroberto
57854359Sroberto			pps_sample(peer, &off);
57954359Sroberto#endif
58054359Sroberto			true_debug(peer, "true_sample720: %luus\n", pp->usec);
58154359Sroberto		}
58254359Sroberto#endif
58354359Sroberto
58454359Sroberto		/*
58554359Sroberto		 * The clock will blurt a timecode every second but we only
58654359Sroberto		 * want one when polled.  If we havn't been polled, bail out.
58754359Sroberto		 */
58854359Sroberto		if (!up->polled)
589285612Sdelphij			return;
59054359Sroberto
591285612Sdelphij                /* We only call doevent if additional things need be done
592285612Sdelphij                 * at poll interval.  Currently, its only for GOES.  We also
593285612Sdelphij                 * call it for clock unknown so that it gets logged.
594285612Sdelphij                 */
595285612Sdelphij                if (up->type == t_goes || up->type == t_unknown)
596285612Sdelphij                    true_doevent(peer, e_Poll);
597285612Sdelphij
59854359Sroberto		if (!refclock_process(pp)) {
59954359Sroberto			refclock_report(peer, CEVNT_BADTIME);
60054359Sroberto			return;
60154359Sroberto		}
60282498Sroberto		/*
60382498Sroberto		 * If clock is good we send a NOMINAL message so that
60482498Sroberto		 * any previous BAD messages are nullified
60582498Sroberto		 */
606285612Sdelphij		pp->lastref = pp->lastrec;
60754359Sroberto		refclock_receive(peer);
608132451Sroberto		refclock_report(peer, CEVNT_NOMINAL);
60954359Sroberto
61054359Sroberto		/*
61154359Sroberto		 * We have succedded in answering the poll.
61254359Sroberto		 * Turn off the flag and return
61354359Sroberto		 */
61454359Sroberto		up->polled = 0;
61554359Sroberto
61654359Sroberto		return;
61754359Sroberto	}
61854359Sroberto
61954359Sroberto	/*
62054359Sroberto	 * No match to known timecodes, report failure and return
62154359Sroberto	 */
62254359Sroberto	refclock_report(peer, CEVNT_BADREPLY);
62354359Sroberto	return;
62454359Sroberto}
62554359Sroberto
62654359Sroberto
62754359Sroberto/*
62854359Sroberto * true_send - time to send the clock a signal to cough up a time sample
62954359Sroberto */
63054359Srobertostatic void
63154359Srobertotrue_send(
63254359Sroberto	struct peer *peer,
63354359Sroberto	const char *cmd
63454359Sroberto	)
63554359Sroberto{
63654359Sroberto	struct refclockproc *pp;
63754359Sroberto
63854359Sroberto	pp = peer->procptr;
63954359Sroberto	if (!(pp->sloppyclockflag & CLK_FLAG1)) {
640293650Sglebius		size_t len = strlen(cmd);
64154359Sroberto
64254359Sroberto		true_debug(peer, "Send '%s'\n", cmd);
64354359Sroberto		if (write(pp->io.fd, cmd, (unsigned)len) != len)
644285612Sdelphij			refclock_report(peer, CEVNT_FAULT);
64554359Sroberto		else
646285612Sdelphij			pp->polls++;
64754359Sroberto	}
64854359Sroberto}
64954359Sroberto
65054359Sroberto
65154359Sroberto/*
65254359Sroberto * state machine for initializing and controlling a clock
65354359Sroberto */
65454359Srobertostatic void
65554359Srobertotrue_doevent(
65654359Sroberto	struct peer *peer,
65754359Sroberto	enum true_event event
65854359Sroberto	)
65954359Sroberto{
66054359Sroberto	struct true_unit *up;
66154359Sroberto	struct refclockproc *pp;
66254359Sroberto
66354359Sroberto	pp = peer->procptr;
664285612Sdelphij	up = pp->unitptr;
66554359Sroberto	if (event != e_TS) {
66654359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
66754359Sroberto			msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
66854359Sroberto				typeStr(up->type),
66954359Sroberto				stateStr(up->state),
67054359Sroberto				eventStr(event));
67154359Sroberto		}
67254359Sroberto	}
67354359Sroberto	true_debug(peer, "clock %s, state %s, event %s\n",
67454359Sroberto		   typeStr(up->type), stateStr(up->state), eventStr(event));
67554359Sroberto	switch (up->type) {
676285612Sdelphij	case t_goes:
67754359Sroberto		switch (event) {
678285612Sdelphij		case e_Init:	/* FALLTHROUGH */
679285612Sdelphij		case e_Satellite:
68054359Sroberto			/*
68154359Sroberto			 * Switch back to on-second time codes and return.
68254359Sroberto			 */
68354359Sroberto			true_send(peer, "C");
68454359Sroberto			up->state = s_Start;
68554359Sroberto			break;
686285612Sdelphij		case e_Poll:
68754359Sroberto			/*
68854359Sroberto			 * After each poll, check the station (satellite).
68954359Sroberto			 */
69054359Sroberto			true_send(peer, "P");
69154359Sroberto			/* No state change needed. */
69254359Sroberto			break;
693285612Sdelphij		default:
69454359Sroberto			break;
69554359Sroberto		}
69654359Sroberto		/* FALLTHROUGH */
697285612Sdelphij	case t_omega:
69854359Sroberto		switch (event) {
699285612Sdelphij		case e_Init:
70054359Sroberto			true_send(peer, "C");
70154359Sroberto			up->state = s_Start;
70254359Sroberto			break;
703285612Sdelphij		case e_TS:
70454359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
70554359Sroberto				true_send(peer, "\03\r");
70654359Sroberto				break;
70754359Sroberto			}
70854359Sroberto			up->state = s_Auto;
70954359Sroberto			break;
710285612Sdelphij		default:
71154359Sroberto			break;
71254359Sroberto		}
71354359Sroberto		break;
714285612Sdelphij	case t_tm:
71554359Sroberto		switch (event) {
716285612Sdelphij		case e_Init:
71754359Sroberto			true_send(peer, "F18\r");
71854359Sroberto			up->state = s_Init;
71954359Sroberto			break;
720285612Sdelphij		case e_F18:
72154359Sroberto			true_send(peer, "F50\r");
722285612Sdelphij                        /*
723285612Sdelphij                         * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
724285612Sdelphij                         * (from a TM/TMD/XL clock during initialization.)
725285612Sdelphij                         */
726285612Sdelphij                        if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
727285612Sdelphij                            strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
728285612Sdelphij                                true_doevent(peer, e_F18);
729285612Sdelphij                                NLOG(NLOG_CLOCKSTATUS) {
730285612Sdelphij                                    msyslog(LOG_INFO, "TM/TMD/XL: %s",
731285612Sdelphij                                            pp->a_lastcode);
732285612Sdelphij                                }
733285612Sdelphij                                return;
734285612Sdelphij                        }
73554359Sroberto			up->state = s_F18;
73654359Sroberto			break;
737285612Sdelphij		case e_F50:
73854359Sroberto			true_send(peer, "F51\r");
73954359Sroberto			up->state = s_F50;
74054359Sroberto			break;
741285612Sdelphij		case e_F51:
74254359Sroberto			true_send(peer, "F08\r");
74354359Sroberto			up->state = s_Start;
74454359Sroberto			break;
745285612Sdelphij		case e_TS:
74654359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
74754359Sroberto				true_send(peer, "\03\r");
74854359Sroberto				break;
74954359Sroberto			}
75054359Sroberto			up->state = s_Auto;
75154359Sroberto			break;
752285612Sdelphij		default:
75354359Sroberto			break;
75454359Sroberto		}
75554359Sroberto		break;
756285612Sdelphij	case t_tcu:
75754359Sroberto		switch (event) {
758285612Sdelphij		case e_Init:
75954359Sroberto			true_send(peer, "MD3\r");	/* GPS Synch'd Gen. */
76054359Sroberto			true_send(peer, "TSU\r");	/* UTC, not GPS. */
76154359Sroberto			true_send(peer, "AU\r");	/* Auto Timestamps. */
76254359Sroberto			up->state = s_Start;
76354359Sroberto			break;
764285612Sdelphij		case e_TS:
76554359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
76654359Sroberto				true_send(peer, "\03\r");
76754359Sroberto				break;
76854359Sroberto			}
76954359Sroberto			up->state = s_Auto;
77054359Sroberto			break;
771285612Sdelphij		default:
77254359Sroberto			break;
77354359Sroberto		}
77454359Sroberto		break;
775285612Sdelphij	case t_tl3:
776285612Sdelphij                switch (event) {
777285612Sdelphij                    case e_Init:
778285612Sdelphij                        true_send(peer, "ST1"); /* Turn on continuous stream */
779285612Sdelphij                        break;
780285612Sdelphij                    case e_TS:
781285612Sdelphij                        up->state = s_Auto;
782285612Sdelphij                        break;
783285612Sdelphij                    default:
784285612Sdelphij                        break;
785285612Sdelphij                }
786285612Sdelphij                break;
787285612Sdelphij	case t_unknown:
788285612Sdelphij               if (event == e_Poll)
789285612Sdelphij                   break;
79054359Sroberto		switch (up->state) {
791285612Sdelphij		case s_Base:
79254359Sroberto			if (event != e_Init)
79354359Sroberto			    abort();
79454359Sroberto			true_send(peer, "P\r");
79554359Sroberto			up->state = s_InqGOES;
79654359Sroberto			break;
797285612Sdelphij		case s_InqGOES:
79854359Sroberto			switch (event) {
799285612Sdelphij			case e_Satellite:
80054359Sroberto				up->type = t_goes;
80154359Sroberto				true_doevent(peer, e_Init);
80254359Sroberto				break;
803285612Sdelphij			case e_Init:	/*FALLTHROUGH*/
804285612Sdelphij			case e_Huh:
805285612Sdelphij			case e_TS:
806285612Sdelphij                                true_send(peer, "ST0"); /* turn off TL3 auto */
807285612Sdelphij                                sleep(1);               /* wait for it */
808285612Sdelphij                                up->state = s_InqTL3;
809285612Sdelphij                                true_send(peer, "QV");  /* see if its a TL3 */
810285612Sdelphij                                break;
811285612Sdelphij                            default:
812285612Sdelphij                                abort();
813285612Sdelphij                        }
814285612Sdelphij                        break;
815285612Sdelphij                    case s_InqTL3:
816285612Sdelphij                        switch (event) {
817285612Sdelphij                            case e_TL3:
818285612Sdelphij                                up->type = t_tl3;
819285612Sdelphij                                up->state = s_Auto;     /* Inq side-effect. */
820285612Sdelphij                                true_send(peer, "ST1"); /* Turn on 1/sec data */
821285612Sdelphij                                break;
822285612Sdelphij                            case e_Init:        /*FALLTHROUGH*/
823285612Sdelphij                            case e_Huh:
82454359Sroberto				up->state = s_InqOmega;
82554359Sroberto				true_send(peer, "C\r");
82654359Sroberto				break;
827285612Sdelphij                            case e_TS:
828285612Sdelphij                                 up->type = t_tl3;    /* Already sending data */
829285612Sdelphij                                 up->state = s_Auto;
830285612Sdelphij                                 break;
83154359Sroberto			    default:
832285612Sdelphij                                msyslog(LOG_INFO,
833285612Sdelphij                                        "TRUE: TL3 init fellthrough! (%d)", event);
834285612Sdelphij                                break;
83554359Sroberto			}
83654359Sroberto			break;
837285612Sdelphij		case s_InqOmega:
83854359Sroberto			switch (event) {
839285612Sdelphij			case e_TS:
84054359Sroberto				up->type = t_omega;
84154359Sroberto				up->state = s_Auto;	/* Inq side-effect. */
84254359Sroberto				break;
843285612Sdelphij			case e_Init:	/*FALLTHROUGH*/
844285612Sdelphij			case e_Huh:
84554359Sroberto				up->state = s_InqTM;
84654359Sroberto				true_send(peer, "F18\r");
84754359Sroberto				break;
848285612Sdelphij			default:
84954359Sroberto				abort();
85054359Sroberto			}
85154359Sroberto			break;
852285612Sdelphij		case s_InqTM:
85354359Sroberto			switch (event) {
854285612Sdelphij			case e_F18:
85554359Sroberto				up->type = t_tm;
85654359Sroberto				true_doevent(peer, e_Init);
85754359Sroberto				break;
858285612Sdelphij			case e_Init:	/*FALLTHROUGH*/
859285612Sdelphij			case e_Huh:
86054359Sroberto				true_send(peer, "PO\r");
86154359Sroberto				up->state = s_InqTCU;
86254359Sroberto				break;
863285612Sdelphij			default:
864285612Sdelphij                                msyslog(LOG_INFO,
865285612Sdelphij                                        "TRUE: TM/TMD init fellthrough!");
866285612Sdelphij			        break;
86754359Sroberto			}
86854359Sroberto			break;
869285612Sdelphij		case s_InqTCU:
87054359Sroberto			switch (event) {
871285612Sdelphij			case e_Location:
87254359Sroberto				up->type = t_tcu;
87354359Sroberto				true_doevent(peer, e_Init);
87454359Sroberto				break;
875285612Sdelphij			case e_Init:	/*FALLTHROUGH*/
876285612Sdelphij			case e_Huh:
87754359Sroberto				up->state = s_Base;
87854359Sroberto				sleep(1);	/* XXX */
87954359Sroberto				break;
880285612Sdelphij			default:
881285612Sdelphij                                msyslog(LOG_INFO,
882285612Sdelphij                                        "TRUE: TCU init fellthrough!");
883285612Sdelphij                                break;
88454359Sroberto			}
88554359Sroberto			break;
88654359Sroberto			/*
88754359Sroberto			 * An expedient hack to prevent lint complaints,
88854359Sroberto			 * these don't actually need to be used here...
88954359Sroberto			 */
890285612Sdelphij		case s_Init:
891285612Sdelphij		case s_F18:
892285612Sdelphij		case s_F50:
893285612Sdelphij		case s_Start:
894285612Sdelphij		case s_Auto:
895285612Sdelphij		case s_Max:
896285612Sdelphij			msyslog(LOG_INFO, "TRUE: state %s is unexpected!",
897285612Sdelphij				stateStr(up->state));
89854359Sroberto		}
89954359Sroberto		break;
900285612Sdelphij	default:
901285612Sdelphij                msyslog(LOG_INFO, "TRUE: cannot identify refclock!");
902285612Sdelphij		abort();
90354359Sroberto		/* NOTREACHED */
90454359Sroberto	}
90554359Sroberto
90654359Sroberto#ifdef CLOCK_PPS720
90754359Sroberto	if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
90854359Sroberto		/* Make counter trigger on gate0, count down from 65535. */
90954359Sroberto		pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
91054359Sroberto		/*
91154359Sroberto		 * (These constants are OK since
91254359Sroberto		 * they represent hardware maximums.)
91354359Sroberto		 */
91454359Sroberto		NLOG(NLOG_CLOCKINFO) {
91554359Sroberto			msyslog(LOG_NOTICE, "PCL-720 initialized");
91654359Sroberto		}
91754359Sroberto		up->pcl720init++;
91854359Sroberto	}
91954359Sroberto#endif
92054359Sroberto
92154359Sroberto
92254359Sroberto}
92354359Sroberto
92454359Sroberto/*
92554359Sroberto * true_poll - called by the transmit procedure
92654359Sroberto */
92754359Srobertostatic void
92854359Srobertotrue_poll(
92954359Sroberto	int unit,
93054359Sroberto	struct peer *peer
93154359Sroberto	)
93254359Sroberto{
93354359Sroberto	struct true_unit *up;
93454359Sroberto	struct refclockproc *pp;
93554359Sroberto
93654359Sroberto	/*
93754359Sroberto	 * You don't need to poll this clock.  It puts out timecodes
93854359Sroberto	 * once per second.  If asked for a timestamp, take note.
93954359Sroberto	 * The next time a timecode comes in, it will be fed back.
94054359Sroberto	 */
94154359Sroberto	pp = peer->procptr;
942285612Sdelphij	up = pp->unitptr;
943285612Sdelphij	if (up->pollcnt > 0) {
944285612Sdelphij		up->pollcnt--;
945285612Sdelphij	} else {
94654359Sroberto		true_doevent(peer, e_Init);
94754359Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
94854359Sroberto	}
94954359Sroberto
95054359Sroberto	/*
95154359Sroberto	 * polled every 64 seconds. Ask true_receive to hand in a
95254359Sroberto	 * timestamp.
95354359Sroberto	 */
95454359Sroberto	up->polled = 1;
95554359Sroberto	pp->polls++;
95654359Sroberto}
95754359Sroberto
95854359Sroberto#ifdef CLOCK_PPS720
95954359Sroberto/*
96054359Sroberto * true_sample720 - sample the PCL-720
96154359Sroberto */
96254359Srobertostatic u_long
96354359Srobertotrue_sample720(void)
96454359Sroberto{
96554359Sroberto	unsigned long f;
96654359Sroberto
96754359Sroberto	/* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
96854359Sroberto	 * If it is not being held low now, we did not get called
96954359Sroberto	 * within 65535us.
97054359Sroberto	 */
97154359Sroberto	if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
97254359Sroberto		NLOG(NLOG_CLOCKINFO) {
97354359Sroberto			msyslog(LOG_NOTICE, "PCL-720 out of synch");
97454359Sroberto		}
97554359Sroberto		return (0);
97654359Sroberto	}
97754359Sroberto	f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
97854359Sroberto#ifdef PPS720_DEBUG
97954359Sroberto	msyslog(LOG_DEBUG, "PCL-720: %luus", f);
98054359Sroberto#endif
98154359Sroberto	return (f);
98254359Sroberto}
98354359Sroberto#endif
98454359Sroberto
98554359Sroberto#else
98654359Srobertoint refclock_true_bs;
98754359Sroberto#endif /* REFCLOCK */
988