xref: /freebsd/contrib/ntp/ntpd/refclock_atom.c (revision 5129159789cc9d7bc514e4546b88e3427695002d)
1 /*
2  * refclock_atom - clock driver for 1-pps signals
3  */
4 #ifdef HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7 
8 #if defined(REFCLOCK) && defined(CLOCK_ATOM)
9 
10 #include <stdio.h>
11 #include <ctype.h>
12 
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_unixtime.h"
16 #include "ntp_refclock.h"
17 #include "ntp_stdlib.h"
18 
19 #ifdef HAVE_SYS_TIME_H
20 # include <sys/time.h>
21 #endif
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 #ifdef HAVE_PPSAPI
29 #include <sys/timepps.h>
30 #endif /* HAVE_PPSAPI */
31 
32 /*
33  * This driver furnishes an interface for pulse-per-second (PPS) signals
34  * produced by a cesium clock, timing receiver or related  equipment. It
35  * can be used to remove accumulated jitter and retime a secondary
36  * server when synchronized to a primary server over a congested, wide-
37  * area network and before redistributing the time to local clients.
38  *
39  * In order for this driver to work, the local clock must be set to
40  * within +-500 ms by another means, such as a radio clock or NTP
41  * itself. The 1-pps signal is connected via a serial port and gadget
42  * box consisting of a one-shot flopflop and RS232 level converter.
43  * Conntection is either via the carrier detect (DCD) lead or via the
44  * receive data (RD) lead. The incidental jitter using the DCD lead is
45  * essentially the interrupt latency. The incidental jitter using the RD
46  * lead has an additional component due to the line sampling clock. When
47  * operated at 38.4 kbps, this arrangement has a worst-case jitter less
48  * than 26 us.
49  *
50  * There are four ways in which this driver can be used. They are
51  * described in decreasing order of merit below. The first way uses the
52  * ppsapi STREAMS module and the LDISC_PPS line discipline, while the
53  * second way uses the ppsclock STREAMS module and the LDISC_PPS line
54  * discipline. Either of these works only for the baseboard serial ports
55  * of the Sun SPARC IPC and clones. However, the ppsapi uses the
56  * proposed IETF interface expected to become standard for PPS signals.
57  * The serial port to be used is specified by the pps command in the
58  * configuration file. This driver reads the timestamp directly by a
59  * designated ioctl() system call.
60  *
61  * The third way uses the LDISC_CLKPPS line discipline and works for
62  * any architecture supporting a serial port. If after a few seconds
63  * this driver finds no ppsclock module configured, it attempts to open
64  * a serial port device /dev/pps%d, where %d is the unit number, and
65  * assign the LDISC_CLKPPS line discipline to it. If the line discipline
66  * fails, no harm is done except the accuracy is reduced somewhat. The
67  * pulse generator in the gadget box is adjusted to produce a start bit
68  * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line
69  * discipline, this produces an ASCII DEL character ('\377') followed by
70  * a timestamp at each seconds epoch.
71  *
72  * The fourth way involves an auxiliary radio clock driver which calls
73  * the PPS driver with a timestamp captured by that driver. This use is
74  * documented in the source code for the driver(s) involved.  Note that
75  * some drivers collect the sample information themselves before calling
76  * pps_sample(), and others call knowing only that they are running
77  * shortly after an on-time tick and they expect to retrieve the PPS
78  * offset, fudge their result, and insert it into the timestream.
79  *
80  * Fudge Factors
81  *
82  * There are no special fudge factors other than the generic. The fudge
83  * time1 parameter can be used to compensate for miscellaneous UART and
84  * OS delays. Allow about 247 us for uart delays at 38400 bps and about
85  * 1 ms for STREAMS nonsense with older workstations. Velocities may
86  * vary with modern workstations.
87  */
88 /*
89  * Interface definitions
90  */
91 #ifdef HAVE_PPSAPI
92 extern int pps_assert;
93 #endif /* HAVE_PPSAPI */
94 #ifdef TTYCLK
95 #define DEVICE		"/dev/pps%d"	/* device name and unit */
96 #ifdef B38400
97 #define SPEED232	B38400	/* uart speed (38400 baud) */
98 #else
99 #define SPEED232	EXTB	/* as above */
100 #endif
101 #endif /* TTYCLK */
102 
103 #define	PRECISION	(-20)	/* precision assumed (about 1 us) */
104 #define	REFID		"PPS\0"	/* reference ID */
105 #define	DESCRIPTION	"PPS Clock Discipline" /* WRU */
106 
107 #define FLAG_TTY	0x01	/* tty_clk heard from */
108 #define FLAG_PPS	0x02	/* ppsclock heard from */
109 #define FLAG_AUX	0x04	/* auxiliary PPS source */
110 
111 static struct peer *pps_peer;	/* atom driver for auxiliary PPS sources */
112 
113 #ifdef TTYCLK
114 static	void	atom_receive	P((struct recvbuf *));
115 #endif /* TTYCLK */
116 
117 /*
118  * Unit control structure
119  */
120 struct atomunit {
121 #ifdef HAVE_PPSAPI
122 	pps_info_t pps_info;	/* pps_info control */
123 #endif /* HAVE_PPSAPI */
124 #ifdef PPS
125 	struct	ppsclockev ev;	/* ppsclock control */
126 #endif /* PPS */
127 	int	flags;		/* flags that wave */
128 };
129 
130 /*
131  * Function prototypes
132  */
133 static	int	atom_start	P((int, struct peer *));
134 static	void	atom_shutdown	P((int, struct peer *));
135 static	void	atom_poll	P((int, struct peer *));
136 #if defined(PPS) || defined(HAVE_PPSAPI)
137 static	int	atom_pps	P((struct peer *));
138 #endif /* PPS || HAVE_PPSAPI */
139 
140 /*
141  * Transfer vector
142  */
143 struct	refclock refclock_atom = {
144 	atom_start,		/* start up driver */
145 	atom_shutdown,		/* shut down driver */
146 	atom_poll,		/* transmit poll message */
147 	noentry,		/* not used (old atom_control) */
148 	noentry,		/* initialize driver */
149 	noentry,		/* not used (old atom_buginfo) */
150 	NOFLAGS			/* not used */
151 };
152 
153 
154 /*
155  * atom_start - initialize data for processing
156  */
157 static int
158 atom_start(
159 	int unit,
160 	struct peer *peer
161 	)
162 {
163 	register struct atomunit *up;
164 	struct refclockproc *pp;
165 	int flags;
166 #ifdef TTYCLK
167 	int fd = 0;
168 	char device[20];
169 	int ldisc = LDISC_CLKPPS;
170 #endif /* TTYCLK */
171 
172 	pps_peer = peer;
173 	flags = 0;
174 
175 #ifdef TTYCLK
176 # if defined(SCO5_CLOCK)
177 	ldisc = LDISC_RAW;   /* DCD timestamps without any line discipline */
178 # endif
179 	/*
180 	 * Open serial port. Use LDISC_CLKPPS line discipline only
181 	 * if the LDISC_PPS line discipline is not availble,
182 	 */
183 # if defined(PPS) || defined(HAVE_PPSAPI)
184 	if (fdpps <= 0)
185 # endif
186 	{
187 		(void)sprintf(device, DEVICE, unit);
188 		if ((fd = refclock_open(device, SPEED232, ldisc)) != 0)
189 			flags |= FLAG_TTY;
190 	}
191 #endif /* TTYCLK */
192 
193 	/*
194 	 * Allocate and initialize unit structure
195 	 */
196 	if (!(up = (struct atomunit *)emalloc(sizeof(struct atomunit)))) {
197 #ifdef TTYCLK
198 		if (flags & FLAG_TTY)
199 			(void) close(fd);
200 #endif /* TTYCLK */
201 		return (0);
202 	}
203 	memset((char *)up, 0, sizeof(struct atomunit));
204 	pp = peer->procptr;
205 	pp->unitptr = (caddr_t)up;
206 #ifdef TTYCLK
207 	if (flags & FLAG_TTY) {
208 		pp->io.clock_recv = atom_receive;
209 		pp->io.srcclock = (caddr_t)peer;
210 		pp->io.datalen = 0;
211 		pp->io.fd = fd;
212 		if (!io_addclock(&pp->io)) {
213 			(void) close(fd);
214 			free(up);
215 			return (0);
216 		}
217 	}
218 #endif /* TTYCLK */
219 
220 	/*
221 	 * Initialize miscellaneous variables
222 	 */
223 	peer->precision = PRECISION;
224 	pp->clockdesc = DESCRIPTION;
225 	memcpy((char *)&pp->refid, REFID, 4);
226 	up->flags = flags;
227 	return (1);
228 }
229 
230 
231 /*
232  * atom_shutdown - shut down the clock
233  */
234 static void
235 atom_shutdown(
236 	int unit,
237 	struct peer *peer
238 	)
239 {
240 	register struct atomunit *up;
241 	struct refclockproc *pp;
242 
243 	pp = peer->procptr;
244 	up = (struct atomunit *)pp->unitptr;
245 #ifdef TTYCLK
246 	if (up->flags & FLAG_TTY)
247 		io_closeclock(&pp->io);
248 #endif /* TTYCLK */
249 	if (pps_peer == peer)
250 		pps_peer = 0;
251 	free(up);
252 }
253 
254 
255 #if defined(PPS) || defined(HAVE_PPSAPI)
256 /*
257  * atom_pps - receive data from the LDISC_PPS discipline
258  */
259 static int
260 atom_pps(
261 	struct peer *peer
262 	)
263 {
264 	register struct atomunit *up;
265 	struct refclockproc *pp;
266 #ifdef HAVE_PPSAPI
267 	struct timespec timeout;
268 # ifdef HAVE_TIMESPEC
269 	struct timespec ts;
270 # else
271 	struct timeval ts;
272 # endif /* HAVE_TIMESPEC */
273 #endif /* HAVE_PPSAPI */
274 	l_fp lftmp;
275 	double doffset;
276 	int i;
277 #if !defined(HAVE_PPSAPI)
278 	int request =
279 # ifdef HAVE_CIOGETEV
280 	  CIOGETEV
281 # endif
282 # ifdef HAVE_TIOCGPPSEV
283 	  TIOCGPPSEV
284 # endif
285 	  ;
286 #endif /* HAVE_PPSAPI */
287 
288 	/*
289 	 * This routine is called once per second when the LDISC_PPS
290 	 * discipline is present. It snatches the pps timestamp from the
291 	 * kernel and saves the sign-extended fraction in a circular
292 	 * buffer for processing at the next poll event.
293 	 */
294 	pp = peer->procptr;
295 	up = (struct atomunit *)pp->unitptr;
296 
297 	/*
298 	 * Convert the timeval to l_fp and save for billboards. Sign-
299 	 * extend the fraction and stash in the buffer. No harm is done
300 	 * if previous data are overwritten. If the discipline comes bum
301 	 * or the data grow stale, just forget it. Round the nanoseconds
302 	 * to microseconds with great care.
303 	 */
304 	if (fdpps <= 0)
305 		return (1);
306 #ifdef HAVE_PPSAPI
307 	timeout.tv_sec = 0;
308 	timeout.tv_nsec = 0;
309 	i = up->pps_info.assert_sequence;
310 	if (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &up->pps_info, &timeout)
311 	    < 0)
312 		return (2);
313 	if (i == up->pps_info.assert_sequence)
314 		return (3);
315 	if (pps_assert)
316 		ts = up->pps_info.assert_timestamp;
317 	else
318 		ts = up->pps_info.clear_timestamp;
319 	pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
320 	ts.tv_nsec = (ts.tv_nsec + 500) / 1000;
321 	if (ts.tv_nsec > 1000000) {
322 		ts.tv_nsec -= 1000000;
323 		ts.tv_sec++;
324 	}
325 	TVUTOTSF(ts.tv_nsec, pp->lastrec.l_uf);
326 #else
327 	i = up->ev.serial;
328 	if (ioctl(fdpps, request, (caddr_t)&up->ev) < 0)
329 		return (2);
330 	if (i == up->ev.serial)
331 		return (3);
332 	pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970;
333 	TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf);
334 #endif /* HAVE_PPSAPI */
335 	up->flags |= FLAG_PPS;
336 	L_CLR(&lftmp);
337 	L_ADDF(&lftmp, pp->lastrec.l_f);
338 	LFPTOD(&lftmp, doffset);
339 	SAMPLE(-doffset + pp->fudgetime1);
340 	return (0);
341 }
342 #endif /* PPS || HAVE_PPSAPI */
343 
344 #ifdef TTYCLK
345 /*
346  * atom_receive - receive data from the LDISC_CLK discipline
347  */
348 static void
349 atom_receive(
350 	struct recvbuf *rbufp
351 	)
352 {
353 	register struct atomunit *up;
354 	struct refclockproc *pp;
355 	struct peer *peer;
356 	l_fp lftmp;
357 	double doffset;
358 
359 	/*
360 	 * This routine is called once per second when the serial
361 	 * interface is in use. It snatches the timestamp from the
362 	 * buffer and saves the sign-extended fraction in a circular
363 	 * buffer for processing at the next poll event.
364 	 */
365 	peer = (struct peer *)rbufp->recv_srcclock;
366 	pp = peer->procptr;
367 	up = (struct atomunit *)pp->unitptr;
368 	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
369 	    &pp->lastrec);
370 
371 	/*
372 	 * Save the timestamp for billboards. Sign-extend the fraction
373 	 * and stash in the buffer. No harm is done if previous data are
374 	 * overwritten. Do this only if the ppsclock gizmo is not
375 	 * working.
376 	 */
377 	if (up->flags & FLAG_PPS)
378 		return;
379 	L_CLR(&lftmp);
380 	L_ADDF(&lftmp, pp->lastrec.l_f);
381 	LFPTOD(&lftmp, doffset);
382 	SAMPLE(-doffset + pp->fudgetime1);
383 }
384 #endif /* TTYCLK */
385 
386 /*
387  * pps_sample - receive PPS data from some other clock driver
388  */
389 int
390 pps_sample(
391 	   l_fp *offset
392 	   )
393 {
394 	register struct peer *peer;
395 	register struct atomunit *up;
396 	struct refclockproc *pp;
397 	l_fp lftmp;
398 	double doffset;
399 
400 	/*
401 	 * This routine is called once per second when the external
402 	 * clock driver processes PPS information. It processes the pps
403 	 * timestamp and saves the sign-extended fraction in a circular
404 	 * buffer for processing at the next poll event.
405 	 */
406 	peer = pps_peer;
407 	if (peer == 0)		/* nobody home */
408 		return 1;
409 	pp = peer->procptr;
410 	up = (struct atomunit *)pp->unitptr;
411 
412 	/*
413 	 * Convert the timeval to l_fp and save for billboards. Sign-
414 	 * extend the fraction and stash in the buffer. No harm is done
415 	 * if previous data are overwritten. If the discipline comes bum
416 	 * or the data grow stale, just forget it.
417 	 */
418 	up->flags |= FLAG_AUX;
419 	pp->lastrec = *offset;
420 	L_CLR(&lftmp);
421 	L_ADDF(&lftmp, pp->lastrec.l_f);
422 	LFPTOD(&lftmp, doffset);
423 	SAMPLE(-doffset + pp->fudgetime1);
424 	return (0);
425 }
426 
427 /*
428  * atom_poll - called by the transmit procedure
429  */
430 static void
431 atom_poll(
432 	int unit,
433 	struct peer *peer
434 	)
435 {
436 #if defined(PPS) || defined(HAVE_PPSAPI)
437 	register struct atomunit *up;
438 #endif /* PPS || HAVE_PPSAPI */
439 	struct refclockproc *pp;
440 
441 	/*
442 	 * Accumulate samples in the median filter. At the end of each
443 	 * poll interval, do a little bookeeping and process the
444 	 * samples.
445 	 */
446 	pp = peer->procptr;
447 #if defined(PPS) || defined(HAVE_PPSAPI)
448 	up = (struct atomunit *)pp->unitptr;
449 	if (!(up->flags & !(FLAG_AUX | FLAG_TTY))) {
450 		int err;
451 
452 		err = atom_pps(peer);
453 		if (err > 0) {
454 			refclock_report(peer, CEVNT_FAULT);
455 			return;
456 		}
457 	}
458 #endif /* PPS || HAVE_PPSAPI */
459 	pp->polls++;
460 	if (peer->burst > 0)
461 		return;
462 	if (pp->coderecv == pp->codeproc) {
463 		refclock_report(peer, CEVNT_TIMEOUT);
464 		return;
465 	}
466 
467 	/*
468 	 * Valid time (leap bits zero) is returned only if the prefer
469 	 * peer has survived the intersection algorithm and within
470 	 * clock_max of local time and not too long ago.  This ensures
471 	 * the pps time is within +-0.5 s of the local time and the
472 	 * seconds numbering is unambiguous.
473 	 */
474 	if (pps_update) {
475 		pp->leap = LEAP_NOWARNING;
476 	} else {
477 		pp->leap = LEAP_NOTINSYNC;
478 		return;
479 	}
480 	pp->variance = 0;
481 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
482 	refclock_receive(peer);
483 	peer->burst = MAXSTAGE;
484 }
485 
486 #else
487 int refclock_atom_bs;
488 #endif /* REFCLOCK */
489