turns printfs back on
[freebsd-src/fkvm-freebsd.git] / contrib / ntp / ntpd / refclock_zyfer.c
blob44f2c4d353946313d8afaa6b3089838c9b667ea0
1 /*
2 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
4 * Harlan Stenn, Jan 2002
5 */
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
11 #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_unixtime.h"
19 #include <stdio.h>
20 #include <ctype.h>
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
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.
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.
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.
42 * The clock sends the following message once per second:
44 * !TIME,2002,017,07,59,32,2,4,1
45 * YYYY DDD HH MM SS m T O
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
76 * Interface definitions
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 */
84 #define LENZYFER 29 /* timecode length */
87 * Unit control structure
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;
98 * Function prototypes
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 *));
106 * Transfer vector
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 */
120 * zyfer_start - open the devices and initialize data for processing
122 static int
123 zyfer_start(
124 int unit,
125 struct peer *peer
128 register struct zyferunit *up;
129 struct refclockproc *pp;
130 int fd;
131 char device[20];
134 * Open serial port.
135 * Something like LDISC_ACTS that looked for ! would be nice...
137 (void)sprintf(device, DEVICE, unit);
138 if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
139 return (0);
141 msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
144 * Allocate and initialize unit structure
146 if (!(up = (struct zyferunit *)
147 emalloc(sizeof(struct zyferunit)))) {
148 (void) close(fd);
149 return (0);
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);
162 pp->unitptr = (caddr_t)up;
165 * Initialize miscellaneous variables
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... */
173 return (1);
178 * zyfer_shutdown - shut down the clock
180 static void
181 zyfer_shutdown(
182 int unit,
183 struct peer *peer
186 register struct zyferunit *up;
187 struct refclockproc *pp;
189 pp = peer->procptr;
190 up = (struct zyferunit *)pp->unitptr;
191 io_closeclock(&pp->io);
192 free(up);
197 * zyfer_receive - receive data from the serial interface
199 static void
200 zyfer_receive(
201 struct recvbuf *rbufp
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 */
222 peer = (struct peer *)rbufp->recv_srcclock;
223 pp = peer->procptr;
224 up = (struct zyferunit *)pp->unitptr;
225 p = (u_char *) &rbufp->recv_space;
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.
236 * We use refclock_gtlin() because we might use LDISC_CLK.
238 * Under FreeBSD, we get the ! followed by two 14-byte packets.
241 if (pp->lencode >= LENZYFER)
242 pp->lencode = 0;
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';
256 if (pp->lencode < LENZYFER)
257 return;
259 record_clock_stats(&peer->srcadr, pp->a_lastcode);
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.
267 if (pp->lencode != LENZYFER) {
268 refclock_report(peer, CEVNT_BADTIME);
269 return;
273 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
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;
282 if (tmode != 2) {
283 refclock_report(peer, CEVNT_BADTIME);
284 return;
287 /* Should we make sure tfom is 4? */
289 if (omode != 1) {
290 pp->leap = LEAP_NOTINSYNC;
291 return;
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);
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;
307 * Good place for record_clock_stats()
309 up->pollcnt = 2;
311 if (up->polled) {
312 up->polled = 0;
313 refclock_receive(peer);
319 * zyfer_poll - called by the transmit procedure
321 static void
322 zyfer_poll(
323 int unit,
324 struct peer *peer
327 register struct zyferunit *up;
328 struct refclockproc *pp;
331 * We don't really do anything here, except arm the receiving
332 * side to capture a sample and check for timeouts.
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;
344 #else
345 int refclock_zyfer_bs;
346 #endif /* REFCLOCK */