1 /***********************************************************************
2 * *
3 * Copyright (c) David L. Mills 1999-2009 *
4 * *
5 * Permission to use, copy, modify, and distribute this software and *
6 * its documentation for any purpose and with or without fee is hereby *
7 * granted, provided that the above copyright notice appears in all *
8 * copies and that both the copyright notice and this permission *
9 * notice appear in supporting documentation, and that the name *
10 * University of Delaware not be used in advertising or publicity *
11 * pertaining to distribution of the software without specific, *
12 * written prior permission. The University of Delaware makes no *
13 * representations about the suitability this software for any *
14 * purpose. It is provided "as is" without express or implied *
15 * warranty. *
16 * *
17 ***********************************************************************
18 * *
19 * This header file complies with "Pulse-Per-Second API for UNIX-like *
20 * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul *
21 * and Marc Brett, from whom much of this code was shamelessly stolen. *
22 * *
23 * this modified timepps.h can be used to provide a PPSAPI interface *
24 * to a machine running Solaris (2.6 and above). *
25 * *
26 ***********************************************************************
27 * *
28 * A full PPSAPI interface to the Solaris kernel would be better, but *
29 * this at least removes the necessity for special coding from the NTP *
30 * NTP drivers. *
31 * *
32 ***********************************************************************
33 * *
34 * Some of this include file *
35 * Copyright (c) 1999 by Ulrich Windl, *
36 * based on code by Reg Clemens <reg@dwf.com> *
37 * based on code by Poul-Henning Kamp <phk@FreeBSD.org> *
38 * *
39 ***********************************************************************
40 * *
41 * "THE BEER-WARE LICENSE" (Revision 42): *
42 * <phk@FreeBSD.org> wrote this file. As long as you retain this *
43 * notice you can do whatever you want with this stuff. If we meet some*
44 * day, and you think this stuff is worth it, you can buy me a beer *
45 * in return. Poul-Henning Kamp *
46 * *
47 **********************************************************************/
48
49 /* Solaris version, TIOCGPPSEV and TIOCSPPS assumed to exist. */
50
51 #ifndef _SYS_TIMEPPS_H_
52 #define _SYS_TIMEPPS_H_
53
54 #include <termios.h> /* to get TOCGPPSEV and TIOCSPPS */
55
56 /* Implementation note: the logical states ``assert'' and ``clear''
57 * are implemented in terms of the UART register, i.e. ``assert''
58 * means the bit is set.
59 */
60
61 /*
62 * The following definitions are architecture independent
63 */
64
65 #define PPS_API_VERS_1 1 /* API version number */
66 #define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
67 #define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */
68 #define PPS_FRAC 4294967296. /* 2^32 as a double */
69
70 #define PPS_NORMALIZE(x) /* normalize timespec */ \
71 do { \
72 if ((x).tv_nsec >= PPS_NANOSECOND) { \
73 (x).tv_nsec -= PPS_NANOSECOND; \
74 (x).tv_sec++; \
75 } else if ((x).tv_nsec < 0) { \
76 (x).tv_nsec += PPS_NANOSECOND; \
77 (x).tv_sec--; \
78 } \
79 } while (0)
80
81 #define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \
82 do { \
83 double d_temp; \
84 \
85 (x).integral += (unsigned int)PPS_JAN_1970; \
86 d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \
87 if (d_temp >= PPS_FRAC) \
88 (x).integral++; \
89 (x).fractional = (unsigned int)d_temp; \
90 } while (0)
91
92 /*
93 * Device/implementation parameters (mode)
94 */
95
96 #define PPS_CAPTUREASSERT 0x01 /* capture assert events */
97 #define PPS_CAPTURECLEAR 0x02 /* capture clear events */
98 #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */
99
100 #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */
101 #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */
102 #define PPS_OFFSETBOTH 0x30 /* apply compensation for both */
103
104 #define PPS_CANWAIT 0x100 /* Can we wait for an event? */
105 #define PPS_CANPOLL 0x200 /* "This bit is reserved for */
106
107 /*
108 * Kernel actions (mode)
109 */
110
111 #define PPS_ECHOASSERT 0x40 /* feed back assert event to output */
112 #define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */
113
114 /*
115 * Timestamp formats (tsformat)
116 */
117
118 #define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */
119 #define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */
120
121 /*
122 * Kernel discipline actions (not used in Solaris)
123 */
124
125 #define PPS_KC_HARDPPS 0 /* enable kernel consumer */
126 #define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */
127 #define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */
128
129 /*
130 * Type definitions
131 */
132
133 typedef unsigned long pps_seq_t; /* sequence number */
134
135 typedef struct ntp_fp {
136 unsigned int integral;
137 unsigned int fractional;
138 } ntp_fp_t; /* NTP-compatible time stamp */
139
140 typedef union pps_timeu { /* timestamp format */
141 struct timespec tspec;
142 ntp_fp_t ntpfp;
143 unsigned long longpad[3];
144 } pps_timeu_t; /* generic data type to represent time stamps */
145
146 /*
147 * Timestamp information structure
148 */
149
150 typedef struct pps_info {
151 pps_seq_t assert_sequence; /* seq. num. of assert event */
152 pps_seq_t clear_sequence; /* seq. num. of clear event */
153 pps_timeu_t assert_tu; /* time of assert event */
154 pps_timeu_t clear_tu; /* time of clear event */
155 int current_mode; /* current mode bits */
156 } pps_info_t;
157
158 #define assert_timestamp assert_tu.tspec
159 #define clear_timestamp clear_tu.tspec
160
161 #define assert_timestamp_ntpfp assert_tu.ntpfp
162 #define clear_timestamp_ntpfp clear_tu.ntpfp
163
164 /*
165 * Parameter structure
166 */
167
168 typedef struct pps_params {
169 int api_version; /* API version # */
170 int mode; /* mode bits */
171 pps_timeu_t assert_off_tu; /* offset compensation for assert */
172 pps_timeu_t clear_off_tu; /* offset compensation for clear */
173 } pps_params_t;
174
175 #define assert_offset assert_off_tu.tspec
176 #define clear_offset clear_off_tu.tspec
177
178 #define assert_offset_ntpfp assert_off_tu.ntpfp
179 #define clear_offset_ntpfp clear_off_tu.ntpfp
180
181 /* addition of NTP fixed-point format */
182
183 #define NTPFP_M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \
184 do { \
185 register u_int32 lo_tmp; \
186 register u_int32 hi_tmp; \
187 \
188 lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
189 hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
190 if (lo_tmp & 0x10000) \
191 hi_tmp++; \
192 (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
193 \
194 (r_i) += (a_i); \
195 if (hi_tmp & 0x10000) \
196 (r_i)++; \
197 } while (0)
198
199 #define NTPFP_L_ADDS(r, a) NTPFP_M_ADD((r)->integral, (r)->fractional, \
200 (int)(a)->integral, (a)->fractional)
201
202 /*
203 * The following definitions are architecture-dependent
204 */
205
206 #define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
207 #define PPS_RO (PPS_CANWAIT | PPS_CANPOLL)
208
209 typedef struct {
210 int filedes; /* file descriptor */
211 pps_params_t params; /* PPS parameters set by user */
212 } pps_unit_t;
213
214 /*
215 *------ Here begins the implementation-specific part! ------
216 */
217
218 #include <errno.h>
219
220 /*
221 * pps handlebars, which are required to be an opaque scalar. This
222 * implementation uses the handle as a pointer so it must be large
223 * enough. uintptr_t is as large as a pointer.
224 */
225 typedef uintptr_t pps_handle_t;
226
227 /*
228 * create PPS handle from file descriptor
229 */
230
231 static inline int
time_pps_create(int filedes,pps_handle_t * handle)232 time_pps_create(
233 int filedes, /* file descriptor */
234 pps_handle_t *handle /* returned handle */
235 )
236 {
237 pps_unit_t *punit;
238 int one = 1;
239
240 /*
241 * Check for valid arguments and attach PPS signal.
242 */
243
244 if (!handle) {
245 errno = EFAULT;
246 return (-1); /* null pointer */
247 }
248
249 if (ioctl(filedes, TIOCSPPS, &one) < 0) {
250 perror("refclock_ioctl: TIOCSPPS failed:");
251 return (-1);
252 }
253
254 /*
255 * Allocate and initialize default unit structure.
256 */
257
258 punit = malloc(sizeof(*punit));
259 if (NULL == punit) {
260 errno = ENOMEM;
261 return (-1); /* what, no memory? */
262 }
263
264 memset(punit, 0, sizeof(*punit));
265 punit->filedes = filedes;
266 punit->params.api_version = PPS_API_VERS_1;
267 punit->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
268
269 *handle = (pps_handle_t)punit;
270 return (0);
271 }
272
273 /*
274 * release PPS handle
275 */
276
277 static inline int
time_pps_destroy(pps_handle_t handle)278 time_pps_destroy(
279 pps_handle_t handle
280 )
281 {
282 pps_unit_t *punit;
283
284 /*
285 * Check for valid arguments and detach PPS signal.
286 */
287
288 if (!handle) {
289 errno = EBADF;
290 return (-1); /* bad handle */
291 }
292 punit = (pps_unit_t *)handle;
293 free(punit);
294 return (0);
295 }
296
297 /*
298 * set parameters for handle
299 */
300
301 static inline int
time_pps_setparams(pps_handle_t handle,const pps_params_t * params)302 time_pps_setparams(
303 pps_handle_t handle,
304 const pps_params_t *params
305 )
306 {
307 pps_unit_t * punit;
308 int mode, mode_in;
309 /*
310 * Check for valid arguments and set parameters.
311 */
312
313 if (!handle) {
314 errno = EBADF;
315 return (-1); /* bad handle */
316 }
317
318 if (!params) {
319 errno = EFAULT;
320 return (-1); /* bad argument */
321 }
322
323 /*
324 * There was no reasonable consensu in the API working group.
325 * I require `api_version' to be set!
326 */
327
328 if (params->api_version != PPS_API_VERS_1) {
329 errno = EINVAL;
330 return(-1);
331 }
332
333 /*
334 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
335 */
336
337 mode_in = params->mode;
338 punit = (pps_unit_t *)handle;
339
340 /*
341 * Only one of the time formats may be selected
342 * if a nonzero assert offset is supplied.
343 */
344 if ((mode_in & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) ==
345 (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
346
347 if (punit->params.assert_offset.tv_sec ||
348 punit->params.assert_offset.tv_nsec) {
349
350 errno = EINVAL;
351 return(-1);
352 }
353
354 /*
355 * If no offset was specified but both time
356 * format flags are used consider it harmless
357 * but turn off PPS_TSFMT_NTPFP so getparams
358 * will not show both formats lit.
359 */
360 mode_in &= ~PPS_TSFMT_NTPFP;
361 }
362
363 /* turn off read-only bits */
364
365 mode_in &= ~PPS_RO;
366
367 /*
368 * test remaining bits, should only have captureassert,
369 * offsetassert, and/or timestamp format bits.
370 */
371
372 if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
373 PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
374 errno = EOPNOTSUPP;
375 return(-1);
376 }
377
378 /*
379 * ok, ready to go.
380 */
381
382 mode = punit->params.mode;
383 memcpy(&punit->params, params, sizeof(punit->params));
384 punit->params.api_version = PPS_API_VERS_1;
385 punit->params.mode = mode | mode_in;
386 return (0);
387 }
388
389 /*
390 * get parameters for handle
391 */
392
393 static inline int
time_pps_getparams(pps_handle_t handle,pps_params_t * params)394 time_pps_getparams(
395 pps_handle_t handle,
396 pps_params_t *params
397 )
398 {
399 pps_unit_t * punit;
400
401 /*
402 * Check for valid arguments and get parameters.
403 */
404
405 if (!handle) {
406 errno = EBADF;
407 return (-1); /* bad handle */
408 }
409
410 if (!params) {
411 errno = EFAULT;
412 return (-1); /* bad argument */
413 }
414
415 punit = (pps_unit_t *)handle;
416 memcpy(params, &punit->params, sizeof(*params));
417 return (0);
418 }
419
420 /*
421 * get capabilities for handle
422 */
423
424 static inline int
time_pps_getcap(pps_handle_t handle,int * mode)425 time_pps_getcap(
426 pps_handle_t handle,
427 int *mode
428 )
429 {
430 /*
431 * Check for valid arguments and get capabilities.
432 */
433
434 if (!handle) {
435 errno = EBADF;
436 return (-1); /* bad handle */
437 }
438
439 if (!mode) {
440 errno = EFAULT;
441 return (-1); /* bad argument */
442 }
443 *mode = PPS_CAP;
444 return (0);
445 }
446
447 /*
448 * Fetch timestamps
449 */
450
451 static inline int
time_pps_fetch(pps_handle_t handle,const int tsformat,pps_info_t * ppsinfo,const struct timespec * timeout)452 time_pps_fetch(
453 pps_handle_t handle,
454 const int tsformat,
455 pps_info_t *ppsinfo,
456 const struct timespec *timeout
457 )
458 {
459 struct ppsclockev {
460 struct timeval tv;
461 u_int serial;
462 } ev;
463
464 pps_info_t infobuf;
465 pps_unit_t * punit;
466
467 /*
468 * Check for valid arguments and fetch timestamps
469 */
470
471 if (!handle) {
472 errno = EBADF;
473 return (-1); /* bad handle */
474 }
475
476 if (!ppsinfo) {
477 errno = EFAULT;
478 return (-1); /* bad argument */
479 }
480
481 /*
482 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
483 * ignore the timeout variable.
484 */
485
486 memset(&infobuf, 0, sizeof(infobuf));
487 punit = (pps_unit_t *)handle;
488
489 /*
490 * if not captureassert, nothing to return.
491 */
492
493 if (!punit->params.mode & PPS_CAPTUREASSERT) {
494 memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo));
495 return (0);
496 }
497
498 if (ioctl(punit->filedes, TIOCGPPSEV, (caddr_t) &ev) < 0) {
499 perror("time_pps_fetch:");
500 errno = EOPNOTSUPP;
501 return(-1);
502 }
503
504 infobuf.assert_sequence = ev.serial;
505 infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec;
506 infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000;
507
508 /*
509 * Translate to specified format then apply offset
510 */
511
512 switch (tsformat) {
513 case PPS_TSFMT_TSPEC:
514 /* timespec format requires no conversion */
515 if (punit->params.mode & PPS_OFFSETASSERT) {
516 infobuf.assert_timestamp.tv_sec +=
517 punit->params.assert_offset.tv_sec;
518 infobuf.assert_timestamp.tv_nsec +=
519 punit->params.assert_offset.tv_nsec;
520 PPS_NORMALIZE(infobuf.assert_timestamp);
521 }
522 break;
523
524 case PPS_TSFMT_NTPFP:
525 /* NTP format requires conversion to fraction form */
526 PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
527 if (punit->params.mode & PPS_OFFSETASSERT)
528 NTPFP_L_ADDS(&infobuf.assert_timestamp_ntpfp,
529 &punit->params.assert_offset_ntpfp);
530 break;
531
532 default:
533 errno = EINVAL;
534 return (-1);
535 }
536
537 infobuf.current_mode = punit->params.mode;
538 memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo));
539 return (0);
540 }
541
542 /*
543 * specify kernel consumer
544 */
545
546 static inline int
time_pps_kcbind(pps_handle_t handle,const int kernel_consumer,const int edge,const int tsformat)547 time_pps_kcbind(
548 pps_handle_t handle,
549 const int kernel_consumer,
550 const int edge,
551 const int tsformat
552 )
553 {
554 /*
555 * Check for valid arguments and bind kernel consumer
556 */
557 if (!handle) {
558 errno = EBADF;
559 return (-1); /* bad handle */
560 }
561 if (geteuid() != 0) {
562 errno = EPERM;
563 return (-1); /* must be superuser */
564 }
565 errno = EOPNOTSUPP;
566 return(-1);
567 }
568
569 #endif /* _SYS_TIMEPPS_H_ */
570