timepps-Solaris.h revision 290001
1/***********************************************************************
2 *								       *
3 * Copyright (c) David L. Mills 1999-2009			       *
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 Solaris (2.6 and above).		       *
25 *								       *
26 ***********************************************************************
27 *								       *
28 * A full PPSAPI interface to the Solaris 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/* Solaris version, TIOCGPPSEV and TIOCSPPS assumed to exist. */
50
51#ifndef _SYS_TIMEPPS_H_
52#define _SYS_TIMEPPS_H_
53
54#include <termios.h>	/* to get TOCGPPSEV and TIOCSPPS */
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
133typedef unsigned long pps_seq_t;	/* sequence number */
134
135typedef struct ntp_fp {
136	unsigned int	integral;
137	unsigned int	fractional;
138} ntp_fp_t;				/* NTP-compatible time stamp */
139
140typedef 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
150typedef 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
168typedef 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/* addition of NTP fixed-point format */
182
183#define NTPFP_M_ADD(r_i, r_f, a_i, a_f) 	/* r += a */ \
184	do { \
185		register u_int32 lo_tmp; \
186		register u_int32 hi_tmp; \
187		\
188		lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
189		hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
190		if (lo_tmp & 0x10000) \
191			hi_tmp++; \
192		(r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
193		\
194		(r_i) += (a_i); \
195		if (hi_tmp & 0x10000) \
196			(r_i)++; \
197	} while (0)
198
199#define	NTPFP_L_ADDS(r, a)	NTPFP_M_ADD((r)->integral, (r)->fractional, \
200					    (int)(a)->integral, (a)->fractional)
201
202/*
203 * The following definitions are architecture-dependent
204 */
205
206#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
207#define PPS_RO	(PPS_CANWAIT | PPS_CANPOLL)
208
209typedef struct {
210	int filedes;		/* file descriptor */
211	pps_params_t params;	/* PPS parameters set by user */
212} pps_unit_t;
213
214/*
215 *------ Here begins the implementation-specific part! ------
216 */
217
218#include <errno.h>
219
220/*
221 * pps handlebars, which are required to be an opaque scalar.  This
222 * implementation uses the handle as a pointer so it must be large
223 * enough.  uintptr_t is as large as a pointer.
224 */
225typedef uintptr_t pps_handle_t;
226
227/*
228 * create PPS handle from file descriptor
229 */
230
231static inline int
232time_pps_create(
233	int filedes,		/* file descriptor */
234	pps_handle_t *handle	/* returned handle */
235	)
236{
237	pps_unit_t *punit;
238	int one = 1;
239
240	/*
241	 * Check for valid arguments and attach PPS signal.
242	 */
243
244	if (!handle) {
245		errno = EFAULT;
246		return (-1);	/* null pointer */
247	}
248
249	if (ioctl(filedes, TIOCSPPS, &one) < 0) {
250		perror("refclock_ioctl: TIOCSPPS failed:");
251		return (-1);
252	}
253
254	/*
255	 * Allocate and initialize default unit structure.
256	 */
257
258	punit = malloc(sizeof(*punit));
259	if (NULL == punit) {
260		errno = ENOMEM;
261		return (-1);	/* what, no memory? */
262	}
263
264	memset(punit, 0, sizeof(*punit));
265	punit->filedes = filedes;
266	punit->params.api_version = PPS_API_VERS_1;
267	punit->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
268
269	*handle = (pps_handle_t)punit;
270	return (0);
271}
272
273/*
274 * release PPS handle
275 */
276
277static inline int
278time_pps_destroy(
279	pps_handle_t handle
280	)
281{
282	pps_unit_t *punit;
283
284	/*
285	 * Check for valid arguments and detach PPS signal.
286	 */
287
288	if (!handle) {
289		errno = EBADF;
290		return (-1);	/* bad handle */
291	}
292	punit = (pps_unit_t *)handle;
293	free(punit);
294	return (0);
295}
296
297/*
298 * set parameters for handle
299 */
300
301static inline int
302time_pps_setparams(
303	pps_handle_t handle,
304	const pps_params_t *params
305	)
306{
307	pps_unit_t *	punit;
308	int		mode, mode_in;
309	/*
310	 * Check for valid arguments and set parameters.
311	 */
312
313	if (!handle) {
314		errno = EBADF;
315		return (-1);	/* bad handle */
316	}
317
318	if (!params) {
319		errno = EFAULT;
320		return (-1);	/* bad argument */
321	}
322
323	/*
324	 * There was no reasonable consensu in the API working group.
325	 * I require `api_version' to be set!
326	 */
327
328	if (params->api_version != PPS_API_VERS_1) {
329		errno = EINVAL;
330		return(-1);
331	}
332
333	/*
334	 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
335	 */
336
337	mode_in = params->mode;
338	punit = (pps_unit_t *)handle;
339
340	/*
341	 * Only one of the time formats may be selected
342	 * if a nonzero assert offset is supplied.
343	 */
344	if ((mode_in & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) ==
345	    (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
346
347		if (punit->params.assert_offset.tv_sec ||
348			punit->params.assert_offset.tv_nsec) {
349
350			errno = EINVAL;
351			return(-1);
352		}
353
354		/*
355		 * If no offset was specified but both time
356		 * format flags are used consider it harmless
357		 * but turn off PPS_TSFMT_NTPFP so getparams
358		 * will not show both formats lit.
359		 */
360		mode_in &= ~PPS_TSFMT_NTPFP;
361	}
362
363	/* turn off read-only bits */
364
365	mode_in &= ~PPS_RO;
366
367	/*
368	 * test remaining bits, should only have captureassert,
369	 * offsetassert, and/or timestamp format bits.
370	 */
371
372	if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
373			PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
374		errno = EOPNOTSUPP;
375		return(-1);
376	}
377
378	/*
379	 * ok, ready to go.
380	 */
381
382	mode = punit->params.mode;
383	memcpy(&punit->params, params, sizeof(punit->params));
384	punit->params.api_version = PPS_API_VERS_1;
385	punit->params.mode = mode | mode_in;
386	return (0);
387}
388
389/*
390 * get parameters for handle
391 */
392
393static inline int
394time_pps_getparams(
395	pps_handle_t handle,
396	pps_params_t *params
397	)
398{
399	pps_unit_t *	punit;
400
401	/*
402	 * Check for valid arguments and get parameters.
403	 */
404
405	if (!handle) {
406		errno = EBADF;
407		return (-1);	/* bad handle */
408	}
409
410	if (!params) {
411		errno = EFAULT;
412		return (-1);	/* bad argument */
413	}
414
415	punit = (pps_unit_t *)handle;
416	memcpy(params, &punit->params, sizeof(*params));
417	return (0);
418}
419
420/*
421 * get capabilities for handle
422 */
423
424static inline int
425time_pps_getcap(
426	pps_handle_t handle,
427	int *mode
428	)
429{
430	/*
431	 * Check for valid arguments and get capabilities.
432	 */
433
434	if (!handle) {
435		errno = EBADF;
436		return (-1);	/* bad handle */
437	}
438
439	if (!mode) {
440		errno = EFAULT;
441		return (-1);	/* bad argument */
442	}
443	*mode = PPS_CAP;
444	return (0);
445}
446
447/*
448 * Fetch timestamps
449 */
450
451static inline int
452time_pps_fetch(
453	pps_handle_t handle,
454	const int tsformat,
455	pps_info_t *ppsinfo,
456	const struct timespec *timeout
457	)
458{
459	struct ppsclockev {
460		struct timeval tv;
461		u_int serial;
462	} ev;
463
464	pps_info_t	infobuf;
465	pps_unit_t *	punit;
466
467	/*
468	 * Check for valid arguments and fetch timestamps
469	 */
470
471	if (!handle) {
472		errno = EBADF;
473		return (-1);	/* bad handle */
474	}
475
476	if (!ppsinfo) {
477		errno = EFAULT;
478		return (-1);	/* bad argument */
479	}
480
481	/*
482	 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
483	 * ignore the timeout variable.
484	 */
485
486	memset(&infobuf, 0, sizeof(infobuf));
487	punit = (pps_unit_t *)handle;
488
489	/*
490	 * if not captureassert, nothing to return.
491	 */
492
493	if (!punit->params.mode & PPS_CAPTUREASSERT) {
494		memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo));
495		return (0);
496	}
497
498	if (ioctl(punit->filedes, TIOCGPPSEV, (caddr_t) &ev) < 0) {
499		perror("time_pps_fetch:");
500		errno = EOPNOTSUPP;
501		return(-1);
502	}
503
504	infobuf.assert_sequence = ev.serial;
505	infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec;
506	infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000;
507
508	/*
509	 * Translate to specified format then apply offset
510	 */
511
512	switch (tsformat) {
513	case PPS_TSFMT_TSPEC:
514		/* timespec format requires no conversion */
515		if (punit->params.mode & PPS_OFFSETASSERT) {
516			infobuf.assert_timestamp.tv_sec  +=
517				punit->params.assert_offset.tv_sec;
518			infobuf.assert_timestamp.tv_nsec +=
519				punit->params.assert_offset.tv_nsec;
520			PPS_NORMALIZE(infobuf.assert_timestamp);
521		}
522		break;
523
524	case PPS_TSFMT_NTPFP:
525		/* NTP format requires conversion to fraction form */
526		PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
527		if (punit->params.mode & PPS_OFFSETASSERT)
528			NTPFP_L_ADDS(&infobuf.assert_timestamp_ntpfp,
529				     &punit->params.assert_offset_ntpfp);
530		break;
531
532	default:
533		errno = EINVAL;
534		return (-1);
535	}
536
537	infobuf.current_mode = punit->params.mode;
538	memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo));
539	return (0);
540}
541
542/*
543 * specify kernel consumer
544 */
545
546static inline int
547time_pps_kcbind(
548	pps_handle_t handle,
549	const int kernel_consumer,
550	const int edge,
551	const int tsformat
552	)
553{
554	/*
555	 * Check for valid arguments and bind kernel consumer
556	 */
557	if (!handle) {
558		errno = EBADF;
559		return (-1);	/* bad handle */
560	}
561	if (geteuid() != 0) {
562		errno = EPERM;
563		return (-1);	/* must be superuser */
564	}
565	errno = EOPNOTSUPP;
566	return(-1);
567}
568
569#endif /* _SYS_TIMEPPS_H_ */
570