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 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
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
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
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
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
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
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
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_ */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>