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 
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 /* 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 
209 typedef 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  */
225 typedef uintptr_t pps_handle_t;
226 
227 /*
228  * create PPS handle from file descriptor
229  */
230 
231 static inline int
232 time_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 
277 static inline int
278 time_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 
301 static inline int
302 time_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 
393 static inline int
394 time_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 
424 static inline int
425 time_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 
451 static inline int
452 time_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 
546 static inline int
547 time_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