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