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