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