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