1 /* $NetBSD: timepps-SCO.h,v 1.5 2020/05/25 20:47:20 christos Exp $ */
2
3 /***********************************************************************
4 * *
5 * Copyright (c) David L. Mills 1999-2000 *
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 SCO Unix. *
27 * *
28 ***********************************************************************
29 * *
30 * A full PPSAPI interface to the SCO Unix 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 /*SCO UNIX version, TIOCDCDTIMESTAMP assumed to exist. */
52
53 #ifndef _SYS_TIMEPPS_H_
54 #define _SYS_TIMEPPS_H_
55
56 #include <termios.h> /* to get TIOCDCDTIMESTAMP */
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 /*
184 * The following definitions are architecture-dependent
185 */
186
187 #define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
188 #define PPS_RO (PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
189
190 typedef struct {
191 int filedes; /* file descriptor */
192 pps_params_t params; /* PPS parameters set by user */
193 struct timeval tv_save;
194 pps_seq_t serial;
195 } pps_unit_t;
196
197 typedef pps_unit_t* pps_handle_t; /* pps handlebars */
198
199 /*
200 *------ Here begins the implementation-specific part! ------
201 */
202
203 #include <errno.h>
204
205 /*
206 * create PPS handle from file descriptor
207 */
208
209 static inline int
time_pps_create(int filedes,pps_handle_t * handle)210 time_pps_create(
211 int filedes, /* file descriptor */
212 pps_handle_t *handle /* returned handle */
213 )
214 {
215 int one = 1;
216
217 /*
218 * Check for valid arguments and attach PPS signal.
219 */
220
221 if (!handle) {
222 errno = EFAULT;
223 return (-1); /* null pointer */
224 }
225
226 /*
227 * Allocate and initialize default unit structure.
228 */
229
230 *handle = malloc(sizeof(pps_unit_t));
231 if (!(*handle)) {
232 errno = EBADF;
233 return (-1); /* what, no memory? */
234 }
235
236 memset(*handle, 0, sizeof(pps_unit_t));
237 (*handle)->filedes = filedes;
238 (*handle)->params.api_version = PPS_API_VERS_1;
239 (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
240 return (0);
241 }
242
243 /*
244 * release PPS handle
245 */
246
247 static inline int
time_pps_destroy(pps_handle_t handle)248 time_pps_destroy(
249 pps_handle_t handle
250 )
251 {
252 /*
253 * Check for valid arguments and detach PPS signal.
254 */
255
256 if (!handle) {
257 errno = EBADF;
258 return (-1); /* bad handle */
259 }
260 free(handle);
261 return (0);
262 }
263
264 /*
265 * set parameters for handle
266 */
267
268 static inline int
time_pps_setparams(pps_handle_t handle,const pps_params_t * params)269 time_pps_setparams(
270 pps_handle_t handle,
271 const pps_params_t *params
272 )
273 {
274 int mode, mode_in;
275 /*
276 * Check for valid arguments and set parameters.
277 */
278
279 if (!handle) {
280 errno = EBADF;
281 return (-1); /* bad handle */
282 }
283
284 if (!params) {
285 errno = EFAULT;
286 return (-1); /* bad argument */
287 }
288
289 /*
290 * There was no reasonable consensu in the API working group.
291 * I require `api_version' to be set!
292 */
293
294 if (params->api_version != PPS_API_VERS_1) {
295 errno = EINVAL;
296 return(-1);
297 }
298
299 /*
300 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
301 */
302
303 mode_in = params->mode;
304
305 /* turn off read-only bits */
306
307 mode_in &= ~PPS_RO;
308
309 /* test remaining bits, should only have captureassert and/or offsetassert */
310
311 if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) {
312 errno = EOPNOTSUPP;
313 return(-1);
314 }
315
316 /*
317 * ok, ready to go.
318 */
319
320 mode = handle->params.mode;
321 memcpy(&handle->params, params, sizeof(pps_params_t));
322 handle->params.api_version = PPS_API_VERS_1;
323 handle->params.mode = mode | mode_in;
324 return (0);
325 }
326
327 /*
328 * get parameters for handle
329 */
330
331 static inline int
time_pps_getparams(pps_handle_t handle,pps_params_t * params)332 time_pps_getparams(
333 pps_handle_t handle,
334 pps_params_t *params
335 )
336 {
337 /*
338 * Check for valid arguments and get parameters.
339 */
340
341 if (!handle) {
342 errno = EBADF;
343 return (-1); /* bad handle */
344 }
345
346 if (!params) {
347 errno = EFAULT;
348 return (-1); /* bad argument */
349 }
350
351 memcpy(params, &handle->params, sizeof(pps_params_t));
352 return (0);
353 }
354
355 /* (
356 * get capabilities for handle
357 */
358
359 static inline int
time_pps_getcap(pps_handle_t handle,int * mode)360 time_pps_getcap(
361 pps_handle_t handle,
362 int *mode
363 )
364 {
365 /*
366 * Check for valid arguments and get capabilities.
367 */
368
369 if (!handle) {
370 errno = EBADF;
371 return (-1); /* bad handle */
372 }
373
374 if (!mode) {
375 errno = EFAULT;
376 return (-1); /* bad argument */
377 }
378 *mode = PPS_CAP;
379 return (0);
380 }
381
382 /*
383 * Fetch timestamps
384 */
385
386 static inline int
time_pps_fetch(pps_handle_t handle,const int tsformat,pps_info_t * ppsinfo,const struct timespec * timeout)387 time_pps_fetch(
388 pps_handle_t handle,
389 const int tsformat,
390 pps_info_t *ppsinfo,
391 const struct timespec *timeout
392 )
393 {
394 struct timeval tv;
395 pps_info_t infobuf;
396
397 /*
398 * Check for valid arguments and fetch timestamps
399 */
400
401 if (!handle) {
402 errno = EBADF;
403 return (-1); /* bad handle */
404 }
405
406 if (!ppsinfo) {
407 errno = EFAULT;
408 return (-1); /* bad argument */
409 }
410
411 /*
412 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
413 * ignore the timeout variable.
414 */
415
416 memset(&infobuf, 0, sizeof(infobuf));
417
418 /*
419 * if not captureassert, nothing to return.
420 */
421
422 if (!handle->params.mode & PPS_CAPTUREASSERT) {
423 memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
424 return (0);
425 }
426
427 if (ioctl(instance->filedes, TIOCDCDTIMESTAMP, &tv) < 0) {
428 perror("time_pps_fetch:");
429 errno = EOPNOTSUPP;
430 return(-1);
431 }
432
433 /*
434 * fake serial here
435 */
436
437 if (tv.tv_sec != handle->tv_save.tv_sec || tv.tv_usec != handle->tv_save.tv_usec) {
438 handle->tv_save = tv;
439 handle->serial++;
440 }
441
442 /*
443 * Apply offsets as specified. Note that only assert timestamps
444 * are captured by this interface.
445 */
446
447 infobuf.assert_sequence = handle->serial;
448 infobuf.assert_timestamp.tv_sec = tv.tv_sec;
449 infobuf.assert_timestamp.tv_nsec = tv.tv_usec * 1000;
450
451 if (handle->params.mode & PPS_OFFSETASSERT) {
452 infobuf.assert_timestamp.tv_sec += handle->params.assert_offset.tv_sec;
453 infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec;
454 PPS_NORMALIZE(infobuf.assert_timestamp);
455 }
456
457 /*
458 * Translate to specified format
459 */
460
461 switch (tsformat) {
462 case PPS_TSFMT_TSPEC:
463 break; /* timespec format requires no translation */
464
465 case PPS_TSFMT_NTPFP: /* NTP format requires conversion to fraction form */
466 PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
467 break;
468
469 default:
470 errno = EINVAL;
471 return (-1);
472 }
473
474 infobuf.current_mode = handle->params.mode;
475 memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
476 return (0);
477 }
478
479 /*
480 * specify kernel consumer
481 */
482
483 static inline int
time_pps_kcbind(pps_handle_t handle,const int kernel_consumer,const int edge,const int tsformat)484 time_pps_kcbind(
485 pps_handle_t handle,
486 const int kernel_consumer,
487 const int edge, const int tsformat
488 )
489 {
490 /*
491 * Check for valid arguments and bind kernel consumer
492 */
493 if (!handle) {
494 errno = EBADF;
495 return (-1); /* bad handle */
496 }
497 if (geteuid() != 0) {
498 errno = EPERM;
499 return (-1); /* must be superuser */
500 }
501 errno = EOPNOTSUPP;
502 return(-1);
503 }
504
505 #endif /* _SYS_TIMEPPS_H_ */
506