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