Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / ntpd / refclock_leitch.c
blob9742b3573df53aaf2a4ced15f5ef50feb799c6d5
1 /* $NetBSD$ */
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 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
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
75 * LEITCH unit control structure
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;
100 * Function prototypes
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);
117 static struct leitchunit leitchunits[MAXUNITS];
118 static u_char unitinuse[MAXUNITS];
119 static u_char stratumtouse[MAXUNITS];
120 static u_int32 refid[MAXUNITS];
122 static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
125 * Transfer vector
127 struct refclock refclock_leitch = {
128 leitch_start, leitch_shutdown, leitch_poll,
129 leitch_control, leitch_init, leitch_buginfo, NOFLAGS
133 * leitch_init - initialize internal leitch driver data
135 static void
136 leitch_init(void)
138 int i;
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);
147 * leitch_shutdown - shut down a LEITCH clock
149 static void
150 leitch_shutdown(
151 int unit,
152 struct peer *peer
155 #ifdef DEBUG
156 if (debug)
157 fprintf(stderr, "leitch_shutdown()\n");
158 #endif
162 * leitch_poll - called by the transmit procedure
164 static void
165 leitch_poll(
166 int unit,
167 struct peer *peer
170 struct leitchunit *leitch;
172 /* start the state machine rolling */
174 #ifdef DEBUG
175 if (debug)
176 fprintf(stderr, "leitch_poll()\n");
177 #endif
178 if (unit >= MAXUNITS) {
179 /* XXXX syslog it */
180 return;
183 leitch = &leitchunits[unit];
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;
195 static void
196 leitch_control(
197 int unit,
198 struct refclockstat *in,
199 struct refclockstat *out,
200 struct peer *passed_peer
203 if (unit >= MAXUNITS) {
204 msyslog(LOG_ERR,
205 "leitch_control: unit %d invalid", unit);
206 return;
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;
217 peer = (&leitchunits[unit])->peer;
218 peer->stratum = stratumtouse[unit];
219 peer->refid = refid[unit];
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;
235 * leitch_start - open the LEITCH devices and initialize data for processing
237 static int
238 leitch_start(
239 int unit,
240 struct peer *peer
243 struct leitchunit *leitch;
244 int fd232;
245 char leitchdev[20];
248 * Check configuration info.
250 if (unit >= MAXUNITS) {
251 msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
252 return (0);
255 if (unitinuse[unit]) {
256 msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
257 return (0);
261 * Open serial port.
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);
271 leitch = &leitchunits[unit];
272 memset((char*)leitch, 0, sizeof(*leitch));
274 #if defined(HAVE_SYSV_TTYS)
276 * System V serial line parameters (termio interface)
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;
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;
296 #endif /* HAVE_SYSV_TTYS */
297 #if defined(HAVE_TERMIOS)
299 * POSIX serial line parameters (termios interface)
301 * The LEITCHCLK option provides timestamping at the driver level.
302 * It requires the tty_clk streams module.
304 { struct termios ttyb, *ttyp;
306 ttyp = &ttyb;
307 if (tcgetattr(fd232, ttyp) < 0) {
308 msyslog(LOG_ERR,
309 "leitch_start: tcgetattr(%s): %m", leitchdev);
310 goto screwed;
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;
322 if (tcflush(fd232, TCIOFLUSH) < 0) {
323 msyslog(LOG_ERR,
324 "leitch_start: tcflush(%s): %m", leitchdev);
325 goto screwed;
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)
341 * 4.3bsd serial line parameters (sgttyb interface)
343 * The LEITCHCLK option provides timestamping at the driver level.
344 * It requires the tty_clk line discipline and 4.3bsd or later.
346 { struct sgttyb ttyb;
347 #if defined(LEITCHCLK)
348 int ldisc = CLKLDISC;
349 #endif /* LEITCHCLK */
351 if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
352 msyslog(LOG_ERR,
353 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
354 goto screwed;
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;
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;
375 #endif /* LEITCHCLK */
377 #endif /* HAVE_BSD_TTYS */
380 * Set up the structures
382 leitch->peer = peer;
383 leitch->unit = unit;
384 leitch->state = STATE_IDLE;
385 leitch->fudge1 = 15; /* 15ms */
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;
396 * All done. Initialize a few random peer variables, then
397 * return success.
399 peer->precision = PRECISION;
400 peer->stratum = stratumtouse[unit];
401 peer->refid = refid[unit];
402 unitinuse[unit] = 1;
403 return(1);
406 * Something broke; abandon ship.
408 screwed:
409 close(fd232);
410 return(0);
414 * leitch_receive - receive data from the serial interface on a leitch
415 * clock
417 static void
418 leitch_receive(
419 struct recvbuf *rbufp
422 struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
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. */
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;
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)) {
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;
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)) {
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;
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)) {
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;
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);
510 * leitch_process - process a pile of samples from the clock
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.
519 static void
520 leitch_process(
521 struct leitchunit *leitch
524 l_fp off;
525 l_fp tmp_fp;
526 /*double doffset;*/
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);
537 if (L_ISGEQ(&off,&tmp_fp))
538 off = tmp_fp;
539 /*LFPTOD(&off, doffset);*/
540 refclock_receive(leitch->peer);
544 * days_per_year
546 static int
547 days_per_year(
548 int year
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);
566 static int
567 leitch_get_date(
568 struct recvbuf *rbufp,
569 struct leitchunit *leitch
572 int i;
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);
585 /* sanity checks */
586 if (leitch->month > 12)
587 return(0);
588 if (leitch->day > days_in_month[leitch->month-1])
589 return(0);
591 /* calculate yearday */
592 i = 0;
593 leitch->yearday = leitch->day;
595 while ( i < (leitch->month-1) )
596 leitch->yearday += days_in_month[i++];
598 if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
599 leitch->month > 2)
600 leitch->yearday--;
602 return(1);
606 * leitch_get_time
608 static int
609 leitch_get_time(
610 struct recvbuf *rbufp,
611 struct leitchunit *leitch,
612 int which
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);
621 if ((leitch->hour > 23) || (leitch->minute > 60) ||
622 (leitch->second > 60))
623 return(0);
624 return(1);
627 #else
628 int refclock_leitch_bs;
629 #endif /* REFCLOCK */