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