]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_atom.c
This commit was generated by cvs2svn to compensate for changes in r154182,
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_atom.c
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 */