1 /*        $NetBSD: refclock_leitch.c,v 1.6 2024/08/18 20:47:18 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[32];
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