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