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