xref: /freebsd/contrib/ntp/ntpd/refclock_leitch.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
3c0b746e5SOllivier Robert  */
4224ba2bdSOllivier Robert 
5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
6c0b746e5SOllivier Robert # include <config.h>
7c0b746e5SOllivier Robert #endif
8c0b746e5SOllivier Robert 
92b15cb3dSCy Schubert #include "ntp_types.h"
10c0b746e5SOllivier Robert 
112b15cb3dSCy Schubert #if defined(REFCLOCK) && defined(CLOCK_LEITCH)
12c0b746e5SOllivier Robert 
13224ba2bdSOllivier Robert #include <stdio.h>
14224ba2bdSOllivier Robert #include <ctype.h>
15224ba2bdSOllivier Robert 
162b15cb3dSCy Schubert #include "ntpd.h"
172b15cb3dSCy Schubert #include "ntp_io.h"
182b15cb3dSCy Schubert #include "ntp_refclock.h"
192b15cb3dSCy Schubert #include "timevalops.h"
20c0b746e5SOllivier Robert #include "ntp_stdlib.h"
21c0b746e5SOllivier Robert 
22c0b746e5SOllivier Robert 
23c0b746e5SOllivier Robert /*
24c0b746e5SOllivier Robert  * Driver for Leitch CSD-5300 Master Clock System
25c0b746e5SOllivier Robert  *
26c0b746e5SOllivier Robert  * COMMANDS:
27c0b746e5SOllivier Robert  *	DATE:	D <CR>
28c0b746e5SOllivier Robert  *	TIME:	T <CR>
29c0b746e5SOllivier Robert  *	STATUS:	S <CR>
30c0b746e5SOllivier Robert  *	LOOP:	L <CR>
31c0b746e5SOllivier Robert  *
32c0b746e5SOllivier Robert  * FORMAT:
33c0b746e5SOllivier Robert  *	DATE: YYMMDD<CR>
34c0b746e5SOllivier Robert  *	TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
35c0b746e5SOllivier Robert  *		second bondaried on the stop bit of the <CR>
36c0b746e5SOllivier Robert  *		second boundaries at '/' above.
37c0b746e5SOllivier Robert  *	STATUS: G (good), D (diag fail), T (time not provided) or
38c0b746e5SOllivier Robert  *		P (last phone update failed)
39c0b746e5SOllivier Robert  */
402b15cb3dSCy Schubert #define PRECISION	(-20)	/* 1x10-8 */
41c0b746e5SOllivier Robert #define MAXUNITS 1		/* max number of LEITCH units */
42c0b746e5SOllivier Robert #define LEITCHREFID	"ATOM"	/* reference id */
43c0b746e5SOllivier Robert #define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
44c0b746e5SOllivier Robert #define LEITCH232 "/dev/leitch%d"	/* name of radio device */
45c0b746e5SOllivier Robert #define SPEED232 B300		/* uart speed (300 baud) */
46ea906c41SOllivier Robert #ifdef DEBUG
47c0b746e5SOllivier Robert #define leitch_send(A,M) \
48c0b746e5SOllivier Robert if (debug) fprintf(stderr,"write leitch %s\n",M); \
49c0b746e5SOllivier Robert if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
50c0b746e5SOllivier Robert 	if (debug) \
51c0b746e5SOllivier Robert 	    fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
52c0b746e5SOllivier Robert 	else \
53c0b746e5SOllivier Robert 	    msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
54ea906c41SOllivier Robert #else
55ea906c41SOllivier Robert #define leitch_send(A,M) \
56ea906c41SOllivier Robert if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
57ea906c41SOllivier Robert 	msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
58ea906c41SOllivier Robert #endif
59c0b746e5SOllivier Robert 
60c0b746e5SOllivier Robert #define STATE_IDLE 0
61c0b746e5SOllivier Robert #define STATE_DATE 1
62c0b746e5SOllivier Robert #define STATE_TIME1 2
63c0b746e5SOllivier Robert #define STATE_TIME2 3
64c0b746e5SOllivier Robert #define STATE_TIME3 4
65c0b746e5SOllivier Robert 
66c0b746e5SOllivier Robert /*
67c0b746e5SOllivier Robert  * LEITCH unit control structure
68c0b746e5SOllivier Robert  */
69c0b746e5SOllivier Robert struct leitchunit {
70c0b746e5SOllivier Robert 	struct peer *peer;
71c0b746e5SOllivier Robert 	struct refclockio leitchio;
72c0b746e5SOllivier Robert 	u_char unit;
73c0b746e5SOllivier Robert 	short year;
74c0b746e5SOllivier Robert 	short yearday;
75c0b746e5SOllivier Robert 	short month;
76c0b746e5SOllivier Robert 	short day;
77c0b746e5SOllivier Robert 	short hour;
78c0b746e5SOllivier Robert 	short second;
79c0b746e5SOllivier Robert 	short minute;
80c0b746e5SOllivier Robert 	short state;
81c0b746e5SOllivier Robert 	u_short fudge1;
82c0b746e5SOllivier Robert 	l_fp reftime1;
83c0b746e5SOllivier Robert 	l_fp reftime2;
84c0b746e5SOllivier Robert 	l_fp reftime3;
85c0b746e5SOllivier Robert 	l_fp codetime1;
86c0b746e5SOllivier Robert 	l_fp codetime2;
87c0b746e5SOllivier Robert 	l_fp codetime3;
88c0b746e5SOllivier Robert 	u_long yearstart;
89c0b746e5SOllivier Robert };
90c0b746e5SOllivier Robert 
91c0b746e5SOllivier Robert /*
92c0b746e5SOllivier Robert  * Function prototypes
93c0b746e5SOllivier Robert  */
942b15cb3dSCy Schubert static	void	leitch_init	(void);
952b15cb3dSCy Schubert static	int	leitch_start	(int, struct peer *);
962b15cb3dSCy Schubert static	void	leitch_shutdown	(int, struct peer *);
972b15cb3dSCy Schubert static	void	leitch_poll	(int, struct peer *);
982b15cb3dSCy Schubert static	void	leitch_control	(int, const struct refclockstat *, struct refclockstat *, struct peer *);
99c0b746e5SOllivier Robert #define	leitch_buginfo	noentry
1002b15cb3dSCy Schubert static	void	leitch_receive	(struct recvbuf *);
1012b15cb3dSCy Schubert static	void	leitch_process	(struct leitchunit *);
102c0b746e5SOllivier Robert #if 0
1032b15cb3dSCy Schubert static	void	leitch_timeout	(struct peer *);
104c0b746e5SOllivier Robert #endif
1052b15cb3dSCy Schubert static	int	leitch_get_date	(struct recvbuf *, struct leitchunit *);
1062b15cb3dSCy Schubert static	int	leitch_get_time	(struct recvbuf *, struct leitchunit *, int);
1072b15cb3dSCy Schubert static	int	days_per_year		(int);
108c0b746e5SOllivier Robert 
109c0b746e5SOllivier Robert static struct leitchunit leitchunits[MAXUNITS];
110c0b746e5SOllivier Robert static u_char unitinuse[MAXUNITS];
111c0b746e5SOllivier Robert static u_char stratumtouse[MAXUNITS];
112c0b746e5SOllivier Robert static u_int32 refid[MAXUNITS];
113c0b746e5SOllivier Robert 
114c0b746e5SOllivier Robert static	char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
115c0b746e5SOllivier Robert 
116c0b746e5SOllivier Robert /*
117c0b746e5SOllivier Robert  * Transfer vector
118c0b746e5SOllivier Robert  */
119c0b746e5SOllivier Robert struct	refclock refclock_leitch = {
120c0b746e5SOllivier Robert 	leitch_start, leitch_shutdown, leitch_poll,
121c0b746e5SOllivier Robert 	leitch_control, leitch_init, leitch_buginfo, NOFLAGS
122c0b746e5SOllivier Robert };
123c0b746e5SOllivier Robert 
124c0b746e5SOllivier Robert /*
125c0b746e5SOllivier Robert  * leitch_init - initialize internal leitch driver data
126c0b746e5SOllivier Robert  */
127c0b746e5SOllivier Robert static void
leitch_init(void)128c0b746e5SOllivier Robert leitch_init(void)
129c0b746e5SOllivier Robert {
130c0b746e5SOllivier Robert 	int i;
131c0b746e5SOllivier Robert 
132c0b746e5SOllivier Robert 	memset((char*)leitchunits, 0, sizeof(leitchunits));
133c0b746e5SOllivier Robert 	memset((char*)unitinuse, 0, sizeof(unitinuse));
134c0b746e5SOllivier Robert 	for (i = 0; i < MAXUNITS; i++)
135c0b746e5SOllivier Robert 	    memcpy((char *)&refid[i], LEITCHREFID, 4);
136c0b746e5SOllivier Robert }
137c0b746e5SOllivier Robert 
138c0b746e5SOllivier Robert /*
139c0b746e5SOllivier Robert  * leitch_shutdown - shut down a LEITCH clock
140c0b746e5SOllivier Robert  */
141c0b746e5SOllivier Robert static void
leitch_shutdown(int unit,struct peer * peer)142c0b746e5SOllivier Robert leitch_shutdown(
143c0b746e5SOllivier Robert 	int unit,
144c0b746e5SOllivier Robert 	struct peer *peer
145c0b746e5SOllivier Robert 	)
146c0b746e5SOllivier Robert {
1472b15cb3dSCy Schubert 	struct leitchunit *leitch;
1482b15cb3dSCy Schubert 
1492b15cb3dSCy Schubert 	if (unit >= MAXUNITS) {
1502b15cb3dSCy Schubert 		return;
1512b15cb3dSCy Schubert 	}
1522b15cb3dSCy Schubert 	leitch = &leitchunits[unit];
1532b15cb3dSCy Schubert 	if (-1 != leitch->leitchio.fd)
1542b15cb3dSCy Schubert 		io_closeclock(&leitch->leitchio);
155c0b746e5SOllivier Robert #ifdef DEBUG
156c0b746e5SOllivier Robert 	if (debug)
157c0b746e5SOllivier Robert 		fprintf(stderr, "leitch_shutdown()\n");
158c0b746e5SOllivier Robert #endif
159c0b746e5SOllivier Robert }
160c0b746e5SOllivier Robert 
161c0b746e5SOllivier Robert /*
162c0b746e5SOllivier Robert  * leitch_poll - called by the transmit procedure
163c0b746e5SOllivier Robert  */
164c0b746e5SOllivier Robert static void
leitch_poll(int unit,struct peer * peer)165c0b746e5SOllivier Robert leitch_poll(
166c0b746e5SOllivier Robert 	int unit,
167c0b746e5SOllivier Robert 	struct peer *peer
168c0b746e5SOllivier Robert 	)
169c0b746e5SOllivier Robert {
170c0b746e5SOllivier Robert 	struct leitchunit *leitch;
171c0b746e5SOllivier Robert 
172c0b746e5SOllivier Robert 	/* start the state machine rolling */
173c0b746e5SOllivier Robert 
174c0b746e5SOllivier Robert #ifdef DEBUG
175c0b746e5SOllivier Robert 	if (debug)
176c0b746e5SOllivier Robert 	    fprintf(stderr, "leitch_poll()\n");
177c0b746e5SOllivier Robert #endif
178ea906c41SOllivier Robert 	if (unit >= MAXUNITS) {
179c0b746e5SOllivier Robert 		/* XXXX syslog it */
180c0b746e5SOllivier Robert 		return;
181c0b746e5SOllivier Robert 	}
182c0b746e5SOllivier Robert 
183c0b746e5SOllivier Robert 	leitch = &leitchunits[unit];
184c0b746e5SOllivier Robert 
185c0b746e5SOllivier Robert 	if (leitch->state != STATE_IDLE) {
186c0b746e5SOllivier Robert 		/* reset and wait for next poll */
187c0b746e5SOllivier Robert 		/* XXXX syslog it */
188c0b746e5SOllivier Robert 		leitch->state = STATE_IDLE;
189c0b746e5SOllivier Robert 	} else {
190c0b746e5SOllivier Robert 		leitch_send(leitch,"D\r");
191c0b746e5SOllivier Robert 		leitch->state = STATE_DATE;
192c0b746e5SOllivier Robert 	}
193c0b746e5SOllivier Robert }
194c0b746e5SOllivier Robert 
195c0b746e5SOllivier Robert static void
leitch_control(int unit,const struct refclockstat * in,struct refclockstat * out,struct peer * passed_peer)196c0b746e5SOllivier Robert leitch_control(
197c0b746e5SOllivier Robert 	int unit,
1982b15cb3dSCy Schubert 	const struct refclockstat *in,
199c0b746e5SOllivier Robert 	struct refclockstat *out,
200c0b746e5SOllivier Robert 	struct peer *passed_peer
201c0b746e5SOllivier Robert 	)
202c0b746e5SOllivier Robert {
203c0b746e5SOllivier Robert 	if (unit >= MAXUNITS) {
204c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
205c0b746e5SOllivier Robert 			"leitch_control: unit %d invalid", unit);
206c0b746e5SOllivier Robert 		return;
207c0b746e5SOllivier Robert 	}
208c0b746e5SOllivier Robert 
209c0b746e5SOllivier Robert 	if (in) {
210c0b746e5SOllivier Robert 		if (in->haveflags & CLK_HAVEVAL1)
211c0b746e5SOllivier Robert 		    stratumtouse[unit] = (u_char)(in->fudgeval1);
212c0b746e5SOllivier Robert 		if (in->haveflags & CLK_HAVEVAL2)
213c0b746e5SOllivier Robert 		    refid[unit] = in->fudgeval2;
214c0b746e5SOllivier Robert 		if (unitinuse[unit]) {
215c0b746e5SOllivier Robert 			struct peer *peer;
216c0b746e5SOllivier Robert 
217c0b746e5SOllivier Robert 			peer = (&leitchunits[unit])->peer;
218c0b746e5SOllivier Robert 			peer->stratum = stratumtouse[unit];
219c0b746e5SOllivier Robert 			peer->refid = refid[unit];
220c0b746e5SOllivier Robert 		}
221c0b746e5SOllivier Robert 	}
222c0b746e5SOllivier Robert 
223c0b746e5SOllivier Robert 	if (out) {
224c0b746e5SOllivier Robert 		memset((char *)out, 0, sizeof (struct refclockstat));
225c0b746e5SOllivier Robert 		out->type = REFCLK_ATOM_LEITCH;
226c0b746e5SOllivier Robert 		out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
227c0b746e5SOllivier Robert 		out->fudgeval1 = (int32)stratumtouse[unit];
228c0b746e5SOllivier Robert 		out->fudgeval2 = refid[unit];
229c0b746e5SOllivier Robert 		out->p_lastcode = "";
230c0b746e5SOllivier Robert 		out->clockdesc = LEITCH_DESCRIPTION;
231c0b746e5SOllivier Robert 	}
232c0b746e5SOllivier Robert }
233c0b746e5SOllivier Robert 
234c0b746e5SOllivier Robert /*
235c0b746e5SOllivier Robert  * leitch_start - open the LEITCH devices and initialize data for processing
236c0b746e5SOllivier Robert  */
237c0b746e5SOllivier Robert static int
leitch_start(int unit,struct peer * peer)238c0b746e5SOllivier Robert leitch_start(
239c0b746e5SOllivier Robert 	int unit,
240c0b746e5SOllivier Robert 	struct peer *peer
241c0b746e5SOllivier Robert 	)
242c0b746e5SOllivier Robert {
243c0b746e5SOllivier Robert 	struct leitchunit *leitch;
244c0b746e5SOllivier Robert 	int fd232;
245*a466cc55SCy Schubert 	char leitchdev[32];
246c0b746e5SOllivier Robert 
247c0b746e5SOllivier Robert 	/*
248c0b746e5SOllivier Robert 	 * Check configuration info.
249c0b746e5SOllivier Robert 	 */
250c0b746e5SOllivier Robert 	if (unit >= MAXUNITS) {
251c0b746e5SOllivier Robert 		msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
252c0b746e5SOllivier Robert 		return (0);
253c0b746e5SOllivier Robert 	}
254c0b746e5SOllivier Robert 
255c0b746e5SOllivier Robert 	if (unitinuse[unit]) {
256c0b746e5SOllivier Robert 		msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
257c0b746e5SOllivier Robert 		return (0);
258c0b746e5SOllivier Robert 	}
259c0b746e5SOllivier Robert 
260c0b746e5SOllivier Robert 	/*
261c0b746e5SOllivier Robert 	 * Open serial port.
262c0b746e5SOllivier Robert 	 */
2632b15cb3dSCy Schubert 	snprintf(leitchdev, sizeof(leitchdev), LEITCH232, unit);
264c0b746e5SOllivier Robert 	fd232 = open(leitchdev, O_RDWR, 0777);
265c0b746e5SOllivier Robert 	if (fd232 == -1) {
266c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
267c0b746e5SOllivier Robert 			"leitch_start: open of %s: %m", leitchdev);
268c0b746e5SOllivier Robert 		return (0);
269c0b746e5SOllivier Robert 	}
270c0b746e5SOllivier Robert 
271c0b746e5SOllivier Robert 	leitch = &leitchunits[unit];
2722b15cb3dSCy Schubert 	memset(leitch, 0, sizeof(*leitch));
273c0b746e5SOllivier Robert 
274c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS)
275c0b746e5SOllivier Robert 	/*
276c0b746e5SOllivier Robert 	 * System V serial line parameters (termio interface)
277c0b746e5SOllivier Robert 	 *
278c0b746e5SOllivier Robert 	 */
279c0b746e5SOllivier Robert 	{	struct termio ttyb;
280c0b746e5SOllivier Robert 	if (ioctl(fd232, TCGETA, &ttyb) < 0) {
281c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
282c0b746e5SOllivier Robert 			"leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
283c0b746e5SOllivier Robert 		goto screwed;
284c0b746e5SOllivier Robert 	}
285c0b746e5SOllivier Robert 	ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
286c0b746e5SOllivier Robert 	ttyb.c_oflag = 0;
287c0b746e5SOllivier Robert 	ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
288c0b746e5SOllivier Robert 	ttyb.c_lflag = ICANON;
289c0b746e5SOllivier Robert 	ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
290c0b746e5SOllivier Robert 	if (ioctl(fd232, TCSETA, &ttyb) < 0) {
291c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
292c0b746e5SOllivier Robert 			"leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
293c0b746e5SOllivier Robert 		goto screwed;
294c0b746e5SOllivier Robert 	}
295c0b746e5SOllivier Robert 	}
296c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */
297c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS)
298c0b746e5SOllivier Robert 	/*
299c0b746e5SOllivier Robert 	 * POSIX serial line parameters (termios interface)
300c0b746e5SOllivier Robert 	 */
301c0b746e5SOllivier Robert 	{	struct termios ttyb, *ttyp;
302c0b746e5SOllivier Robert 
303c0b746e5SOllivier Robert 	ttyp = &ttyb;
304c0b746e5SOllivier Robert 	if (tcgetattr(fd232, ttyp) < 0) {
305c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
306c0b746e5SOllivier Robert 			"leitch_start: tcgetattr(%s): %m", leitchdev);
307c0b746e5SOllivier Robert 		goto screwed;
308c0b746e5SOllivier Robert 	}
309c0b746e5SOllivier Robert 	ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
310c0b746e5SOllivier Robert 	ttyp->c_oflag = 0;
311c0b746e5SOllivier Robert 	ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
312c0b746e5SOllivier Robert 	ttyp->c_lflag = ICANON;
313c0b746e5SOllivier Robert 	ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
314c0b746e5SOllivier Robert 	if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
315c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
316c0b746e5SOllivier Robert 			"leitch_start: tcsetattr(%s): %m", leitchdev);
317c0b746e5SOllivier Robert 		goto screwed;
318c0b746e5SOllivier Robert 	}
319c0b746e5SOllivier Robert 	if (tcflush(fd232, TCIOFLUSH) < 0) {
320c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
321c0b746e5SOllivier Robert 			"leitch_start: tcflush(%s): %m", leitchdev);
322c0b746e5SOllivier Robert 		goto screwed;
323c0b746e5SOllivier Robert 	}
324c0b746e5SOllivier Robert 	}
325c0b746e5SOllivier Robert #endif /* HAVE_TERMIOS */
326c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS)
327c0b746e5SOllivier Robert 	/*
328c0b746e5SOllivier Robert 	 * 4.3bsd serial line parameters (sgttyb interface)
329c0b746e5SOllivier Robert 	 */
3302b15cb3dSCy Schubert 	{
3312b15cb3dSCy Schubert 		struct sgttyb ttyb;
332c0b746e5SOllivier Robert 
333c0b746e5SOllivier Robert 	if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
334c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
335c0b746e5SOllivier Robert 			"leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
336c0b746e5SOllivier Robert 		goto screwed;
337c0b746e5SOllivier Robert 	}
338c0b746e5SOllivier Robert 	ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
339c0b746e5SOllivier Robert 	ttyb.sg_erase = ttyb.sg_kill = '\0';
340c0b746e5SOllivier Robert 	ttyb.sg_flags = EVENP|ODDP|CRMOD;
341c0b746e5SOllivier Robert 	if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
342c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
343c0b746e5SOllivier Robert 			"leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
344c0b746e5SOllivier Robert 		goto screwed;
345c0b746e5SOllivier Robert 	}
346c0b746e5SOllivier Robert 	}
347c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */
348c0b746e5SOllivier Robert 
349c0b746e5SOllivier Robert 	/*
350c0b746e5SOllivier Robert 	 * Set up the structures
351c0b746e5SOllivier Robert 	 */
352c0b746e5SOllivier Robert 	leitch->peer = peer;
353c0b746e5SOllivier Robert 	leitch->unit = unit;
354c0b746e5SOllivier Robert 	leitch->state = STATE_IDLE;
355c0b746e5SOllivier Robert 	leitch->fudge1 = 15;	/* 15ms */
356c0b746e5SOllivier Robert 
357c0b746e5SOllivier Robert 	leitch->leitchio.clock_recv = leitch_receive;
3582b15cb3dSCy Schubert 	leitch->leitchio.srcclock = peer;
359c0b746e5SOllivier Robert 	leitch->leitchio.datalen = 0;
360c0b746e5SOllivier Robert 	leitch->leitchio.fd = fd232;
361c0b746e5SOllivier Robert 	if (!io_addclock(&leitch->leitchio)) {
3622b15cb3dSCy Schubert 		leitch->leitchio.fd = -1;
363c0b746e5SOllivier Robert 		goto screwed;
364c0b746e5SOllivier Robert 	}
365c0b746e5SOllivier Robert 
366c0b746e5SOllivier Robert 	/*
367c0b746e5SOllivier Robert 	 * All done.  Initialize a few random peer variables, then
368c0b746e5SOllivier Robert 	 * return success.
369c0b746e5SOllivier Robert 	 */
3702b15cb3dSCy Schubert 	peer->precision = PRECISION;
371c0b746e5SOllivier Robert 	peer->stratum = stratumtouse[unit];
372c0b746e5SOllivier Robert 	peer->refid = refid[unit];
373c0b746e5SOllivier Robert 	unitinuse[unit] = 1;
374c0b746e5SOllivier Robert 	return(1);
375c0b746e5SOllivier Robert 
376c0b746e5SOllivier Robert 	/*
377c0b746e5SOllivier Robert 	 * Something broke; abandon ship.
378c0b746e5SOllivier Robert 	 */
379c0b746e5SOllivier Robert     screwed:
380c0b746e5SOllivier Robert 	close(fd232);
381c0b746e5SOllivier Robert 	return(0);
382c0b746e5SOllivier Robert }
383c0b746e5SOllivier Robert 
384c0b746e5SOllivier Robert /*
385c0b746e5SOllivier Robert  * leitch_receive - receive data from the serial interface on a leitch
386c0b746e5SOllivier Robert  * clock
387c0b746e5SOllivier Robert  */
388c0b746e5SOllivier Robert static void
leitch_receive(struct recvbuf * rbufp)389c0b746e5SOllivier Robert leitch_receive(
390c0b746e5SOllivier Robert 	struct recvbuf *rbufp
391c0b746e5SOllivier Robert 	)
392c0b746e5SOllivier Robert {
3932b15cb3dSCy Schubert 	struct leitchunit *leitch = rbufp->recv_peer->procptr->unitptr;
394c0b746e5SOllivier Robert 
395c0b746e5SOllivier Robert #ifdef DEBUG
396c0b746e5SOllivier Robert 	if (debug)
397c0b746e5SOllivier Robert 	    fprintf(stderr, "leitch_recieve(%*.*s)\n",
398c0b746e5SOllivier Robert 		    rbufp->recv_length, rbufp->recv_length,
399c0b746e5SOllivier Robert 		    rbufp->recv_buffer);
400c0b746e5SOllivier Robert #endif
401c0b746e5SOllivier Robert 	if (rbufp->recv_length != 7)
402c0b746e5SOllivier Robert 	    return; /* The date is return with a trailing newline,
403c0b746e5SOllivier Robert 		       discard it. */
404c0b746e5SOllivier Robert 
405c0b746e5SOllivier Robert 	switch (leitch->state) {
406c0b746e5SOllivier Robert 	    case STATE_IDLE:	/* unexpected, discard and resync */
407c0b746e5SOllivier Robert 		return;
408c0b746e5SOllivier Robert 	    case STATE_DATE:
409c0b746e5SOllivier Robert 		if (!leitch_get_date(rbufp,leitch)) {
410c0b746e5SOllivier Robert 			leitch->state = STATE_IDLE;
411c0b746e5SOllivier Robert 			break;
412c0b746e5SOllivier Robert 		}
413c0b746e5SOllivier Robert 		leitch_send(leitch,"T\r");
414c0b746e5SOllivier Robert #ifdef DEBUG
415c0b746e5SOllivier Robert 		if (debug)
416c0b746e5SOllivier Robert 		    fprintf(stderr, "%u\n",leitch->yearday);
417c0b746e5SOllivier Robert #endif
418c0b746e5SOllivier Robert 		leitch->state = STATE_TIME1;
419c0b746e5SOllivier Robert 		break;
420c0b746e5SOllivier Robert 	    case STATE_TIME1:
421c0b746e5SOllivier Robert 		if (!leitch_get_time(rbufp,leitch,1)) {
422c0b746e5SOllivier Robert 		}
423c0b746e5SOllivier Robert 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
424c0b746e5SOllivier Robert 			       leitch->second, 1, rbufp->recv_time.l_ui,
425c0b746e5SOllivier Robert 			       &leitch->yearstart, &leitch->reftime1.l_ui)) {
426c0b746e5SOllivier Robert 			leitch->state = STATE_IDLE;
427c0b746e5SOllivier Robert 			break;
428c0b746e5SOllivier Robert 		}
4299c2daa00SOllivier Robert 		leitch->reftime1.l_uf = 0;
430c0b746e5SOllivier Robert #ifdef DEBUG
431c0b746e5SOllivier Robert 		if (debug)
432c0b746e5SOllivier Robert 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
433c0b746e5SOllivier Robert #endif
434c0b746e5SOllivier Robert 		MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
435c0b746e5SOllivier Robert 		leitch->codetime1 = rbufp->recv_time;
436c0b746e5SOllivier Robert 		leitch->state = STATE_TIME2;
437c0b746e5SOllivier Robert 		break;
438c0b746e5SOllivier Robert 	    case STATE_TIME2:
439c0b746e5SOllivier Robert 		if (!leitch_get_time(rbufp,leitch,2)) {
440c0b746e5SOllivier Robert 		}
441c0b746e5SOllivier Robert 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
442c0b746e5SOllivier Robert 			       leitch->second, 1, rbufp->recv_time.l_ui,
443c0b746e5SOllivier Robert 			       &leitch->yearstart, &leitch->reftime2.l_ui)) {
444c0b746e5SOllivier Robert 			leitch->state = STATE_IDLE;
445c0b746e5SOllivier Robert 			break;
446c0b746e5SOllivier Robert 		}
447c0b746e5SOllivier Robert #ifdef DEBUG
448c0b746e5SOllivier Robert 		if (debug)
449c0b746e5SOllivier Robert 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
450c0b746e5SOllivier Robert #endif
451c0b746e5SOllivier Robert 		MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
452c0b746e5SOllivier Robert 		leitch->codetime2 = rbufp->recv_time;
453c0b746e5SOllivier Robert 		leitch->state = STATE_TIME3;
454c0b746e5SOllivier Robert 		break;
455c0b746e5SOllivier Robert 	    case STATE_TIME3:
456c0b746e5SOllivier Robert 		if (!leitch_get_time(rbufp,leitch,3)) {
457c0b746e5SOllivier Robert 		}
458c0b746e5SOllivier Robert 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
459c0b746e5SOllivier Robert 			       leitch->second, GMT, rbufp->recv_time.l_ui,
460c0b746e5SOllivier Robert 			       &leitch->yearstart, &leitch->reftime3.l_ui)) {
461c0b746e5SOllivier Robert 			leitch->state = STATE_IDLE;
462c0b746e5SOllivier Robert 			break;
463c0b746e5SOllivier Robert 		}
464c0b746e5SOllivier Robert #ifdef DEBUG
465c0b746e5SOllivier Robert 		if (debug)
466c0b746e5SOllivier Robert 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
467c0b746e5SOllivier Robert #endif
468c0b746e5SOllivier Robert 		MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
469c0b746e5SOllivier Robert 		leitch->codetime3 = rbufp->recv_time;
470c0b746e5SOllivier Robert 		leitch_process(leitch);
471c0b746e5SOllivier Robert 		leitch->state = STATE_IDLE;
472c0b746e5SOllivier Robert 		break;
473c0b746e5SOllivier Robert 	    default:
474c0b746e5SOllivier Robert 		msyslog(LOG_ERR,
475c0b746e5SOllivier Robert 			"leitech_receive: invalid state %d unit %d",
476c0b746e5SOllivier Robert 			leitch->state, leitch->unit);
477c0b746e5SOllivier Robert 	}
478c0b746e5SOllivier Robert }
479c0b746e5SOllivier Robert 
480c0b746e5SOllivier Robert /*
481c0b746e5SOllivier Robert  * leitch_process - process a pile of samples from the clock
482c0b746e5SOllivier Robert  *
483c0b746e5SOllivier Robert  * This routine uses a three-stage median filter to calculate offset and
484c0b746e5SOllivier Robert  * dispersion. reduce jitter. The dispersion is calculated as the span
485c0b746e5SOllivier Robert  * of the filter (max - min), unless the quality character (format 2) is
486c0b746e5SOllivier Robert  * non-blank, in which case the dispersion is calculated on the basis of
487c0b746e5SOllivier Robert  * the inherent tolerance of the internal radio oscillator, which is
488c0b746e5SOllivier Robert  * +-2e-5 according to the radio specifications.
489c0b746e5SOllivier Robert  */
490c0b746e5SOllivier Robert static void
leitch_process(struct leitchunit * leitch)491c0b746e5SOllivier Robert leitch_process(
492c0b746e5SOllivier Robert 	struct leitchunit *leitch
493c0b746e5SOllivier Robert 	)
494c0b746e5SOllivier Robert {
495c0b746e5SOllivier Robert 	l_fp off;
496c0b746e5SOllivier Robert 	l_fp tmp_fp;
497c0b746e5SOllivier Robert       /*double doffset;*/
498c0b746e5SOllivier Robert 
499c0b746e5SOllivier Robert 	off = leitch->reftime1;
500c0b746e5SOllivier Robert 	L_SUB(&off,&leitch->codetime1);
501c0b746e5SOllivier Robert 	tmp_fp = leitch->reftime2;
502c0b746e5SOllivier Robert 	L_SUB(&tmp_fp,&leitch->codetime2);
503c0b746e5SOllivier Robert 	if (L_ISGEQ(&off,&tmp_fp))
504c0b746e5SOllivier Robert 	    off = tmp_fp;
505c0b746e5SOllivier Robert 	tmp_fp = leitch->reftime3;
506c0b746e5SOllivier Robert 	L_SUB(&tmp_fp,&leitch->codetime3);
507c0b746e5SOllivier Robert 
508c0b746e5SOllivier Robert 	if (L_ISGEQ(&off,&tmp_fp))
509c0b746e5SOllivier Robert 	    off = tmp_fp;
510c0b746e5SOllivier Robert       /*LFPTOD(&off, doffset);*/
511c0b746e5SOllivier Robert 	refclock_receive(leitch->peer);
512c0b746e5SOllivier Robert }
513c0b746e5SOllivier Robert 
514c0b746e5SOllivier Robert /*
515c0b746e5SOllivier Robert  * days_per_year
516c0b746e5SOllivier Robert  */
517c0b746e5SOllivier Robert static int
days_per_year(int year)518c0b746e5SOllivier Robert days_per_year(
519c0b746e5SOllivier Robert 	int year
520c0b746e5SOllivier Robert 	)
521c0b746e5SOllivier Robert {
522c0b746e5SOllivier Robert 	if (year%4) {	/* not a potential leap year */
523c0b746e5SOllivier Robert 		return (365);
524c0b746e5SOllivier Robert 	} else {
525c0b746e5SOllivier Robert 		if (year % 100) {	/* is a leap year */
526c0b746e5SOllivier Robert 			return (366);
527c0b746e5SOllivier Robert 		} else {
528c0b746e5SOllivier Robert 			if (year % 400) {
529c0b746e5SOllivier Robert 				return (365);
530c0b746e5SOllivier Robert 			} else {
531c0b746e5SOllivier Robert 				return (366);
532c0b746e5SOllivier Robert 			}
533c0b746e5SOllivier Robert 		}
534c0b746e5SOllivier Robert 	}
535c0b746e5SOllivier Robert }
536c0b746e5SOllivier Robert 
537c0b746e5SOllivier Robert static int
leitch_get_date(struct recvbuf * rbufp,struct leitchunit * leitch)538c0b746e5SOllivier Robert leitch_get_date(
539c0b746e5SOllivier Robert 	struct recvbuf *rbufp,
540c0b746e5SOllivier Robert 	struct leitchunit *leitch
541c0b746e5SOllivier Robert 	)
542c0b746e5SOllivier Robert {
543c0b746e5SOllivier Robert 	int i;
544c0b746e5SOllivier Robert 
545c0b746e5SOllivier Robert 	if (rbufp->recv_length < 6)
546c0b746e5SOllivier Robert 	    return(0);
547c0b746e5SOllivier Robert #undef  BAD    /* confict: defined as (-1) in AIX sys/param.h */
548c0b746e5SOllivier Robert #define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
549c0b746e5SOllivier Robert 	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
550c0b746e5SOllivier Robert 	    return(0);
551c0b746e5SOllivier Robert #define ATOB(A) ((rbufp->recv_buffer[A])-'0')
552c0b746e5SOllivier Robert 	leitch->year = ATOB(0)*10 + ATOB(1);
553c0b746e5SOllivier Robert 	leitch->month = ATOB(2)*10 + ATOB(3);
554c0b746e5SOllivier Robert 	leitch->day = ATOB(4)*10 + ATOB(5);
555c0b746e5SOllivier Robert 
556c0b746e5SOllivier Robert 	/* sanity checks */
557c0b746e5SOllivier Robert 	if (leitch->month > 12)
558c0b746e5SOllivier Robert 	    return(0);
559c0b746e5SOllivier Robert 	if (leitch->day > days_in_month[leitch->month-1])
560c0b746e5SOllivier Robert 	    return(0);
561c0b746e5SOllivier Robert 
562c0b746e5SOllivier Robert 	/* calculate yearday */
563c0b746e5SOllivier Robert 	i = 0;
564c0b746e5SOllivier Robert 	leitch->yearday = leitch->day;
565c0b746e5SOllivier Robert 
566c0b746e5SOllivier Robert 	while ( i < (leitch->month-1) )
567c0b746e5SOllivier Robert 	    leitch->yearday += days_in_month[i++];
568c0b746e5SOllivier Robert 
569c0b746e5SOllivier Robert 	if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
570c0b746e5SOllivier Robert 	    leitch->month > 2)
571c0b746e5SOllivier Robert 	    leitch->yearday--;
572c0b746e5SOllivier Robert 
573c0b746e5SOllivier Robert 	return(1);
574c0b746e5SOllivier Robert }
575c0b746e5SOllivier Robert 
576c0b746e5SOllivier Robert /*
577c0b746e5SOllivier Robert  * leitch_get_time
578c0b746e5SOllivier Robert  */
579c0b746e5SOllivier Robert static int
leitch_get_time(struct recvbuf * rbufp,struct leitchunit * leitch,int which)580c0b746e5SOllivier Robert leitch_get_time(
581c0b746e5SOllivier Robert 	struct recvbuf *rbufp,
582c0b746e5SOllivier Robert 	struct leitchunit *leitch,
583c0b746e5SOllivier Robert 	int which
584c0b746e5SOllivier Robert 	)
585c0b746e5SOllivier Robert {
586c0b746e5SOllivier Robert 	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
587c0b746e5SOllivier Robert 	    return(0);
588c0b746e5SOllivier Robert 	leitch->hour = ATOB(0)*10 +ATOB(1);
589c0b746e5SOllivier Robert 	leitch->minute = ATOB(2)*10 +ATOB(3);
590c0b746e5SOllivier Robert 	leitch->second = ATOB(4)*10 +ATOB(5);
591c0b746e5SOllivier Robert 
592c0b746e5SOllivier Robert 	if ((leitch->hour > 23) || (leitch->minute > 60) ||
593c0b746e5SOllivier Robert 	    (leitch->second > 60))
594c0b746e5SOllivier Robert 	    return(0);
595c0b746e5SOllivier Robert 	return(1);
596c0b746e5SOllivier Robert }
597c0b746e5SOllivier Robert 
598c0b746e5SOllivier Robert #else
5992b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT
600c0b746e5SOllivier Robert #endif /* REFCLOCK */
601