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