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