xref: /freebsd/contrib/ntp/ntpd/refclock_atom.c (revision 2a4a1db342263067035ce69a4017c645da63455d)
1 /*
2  * refclock_atom - clock driver for 1-pps signals
3  */
4 #ifdef HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7 
8 #include <stdio.h>
9 #include <ctype.h>
10 
11 #include "ntpd.h"
12 #include "ntp_io.h"
13 #include "ntp_unixtime.h"
14 #include "ntp_refclock.h"
15 #include "ntp_stdlib.h"
16 
17 #if defined(REFCLOCK) && defined(CLOCK_ATOM)
18 
19 #ifdef HAVE_PPSAPI
20 # ifdef HAVE_TIMEPPS_H
21 #  include <timepps.h>
22 # else
23 #  ifdef HAVE_SYS_TIMEPPS_H
24 #   include <sys/timepps.h>
25 #  endif
26 # endif
27 #endif /* HAVE_PPSAPI */
28 
29 /*
30  * This driver furnishes an interface for pulse-per-second (PPS) signals
31  * produced by a cesium clock, timing receiver or related equipment. It
32  * can be used to remove accumulated jitter and retime a secondary
33  * server when synchronized to a primary server over a congested, wide-
34  * area network and before redistributing the time to local clients.
35  *
36  * Before this driver becomes active, the local clock must be set to
37  * within +-500 ms by another means, such as a radio clock or NTP
38  * itself. There are two ways to connect the PPS signal, normally at TTL
39  * levels, to the computer. One is to shift to EIA levels and connect to
40  * pin 8 (DCD) of a serial port. This requires a level converter and
41  * may require a one-shot flipflop to lengthen the pulse. The other is
42  * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
43  * port. These methods are architecture dependent.
44  *
45  * Both methods require a modified device driver and kernel interface
46  * compatible with the Pulse-per-Second API for Unix-like Operating
47  * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
48  * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
49  * present only the Alpha implementation provides the full generality of
50  * the API with multiple PPS drivers and multiple handles per driver.
51  *
52  * In many configurations a single port is used for the radio timecode
53  * and PPS signal. In order to provide for this configuration and others
54  * involving dedicated multiple serial/parallel ports, the driver first
55  * attempts to open the device /dev/pps%d, where %d is the unit number.
56  * If this fails, the driver attempts to open the device specified by
57  * the pps configuration command. If a port is to be shared, the pps
58  * command must be placed before the radio device(s) and the radio
59  * device(s) must be placed before the PPS driver(s) in the
60  * configuration file.
61  *
62  * This driver normally uses the PLL/FLL clock discipline implemented in
63  * the ntpd code. If kernel support is available, the kernel PLL/FLL
64  * clock discipline is used instead. The default configuration is not to
65  * use the kernel PPS discipline, if present. The kernel PPS discipline
66  * can be enabled using the pps command.
67  *
68  * Fudge Factors
69  *
70  * There are no special fudge factors other than the generic. The fudge
71  * time1 parameter can be used to compensate for miscellaneous device
72  * driver and OS delays.
73  */
74 /*
75  * Interface definitions
76  */
77 #ifdef HAVE_PPSAPI
78 extern int pps_assert;		/* selects rising or falling edge */
79 extern int pps_hardpps;		/* enables the kernel PPS interface */
80 #define DEVICE		"/dev/pps%d" /* device name and unit */
81 #endif /* HAVE_PPSAPI */
82 
83 #define	PRECISION	(-20)	/* precision assumed (about 1 us) */
84 #define	REFID		"PPS\0"	/* reference ID */
85 #define	DESCRIPTION	"PPS Clock Discipline" /* WRU */
86 #define NANOSECOND	1000000000 /* one second (ns) */
87 #define RANGEGATE	500000	/* range gate (ns) */
88 #define ASTAGE		8	/* filter stages */
89 
90 static struct peer *pps_peer;	/* atom driver for PPS sources */
91 
92 #ifdef HAVE_PPSAPI
93 /*
94  * PPS unit control structure
95  */
96 struct ppsunit {
97 	struct timespec ts;	/* last timestamp */
98 	int fddev;		/* pps device descriptor */
99 	pps_params_t pps_params; /* pps parameters */
100 	pps_info_t pps_info;	/* last pps data */
101 	pps_handle_t handle;	/* pps handlebars */
102 };
103 #endif /* HAVE_PPSAPI */
104 
105 /*
106  * Function prototypes
107  */
108 static	int	atom_start	P((int, struct peer *));
109 static	void	atom_poll	P((int, struct peer *));
110 #ifdef HAVE_PPSAPI
111 static	void	atom_shutdown	P((int, struct peer *));
112 static	void	atom_control	P((int, struct refclockstat *, struct
113 				    refclockstat *, struct peer *));
114 static	int	atom_pps	P((struct peer *));
115 static	int	atom_ppsapi	P((struct peer *, int, int));
116 #endif /* HAVE_PPSAPI */
117 
118 /*
119  * Transfer vector
120  */
121 struct	refclock refclock_atom = {
122 	atom_start,		/* start up driver */
123 #ifdef HAVE_PPSAPI
124 	atom_shutdown,		/* shut down driver */
125 #else
126 	noentry,		/* shut down driver */
127 #endif /* HAVE_PPSAPI */
128 	atom_poll,		/* transmit poll message */
129 #ifdef HAVE_PPSAPI
130 	atom_control,		/* fudge control */
131 #else
132 	noentry,		/* fudge control */
133 #endif /* HAVE_PPSAPI */
134 	noentry,		/* initialize driver */
135 	noentry,		/* not used (old atom_buginfo) */
136 	NOFLAGS			/* not used */
137 };
138 
139 
140 /*
141  * atom_start - initialize data for processing
142  */
143 static int
144 atom_start(
145 	int unit,		/* unit number (not used) */
146 	struct peer *peer	/* peer structure pointer */
147 	)
148 {
149 	struct refclockproc *pp;
150 #ifdef HAVE_PPSAPI
151 	register struct ppsunit *up;
152 	char device[80];
153 #endif /* HAVE_PPSAPI */
154 
155 	/*
156 	 * Allocate and initialize unit structure
157 	 */
158 	pps_peer = peer;
159 	pp = peer->procptr;
160 	peer->precision = PRECISION;
161 	pp->clockdesc = DESCRIPTION;
162 	memcpy((char *)&pp->refid, REFID, 4);
163 	peer->burst = ASTAGE;
164 	peer->stratum = STRATUM_UNSPEC;
165 #ifdef HAVE_PPSAPI
166 	up = emalloc(sizeof(struct ppsunit));
167 	memset(up, 0, sizeof(struct ppsunit));
168 	pp->unitptr = (caddr_t)up;
169 
170 	/*
171 	 * Open PPS device. If this fails and some driver has already
172 	 * opened the associated radio device, fdpps has the file
173 	 * descriptor for it.
174 	 */
175 	sprintf(device, DEVICE, unit);
176 	up->fddev = open(device, O_RDWR, 0777);
177 	if (up->fddev <= 0 && fdpps > 0) {
178 		strcpy(device, pps_device);
179 		up->fddev = fdpps;
180 	}
181 	if (up->fddev <= 0) {
182 		msyslog(LOG_ERR,
183 		    "refclock_atom: %s: %m", device);
184 		return (0);
185 	}
186 
187 	/*
188 	 * Light off the PPSAPI interface. If this PPS device is shared
189 	 * with the radio device, take the default options from the pps
190 	 * command. This is for legacy purposes.
191 	 */
192 	if (time_pps_create(up->fddev, &up->handle) < 0) {
193 		msyslog(LOG_ERR,
194 		    "refclock_atom: time_pps_create failed: %m");
195 		return (0);
196 	}
197 	return (atom_ppsapi(peer, pps_assert, pps_hardpps));
198 #else /* HAVE_PPSAPI */
199 	return (1);
200 #endif /* HAVE_PPSAPI */
201 }
202 
203 
204 #ifdef HAVE_PPSAPI
205 /*
206  * atom_control - fudge control
207  */
208 static void
209 atom_control(
210 	int unit,		/* unit (not used */
211 	struct refclockstat *in, /* input parameters (not uded) */
212 	struct refclockstat *out, /* output parameters (not used) */
213 	struct peer *peer	/* peer structure pointer */
214 	)
215 {
216 	struct refclockproc *pp;
217 
218 	pp = peer->procptr;
219 	atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
220 	    pp->sloppyclockflag & CLK_FLAG3);
221 }
222 
223 
224 /*
225  * Initialize PPSAPI
226  */
227 int
228 atom_ppsapi(
229 	struct peer *peer,	/* peer structure pointer */
230 	int enb_clear,		/* clear enable */
231 	int enb_hardpps		/* hardpps enable */
232 	)
233 {
234 	struct refclockproc *pp;
235 	register struct ppsunit *up;
236 	int capability;
237 
238 	pp = peer->procptr;
239 	up = (struct ppsunit *)pp->unitptr;
240 	if (time_pps_getcap(up->handle, &capability) < 0) {
241 		msyslog(LOG_ERR,
242 		    "refclock_atom: time_pps_getcap failed: %m");
243 		return (0);
244 	}
245 	memset(&up->pps_params, 0, sizeof(pps_params_t));
246 	if (enb_clear)
247 		up->pps_params.mode = capability & PPS_CAPTURECLEAR;
248 	else
249 		up->pps_params.mode = capability & PPS_CAPTUREASSERT;
250 	if (!up->pps_params.mode) {
251 		msyslog(LOG_ERR,
252 		    "refclock_atom: invalid capture edge %d",
253 		    pps_assert);
254 		return (0);
255 	}
256 	up->pps_params.mode |= PPS_TSFMT_TSPEC;
257 	if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
258 		msyslog(LOG_ERR,
259 		    "refclock_atom: time_pps_setparams failed: %m");
260 		return (0);
261 	}
262 	if (enb_hardpps) {
263 		if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
264 		    up->pps_params.mode & ~PPS_TSFMT_TSPEC,
265 		    PPS_TSFMT_TSPEC) < 0) {
266 			msyslog(LOG_ERR,
267 			    "refclock_atom: time_pps_kcbind failed: %m");
268 			return (0);
269 		}
270 		pps_enable = 1;
271 	}
272 #if DEBUG
273 	if (debug) {
274 		time_pps_getparams(up->handle, &up->pps_params);
275 		printf(
276 		    "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
277 		    up->fddev, capability, up->pps_params.api_version,
278 		    up->pps_params.mode, enb_hardpps);
279 	}
280 #endif
281 	return (1);
282 }
283 
284 
285 /*
286  * atom_shutdown - shut down the clock
287  */
288 static void
289 atom_shutdown(
290 	int unit,		/* unit number (not used) */
291 	struct peer *peer	/* peer structure pointer */
292 	)
293 {
294 	struct refclockproc *pp;
295 	register struct ppsunit *up;
296 
297 	pp = peer->procptr;
298 	up = (struct ppsunit *)pp->unitptr;
299 	if (up->fddev > 0)
300 		close(up->fddev);
301 	if (up->handle != 0)
302 		time_pps_destroy(up->handle);
303 	if (pps_peer == peer)
304 		pps_peer = 0;
305 	free(up);
306 }
307 
308 
309 /*
310  * atom_pps - receive data from the PPSAPI interface
311  *
312  * This routine is called once per second when the PPSAPI interface is
313  * present. It snatches the PPS timestamp from the kernel and saves the
314  * sign-extended fraction in a circular buffer for processing at the
315  * next poll event.
316  */
317 static int
318 atom_pps(
319 	struct peer *peer	/* peer structure pointer */
320 	)
321 {
322 	register struct ppsunit *up;
323 	struct refclockproc *pp;
324 	pps_info_t pps_info;
325 	struct timespec timeout, ts;
326 	double dtemp;
327 
328 	/*
329 	 * Convert the timespec nanoseconds field to signed double and
330 	 * save in the median filter. for billboards. No harm is done if
331 	 * previous data are overwritten. If the discipline comes bum or
332 	 * the data grow stale, just forget it. A range gate rejects new
333 	 * samples if less than a jiggle time from the next second.
334 	 */
335 	pp = peer->procptr;
336 	up = (struct ppsunit *)pp->unitptr;
337 	if (up->handle == 0)
338 		return (-1);
339 	timeout.tv_sec = 0;
340 	timeout.tv_nsec = 0;
341 	memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
342 	if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
343 	    &timeout) < 0)
344 		return (-1);
345 	if (up->pps_params.mode & PPS_CAPTUREASSERT) {
346 		if (pps_info.assert_sequence ==
347 		    up->pps_info.assert_sequence)
348 			return (1);
349 		ts = up->pps_info.assert_timestamp;
350 	} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
351 		if (pps_info.clear_sequence ==
352 		    up->pps_info.clear_sequence)
353 			return (1);
354 		ts = up->pps_info.clear_timestamp;
355 	} else {
356 		return (-1);
357 	}
358 	if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
359 	    up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
360 	    (ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
361 	    up->ts.tv_nsec < RANGEGATE))) {
362 		up->ts = ts;
363 		return (1);
364 	}
365 	up->ts = ts;
366 	pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
367 	dtemp = ts.tv_nsec * FRAC / 1e9;
368 	if (dtemp >= FRAC)
369 		pp->lastrec.l_ui++;
370 	pp->lastrec.l_uf = (u_int32)dtemp;
371 	if (ts.tv_nsec > NANOSECOND / 2)
372 		ts.tv_nsec -= NANOSECOND;
373 	dtemp = -(double)ts.tv_nsec / NANOSECOND;
374 	SAMPLE(dtemp + pp->fudgetime1);
375 #ifdef DEBUG
376 	if (debug > 1)
377 		printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
378 #endif
379 	return (0);
380 }
381 #endif /* HAVE_PPSAPI */
382 
383 
384 /*
385  * pps_sample - receive PPS data from some other clock driver
386  *
387  * This routine is called once per second when the external clock driver
388  * processes PPS information. It processes the PPS timestamp and saves
389  * the sign-extended fraction in a circular buffer for processing at the
390  * next poll event. This works only for a single PPS device.
391  */
392 int
393 pps_sample(
394 	   l_fp *offset		/* PPS offset */
395 	   )
396 {
397 	register struct peer *peer;
398 	struct refclockproc *pp;
399 	l_fp lftmp;
400 	double doffset;
401 
402 	peer = pps_peer;
403 	if (peer == 0)		/* nobody home */
404 		return (1);
405 	pp = peer->procptr;
406 
407 	/*
408 	 * Convert the timeval to l_fp and save for billboards. Sign-
409 	 * extend the fraction and stash in the buffer. No harm is done
410 	 * if previous data are overwritten. If the discipline comes bum
411 	 * or the data grow stale, just forget it.
412 	 */
413 	pp->lastrec = *offset;
414 	L_CLR(&lftmp);
415 	L_ADDF(&lftmp, pp->lastrec.l_f);
416 	LFPTOD(&lftmp, doffset);
417 	SAMPLE(-doffset + pp->fudgetime1);
418 	return (0);
419 }
420 
421 /*
422  * atom_poll - called by the transmit procedure
423  *
424  * This routine is called once per second when in burst mode to save PPS
425  * sample offsets in the median filter. At the end of the burst period
426  * the samples are processed as a heap and the clock filter updated.
427  */
428 static void
429 atom_poll(
430 	int unit,		/* unit number (not used) */
431 	struct peer *peer	/* peer structure pointer */
432 	)
433 {
434 	struct refclockproc *pp;
435 #ifdef HAVE_PPSAPI
436 	int err;
437 #endif /* HAVE_PPSAPI */
438 
439 	/*
440 	 * Accumulate samples in the median filter. If a noise sample,
441 	 * return with no prejudice; if a protocol error, get mean;
442 	 * otherwise, cool. At the end of each poll interval, do a
443 	 * little bookeeping and process the surviving samples.
444 	 */
445 	pp = peer->procptr;
446 	pp->polls++;
447 #ifdef HAVE_PPSAPI
448 	err = atom_pps(peer);
449 	if (err < 0) {
450 		refclock_report(peer, CEVNT_FAULT);
451 		return;
452 	}
453 #endif /* HAVE_PPSAPI */
454 
455 	/*
456 	 * Valid time is returned only if the prefer peer has survived
457 	 * the intersection algorithm and within clock_max of local time
458 	 * and not too long ago. This ensures the PPS time is within
459 	 * +-0.5 s of the local time and the seconds numbering is
460 	 * unambiguous. Note that the leap bits are set no-warning on
461 	 * the first valid update and the stratum is set at the prefer
462 	 * peer.
463 	 */
464 	if (peer->burst > 0)
465 		return;
466 	peer->stratum = STRATUM_UNSPEC;
467 	if (pp->codeproc == pp->coderecv) {
468 		refclock_report(peer, CEVNT_TIMEOUT);
469 		peer->burst = ASTAGE;
470 		return;
471 
472 	} else if (!sys_prefer) {
473 		pp->codeproc = pp->coderecv;
474 		peer->burst = ASTAGE;
475 		return;
476 
477 	} else if (fabs(sys_prefer->offset) > clock_max) {
478 		pp->codeproc = pp->coderecv;
479 		peer->burst = ASTAGE;
480 		return;
481 	}
482 	peer->stratum = sys_prefer->stratum;
483 	if (peer->stratum <= 1)
484 		peer->refid = pp->refid;
485 	else
486 		peer->refid = peer->srcadr.sin_addr.s_addr;
487 	pp->leap = LEAP_NOWARNING;
488 	refclock_receive(peer);
489 	peer->burst = ASTAGE;
490 }
491 #else
492 int refclock_atom_bs;
493 int
494 pps_sample(
495 	   l_fp *offset		/* PPS offset */
496 	   )
497 {
498 	return 1;
499 }
500 #endif /* REFCLOCK */
501