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