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