turns printfs back on
[freebsd-src/fkvm-freebsd.git] / contrib / ntp / ntpd / refclock_leitch.c
blobe1ba0c409582061311187dc7f3baa1162559fb92
1 /*
2 * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
3 */
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
9 #if defined(REFCLOCK) && defined(CLOCK_LEITCH)
11 #include "ntpd.h"
12 #include "ntp_io.h"
13 #include "ntp_refclock.h"
14 #include "ntp_unixtime.h"
16 #include <stdio.h>
17 #include <ctype.h>
19 #ifdef STREAM
20 #include <stropts.h>
21 #if defined(LEITCHCLK)
22 #include <sys/clkdefs.h>
23 #endif /* LEITCHCLK */
24 #endif /* STREAM */
26 #include "ntp_stdlib.h"
30 * Driver for Leitch CSD-5300 Master Clock System
32 * COMMANDS:
33 * DATE: D <CR>
34 * TIME: T <CR>
35 * STATUS: S <CR>
36 * LOOP: L <CR>
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)
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
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
72 * LEITCH unit control structure
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;
97 * Function prototypes
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));
114 static struct leitchunit leitchunits[MAXUNITS];
115 static u_char unitinuse[MAXUNITS];
116 static u_char stratumtouse[MAXUNITS];
117 static u_int32 refid[MAXUNITS];
119 static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
122 * Transfer vector
124 struct refclock refclock_leitch = {
125 leitch_start, leitch_shutdown, leitch_poll,
126 leitch_control, leitch_init, leitch_buginfo, NOFLAGS
130 * leitch_init - initialize internal leitch driver data
132 static void
133 leitch_init(void)
135 int i;
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);
144 * leitch_shutdown - shut down a LEITCH clock
146 static void
147 leitch_shutdown(
148 int unit,
149 struct peer *peer
152 #ifdef DEBUG
153 if (debug)
154 fprintf(stderr, "leitch_shutdown()\n");
155 #endif
159 * leitch_poll - called by the transmit procedure
161 static void
162 leitch_poll(
163 int unit,
164 struct peer *peer
167 struct leitchunit *leitch;
169 /* start the state machine rolling */
171 #ifdef DEBUG
172 if (debug)
173 fprintf(stderr, "leitch_poll()\n");
174 #endif
175 if (unit >= MAXUNITS) {
176 /* XXXX syslog it */
177 return;
180 leitch = &leitchunits[unit];
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;
192 static void
193 leitch_control(
194 int unit,
195 struct refclockstat *in,
196 struct refclockstat *out,
197 struct peer *passed_peer
200 if (unit >= MAXUNITS) {
201 msyslog(LOG_ERR,
202 "leitch_control: unit %d invalid", unit);
203 return;
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;
214 peer = (&leitchunits[unit])->peer;
215 peer->stratum = stratumtouse[unit];
216 peer->refid = refid[unit];
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;
232 * leitch_start - open the LEITCH devices and initialize data for processing
234 static int
235 leitch_start(
236 int unit,
237 struct peer *peer
240 struct leitchunit *leitch;
241 int fd232;
242 char leitchdev[20];
245 * Check configuration info.
247 if (unit >= MAXUNITS) {
248 msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
249 return (0);
252 if (unitinuse[unit]) {
253 msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
254 return (0);
258 * Open serial port.
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);
268 leitch = &leitchunits[unit];
269 memset((char*)leitch, 0, sizeof(*leitch));
271 #if defined(HAVE_SYSV_TTYS)
273 * System V serial line parameters (termio interface)
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;
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;
293 #endif /* HAVE_SYSV_TTYS */
294 #if defined(HAVE_TERMIOS)
296 * POSIX serial line parameters (termios interface)
298 * The LEITCHCLK option provides timestamping at the driver level.
299 * It requires the tty_clk streams module.
301 { struct termios ttyb, *ttyp;
303 ttyp = &ttyb;
304 if (tcgetattr(fd232, ttyp) < 0) {
305 msyslog(LOG_ERR,
306 "leitch_start: tcgetattr(%s): %m", leitchdev);
307 goto screwed;
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;
319 if (tcflush(fd232, TCIOFLUSH) < 0) {
320 msyslog(LOG_ERR,
321 "leitch_start: tcflush(%s): %m", leitchdev);
322 goto screwed;
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)
338 * 4.3bsd serial line parameters (sgttyb interface)
340 * The LEITCHCLK option provides timestamping at the driver level.
341 * It requires the tty_clk line discipline and 4.3bsd or later.
343 { struct sgttyb ttyb;
344 #if defined(LEITCHCLK)
345 int ldisc = CLKLDISC;
346 #endif /* LEITCHCLK */
348 if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
349 msyslog(LOG_ERR,
350 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
351 goto screwed;
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;
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;
372 #endif /* LEITCHCLK */
374 #endif /* HAVE_BSD_TTYS */
377 * Set up the structures
379 leitch->peer = peer;
380 leitch->unit = unit;
381 leitch->state = STATE_IDLE;
382 leitch->fudge1 = 15; /* 15ms */
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;
393 * All done. Initialize a few random peer variables, then
394 * return success.
396 peer->precision = 0;
397 peer->stratum = stratumtouse[unit];
398 peer->refid = refid[unit];
399 unitinuse[unit] = 1;
400 return(1);
403 * Something broke; abandon ship.
405 screwed:
406 close(fd232);
407 return(0);
411 * leitch_receive - receive data from the serial interface on a leitch
412 * clock
414 static void
415 leitch_receive(
416 struct recvbuf *rbufp
419 struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
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. */
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;
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)) {
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;
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)) {
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;
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)) {
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;
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);
507 * leitch_process - process a pile of samples from the clock
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.
516 static void
517 leitch_process(
518 struct leitchunit *leitch
521 l_fp off;
522 l_fp tmp_fp;
523 /*double doffset;*/
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);
534 if (L_ISGEQ(&off,&tmp_fp))
535 off = tmp_fp;
536 /*LFPTOD(&off, doffset);*/
537 refclock_receive(leitch->peer);
541 * days_per_year
543 static int
544 days_per_year(
545 int year
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);
563 static int
564 leitch_get_date(
565 struct recvbuf *rbufp,
566 struct leitchunit *leitch
569 int i;
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);
582 /* sanity checks */
583 if (leitch->month > 12)
584 return(0);
585 if (leitch->day > days_in_month[leitch->month-1])
586 return(0);
588 /* calculate yearday */
589 i = 0;
590 leitch->yearday = leitch->day;
592 while ( i < (leitch->month-1) )
593 leitch->yearday += days_in_month[i++];
595 if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
596 leitch->month > 2)
597 leitch->yearday--;
599 return(1);
603 * leitch_get_time
605 static int
606 leitch_get_time(
607 struct recvbuf *rbufp,
608 struct leitchunit *leitch,
609 int which
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);
618 if ((leitch->hour > 23) || (leitch->minute > 60) ||
619 (leitch->second > 60))
620 return(0);
621 return(1);
624 #else
625 int refclock_leitch_bs;
626 #endif /* REFCLOCK */