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
leitch_init(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
leitch_shutdown(int unit,struct peer * peer)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
leitch_poll(int unit,struct peer * peer)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
leitch_control(int unit,const struct refclockstat * in,struct refclockstat * out,struct peer * passed_peer)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
leitch_start(int unit,struct peer * peer)238 leitch_start(
239 int unit,
240 struct peer *peer
241 )
242 {
243 struct leitchunit *leitch;
244 int fd232;
245 char leitchdev[32];
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
leitch_receive(struct recvbuf * rbufp)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
leitch_process(struct leitchunit * leitch)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
days_per_year(int year)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
leitch_get_date(struct recvbuf * rbufp,struct leitchunit * leitch)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
leitch_get_time(struct recvbuf * rbufp,struct leitchunit * leitch,int which)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