]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_zyfer.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ntp / ntpd / refclock_zyfer.c
1 /*
2  * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
3  *
4  * Harlan Stenn, Jan 2002
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
12
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_unixtime.h"
18
19 #include <stdio.h>
20 #include <ctype.h>
21
22 #ifdef HAVE_SYS_TERMIOS_H
23 # include <sys/termios.h>
24 #endif
25 #ifdef HAVE_SYS_PPSCLOCK_H
26 # include <sys/ppsclock.h>
27 #endif
28
29 /*
30  * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
31  * This clock also provides PPS as well as IRIG outputs.
32  * Precision is limited by the serial driver, etc.
33  *
34  * If I was really brave I'd hack/generalize the serial driver to deal
35  * with arbitrary on-time characters.  This clock *begins* the stream with
36  * `!`, the on-time character, and the string is *not* EOL-terminated.
37  *
38  * Configure the beast for 9600, 8N1.  While I see leap-second stuff
39  * in the documentation, the published specs on the TOD format only show
40  * the seconds going to '59'.  I see no leap warning in the TOD format.
41  *
42  * The clock sends the following message once per second:
43  *
44  *      !TIME,2002,017,07,59,32,2,4,1
45  *            YYYY DDD HH MM SS m T O
46  *
47  *      !               On-time character
48  *      YYYY            Year
49  *      DDD     001-366 Day of Year
50  *      HH      00-23   Hour
51  *      MM      00-59   Minute
52  *      SS      00-59   Second (probably 00-60)
53  *      m       1-5     Time Mode:
54  *                      1 = GPS time
55  *                      2 = UTC time
56  *                      3 = LGPS time (Local GPS)
57  *                      4 = LUTC time (Local UTC)
58  *                      5 = Manual time
59  *      T       4-9     Time Figure Of Merit:
60  *                      4         x <= 1us
61  *                      5   1us < x <= 10 us
62  *                      6  10us < x <= 100us
63  *                      7 100us < x <= 1ms
64  *                      8   1ms < x <= 10ms
65  *                      9  10ms < x
66  *      O       0-4     Operation Mode:
67  *                      0 Warm-up
68  *                      1 Time Locked
69  *                      2 Coasting
70  *                      3 Recovering
71  *                      4 Manual
72  *
73  */
74
75 /*
76  * Interface definitions
77  */
78 #define DEVICE          "/dev/zyfer%d" /* device name and unit */
79 #define SPEED232        B9600   /* uart speed (9600 baud) */
80 #define PRECISION       (-20)   /* precision assumed (about 1 us) */
81 #define REFID           "GPS\0" /* reference ID */
82 #define DESCRIPTION     "Zyfer GPStarplus" /* WRU */
83
84 #define LENZYFER        29      /* timecode length */
85
86 /*
87  * Unit control structure
88  */
89 struct zyferunit {
90         u_char  Rcvbuf[LENZYFER + 1];
91         u_char  polled;         /* poll message flag */
92         int     pollcnt;
93         l_fp    tstamp;         /* timestamp of last poll */
94         int     Rcvptr;
95 };
96
97 /*
98  * Function prototypes
99  */
100 static  int     zyfer_start     P((int, struct peer *));
101 static  void    zyfer_shutdown  P((int, struct peer *));
102 static  void    zyfer_receive   P((struct recvbuf *));
103 static  void    zyfer_poll      P((int, struct peer *));
104
105 /*
106  * Transfer vector
107  */
108 struct  refclock refclock_zyfer = {
109         zyfer_start,            /* start up driver */
110         zyfer_shutdown,         /* shut down driver */
111         zyfer_poll,             /* transmit poll message */
112         noentry,                /* not used (old zyfer_control) */
113         noentry,                /* initialize driver (not used) */
114         noentry,                /* not used (old zyfer_buginfo) */
115         NOFLAGS                 /* not used */
116 };
117
118
119 /*
120  * zyfer_start - open the devices and initialize data for processing
121  */
122 static int
123 zyfer_start(
124         int unit,
125         struct peer *peer
126         )
127 {
128         register struct zyferunit *up;
129         struct refclockproc *pp;
130         int fd;
131         char device[20];
132
133         /*
134          * Open serial port.
135          * Something like LDISC_ACTS that looked for ! would be nice...
136          */
137         (void)sprintf(device, DEVICE, unit);
138         if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
139             return (0);
140
141         msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
142
143         /*
144          * Allocate and initialize unit structure
145          */
146         if (!(up = (struct zyferunit *)
147               emalloc(sizeof(struct zyferunit)))) {
148                 (void) close(fd);
149                 return (0);
150         }
151         memset((char *)up, 0, sizeof(struct zyferunit));
152         pp = peer->procptr;
153         pp->io.clock_recv = zyfer_receive;
154         pp->io.srcclock = (caddr_t)peer;
155         pp->io.datalen = 0;
156         pp->io.fd = fd;
157         if (!io_addclock(&pp->io)) {
158                 (void) close(fd);
159                 free(up);
160                 return (0);
161         }
162         pp->unitptr = (caddr_t)up;
163
164         /*
165          * Initialize miscellaneous variables
166          */
167         peer->precision = PRECISION;
168         pp->clockdesc = DESCRIPTION;
169         memcpy((char *)&pp->refid, REFID, 4);
170         up->pollcnt = 2;
171         up->polled = 0;         /* May not be needed... */
172
173         return (1);
174 }
175
176
177 /*
178  * zyfer_shutdown - shut down the clock
179  */
180 static void
181 zyfer_shutdown(
182         int unit,
183         struct peer *peer
184         )
185 {
186         register struct zyferunit *up;
187         struct refclockproc *pp;
188
189         pp = peer->procptr;
190         up = (struct zyferunit *)pp->unitptr;
191         io_closeclock(&pp->io);
192         free(up);
193 }
194
195
196 /*
197  * zyfer_receive - receive data from the serial interface
198  */
199 static void
200 zyfer_receive(
201         struct recvbuf *rbufp
202         )
203 {
204         register struct zyferunit *up;
205         struct refclockproc *pp;
206         struct peer *peer;
207         int tmode;              /* Time mode */
208         int tfom;               /* Time Figure Of Merit */
209         int omode;              /* Operation mode */
210         u_char *p;
211 #ifdef PPS
212         struct ppsclockev ppsev;
213         int request;
214 #ifdef HAVE_CIOGETEV
215         request = CIOGETEV;
216 #endif
217 #ifdef HAVE_TIOCGPPSEV
218         request = TIOCGPPSEV;
219 #endif
220 #endif /* PPS */
221
222         peer = (struct peer *)rbufp->recv_srcclock;
223         pp = peer->procptr;
224         up = (struct zyferunit *)pp->unitptr;
225         p = (u_char *) &rbufp->recv_space;
226         /*
227          * If lencode is 0:
228          * - if *rbufp->recv_space is !
229          * - - call refclock_gtlin to get things going
230          * - else flush
231          * else stuff it on the end of lastcode
232          * If we don't have LENZYFER bytes
233          * - wait for more data
234          * Crack the beast, and if it's OK, process it.
235          *
236          * We use refclock_gtlin() because we might use LDISC_CLK.
237          *
238          * Under FreeBSD, we get the ! followed by two 14-byte packets.
239          */
240
241         if (pp->lencode >= LENZYFER)
242                 pp->lencode = 0;
243
244         if (!pp->lencode) {
245                 if (*p == '!')
246                         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
247                                                      BMAX, &pp->lastrec);
248                 else
249                         return;
250         } else {
251                 memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
252                 pp->lencode += rbufp->recv_length;
253                 pp->a_lastcode[pp->lencode] = '\0';
254         }
255
256         if (pp->lencode < LENZYFER)
257                 return;
258
259         record_clock_stats(&peer->srcadr, pp->a_lastcode);
260
261         /*
262          * We get down to business, check the timecode format and decode
263          * its contents. If the timecode has invalid length or is not in
264          * proper format, we declare bad format and exit.
265          */
266
267         if (pp->lencode != LENZYFER) {
268                 refclock_report(peer, CEVNT_BADTIME);
269                 return;
270         }
271
272         /*
273          * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
274          */
275         if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
276                    &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
277                    &tmode, &tfom, &omode) != 8) {
278                 refclock_report(peer, CEVNT_BADREPLY);
279                 return;
280         }
281
282         if (tmode != 2) {
283                 refclock_report(peer, CEVNT_BADTIME);
284                 return;
285         }
286
287         /* Should we make sure tfom is 4? */
288
289         if (omode != 1) {
290                 pp->leap = LEAP_NOTINSYNC;
291                 return;
292         }
293 #ifdef PPS
294         if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
295                 ppsev.tv.tv_sec += (u_int32) JAN_1970;
296                 TVTOTS(&ppsev.tv,&up->tstamp);
297         }
298         /* record the last ppsclock event time stamp */
299         pp->lastrec = up->tstamp;
300 #endif /* PPS */
301         if (!refclock_process(pp)) {
302                 refclock_report(peer, CEVNT_BADTIME);
303                 return;
304         }
305
306         /*
307          * Good place for record_clock_stats()
308          */
309         up->pollcnt = 2;
310
311         if (up->polled) {
312                 up->polled = 0;
313                 refclock_receive(peer);
314         }
315 }
316
317
318 /*
319  * zyfer_poll - called by the transmit procedure
320  */
321 static void
322 zyfer_poll(
323         int unit,
324         struct peer *peer
325         )
326 {
327         register struct zyferunit *up;
328         struct refclockproc *pp;
329
330         /*
331          * We don't really do anything here, except arm the receiving
332          * side to capture a sample and check for timeouts.
333          */
334         pp = peer->procptr;
335         up = (struct zyferunit *)pp->unitptr;
336         if (!up->pollcnt)
337                 refclock_report(peer, CEVNT_TIMEOUT);
338         else
339                 up->pollcnt--;
340         pp->polls++;
341         up->polled = 1;
342 }
343
344 #else
345 int refclock_zyfer_bs;
346 #endif /* REFCLOCK */