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