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