xref: /freebsd/contrib/ntp/ntpd/refclock_leitch.c (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
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 #include "ntp_types.h"
10 
11 #if defined(REFCLOCK) && defined(CLOCK_LEITCH)
12 
13 #include <stdio.h>
14 #include <ctype.h>
15 
16 #include "ntpd.h"
17 #include "ntp_io.h"
18 #include "ntp_refclock.h"
19 #include "timevalops.h"
20 #include "ntp_stdlib.h"
21 
22 
23 /*
24  * Driver for Leitch CSD-5300 Master Clock System
25  *
26  * COMMANDS:
27  *	DATE:	D <CR>
28  *	TIME:	T <CR>
29  *	STATUS:	S <CR>
30  *	LOOP:	L <CR>
31  *
32  * FORMAT:
33  *	DATE: YYMMDD<CR>
34  *	TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
35  *		second bondaried on the stop bit of the <CR>
36  *		second boundaries at '/' above.
37  *	STATUS: G (good), D (diag fail), T (time not provided) or
38  *		P (last phone update failed)
39  */
40 #define PRECISION	(-20)	/* 1x10-8 */
41 #define MAXUNITS 1		/* max number of LEITCH units */
42 #define LEITCHREFID	"ATOM"	/* reference id */
43 #define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
44 #define LEITCH232 "/dev/leitch%d"	/* name of radio device */
45 #define SPEED232 B300		/* uart speed (300 baud) */
46 #ifdef DEBUG
47 #define leitch_send(A,M) \
48 if (debug) fprintf(stderr,"write leitch %s\n",M); \
49 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
50 	if (debug) \
51 	    fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
52 	else \
53 	    msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
54 #else
55 #define leitch_send(A,M) \
56 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
57 	msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
58 #endif
59 
60 #define STATE_IDLE 0
61 #define STATE_DATE 1
62 #define STATE_TIME1 2
63 #define STATE_TIME2 3
64 #define STATE_TIME3 4
65 
66 /*
67  * LEITCH unit control structure
68  */
69 struct leitchunit {
70 	struct peer *peer;
71 	struct refclockio leitchio;
72 	u_char unit;
73 	short year;
74 	short yearday;
75 	short month;
76 	short day;
77 	short hour;
78 	short second;
79 	short minute;
80 	short state;
81 	u_short fudge1;
82 	l_fp reftime1;
83 	l_fp reftime2;
84 	l_fp reftime3;
85 	l_fp codetime1;
86 	l_fp codetime2;
87 	l_fp codetime3;
88 	u_long yearstart;
89 };
90 
91 /*
92  * Function prototypes
93  */
94 static	void	leitch_init	(void);
95 static	int	leitch_start	(int, struct peer *);
96 static	void	leitch_shutdown	(int, struct peer *);
97 static	void	leitch_poll	(int, struct peer *);
98 static	void	leitch_control	(int, const struct refclockstat *, struct refclockstat *, struct peer *);
99 #define	leitch_buginfo	noentry
100 static	void	leitch_receive	(struct recvbuf *);
101 static	void	leitch_process	(struct leitchunit *);
102 #if 0
103 static	void	leitch_timeout	(struct peer *);
104 #endif
105 static	int	leitch_get_date	(struct recvbuf *, struct leitchunit *);
106 static	int	leitch_get_time	(struct recvbuf *, struct leitchunit *, int);
107 static	int	days_per_year		(int);
108 
109 static struct leitchunit leitchunits[MAXUNITS];
110 static u_char unitinuse[MAXUNITS];
111 static u_char stratumtouse[MAXUNITS];
112 static u_int32 refid[MAXUNITS];
113 
114 static	char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
115 
116 /*
117  * Transfer vector
118  */
119 struct	refclock refclock_leitch = {
120 	leitch_start, leitch_shutdown, leitch_poll,
121 	leitch_control, leitch_init, leitch_buginfo, NOFLAGS
122 };
123 
124 /*
125  * leitch_init - initialize internal leitch driver data
126  */
127 static void
128 leitch_init(void)
129 {
130 	int i;
131 
132 	memset((char*)leitchunits, 0, sizeof(leitchunits));
133 	memset((char*)unitinuse, 0, sizeof(unitinuse));
134 	for (i = 0; i < MAXUNITS; i++)
135 	    memcpy((char *)&refid[i], LEITCHREFID, 4);
136 }
137 
138 /*
139  * leitch_shutdown - shut down a LEITCH clock
140  */
141 static void
142 leitch_shutdown(
143 	int unit,
144 	struct peer *peer
145 	)
146 {
147 	struct leitchunit *leitch;
148 
149 	if (unit >= MAXUNITS) {
150 		return;
151 	}
152 	leitch = &leitchunits[unit];
153 	if (-1 != leitch->leitchio.fd)
154 		io_closeclock(&leitch->leitchio);
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 	const 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 	snprintf(leitchdev, sizeof(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(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 	{	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 #if defined(HAVE_BSD_TTYS)
327 	/*
328 	 * 4.3bsd serial line parameters (sgttyb interface)
329 	 */
330 	{
331 		struct sgttyb ttyb;
332 
333 	if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
334 		msyslog(LOG_ERR,
335 			"leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
336 		goto screwed;
337 	}
338 	ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
339 	ttyb.sg_erase = ttyb.sg_kill = '\0';
340 	ttyb.sg_flags = EVENP|ODDP|CRMOD;
341 	if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
342 		msyslog(LOG_ERR,
343 			"leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
344 		goto screwed;
345 	}
346 	}
347 #endif /* HAVE_BSD_TTYS */
348 
349 	/*
350 	 * Set up the structures
351 	 */
352 	leitch->peer = peer;
353 	leitch->unit = unit;
354 	leitch->state = STATE_IDLE;
355 	leitch->fudge1 = 15;	/* 15ms */
356 
357 	leitch->leitchio.clock_recv = leitch_receive;
358 	leitch->leitchio.srcclock = peer;
359 	leitch->leitchio.datalen = 0;
360 	leitch->leitchio.fd = fd232;
361 	if (!io_addclock(&leitch->leitchio)) {
362 		leitch->leitchio.fd = -1;
363 		goto screwed;
364 	}
365 
366 	/*
367 	 * All done.  Initialize a few random peer variables, then
368 	 * return success.
369 	 */
370 	peer->precision = PRECISION;
371 	peer->stratum = stratumtouse[unit];
372 	peer->refid = refid[unit];
373 	unitinuse[unit] = 1;
374 	return(1);
375 
376 	/*
377 	 * Something broke; abandon ship.
378 	 */
379     screwed:
380 	close(fd232);
381 	return(0);
382 }
383 
384 /*
385  * leitch_receive - receive data from the serial interface on a leitch
386  * clock
387  */
388 static void
389 leitch_receive(
390 	struct recvbuf *rbufp
391 	)
392 {
393 	struct leitchunit *leitch = rbufp->recv_peer->procptr->unitptr;
394 
395 #ifdef DEBUG
396 	if (debug)
397 	    fprintf(stderr, "leitch_recieve(%*.*s)\n",
398 		    rbufp->recv_length, rbufp->recv_length,
399 		    rbufp->recv_buffer);
400 #endif
401 	if (rbufp->recv_length != 7)
402 	    return; /* The date is return with a trailing newline,
403 		       discard it. */
404 
405 	switch (leitch->state) {
406 	    case STATE_IDLE:	/* unexpected, discard and resync */
407 		return;
408 	    case STATE_DATE:
409 		if (!leitch_get_date(rbufp,leitch)) {
410 			leitch->state = STATE_IDLE;
411 			break;
412 		}
413 		leitch_send(leitch,"T\r");
414 #ifdef DEBUG
415 		if (debug)
416 		    fprintf(stderr, "%u\n",leitch->yearday);
417 #endif
418 		leitch->state = STATE_TIME1;
419 		break;
420 	    case STATE_TIME1:
421 		if (!leitch_get_time(rbufp,leitch,1)) {
422 		}
423 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
424 			       leitch->second, 1, rbufp->recv_time.l_ui,
425 			       &leitch->yearstart, &leitch->reftime1.l_ui)) {
426 			leitch->state = STATE_IDLE;
427 			break;
428 		}
429 		leitch->reftime1.l_uf = 0;
430 #ifdef DEBUG
431 		if (debug)
432 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
433 #endif
434 		MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
435 		leitch->codetime1 = rbufp->recv_time;
436 		leitch->state = STATE_TIME2;
437 		break;
438 	    case STATE_TIME2:
439 		if (!leitch_get_time(rbufp,leitch,2)) {
440 		}
441 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
442 			       leitch->second, 1, rbufp->recv_time.l_ui,
443 			       &leitch->yearstart, &leitch->reftime2.l_ui)) {
444 			leitch->state = STATE_IDLE;
445 			break;
446 		}
447 #ifdef DEBUG
448 		if (debug)
449 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
450 #endif
451 		MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
452 		leitch->codetime2 = rbufp->recv_time;
453 		leitch->state = STATE_TIME3;
454 		break;
455 	    case STATE_TIME3:
456 		if (!leitch_get_time(rbufp,leitch,3)) {
457 		}
458 		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
459 			       leitch->second, GMT, rbufp->recv_time.l_ui,
460 			       &leitch->yearstart, &leitch->reftime3.l_ui)) {
461 			leitch->state = STATE_IDLE;
462 			break;
463 		}
464 #ifdef DEBUG
465 		if (debug)
466 		    fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
467 #endif
468 		MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
469 		leitch->codetime3 = rbufp->recv_time;
470 		leitch_process(leitch);
471 		leitch->state = STATE_IDLE;
472 		break;
473 	    default:
474 		msyslog(LOG_ERR,
475 			"leitech_receive: invalid state %d unit %d",
476 			leitch->state, leitch->unit);
477 	}
478 }
479 
480 /*
481  * leitch_process - process a pile of samples from the clock
482  *
483  * This routine uses a three-stage median filter to calculate offset and
484  * dispersion. reduce jitter. The dispersion is calculated as the span
485  * of the filter (max - min), unless the quality character (format 2) is
486  * non-blank, in which case the dispersion is calculated on the basis of
487  * the inherent tolerance of the internal radio oscillator, which is
488  * +-2e-5 according to the radio specifications.
489  */
490 static void
491 leitch_process(
492 	struct leitchunit *leitch
493 	)
494 {
495 	l_fp off;
496 	l_fp tmp_fp;
497       /*double doffset;*/
498 
499 	off = leitch->reftime1;
500 	L_SUB(&off,&leitch->codetime1);
501 	tmp_fp = leitch->reftime2;
502 	L_SUB(&tmp_fp,&leitch->codetime2);
503 	if (L_ISGEQ(&off,&tmp_fp))
504 	    off = tmp_fp;
505 	tmp_fp = leitch->reftime3;
506 	L_SUB(&tmp_fp,&leitch->codetime3);
507 
508 	if (L_ISGEQ(&off,&tmp_fp))
509 	    off = tmp_fp;
510       /*LFPTOD(&off, doffset);*/
511 	refclock_receive(leitch->peer);
512 }
513 
514 /*
515  * days_per_year
516  */
517 static int
518 days_per_year(
519 	int year
520 	)
521 {
522 	if (year%4) {	/* not a potential leap year */
523 		return (365);
524 	} else {
525 		if (year % 100) {	/* is a leap year */
526 			return (366);
527 		} else {
528 			if (year % 400) {
529 				return (365);
530 			} else {
531 				return (366);
532 			}
533 		}
534 	}
535 }
536 
537 static int
538 leitch_get_date(
539 	struct recvbuf *rbufp,
540 	struct leitchunit *leitch
541 	)
542 {
543 	int i;
544 
545 	if (rbufp->recv_length < 6)
546 	    return(0);
547 #undef  BAD    /* confict: defined as (-1) in AIX sys/param.h */
548 #define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
549 	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
550 	    return(0);
551 #define ATOB(A) ((rbufp->recv_buffer[A])-'0')
552 	leitch->year = ATOB(0)*10 + ATOB(1);
553 	leitch->month = ATOB(2)*10 + ATOB(3);
554 	leitch->day = ATOB(4)*10 + ATOB(5);
555 
556 	/* sanity checks */
557 	if (leitch->month > 12)
558 	    return(0);
559 	if (leitch->day > days_in_month[leitch->month-1])
560 	    return(0);
561 
562 	/* calculate yearday */
563 	i = 0;
564 	leitch->yearday = leitch->day;
565 
566 	while ( i < (leitch->month-1) )
567 	    leitch->yearday += days_in_month[i++];
568 
569 	if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
570 	    leitch->month > 2)
571 	    leitch->yearday--;
572 
573 	return(1);
574 }
575 
576 /*
577  * leitch_get_time
578  */
579 static int
580 leitch_get_time(
581 	struct recvbuf *rbufp,
582 	struct leitchunit *leitch,
583 	int which
584 	)
585 {
586 	if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
587 	    return(0);
588 	leitch->hour = ATOB(0)*10 +ATOB(1);
589 	leitch->minute = ATOB(2)*10 +ATOB(3);
590 	leitch->second = ATOB(4)*10 +ATOB(5);
591 
592 	if ((leitch->hour > 23) || (leitch->minute > 60) ||
593 	    (leitch->second > 60))
594 	    return(0);
595 	return(1);
596 }
597 
598 #else
599 NONEMPTY_TRANSLATION_UNIT
600 #endif /* REFCLOCK */
601