Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_zyfer.c
blob4a3fb6587e7c81de7a1c3c06b38bbbdee99677ff
1 /* $NetBSD$ */
3 /*
4 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
6 * Harlan Stenn, Jan 2002
7 */
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
13 #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
15 #include "ntpd.h"
16 #include "ntp_io.h"
17 #include "ntp_refclock.h"
18 #include "ntp_stdlib.h"
19 #include "ntp_unixtime.h"
21 #include <stdio.h>
22 #include <ctype.h>
24 #ifdef HAVE_SYS_TERMIOS_H
25 # include <sys/termios.h>
26 #endif
27 #ifdef HAVE_SYS_PPSCLOCK_H
28 # include <sys/ppsclock.h>
29 #endif
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.
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.
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.
44 * The clock sends the following message once per second:
46 * !TIME,2002,017,07,59,32,2,4,1
47 * YYYY DDD HH MM SS m T O
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
78 * Interface definitions
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 */
86 #define LENZYFER 29 /* timecode length */
89 * Unit control structure
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;
100 * Function prototypes
102 static int zyfer_start P((int, struct peer *));
103 static void zyfer_shutdown P((int, struct peer *));
104 static void zyfer_receive P((struct recvbuf *));
105 static void zyfer_poll P((int, struct peer *));
108 * Transfer vector
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 */
122 * zyfer_start - open the devices and initialize data for processing
124 static int
125 zyfer_start(
126 int unit,
127 struct peer *peer
130 register struct zyferunit *up;
131 struct refclockproc *pp;
132 int fd;
133 char device[20];
136 * Open serial port.
137 * Something like LDISC_ACTS that looked for ! would be nice...
139 (void)sprintf(device, DEVICE, unit);
140 if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
141 return (0);
143 msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
146 * Allocate and initialize unit structure
148 if (!(up = (struct zyferunit *)
149 emalloc(sizeof(struct zyferunit)))) {
150 (void) close(fd);
151 return (0);
153 memset((char *)up, 0, sizeof(struct zyferunit));
154 pp = peer->procptr;
155 pp->io.clock_recv = zyfer_receive;
156 pp->io.srcclock = (caddr_t)peer;
157 pp->io.datalen = 0;
158 pp->io.fd = fd;
159 if (!io_addclock(&pp->io)) {
160 (void) close(fd);
161 free(up);
162 return (0);
164 pp->unitptr = (caddr_t)up;
167 * Initialize miscellaneous variables
169 peer->precision = PRECISION;
170 pp->clockdesc = DESCRIPTION;
171 memcpy((char *)&pp->refid, REFID, 4);
172 up->pollcnt = 2;
173 up->polled = 0; /* May not be needed... */
175 return (1);
180 * zyfer_shutdown - shut down the clock
182 static void
183 zyfer_shutdown(
184 int unit,
185 struct peer *peer
188 register struct zyferunit *up;
189 struct refclockproc *pp;
191 pp = peer->procptr;
192 up = (struct zyferunit *)pp->unitptr;
193 io_closeclock(&pp->io);
194 free(up);
199 * zyfer_receive - receive data from the serial interface
201 static void
202 zyfer_receive(
203 struct recvbuf *rbufp
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 #ifdef PPS
214 struct ppsclockev ppsev;
215 int request;
216 #ifdef HAVE_CIOGETEV
217 request = CIOGETEV;
218 #endif
219 #ifdef HAVE_TIOCGPPSEV
220 request = TIOCGPPSEV;
221 #endif
222 #endif /* PPS */
224 peer = (struct peer *)rbufp->recv_srcclock;
225 pp = peer->procptr;
226 up = (struct zyferunit *)pp->unitptr;
227 p = (u_char *) &rbufp->recv_space;
229 * If lencode is 0:
230 * - if *rbufp->recv_space is !
231 * - - call refclock_gtlin to get things going
232 * - else flush
233 * else stuff it on the end of lastcode
234 * If we don't have LENZYFER bytes
235 * - wait for more data
236 * Crack the beast, and if it's OK, process it.
238 * We use refclock_gtlin() because we might use LDISC_CLK.
240 * Under FreeBSD, we get the ! followed by two 14-byte packets.
243 if (pp->lencode >= LENZYFER)
244 pp->lencode = 0;
246 if (!pp->lencode) {
247 if (*p == '!')
248 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
249 BMAX, &pp->lastrec);
250 else
251 return;
252 } else {
253 memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
254 pp->lencode += rbufp->recv_length;
255 pp->a_lastcode[pp->lencode] = '\0';
258 if (pp->lencode < LENZYFER)
259 return;
261 record_clock_stats(&peer->srcadr, pp->a_lastcode);
264 * We get down to business, check the timecode format and decode
265 * its contents. If the timecode has invalid length or is not in
266 * proper format, we declare bad format and exit.
269 if (pp->lencode != LENZYFER) {
270 refclock_report(peer, CEVNT_BADTIME);
271 return;
275 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
277 if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
278 &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
279 &tmode, &tfom, &omode) != 8) {
280 refclock_report(peer, CEVNT_BADREPLY);
281 return;
284 if (tmode != 2) {
285 refclock_report(peer, CEVNT_BADTIME);
286 return;
289 /* Should we make sure tfom is 4? */
291 if (omode != 1) {
292 pp->leap = LEAP_NOTINSYNC;
293 return;
295 #ifdef PPS
296 if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
297 ppsev.tv.tv_sec += (u_int32) JAN_1970;
298 TVTOTS(&ppsev.tv,&up->tstamp);
300 /* record the last ppsclock event time stamp */
301 pp->lastrec = up->tstamp;
302 #endif /* PPS */
303 if (!refclock_process(pp)) {
304 refclock_report(peer, CEVNT_BADTIME);
305 return;
309 * Good place for record_clock_stats()
311 up->pollcnt = 2;
313 if (up->polled) {
314 up->polled = 0;
315 refclock_receive(peer);
321 * zyfer_poll - called by the transmit procedure
323 static void
324 zyfer_poll(
325 int unit,
326 struct peer *peer
329 register struct zyferunit *up;
330 struct refclockproc *pp;
333 * We don't really do anything here, except arm the receiving
334 * side to capture a sample and check for timeouts.
336 pp = peer->procptr;
337 up = (struct zyferunit *)pp->unitptr;
338 if (!up->pollcnt)
339 refclock_report(peer, CEVNT_TIMEOUT);
340 else
341 up->pollcnt--;
342 pp->polls++;
343 up->polled = 1;
346 #else
347 int refclock_zyfer_bs;
348 #endif /* REFCLOCK */