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