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