]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpd/refclock_leitch.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 #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
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
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
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
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
238 leitch_start(
239         int unit,
240         struct peer *peer
241         )
242 {
243         struct leitchunit *leitch;
244         int fd232;
245         char leitchdev[20];
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
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
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
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
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
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 */