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