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