1226586Sdim/***********************************************************************
2226586Sdim *								       *
3226586Sdim * Copyright (c) David L. Mills 1999-2000			       *
4226586Sdim *								       *
5226586Sdim * Permission to use, copy, modify, and distribute this software and   *
6226586Sdim * its documentation for any purpose and without fee is hereby	       *
7226586Sdim * granted, provided that the above copyright notice appears in all    *
8226586Sdim * copies and that both the copyright notice and this permission       *
9226586Sdim * notice appear in supporting documentation, and that the name        *
10226586Sdim * University of Delaware not be used in advertising or publicity      *
11226586Sdim * pertaining to distribution of the software without specific,        *
12226586Sdim * written prior permission. The University of Delaware makes no       *
13226586Sdim * representations about the suitability this software for any	       *
14249423Sdim * purpose. It is provided "as is" without express or implied          *
15226586Sdim * warranty.							       *
16226586Sdim *								       *
17226586Sdim ***********************************************************************
18226586Sdim *								       *
19226586Sdim * This header file complies with "Pulse-Per-Second API for UNIX-like  *
20226586Sdim * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul  *
21226586Sdim * and Marc Brett, from whom much of this code was shamelessly stolen. *
22226586Sdim *								       *
23226586Sdim * this modified timepps.h can be used to provide a PPSAPI interface   *
24226586Sdim * to a machine running Solaris (2.6 and above).		       *
25226586Sdim *								       *
26226586Sdim ***********************************************************************
27226586Sdim *								       *
28226586Sdim * A full PPSAPI interface to the Solaris kernel would be better, but  *
29226586Sdim * this at least removes the necessity for special coding from the NTP *
30226586Sdim * NTP drivers. 						       *
31226586Sdim *								       *
32226586Sdim ***********************************************************************
33226586Sdim *								       *
34288943Sdim * Some of this include file					       *
35226586Sdim * Copyright (c) 1999 by Ulrich Windl,				       *
36226586Sdim *	based on code by Reg Clemens <reg@dwf.com>		       *
37226586Sdim *		based on code by Poul-Henning Kamp <phk@FreeBSD.org>   *
38234353Sdim *								       *
39234353Sdim ***********************************************************************
40234353Sdim *								       *
41234353Sdim * "THE BEER-WARE LICENSE" (Revision 42):                              *
42226586Sdim * <phk@FreeBSD.org> wrote this file.  As long as you retain this      *
43226586Sdim * notice you can do whatever you want with this stuff. If we meet some*
44226586Sdim * day, and you think this stuff is worth it, you can buy me a beer    *
45226586Sdim * in return.	Poul-Henning Kamp				       *
46226586Sdim *								       *
47226586Sdim **********************************************************************/
48234353Sdim
49243830Sdim/* Solaris version, TIOCGPPSEV and TIOCSPPS assumed to exist. */
50276479Sdim
51276479Sdim#ifndef _SYS_TIMEPPS_H_
52226586Sdim#define _SYS_TIMEPPS_H_
53226586Sdim
54226586Sdim#include <termios.h>	/* to get TOCGPPSEV and TIOCSPPS */
55239462Sdim
56234353Sdim/* Implementation note: the logical states ``assert'' and ``clear''
57234353Sdim * are implemented in terms of the UART register, i.e. ``assert''
58226586Sdim * means the bit is set.
59226586Sdim */
60226586Sdim
61226586Sdim/*
62243830Sdim * The following definitions are architecture independent
63239462Sdim */
64239462Sdim
65239462Sdim#define PPS_API_VERS_1	1		/* API version number */
66239462Sdim#define PPS_JAN_1970	2208988800UL	/* 1970 - 1900 in seconds */
67239462Sdim#define PPS_NANOSECOND	1000000000L	/* one nanosecond in decimal */
68239462Sdim#define PPS_FRAC	4294967296.	/* 2^32 as a double */
69239462Sdim
70243830Sdim#define PPS_NORMALIZE(x)	/* normalize timespec */ \
71249423Sdim	do { \
72251662Sdim		if ((x).tv_nsec >= PPS_NANOSECOND) { \
73251662Sdim			(x).tv_nsec -= PPS_NANOSECOND; \
74243830Sdim			(x).tv_sec++; \
75243830Sdim		} else if ((x).tv_nsec < 0) { \
76239462Sdim			(x).tv_nsec += PPS_NANOSECOND; \
77249423Sdim			(x).tv_sec--; \
78251662Sdim		} \
79251662Sdim	} while (0)
80243830Sdim
81243830Sdim#define PPS_TSPECTONTP(x)	/* convert timespec to l_fp */ \
82239462Sdim	do { \
83239462Sdim		double d_temp; \
84239462Sdim	\
85276479Sdim		(x).integral += (unsigned int)PPS_JAN_1970; \
86276479Sdim		d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \
87276479Sdim		if (d_temp >= PPS_FRAC) \
88276479Sdim			(x).integral++; \
89276479Sdim		(x).fractional = (unsigned int)d_temp; \
90276479Sdim	} while (0)
91226586Sdim
92226586Sdim/*
93226586Sdim * Device/implementation parameters (mode)
94226586Sdim */
95234353Sdim
96226586Sdim#define PPS_CAPTUREASSERT	0x01	/* capture assert events */
97226586Sdim#define PPS_CAPTURECLEAR	0x02	/* capture clear events */
98226586Sdim#define PPS_CAPTUREBOTH 	0x03	/* capture assert and clear events */
99234353Sdim
100234353Sdim#define PPS_OFFSETASSERT	0x10	/* apply compensation for assert ev. */
101226586Sdim#define PPS_OFFSETCLEAR 	0x20	/* apply compensation for clear ev. */
102226586Sdim#define PPS_OFFSETBOTH		0x30	/* apply compensation for both */
103226586Sdim
104226586Sdim#define PPS_CANWAIT		0x100	/* Can we wait for an event? */
105226586Sdim#define PPS_CANPOLL		0x200	/* "This bit is reserved for */
106226586Sdim
107226586Sdim/*
108226586Sdim * Kernel actions (mode)
109226586Sdim */
110226586Sdim
111226586Sdim#define PPS_ECHOASSERT		0x40	/* feed back assert event to output */
112226586Sdim#define PPS_ECHOCLEAR		0x80	/* feed back clear event to output */
113226586Sdim
114226586Sdim/*
115226586Sdim * Timestamp formats (tsformat)
116226586Sdim */
117226586Sdim
118226586Sdim#define PPS_TSFMT_TSPEC 	0x1000	/* select timespec format */
119226586Sdim#define PPS_TSFMT_NTPFP 	0x2000	/* select NTP format */
120226586Sdim
121226586Sdim/*
122226586Sdim * Kernel discipline actions (not used in Solaris)
123226586Sdim */
124226586Sdim
125234353Sdim#define PPS_KC_HARDPPS		0	/* enable kernel consumer */
126226586Sdim#define PPS_KC_HARDPPS_PLL	1	/* phase-lock mode */
127226586Sdim#define PPS_KC_HARDPPS_FLL	2	/* frequency-lock mode */
128226586Sdim
129226586Sdim/*
130226586Sdim * Type definitions
131234353Sdim */
132234353Sdim
133226586Sdimtypedef unsigned long pps_seq_t;	/* sequence number */
134226586Sdim
135226586Sdimtypedef struct ntp_fp {
136226586Sdim	unsigned int	integral;
137226586Sdim	unsigned int	fractional;
138226586Sdim} ntp_fp_t;				/* NTP-compatible time stamp */
139226586Sdim
140226586Sdimtypedef union pps_timeu {		/* timestamp format */
141226586Sdim	struct timespec tspec;
142226586Sdim	ntp_fp_t	ntpfp;
143226586Sdim	unsigned long	longpad[3];
144226586Sdim} pps_timeu_t;				/* generic data type to represent time stamps */
145226586Sdim
146226586Sdim/*
147226586Sdim * Timestamp information structure
148226586Sdim */
149226586Sdim
150226586Sdimtypedef struct pps_info {
151226586Sdim	pps_seq_t	assert_sequence;	/* seq. num. of assert event */
152226586Sdim	pps_seq_t	clear_sequence; 	/* seq. num. of clear event */
153226586Sdim	pps_timeu_t	assert_tu;		/* time of assert event */
154226586Sdim	pps_timeu_t	clear_tu;		/* time of clear event */
155226586Sdim	int		current_mode;		/* current mode bits */
156226586Sdim} pps_info_t;
157234353Sdim
158226586Sdim#define assert_timestamp	assert_tu.tspec
159226586Sdim#define clear_timestamp 	clear_tu.tspec
160226586Sdim
161276479Sdim#define assert_timestamp_ntpfp	assert_tu.ntpfp
162243830Sdim#define clear_timestamp_ntpfp	clear_tu.ntpfp
163226586Sdim
164226586Sdim/*
165226586Sdim * Parameter structure
166226586Sdim */
167226586Sdim
168226586Sdimtypedef struct pps_params {
169226586Sdim	int		api_version;	/* API version # */
170226586Sdim	int		mode;		/* mode bits */
171226586Sdim	pps_timeu_t assert_off_tu;	/* offset compensation for assert */
172226586Sdim	pps_timeu_t clear_off_tu;	/* offset compensation for clear */
173226586Sdim} pps_params_t;
174239462Sdim
175234353Sdim#define assert_offset		assert_off_tu.tspec
176226586Sdim#define clear_offset		clear_off_tu.tspec
177234353Sdim
178226586Sdim#define assert_offset_ntpfp	assert_off_tu.ntpfp
179226586Sdim#define clear_offset_ntpfp	clear_off_tu.ntpfp
180226586Sdim
181226586Sdim/*
182226586Sdim * The following definitions are architecture-dependent
183226586Sdim */
184226586Sdim
185226586Sdim#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
186226586Sdim#define PPS_RO	(PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
187226586Sdim
188226586Sdimtypedef struct {
189226586Sdim	int filedes;		/* file descriptor */
190226586Sdim	pps_params_t params;	/* PPS parameters set by user */
191239462Sdim} pps_unit_t;
192239462Sdim
193226586Sdimtypedef pps_unit_t* pps_handle_t; /* pps handlebars */
194261991Sdim
195261991Sdim/*
196226586Sdim *------ Here begins the implementation-specific part! ------
197239462Sdim */
198239462Sdim
199239462Sdim#include <errno.h>
200239462Sdim
201239462Sdim/*
202239462Sdim * create PPS handle from file descriptor
203239462Sdim */
204239462Sdim
205239462Sdimstatic inline int
206239462Sdimtime_pps_create(
207239462Sdim	int filedes,		/* file descriptor */
208239462Sdim	pps_handle_t *handle	/* returned handle */
209239462Sdim	)
210239462Sdim{
211239462Sdim	int one = 1;
212239462Sdim
213239462Sdim	/*
214239462Sdim	 * Check for valid arguments and attach PPS signal.
215239462Sdim	 */
216239462Sdim
217226586Sdim	if (!handle) {
218243830Sdim		errno = EFAULT;
219234353Sdim		return (-1);	/* null pointer */
220239462Sdim	}
221276479Sdim
222276479Sdim	if (ioctl(filedes, TIOCSPPS, &one) < 0) {
223226586Sdim		perror("refclock_ioctl: TIOCSPPS failed:");
224226586Sdim		return (-1);
225226586Sdim	}
226226586Sdim
227226586Sdim	/*
228226586Sdim	 * Allocate and initialize default unit structure.
229226586Sdim	 */
230226586Sdim
231226586Sdim	*handle = malloc(sizeof(pps_unit_t));
232226586Sdim	if (!(*handle)) {
233234353Sdim		errno = EBADF;
234226586Sdim		return (-1);	/* what, no memory? */
235226586Sdim	}
236226586Sdim
237234353Sdim	memset(*handle, 0, sizeof(pps_unit_t));
238234353Sdim	(*handle)->filedes = filedes;
239234353Sdim	(*handle)->params.api_version = PPS_API_VERS_1;
240226586Sdim	(*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
241226586Sdim	return (0);
242226586Sdim}
243226586Sdim
244226586Sdim/*
245226586Sdim * release PPS handle
246226586Sdim */
247226586Sdim
248226586Sdimstatic inline int
249226586Sdimtime_pps_destroy(
250226586Sdim	pps_handle_t handle
251243830Sdim	)
252226586Sdim{
253226586Sdim	/*
254226586Sdim	 * Check for valid arguments and detach PPS signal.
255226586Sdim	 */
256243830Sdim
257243830Sdim	if (!handle) {
258243830Sdim		errno = EBADF;
259226586Sdim		return (-1);	/* bad handle */
260226586Sdim	}
261226586Sdim	free(handle);
262226586Sdim	return (0);
263226586Sdim}
264226586Sdim
265226586Sdim/*
266226586Sdim * set parameters for handle
267226586Sdim */
268226586Sdim
269226586Sdimstatic inline int
270234353Sdimtime_pps_setparams(
271234353Sdim	pps_handle_t handle,
272234353Sdim	const pps_params_t *params
273234353Sdim	)
274234353Sdim{
275234353Sdim	int	mode, mode_in;
276226586Sdim	/*
277226586Sdim	 * Check for valid arguments and set parameters.
278243830Sdim	 */
279243830Sdim
280243830Sdim	if (!handle) {
281243830Sdim		errno = EBADF;
282226586Sdim		return (-1);	/* bad handle */
283234353Sdim	}
284234353Sdim
285234353Sdim	if (!params) {
286234353Sdim		errno = EFAULT;
287234353Sdim		return (-1);	/* bad argument */
288226586Sdim	}
289226586Sdim
290243830Sdim	/*
291243830Sdim	 * There was no reasonable consensu in the API working group.
292243830Sdim	 * I require `api_version' to be set!
293226586Sdim	 */
294226586Sdim
295226586Sdim	if (params->api_version != PPS_API_VERS_1) {
296276479Sdim		errno = EINVAL;
297226586Sdim		return(-1);
298226586Sdim	}
299226586Sdim
300226586Sdim	/*
301226586Sdim	 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
302226586Sdim	 */
303226586Sdim
304226586Sdim	mode_in = params->mode;
305226586Sdim
306226586Sdim	/* turn off read-only bits */
307226586Sdim
308226586Sdim	mode_in &= ~PPS_RO;
309226586Sdim
310226586Sdim	/* test remaining bits, should only have captureassert and/or offsetassert */
311226586Sdim
312226586Sdim	if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) {
313226586Sdim		errno = EOPNOTSUPP;
314226586Sdim		return(-1);
315226586Sdim	}
316226586Sdim
317226586Sdim	/*
318226586Sdim	 * ok, ready to go.
319226586Sdim	 */
320249423Sdim
321261991Sdim	mode = handle->params.mode;
322261991Sdim	memcpy(&handle->params, params, sizeof(pps_params_t));
323226586Sdim	handle->params.api_version = PPS_API_VERS_1;
324234353Sdim	handle->params.mode = mode | mode_in;
325226586Sdim	return (0);
326234353Sdim}
327234353Sdim
328226586Sdim/*
329226586Sdim * get parameters for handle
330226586Sdim */
331226586Sdim
332226586Sdimstatic inline int
333234353Sdimtime_pps_getparams(
334239462Sdim	pps_handle_t handle,
335234353Sdim	pps_params_t *params
336234353Sdim	)
337226586Sdim{
338226586Sdim	/*
339234353Sdim	 * Check for valid arguments and get parameters.
340234353Sdim	 */
341234353Sdim
342234353Sdim	if (!handle) {
343234353Sdim		errno = EBADF;
344234353Sdim		return (-1);	/* bad handle */
345239462Sdim	}
346234353Sdim
347234353Sdim	if (!params) {
348234353Sdim		errno = EFAULT;
349234353Sdim		return (-1);	/* bad argument */
350234353Sdim	}
351234353Sdim
352234353Sdim	memcpy(params, &handle->params, sizeof(pps_params_t));
353234353Sdim	return (0);
354234353Sdim}
355234353Sdim
356234353Sdim/* (
357234353Sdim * get capabilities for handle
358234353Sdim */
359234353Sdim
360234353Sdimstatic inline int
361243830Sdimtime_pps_getcap(
362234353Sdim	pps_handle_t handle,
363234353Sdim	int *mode
364234353Sdim	)
365234353Sdim{
366234353Sdim	/*
367234353Sdim	 * Check for valid arguments and get capabilities.
368234353Sdim	 */
369234353Sdim
370243830Sdim	if (!handle) {
371276479Sdim		errno = EBADF;
372243830Sdim		return (-1);	/* bad handle */
373234353Sdim	}
374234353Sdim
375234353Sdim	if (!mode) {
376234353Sdim		errno = EFAULT;
377234353Sdim		return (-1);	/* bad argument */
378234353Sdim	}
379234353Sdim	*mode = PPS_CAP;
380234353Sdim	return (0);
381243830Sdim}
382243830Sdim
383261991Sdim/*
384243830Sdim * Fetch timestamps
385243830Sdim */
386243830Sdim
387243830Sdimstatic inline int
388234353Sdimtime_pps_fetch(
389226586Sdim	pps_handle_t handle,
390226586Sdim	const int tsformat,
391226586Sdim	pps_info_t *ppsinfo,
392226586Sdim	const struct timespec *timeout
393234353Sdim	)
394261991Sdim{
395226586Sdim	struct ppsclockev {
396226586Sdim		struct timeval tv;
397239462Sdim		u_int serial;
398226586Sdim	} ev;
399276479Sdim
400243830Sdim	pps_info_t infobuf;
401243830Sdim
402243830Sdim	/*
403234353Sdim	 * Check for valid arguments and fetch timestamps
404226586Sdim	 */
405226586Sdim
406226586Sdim	if (!handle) {
407226586Sdim		errno = EBADF;
408226586Sdim		return (-1);	/* bad handle */
409226586Sdim	}
410226586Sdim
411226586Sdim	if (!ppsinfo) {
412226586Sdim		errno = EFAULT;
413243830Sdim		return (-1);	/* bad argument */
414234353Sdim	}
415251662Sdim
416251662Sdim	/*
417251662Sdim	 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
418251662Sdim	 * ignore the timeout variable.
419251662Sdim	 */
420226586Sdim
421251662Sdim	memset(&infobuf, 0, sizeof(infobuf));
422251662Sdim
423251662Sdim	/*
424251662Sdim	 * if not captureassert, nothing to return.
425251662Sdim	 */
426251662Sdim
427239462Sdim	if (!handle->params.mode & PPS_CAPTUREASSERT) {
428251662Sdim		memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
429251662Sdim		return (0);
430251662Sdim	}
431251662Sdim
432251662Sdim	if (ioctl(handle->filedes, TIOCGPPSEV, (caddr_t) &ev) < 0) {
433251662Sdim		perror("time_pps_fetch:");
434251662Sdim		errno = EOPNOTSUPP;
435251662Sdim		return(-1);
436251662Sdim	}
437251662Sdim
438251662Sdim	/*
439251662Sdim	 * Apply offsets as specified. Note that only assert timestamps
440251662Sdim	 * are captured by this interface.
441226586Sdim	 */
442226586Sdim
443226586Sdim	infobuf.assert_sequence = ev.serial;
444226586Sdim	infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec;
445226586Sdim	infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000;
446249423Sdim
447249423Sdim	if (handle->params.mode & PPS_OFFSETASSERT) {
448249423Sdim		infobuf.assert_timestamp.tv_sec  += handle->params.assert_offset.tv_sec;
449234353Sdim		infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec;
450234353Sdim		PPS_NORMALIZE(infobuf.assert_timestamp);
451226586Sdim	}
452234353Sdim
453226586Sdim	/*
454226586Sdim	 * Translate to specified format
455226586Sdim	 */
456226586Sdim
457226586Sdim	switch (tsformat) {
458261991Sdim	case PPS_TSFMT_TSPEC:
459261991Sdim		break;		 /* timespec format requires no translation */
460226586Sdim
461226586Sdim	case PPS_TSFMT_NTPFP:	/* NTP format requires conversion to fraction form */
462226586Sdim		PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
463234353Sdim		break;
464249423Sdim
465249423Sdim	default:
466226586Sdim		errno = EINVAL;
467226586Sdim		return (-1);
468249423Sdim	}
469249423Sdim
470249423Sdim	infobuf.current_mode = handle->params.mode;
471239462Sdim	memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
472234353Sdim	return (0);
473249423Sdim}
474239462Sdim
475239462Sdim/*
476249423Sdim * specify kernel consumer
477239462Sdim */
478239462Sdim
479239462Sdimstatic inline int
480249423Sdimtime_pps_kcbind(
481249423Sdim	pps_handle_t handle,
482249423Sdim	const int kernel_consumer,
483249423Sdim	const int edge, const int tsformat
484249423Sdim	)
485249423Sdim{
486249423Sdim	/*
487239462Sdim	 * Check for valid arguments and bind kernel consumer
488239462Sdim	 */
489239462Sdim	if (!handle) {
490239462Sdim		errno = EBADF;
491239462Sdim		return (-1);	/* bad handle */
492239462Sdim	}
493239462Sdim	if (geteuid() != 0) {
494239462Sdim		errno = EPERM;
495239462Sdim		return (-1);	/* must be superuser */
496239462Sdim	}
497276479Sdim	errno = EOPNOTSUPP;
498243830Sdim	return(-1);
499239462Sdim}
500249423Sdim
501249423Sdim#endif /* _SYS_TIMEPPS_H_ */
502249423Sdim