refclock_true.c revision 54359
154359Sroberto/*
254359Sroberto * refclock_true - clock driver for the Kinemetrics Truetime receivers
354359Sroberto *	Receiver Version 3.0C - tested plain, with CLKLDISC
454359Sroberto *	Developement work being done:
554359Sroberto * 	- Properly handle varying satellite positions (more acurately)
654359Sroberto *	- Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
754359Sroberto */
854359Sroberto
954359Sroberto#ifdef HAVE_CONFIG_H
1054359Sroberto#include <config.h>
1154359Sroberto#endif
1254359Sroberto
1354359Sroberto#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
1454359Sroberto
1554359Sroberto#include <stdio.h>
1654359Sroberto#include <ctype.h>
1754359Sroberto#include <sys/time.h>
1854359Sroberto
1954359Sroberto#include "ntpd.h"
2054359Sroberto#include "ntp_io.h"
2154359Sroberto#include "ntp_refclock.h"
2254359Sroberto#include "ntp_unixtime.h"
2354359Sroberto#include "ntp_stdlib.h"
2454359Sroberto
2554359Sroberto/* This should be an atom clock but those are very hard to build.
2654359Sroberto *
2754359Sroberto * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
2854359Sroberto * of TTL input and output pins, all brought out to the back panel.  If you
2954359Sroberto * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
3054359Sroberto * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
3154359Sroberto * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
3254359Sroberto * number of uSecs since the last PPS upward swing, mediated by reading OUT0
3354359Sroberto * to find out if the counter has wrapped around (this happens if more than
3454359Sroberto * 65535us (65ms) elapses between the PPS event and our being called.)
3554359Sroberto */
3654359Sroberto#ifdef CLOCK_PPS720
3754359Sroberto# undef min	/* XXX */
3854359Sroberto# undef max	/* XXX */
3954359Sroberto# include <machine/inline.h>
4054359Sroberto# include <sys/pcl720.h>
4154359Sroberto# include <sys/i8253.h>
4254359Sroberto# define PCL720_IOB 0x2a0	/* XXX */
4354359Sroberto# define PCL720_CTR 0		/* XXX */
4454359Sroberto#endif
4554359Sroberto
4654359Sroberto/*
4754359Sroberto * Support for Kinemetrics Truetime Receivers
4854359Sroberto *	GOES
4954359Sroberto *	GPS/TM-TMD
5054359Sroberto *	XL-DC		(a 151-602-210, reported by the driver as a GPS/TM-TMD)
5154359Sroberto *	GPS-800 TCU	(an 805-957 with the RS232 Talker/Listener module)
5254359Sroberto *	OM-DC:		getting stale ("OMEGA")
5354359Sroberto *
5454359Sroberto * Most of this code is originally from refclock_wwvb.c with thanks.
5554359Sroberto * It has been so mangled that wwvb is not a recognizable ancestor.
5654359Sroberto *
5754359Sroberto * Timcode format: ADDD:HH:MM:SSQCL
5854359Sroberto *	A - control A		(this is stripped before we see it)
5954359Sroberto *	Q - Quality indication	(see below)
6054359Sroberto *	C - Carriage return
6154359Sroberto *	L - Line feed
6254359Sroberto *
6354359Sroberto * Quality codes indicate possible error of
6454359Sroberto *   468-DC GOES Receiver:
6554359Sroberto *   GPS-TM/TMD Receiver:
6654359Sroberto *       ?     +/- 500 milliseconds	#     +/- 50 milliseconds
6754359Sroberto *       *     +/- 5 milliseconds	.     +/- 1 millisecond
6854359Sroberto *     space   less than 1 millisecond
6954359Sroberto *   OM-DC OMEGA Receiver:
7054359Sroberto *       >     >+- 5 seconds
7154359Sroberto *       ?     >+/- 500 milliseconds    #     >+/- 50 milliseconds
7254359Sroberto *       *     >+/- 5 milliseconds      .     >+/- 1 millisecond
7354359Sroberto *      A-H    less than 1 millisecond.  Character indicates which station
7454359Sroberto *             is being received as follows:
7554359Sroberto *             A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
7654359Sroberto *             E = La Reunion, F = Argentina, G = Australia, H = Japan.
7754359Sroberto *
7854359Sroberto * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
7954359Sroberto *
8054359Sroberto * Notes on 468-DC and OMEGA receiver:
8154359Sroberto *
8254359Sroberto * Send the clock a 'R' or 'C' and once per second a timestamp will
8354359Sroberto * appear.  Send a 'P' to get the satellite position once (GOES only.)
8454359Sroberto *
8554359Sroberto * Notes on the 468-DC receiver:
8654359Sroberto *
8754359Sroberto * Since the old east/west satellite locations are only historical, you can't
8854359Sroberto * set your clock propagation delay settings correctly and still use
8954359Sroberto * automatic mode. The manual says to use a compromise when setting the
9054359Sroberto * switches. This results in significant errors. The solution; use fudge
9154359Sroberto * time1 and time2 to incorporate corrections. If your clock is set for
9254359Sroberto * 50 and it should be 58 for using the west and 46 for using the east,
9354359Sroberto * use the line
9454359Sroberto *
9554359Sroberto * fudge 127.127.5.0 time1 +0.008 time2 -0.004
9654359Sroberto *
9754359Sroberto * This corrects the 4 milliseconds advance and 8 milliseconds retard
9854359Sroberto * needed. The software will ask the clock which satellite it sees.
9954359Sroberto *
10054359Sroberto * Ntp.conf parameters:
10154359Sroberto * time1 - offset applied to samples when reading WEST satellite (default = 0)
10254359Sroberto * time2 - offset applied to samples when reading EAST satellite (default = 0)
10354359Sroberto * val1  - stratum to assign to this clock (default = 0)
10454359Sroberto * val2  - refid assigned to this clock (default = "TRUE", see below)
10554359Sroberto * flag1 - will silence the clock side of ntpd, just reading the clock
10654359Sroberto *         without trying to write to it.  (default = 0)
10754359Sroberto * flag2 - generate a debug file /tmp/true%d.
10854359Sroberto * flag3 - enable ppsclock streams module
10954359Sroberto * flag4 - use the PCL-720 (BSD/OS only)
11054359Sroberto */
11154359Sroberto
11254359Sroberto/*
11354359Sroberto * Definitions
11454359Sroberto */
11554359Sroberto#define	DEVICE		"/dev/true%d"
11654359Sroberto#define	SPEED232	B9600	/* 9600 baud */
11754359Sroberto
11854359Sroberto/*
11954359Sroberto * Radio interface parameters
12054359Sroberto */
12154359Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
12254359Sroberto#define	REFID		"TRUE"	/* reference id */
12354359Sroberto#define	DESCRIPTION	"Kinemetrics/TrueTime Receiver"
12454359Sroberto
12554359Sroberto/*
12654359Sroberto * Tags which station (satellite) we see
12754359Sroberto */
12854359Sroberto#define GOES_WEST	0	/* Default to WEST satellite and apply time1 */
12954359Sroberto#define GOES_EAST	1	/* until you discover otherwise */
13054359Sroberto
13154359Sroberto/*
13254359Sroberto * used by the state machine
13354359Sroberto */
13454359Srobertoenum true_event	{e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
13554359Sroberto		 e_Poll, e_Location, e_TS, e_Max};
13654359Srobertoconst char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
13754359Sroberto			"Poll", "Location", "TS"};
13854359Sroberto#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
13954359Sroberto
14054359Srobertoenum true_state	{s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
14154359Sroberto		 s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
14254359Srobertoconst char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
14354359Sroberto			"Init", "F18", "F50", "Start", "Auto"};
14454359Sroberto#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
14554359Sroberto
14654359Srobertoenum true_type	{t_unknown, t_goes, t_tm, t_tcu, t_omega, t_Max};
14754359Srobertoconst char *types[] = {"unknown", "goes", "tm", "tcu", "omega"};
14854359Sroberto#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
14954359Sroberto
15054359Sroberto/*
15154359Sroberto * unit control structure
15254359Sroberto */
15354359Srobertostruct true_unit {
15454359Sroberto	unsigned int	pollcnt;	/* poll message counter */
15554359Sroberto	unsigned int	station;	/* which station we are on */
15654359Sroberto	unsigned int	polled;		/* Hand in a time sample? */
15754359Sroberto	enum true_state	state;		/* state machine */
15854359Sroberto	enum true_type	type;		/* what kind of clock is it? */
15954359Sroberto	int		unit;		/* save an extra copy of this */
16054359Sroberto	FILE		*debug;		/* debug logging file */
16154359Sroberto#ifdef CLOCK_PPS720
16254359Sroberto	int		pcl720init;	/* init flag for PCL 720 */
16354359Sroberto#endif
16454359Sroberto};
16554359Sroberto
16654359Sroberto/*
16754359Sroberto * Function prototypes
16854359Sroberto */
16954359Srobertostatic	int	true_start	P((int, struct peer *));
17054359Srobertostatic	void	true_shutdown	P((int, struct peer *));
17154359Srobertostatic	void	true_receive	P((struct recvbuf *));
17254359Srobertostatic	void	true_poll	P((int, struct peer *));
17354359Srobertostatic	void	true_send	P((struct peer *, const char *));
17454359Srobertostatic	void	true_doevent	P((struct peer *, enum true_event));
17554359Sroberto
17654359Sroberto#ifdef CLOCK_PPS720
17754359Srobertostatic	u_long	true_sample720	P((void));
17854359Sroberto#endif
17954359Sroberto
18054359Sroberto/*
18154359Sroberto * Transfer vector
18254359Sroberto */
18354359Srobertostruct	refclock refclock_true = {
18454359Sroberto	true_start,		/* start up driver */
18554359Sroberto	true_shutdown,		/* shut down driver */
18654359Sroberto	true_poll,		/* transmit poll message */
18754359Sroberto	noentry,		/* not used (old true_control) */
18854359Sroberto	noentry,		/* initialize driver (not used) */
18954359Sroberto	noentry,		/* not used (old true_buginfo) */
19054359Sroberto	NOFLAGS			/* not used */
19154359Sroberto};
19254359Sroberto
19354359Sroberto
19454359Sroberto#if !defined(__STDC__)
19554359Sroberto# define true_debug (void)
19654359Sroberto#else
19754359Srobertostatic void
19854359Srobertotrue_debug(struct peer *peer, const char *fmt, ...)
19954359Sroberto{
20054359Sroberto	va_list ap;
20154359Sroberto	int want_debugging, now_debugging;
20254359Sroberto	struct refclockproc *pp;
20354359Sroberto	struct true_unit *up;
20454359Sroberto
20554359Sroberto	va_start(ap, fmt);
20654359Sroberto	pp = peer->procptr;
20754359Sroberto	up = (struct true_unit *)pp->unitptr;
20854359Sroberto
20954359Sroberto	want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
21054359Sroberto	now_debugging = (up->debug != NULL);
21154359Sroberto	if (want_debugging != now_debugging)
21254359Sroberto	{
21354359Sroberto		if (want_debugging) {
21454359Sroberto		    char filename[20];
21554359Sroberto
21654359Sroberto		    sprintf(filename, "/tmp/true%d.debug", up->unit);
21754359Sroberto		    up->debug = fopen(filename, "w");
21854359Sroberto		    if (up->debug) {
21954359Sroberto#ifdef HAVE_SETVBUF
22054359Sroberto			    static char buf[BUFSIZ];
22154359Sroberto			    setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
22254359Sroberto#else
22354359Sroberto			    setlinebuf(up->debug);
22454359Sroberto#endif
22554359Sroberto		    }
22654359Sroberto	    } else {
22754359Sroberto		    fclose(up->debug);
22854359Sroberto		    up->debug = NULL;
22954359Sroberto	    }
23054359Sroberto	}
23154359Sroberto
23254359Sroberto	if (up->debug) {
23354359Sroberto		fprintf(up->debug, "true%d: ", up->unit);
23454359Sroberto		vfprintf(up->debug, fmt, ap);
23554359Sroberto	}
23654359Sroberto}
23754359Sroberto#endif /*STDC*/
23854359Sroberto
23954359Sroberto/*
24054359Sroberto * true_start - open the devices and initialize data for processing
24154359Sroberto */
24254359Srobertostatic int
24354359Srobertotrue_start(
24454359Sroberto	int unit,
24554359Sroberto	struct peer *peer
24654359Sroberto	)
24754359Sroberto{
24854359Sroberto	register struct true_unit *up;
24954359Sroberto	struct refclockproc *pp;
25054359Sroberto	char device[20];
25154359Sroberto	int fd;
25254359Sroberto
25354359Sroberto	/*
25454359Sroberto	 * Open serial port
25554359Sroberto	 */
25654359Sroberto	(void)sprintf(device, DEVICE, unit);
25754359Sroberto	if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
25854359Sroberto	    return (0);
25954359Sroberto
26054359Sroberto	/*
26154359Sroberto	 * Allocate and initialize unit structure
26254359Sroberto	 */
26354359Sroberto	if (!(up = (struct true_unit *)
26454359Sroberto	      emalloc(sizeof(struct true_unit)))) {
26554359Sroberto		(void) close(fd);
26654359Sroberto		return (0);
26754359Sroberto	}
26854359Sroberto	memset((char *)up, 0, sizeof(struct true_unit));
26954359Sroberto	pp = peer->procptr;
27054359Sroberto	pp->io.clock_recv = true_receive;
27154359Sroberto	pp->io.srcclock = (caddr_t)peer;
27254359Sroberto	pp->io.datalen = 0;
27354359Sroberto	pp->io.fd = fd;
27454359Sroberto	if (!io_addclock(&pp->io)) {
27554359Sroberto		(void) close(fd);
27654359Sroberto		free(up);
27754359Sroberto		return (0);
27854359Sroberto	}
27954359Sroberto	pp->unitptr = (caddr_t)up;
28054359Sroberto
28154359Sroberto	/*
28254359Sroberto	 * Initialize miscellaneous variables
28354359Sroberto	 */
28454359Sroberto	peer->precision = PRECISION;
28554359Sroberto	pp->clockdesc = DESCRIPTION;
28654359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
28754359Sroberto	up->pollcnt = 2;
28854359Sroberto	up->type = t_unknown;
28954359Sroberto	up->state = s_Base;
29054359Sroberto	true_doevent(peer, e_Init);
29154359Sroberto	return (1);
29254359Sroberto}
29354359Sroberto
29454359Sroberto/*
29554359Sroberto * true_shutdown - shut down the clock
29654359Sroberto */
29754359Srobertostatic void
29854359Srobertotrue_shutdown(
29954359Sroberto	int unit,
30054359Sroberto	struct peer *peer
30154359Sroberto	)
30254359Sroberto{
30354359Sroberto	register struct true_unit *up;
30454359Sroberto	struct refclockproc *pp;
30554359Sroberto
30654359Sroberto	pp = peer->procptr;
30754359Sroberto	up = (struct true_unit *)pp->unitptr;
30854359Sroberto	io_closeclock(&pp->io);
30954359Sroberto	free(up);
31054359Sroberto}
31154359Sroberto
31254359Sroberto
31354359Sroberto/*
31454359Sroberto * true_receive - receive data from the serial interface on a clock
31554359Sroberto */
31654359Srobertostatic void
31754359Srobertotrue_receive(
31854359Sroberto	struct recvbuf *rbufp
31954359Sroberto	)
32054359Sroberto{
32154359Sroberto	register struct true_unit *up;
32254359Sroberto	struct refclockproc *pp;
32354359Sroberto	struct peer *peer;
32454359Sroberto	u_short new_station;
32554359Sroberto	char synced;
32654359Sroberto	int i;
32754359Sroberto	int lat, lon, off;	/* GOES Satellite position */
32854359Sroberto
32954359Sroberto	/*
33054359Sroberto	 * Get the clock this applies to and pointers to the data.
33154359Sroberto	 */
33254359Sroberto	peer = (struct peer *)rbufp->recv_srcclock;
33354359Sroberto	pp = peer->procptr;
33454359Sroberto	up = (struct true_unit *)pp->unitptr;
33554359Sroberto
33654359Sroberto	/*
33754359Sroberto	 * Read clock output.  Automatically handles STREAMS, CLKLDISC.
33854359Sroberto	 */
33954359Sroberto	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
34054359Sroberto
34154359Sroberto	/*
34254359Sroberto	 * There is a case where <cr><lf> generates 2 timestamps.
34354359Sroberto	 */
34454359Sroberto	if (pp->lencode == 0)
34554359Sroberto	    return;
34654359Sroberto	pp->a_lastcode[pp->lencode] = '\0';
34754359Sroberto	true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode);
34854359Sroberto
34954359Sroberto	up->pollcnt = 2;
35054359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
35154359Sroberto
35254359Sroberto	/*
35354359Sroberto	 * We get down to business, check the timecode format and decode
35454359Sroberto	 * its contents. This code decodes a multitude of different
35554359Sroberto	 * clock messages. Timecodes are processed if needed. All replies
35654359Sroberto	 * will be run through the state machine to tweak driver options
35754359Sroberto	 * and program the clock.
35854359Sroberto	 */
35954359Sroberto
36054359Sroberto	/*
36154359Sroberto	 * Clock misunderstood our last command?
36254359Sroberto	 */
36354359Sroberto	if (pp->a_lastcode[0] == '?') {
36454359Sroberto		true_doevent(peer, e_Huh);
36554359Sroberto		return;
36654359Sroberto	}
36754359Sroberto
36854359Sroberto	/*
36954359Sroberto	 * Timecode: "nnnnn+nnn-nnn"
37054359Sroberto	 * (from GOES clock when asked about satellite position)
37154359Sroberto	 */
37254359Sroberto	if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
37354359Sroberto	    (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
37454359Sroberto	    sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
37554359Sroberto	    ) {
37654359Sroberto		const char *label = "Botch!";
37754359Sroberto
37854359Sroberto		/*
37954359Sroberto		 * This is less than perfect.  Call the (satellite)
38054359Sroberto		 * either EAST or WEST and adjust slop accodingly
38154359Sroberto		 * Perfectionists would recalculate the exact delay
38254359Sroberto		 * and adjust accordingly...
38354359Sroberto		 */
38454359Sroberto		if (lon > 7000 && lon < 14000) {
38554359Sroberto			if (lon < 10000) {
38654359Sroberto				new_station = GOES_EAST;
38754359Sroberto				label = "EAST";
38854359Sroberto			} else {
38954359Sroberto				new_station = GOES_WEST;
39054359Sroberto				label = "WEST";
39154359Sroberto			}
39254359Sroberto
39354359Sroberto			if (new_station != up->station) {
39454359Sroberto				double dtemp;
39554359Sroberto
39654359Sroberto				dtemp = pp->fudgetime1;
39754359Sroberto				pp->fudgetime1 = pp->fudgetime2;
39854359Sroberto				pp->fudgetime2 = dtemp;
39954359Sroberto				up->station = new_station;
40054359Sroberto			}
40154359Sroberto		}
40254359Sroberto		else {
40354359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
40454359Sroberto			label = "UNKNOWN";
40554359Sroberto		}
40654359Sroberto		true_debug(peer, "GOES: station %s\n", label);
40754359Sroberto		true_doevent(peer, e_Satellite);
40854359Sroberto		return;
40954359Sroberto	}
41054359Sroberto
41154359Sroberto	/*
41254359Sroberto	 * Timecode: "Fnn"
41354359Sroberto	 * (from TM/TMD clock when it wants to tell us what it's up to.)
41454359Sroberto	 */
41554359Sroberto	if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
41654359Sroberto		switch (i) {
41754359Sroberto		    case 50:
41854359Sroberto			true_doevent(peer, e_F50);
41954359Sroberto			break;
42054359Sroberto		    case 51:
42154359Sroberto			true_doevent(peer, e_F51);
42254359Sroberto			break;
42354359Sroberto		    default:
42454359Sroberto			true_debug(peer, "got F%02d - ignoring\n", i);
42554359Sroberto			break;
42654359Sroberto		}
42754359Sroberto		return;
42854359Sroberto	}
42954359Sroberto
43054359Sroberto	/*
43154359Sroberto	 * Timecode: " TRUETIME Mk III"
43254359Sroberto	 * (from a TM/TMD clock during initialization.)
43354359Sroberto	 */
43454359Sroberto	if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0) {
43554359Sroberto		true_doevent(peer, e_F18);
43654359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
43754359Sroberto			msyslog(LOG_INFO, "TM/TMD: %s", pp->a_lastcode);
43854359Sroberto		}
43954359Sroberto		return;
44054359Sroberto	}
44154359Sroberto
44254359Sroberto	/*
44354359Sroberto	 * Timecode: "N03726428W12209421+000033"
44454359Sroberto	 *                      1         2
44554359Sroberto	 *            0123456789012345678901234
44654359Sroberto	 * (from a TCU during initialization)
44754359Sroberto	 */
44854359Sroberto	if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
44954359Sroberto	    (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
45054359Sroberto	    pp->a_lastcode[18] == '+') {
45154359Sroberto		true_doevent(peer, e_Location);
45254359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
45354359Sroberto			msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
45454359Sroberto		}
45554359Sroberto		return;
45654359Sroberto	}
45754359Sroberto	/*
45854359Sroberto	 * Timecode: "ddd:hh:mm:ssQ"
45954359Sroberto	 * (from all clocks supported by this driver.)
46054359Sroberto	 */
46154359Sroberto	if (pp->a_lastcode[3] == ':' &&
46254359Sroberto	    pp->a_lastcode[6] == ':' &&
46354359Sroberto	    pp->a_lastcode[9] == ':' &&
46454359Sroberto	    sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
46554359Sroberto		   &pp->day, &pp->hour, &pp->minute,
46654359Sroberto		   &pp->second, &synced) == 5) {
46754359Sroberto
46854359Sroberto		/*
46954359Sroberto		 * Adjust the synchronize indicator according to timecode
47054359Sroberto		 */
47154359Sroberto		if (synced != ' ' && synced != '.' && synced != '*')
47254359Sroberto		    pp->leap = LEAP_NOTINSYNC;
47354359Sroberto
47454359Sroberto		true_doevent(peer, e_TS);
47554359Sroberto
47654359Sroberto#ifdef CLOCK_PPS720
47754359Sroberto		/* If it's taken more than 65ms to get here, we'll lose. */
47854359Sroberto		if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
47954359Sroberto			l_fp   off;
48054359Sroberto
48154359Sroberto#ifdef CLOCK_ATOM
48254359Sroberto			/*
48354359Sroberto			 * find out what time it really is. Include
48454359Sroberto			 * the count from the PCL720
48554359Sroberto			 */
48654359Sroberto			if (!clocktime(pp->day, pp->hour, pp->minute,
48754359Sroberto				       pp->second, GMT, pp->lastrec.l_ui,
48854359Sroberto				       &pp->yearstart, &off.l_ui)) {
48954359Sroberto				refclock_report(peer, CEVNT_BADTIME);
49054359Sroberto				return;
49154359Sroberto			}
49254359Sroberto#endif
49354359Sroberto
49454359Sroberto			pp->usec = true_sample720();
49554359Sroberto#ifdef CLOCK_ATOM
49654359Sroberto			TVUTOTSF(pp->usec, off.l_uf);
49754359Sroberto#endif
49854359Sroberto
49954359Sroberto			/*
50054359Sroberto			 * Stomp all over the timestamp that was pulled out
50154359Sroberto			 * of the input stream. It's irrelevant since we've
50254359Sroberto			 * adjusted the input time to reflect now (via pp->usec)
50354359Sroberto			 * rather than when the data was collected.
50454359Sroberto			 */
50554359Sroberto			get_systime(&pp->lastrec);
50654359Sroberto#ifdef CLOCK_ATOM
50754359Sroberto			/*
50854359Sroberto			 * Create a true offset for feeding to pps_sample()
50954359Sroberto			 */
51054359Sroberto			L_SUB(&off, &pp->lastrec);
51154359Sroberto
51254359Sroberto			pps_sample(peer, &off);
51354359Sroberto#endif
51454359Sroberto			true_debug(peer, "true_sample720: %luus\n", pp->usec);
51554359Sroberto		}
51654359Sroberto#endif
51754359Sroberto
51854359Sroberto		/*
51954359Sroberto		 * The clock will blurt a timecode every second but we only
52054359Sroberto		 * want one when polled.  If we havn't been polled, bail out.
52154359Sroberto		 */
52254359Sroberto		if (!up->polled)
52354359Sroberto		    return;
52454359Sroberto
52554359Sroberto		true_doevent(peer, e_Poll);
52654359Sroberto		if (!refclock_process(pp)) {
52754359Sroberto			refclock_report(peer, CEVNT_BADTIME);
52854359Sroberto			return;
52954359Sroberto		}
53054359Sroberto		refclock_receive(peer);
53154359Sroberto
53254359Sroberto		/*
53354359Sroberto		 * We have succedded in answering the poll.
53454359Sroberto		 * Turn off the flag and return
53554359Sroberto		 */
53654359Sroberto		up->polled = 0;
53754359Sroberto
53854359Sroberto		return;
53954359Sroberto	}
54054359Sroberto
54154359Sroberto	/*
54254359Sroberto	 * No match to known timecodes, report failure and return
54354359Sroberto	 */
54454359Sroberto	refclock_report(peer, CEVNT_BADREPLY);
54554359Sroberto	return;
54654359Sroberto}
54754359Sroberto
54854359Sroberto
54954359Sroberto/*
55054359Sroberto * true_send - time to send the clock a signal to cough up a time sample
55154359Sroberto */
55254359Srobertostatic void
55354359Srobertotrue_send(
55454359Sroberto	struct peer *peer,
55554359Sroberto	const char *cmd
55654359Sroberto	)
55754359Sroberto{
55854359Sroberto	struct refclockproc *pp;
55954359Sroberto
56054359Sroberto	pp = peer->procptr;
56154359Sroberto	if (!(pp->sloppyclockflag & CLK_FLAG1)) {
56254359Sroberto		register int len = strlen(cmd);
56354359Sroberto
56454359Sroberto		true_debug(peer, "Send '%s'\n", cmd);
56554359Sroberto		if (write(pp->io.fd, cmd, (unsigned)len) != len)
56654359Sroberto		    refclock_report(peer, CEVNT_FAULT);
56754359Sroberto		else
56854359Sroberto		    pp->polls++;
56954359Sroberto	}
57054359Sroberto}
57154359Sroberto
57254359Sroberto
57354359Sroberto/*
57454359Sroberto * state machine for initializing and controlling a clock
57554359Sroberto */
57654359Srobertostatic void
57754359Srobertotrue_doevent(
57854359Sroberto	struct peer *peer,
57954359Sroberto	enum true_event event
58054359Sroberto	)
58154359Sroberto{
58254359Sroberto	struct true_unit *up;
58354359Sroberto	struct refclockproc *pp;
58454359Sroberto
58554359Sroberto	pp = peer->procptr;
58654359Sroberto	up = (struct true_unit *)pp->unitptr;
58754359Sroberto	if (event != e_TS) {
58854359Sroberto		NLOG(NLOG_CLOCKSTATUS) {
58954359Sroberto			msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
59054359Sroberto				typeStr(up->type),
59154359Sroberto				stateStr(up->state),
59254359Sroberto				eventStr(event));
59354359Sroberto		}
59454359Sroberto	}
59554359Sroberto	true_debug(peer, "clock %s, state %s, event %s\n",
59654359Sroberto		   typeStr(up->type), stateStr(up->state), eventStr(event));
59754359Sroberto	switch (up->type) {
59854359Sroberto	    case t_goes:
59954359Sroberto		switch (event) {
60054359Sroberto		    case e_Init:	/* FALLTHROUGH */
60154359Sroberto		    case e_Satellite:
60254359Sroberto			/*
60354359Sroberto			 * Switch back to on-second time codes and return.
60454359Sroberto			 */
60554359Sroberto			true_send(peer, "C");
60654359Sroberto			up->state = s_Start;
60754359Sroberto			break;
60854359Sroberto		    case e_Poll:
60954359Sroberto			/*
61054359Sroberto			 * After each poll, check the station (satellite).
61154359Sroberto			 */
61254359Sroberto			true_send(peer, "P");
61354359Sroberto			/* No state change needed. */
61454359Sroberto			break;
61554359Sroberto		    default:
61654359Sroberto			break;
61754359Sroberto		}
61854359Sroberto		/* FALLTHROUGH */
61954359Sroberto	    case t_omega:
62054359Sroberto		switch (event) {
62154359Sroberto		    case e_Init:
62254359Sroberto			true_send(peer, "C");
62354359Sroberto			up->state = s_Start;
62454359Sroberto			break;
62554359Sroberto		    case e_TS:
62654359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
62754359Sroberto				true_send(peer, "\03\r");
62854359Sroberto				break;
62954359Sroberto			}
63054359Sroberto			up->state = s_Auto;
63154359Sroberto			break;
63254359Sroberto		    default:
63354359Sroberto			break;
63454359Sroberto		}
63554359Sroberto		break;
63654359Sroberto	    case t_tm:
63754359Sroberto		switch (event) {
63854359Sroberto		    case e_Init:
63954359Sroberto			true_send(peer, "F18\r");
64054359Sroberto			up->state = s_Init;
64154359Sroberto			break;
64254359Sroberto		    case e_F18:
64354359Sroberto			true_send(peer, "F50\r");
64454359Sroberto			up->state = s_F18;
64554359Sroberto			break;
64654359Sroberto		    case e_F50:
64754359Sroberto			true_send(peer, "F51\r");
64854359Sroberto			up->state = s_F50;
64954359Sroberto			break;
65054359Sroberto		    case e_F51:
65154359Sroberto			true_send(peer, "F08\r");
65254359Sroberto			up->state = s_Start;
65354359Sroberto			break;
65454359Sroberto		    case e_TS:
65554359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
65654359Sroberto				true_send(peer, "\03\r");
65754359Sroberto				break;
65854359Sroberto			}
65954359Sroberto			up->state = s_Auto;
66054359Sroberto			break;
66154359Sroberto		    default:
66254359Sroberto			break;
66354359Sroberto		}
66454359Sroberto		break;
66554359Sroberto	    case t_tcu:
66654359Sroberto		switch (event) {
66754359Sroberto		    case e_Init:
66854359Sroberto			true_send(peer, "MD3\r");	/* GPS Synch'd Gen. */
66954359Sroberto			true_send(peer, "TSU\r");	/* UTC, not GPS. */
67054359Sroberto			true_send(peer, "AU\r");	/* Auto Timestamps. */
67154359Sroberto			up->state = s_Start;
67254359Sroberto			break;
67354359Sroberto		    case e_TS:
67454359Sroberto			if (up->state != s_Start && up->state != s_Auto) {
67554359Sroberto				true_send(peer, "\03\r");
67654359Sroberto				break;
67754359Sroberto			}
67854359Sroberto			up->state = s_Auto;
67954359Sroberto			break;
68054359Sroberto		    default:
68154359Sroberto			break;
68254359Sroberto		}
68354359Sroberto		break;
68454359Sroberto	    case t_unknown:
68554359Sroberto		switch (up->state) {
68654359Sroberto		    case s_Base:
68754359Sroberto			if (event != e_Init)
68854359Sroberto			    abort();
68954359Sroberto			true_send(peer, "P\r");
69054359Sroberto			up->state = s_InqGOES;
69154359Sroberto			break;
69254359Sroberto		    case s_InqGOES:
69354359Sroberto			switch (event) {
69454359Sroberto			    case e_Satellite:
69554359Sroberto				up->type = t_goes;
69654359Sroberto				true_doevent(peer, e_Init);
69754359Sroberto				break;
69854359Sroberto			    case e_Init:	/*FALLTHROUGH*/
69954359Sroberto			    case e_Huh:	/*FALLTHROUGH*/
70054359Sroberto			    case e_TS:
70154359Sroberto				up->state = s_InqOmega;
70254359Sroberto				true_send(peer, "C\r");
70354359Sroberto				break;
70454359Sroberto			    default:
70554359Sroberto				abort();
70654359Sroberto			}
70754359Sroberto			break;
70854359Sroberto		    case s_InqOmega:
70954359Sroberto			switch (event) {
71054359Sroberto			    case e_TS:
71154359Sroberto				up->type = t_omega;
71254359Sroberto				up->state = s_Auto;	/* Inq side-effect. */
71354359Sroberto				break;
71454359Sroberto			    case e_Init:	/*FALLTHROUGH*/
71554359Sroberto			    case e_Huh:
71654359Sroberto				up->state = s_InqTM;
71754359Sroberto				true_send(peer, "F18\r");
71854359Sroberto				break;
71954359Sroberto			    default:
72054359Sroberto				abort();
72154359Sroberto			}
72254359Sroberto			break;
72354359Sroberto		    case s_InqTM:
72454359Sroberto			switch (event) {
72554359Sroberto			    case e_F18:
72654359Sroberto				up->type = t_tm;
72754359Sroberto				true_doevent(peer, e_Init);
72854359Sroberto				break;
72954359Sroberto			    case e_Init:	/*FALLTHROUGH*/
73054359Sroberto			    case e_Huh:
73154359Sroberto				true_send(peer, "PO\r");
73254359Sroberto				up->state = s_InqTCU;
73354359Sroberto				break;
73454359Sroberto			    default:
73554359Sroberto				abort();
73654359Sroberto			}
73754359Sroberto			break;
73854359Sroberto		    case s_InqTCU:
73954359Sroberto			switch (event) {
74054359Sroberto			    case e_Location:
74154359Sroberto				up->type = t_tcu;
74254359Sroberto				true_doevent(peer, e_Init);
74354359Sroberto				break;
74454359Sroberto			    case e_Init:	/*FALLTHROUGH*/
74554359Sroberto			    case e_Huh:
74654359Sroberto				up->state = s_Base;
74754359Sroberto				sleep(1);	/* XXX */
74854359Sroberto				break;
74954359Sroberto			    default:
75054359Sroberto				abort();
75154359Sroberto			}
75254359Sroberto			break;
75354359Sroberto			/*
75454359Sroberto			 * An expedient hack to prevent lint complaints,
75554359Sroberto			 * these don't actually need to be used here...
75654359Sroberto			 */
75754359Sroberto		    case s_Init:
75854359Sroberto		    case s_F18:
75954359Sroberto		    case s_F50:
76054359Sroberto		    case s_Start:
76154359Sroberto		    case s_Auto:
76254359Sroberto		    case s_Max:
76354359Sroberto			msyslog(LOG_INFO, "TRUE: state %s is unexpected!", stateStr(up->state));
76454359Sroberto		}
76554359Sroberto		break;
76654359Sroberto	    default:
76754359Sroberto		abort();
76854359Sroberto		/* NOTREACHED */
76954359Sroberto	}
77054359Sroberto
77154359Sroberto#ifdef CLOCK_PPS720
77254359Sroberto	if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
77354359Sroberto		/* Make counter trigger on gate0, count down from 65535. */
77454359Sroberto		pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
77554359Sroberto		/*
77654359Sroberto		 * (These constants are OK since
77754359Sroberto		 * they represent hardware maximums.)
77854359Sroberto		 */
77954359Sroberto		NLOG(NLOG_CLOCKINFO) {
78054359Sroberto			msyslog(LOG_NOTICE, "PCL-720 initialized");
78154359Sroberto		}
78254359Sroberto		up->pcl720init++;
78354359Sroberto	}
78454359Sroberto#endif
78554359Sroberto
78654359Sroberto
78754359Sroberto}
78854359Sroberto
78954359Sroberto/*
79054359Sroberto * true_poll - called by the transmit procedure
79154359Sroberto */
79254359Srobertostatic void
79354359Srobertotrue_poll(
79454359Sroberto	int unit,
79554359Sroberto	struct peer *peer
79654359Sroberto	)
79754359Sroberto{
79854359Sroberto	struct true_unit *up;
79954359Sroberto	struct refclockproc *pp;
80054359Sroberto
80154359Sroberto	/*
80254359Sroberto	 * You don't need to poll this clock.  It puts out timecodes
80354359Sroberto	 * once per second.  If asked for a timestamp, take note.
80454359Sroberto	 * The next time a timecode comes in, it will be fed back.
80554359Sroberto	 */
80654359Sroberto	pp = peer->procptr;
80754359Sroberto	up = (struct true_unit *)pp->unitptr;
80854359Sroberto	if (up->pollcnt > 0)
80954359Sroberto	    up->pollcnt--;
81054359Sroberto	else {
81154359Sroberto		true_doevent(peer, e_Init);
81254359Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
81354359Sroberto	}
81454359Sroberto
81554359Sroberto	/*
81654359Sroberto	 * polled every 64 seconds. Ask true_receive to hand in a
81754359Sroberto	 * timestamp.
81854359Sroberto	 */
81954359Sroberto	up->polled = 1;
82054359Sroberto	pp->polls++;
82154359Sroberto}
82254359Sroberto
82354359Sroberto#ifdef CLOCK_PPS720
82454359Sroberto/*
82554359Sroberto * true_sample720 - sample the PCL-720
82654359Sroberto */
82754359Srobertostatic u_long
82854359Srobertotrue_sample720(void)
82954359Sroberto{
83054359Sroberto	unsigned long f;
83154359Sroberto
83254359Sroberto	/* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
83354359Sroberto	 * If it is not being held low now, we did not get called
83454359Sroberto	 * within 65535us.
83554359Sroberto	 */
83654359Sroberto	if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
83754359Sroberto		NLOG(NLOG_CLOCKINFO) {
83854359Sroberto			msyslog(LOG_NOTICE, "PCL-720 out of synch");
83954359Sroberto		}
84054359Sroberto		return (0);
84154359Sroberto	}
84254359Sroberto	f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
84354359Sroberto#ifdef PPS720_DEBUG
84454359Sroberto	msyslog(LOG_DEBUG, "PCL-720: %luus", f);
84554359Sroberto#endif
84654359Sroberto	return (f);
84754359Sroberto}
84854359Sroberto#endif
84954359Sroberto
85054359Sroberto#else
85154359Srobertoint refclock_true_bs;
85254359Sroberto#endif /* REFCLOCK */
853