1181834Sroberto/***********************************************************************
2181834Sroberto *								       *
3181834Sroberto * Copyright (c) David L. Mills 1999-2000			       *
4181834Sroberto *								       *
5181834Sroberto * Permission to use, copy, modify, and distribute this software and   *
6181834Sroberto * its documentation for any purpose and without fee is hereby	       *
7181834Sroberto * granted, provided that the above copyright notice appears in all    *
8181834Sroberto * copies and that both the copyright notice and this permission       *
9181834Sroberto * notice appear in supporting documentation, and that the name        *
10181834Sroberto * University of Delaware not be used in advertising or publicity      *
11181834Sroberto * pertaining to distribution of the software without specific,        *
12181834Sroberto * written prior permission. The University of Delaware makes no       *
13181834Sroberto * representations about the suitability this software for any	       *
14181834Sroberto * purpose. It is provided "as is" without express or implied          *
15181834Sroberto * warranty.							       *
16181834Sroberto *								       *
17181834Sroberto ***********************************************************************
18181834Sroberto *								       *
19181834Sroberto * This header file complies with "Pulse-Per-Second API for UNIX-like  *
20181834Sroberto * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul  *
21181834Sroberto * and Marc Brett, from whom much of this code was shamelessly stolen. *
22181834Sroberto *								       *
23181834Sroberto * this modified timepps.h can be used to provide a PPSAPI interface   *
24181834Sroberto * to a machine running SunOS.					       *
25181834Sroberto *								       *
26181834Sroberto ***********************************************************************
27181834Sroberto *								       *
28181834Sroberto * A full PPSAPI interface to the SunOS kernel would be better, but    *
29181834Sroberto * this at least removes the necessity for special coding from the NTP *
30181834Sroberto * NTP drivers. 						       *
31181834Sroberto *								       *
32181834Sroberto ***********************************************************************
33181834Sroberto *								       *
34181834Sroberto * Some of this include file					       *
35181834Sroberto * Copyright (c) 1999 by Ulrich Windl,				       *
36181834Sroberto *	based on code by Reg Clemens <reg@dwf.com>		       *
37181834Sroberto *		based on code by Poul-Henning Kamp <phk@FreeBSD.org>   *
38181834Sroberto *								       *
39181834Sroberto ***********************************************************************
40181834Sroberto *								       *
41181834Sroberto * "THE BEER-WARE LICENSE" (Revision 42):                              *
42181834Sroberto * <phk@FreeBSD.org> wrote this file.  As long as you retain this      *
43181834Sroberto * notice you can do whatever you want with this stuff. If we meet some*
44181834Sroberto * day, and you think this stuff is worth it, you can buy me a beer    *
45181834Sroberto * in return.	Poul-Henning Kamp				       *
46181834Sroberto *								       *
47181834Sroberto **********************************************************************/
48181834Sroberto
49181834Sroberto/* SunOS version, CIOGETEV assumed to exist for SunOS */
50181834Sroberto
51181834Sroberto#ifndef _SYS_TIMEPPS_H_
52181834Sroberto#define _SYS_TIMEPPS_H_
53181834Sroberto
54181834Sroberto#include <termios.h>	/* to get CIOGETEV */
55181834Sroberto
56181834Sroberto/* Implementation note: the logical states ``assert'' and ``clear''
57181834Sroberto * are implemented in terms of the UART register, i.e. ``assert''
58181834Sroberto * means the bit is set.
59181834Sroberto */
60181834Sroberto
61181834Sroberto/*
62181834Sroberto * The following definitions are architecture independent
63181834Sroberto */
64181834Sroberto
65181834Sroberto#define PPS_API_VERS_1	1		/* API version number */
66181834Sroberto#define PPS_JAN_1970	2208988800UL	/* 1970 - 1900 in seconds */
67181834Sroberto#define PPS_NANOSECOND	1000000000L	/* one nanosecond in decimal */
68181834Sroberto#define PPS_FRAC	4294967296.	/* 2^32 as a double */
69181834Sroberto
70181834Sroberto#define PPS_NORMALIZE(x)	/* normalize timespec */ \
71181834Sroberto	do { \
72181834Sroberto		if ((x).tv_nsec >= PPS_NANOSECOND) { \
73181834Sroberto			(x).tv_nsec -= PPS_NANOSECOND; \
74181834Sroberto			(x).tv_sec++; \
75181834Sroberto		} else if ((x).tv_nsec < 0) { \
76181834Sroberto			(x).tv_nsec += PPS_NANOSECOND; \
77181834Sroberto			(x).tv_sec--; \
78181834Sroberto		} \
79181834Sroberto	} while (0)
80181834Sroberto
81181834Sroberto#define PPS_TSPECTONTP(x)	/* convert timespec to l_fp */ \
82181834Sroberto	do { \
83181834Sroberto		double d_temp; \
84181834Sroberto	\
85181834Sroberto		(x).integral += (unsigned int)PPS_JAN_1970; \
86181834Sroberto		d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \
87181834Sroberto		if (d_temp >= PPS_FRAC) \
88181834Sroberto			(x).integral++; \
89181834Sroberto		(x).fractional = (unsigned int)d_temp; \
90181834Sroberto	} while (0)
91181834Sroberto
92181834Sroberto/*
93181834Sroberto * Device/implementation parameters (mode)
94181834Sroberto */
95181834Sroberto
96181834Sroberto#define PPS_CAPTUREASSERT	0x01	/* capture assert events */
97181834Sroberto#define PPS_CAPTURECLEAR	0x02	/* capture clear events */
98181834Sroberto#define PPS_CAPTUREBOTH 	0x03	/* capture assert and clear events */
99181834Sroberto
100181834Sroberto#define PPS_OFFSETASSERT	0x10	/* apply compensation for assert ev. */
101181834Sroberto#define PPS_OFFSETCLEAR 	0x20	/* apply compensation for clear ev. */
102181834Sroberto#define PPS_OFFSETBOTH		0x30	/* apply compensation for both */
103181834Sroberto
104181834Sroberto#define PPS_CANWAIT		0x100	/* Can we wait for an event? */
105181834Sroberto#define PPS_CANPOLL		0x200	/* "This bit is reserved for */
106181834Sroberto
107181834Sroberto/*
108181834Sroberto * Kernel actions (mode)
109181834Sroberto */
110181834Sroberto
111181834Sroberto#define PPS_ECHOASSERT		0x40	/* feed back assert event to output */
112181834Sroberto#define PPS_ECHOCLEAR		0x80	/* feed back clear event to output */
113181834Sroberto
114181834Sroberto/*
115181834Sroberto * Timestamp formats (tsformat)
116181834Sroberto */
117181834Sroberto
118181834Sroberto#define PPS_TSFMT_TSPEC 	0x1000	/* select timespec format */
119181834Sroberto#define PPS_TSFMT_NTPFP 	0x2000	/* select NTP format */
120181834Sroberto
121181834Sroberto/*
122181834Sroberto * Kernel discipline actions (not used in SunOS)
123181834Sroberto */
124181834Sroberto
125181834Sroberto#define PPS_KC_HARDPPS		0	/* enable kernel consumer */
126181834Sroberto#define PPS_KC_HARDPPS_PLL	1	/* phase-lock mode */
127181834Sroberto#define PPS_KC_HARDPPS_FLL	2	/* frequency-lock mode */
128181834Sroberto
129181834Sroberto/*
130181834Sroberto * Type definitions
131181834Sroberto */
132181834Sroberto
133181834Srobertotypedef unsigned long pps_seq_t;	/* sequence number */
134181834Sroberto
135181834Srobertotypedef struct ntp_fp {
136181834Sroberto	unsigned int	integral;
137181834Sroberto	unsigned int	fractional;
138181834Sroberto} ntp_fp_t;				/* NTP-compatible time stamp */
139181834Sroberto
140181834Srobertotypedef union pps_timeu {		/* timestamp format */
141181834Sroberto	struct timespec tspec;
142181834Sroberto	ntp_fp_t	ntpfp;
143181834Sroberto	unsigned long	longpad[3];
144181834Sroberto} pps_timeu_t;				/* generic data type to represent time stamps */
145181834Sroberto
146181834Sroberto/*
147181834Sroberto * Timestamp information structure
148181834Sroberto */
149181834Sroberto
150181834Srobertotypedef struct pps_info {
151181834Sroberto	pps_seq_t	assert_sequence;	/* seq. num. of assert event */
152181834Sroberto	pps_seq_t	clear_sequence; 	/* seq. num. of clear event */
153181834Sroberto	pps_timeu_t	assert_tu;		/* time of assert event */
154181834Sroberto	pps_timeu_t	clear_tu;		/* time of clear event */
155181834Sroberto	int		current_mode;		/* current mode bits */
156181834Sroberto} pps_info_t;
157181834Sroberto
158181834Sroberto#define assert_timestamp	assert_tu.tspec
159181834Sroberto#define clear_timestamp 	clear_tu.tspec
160181834Sroberto
161181834Sroberto#define assert_timestamp_ntpfp	assert_tu.ntpfp
162181834Sroberto#define clear_timestamp_ntpfp	clear_tu.ntpfp
163181834Sroberto
164181834Sroberto/*
165181834Sroberto * Parameter structure
166181834Sroberto */
167181834Sroberto
168181834Srobertotypedef struct pps_params {
169181834Sroberto	int		api_version;	/* API version # */
170181834Sroberto	int		mode;		/* mode bits */
171181834Sroberto	pps_timeu_t assert_off_tu;	/* offset compensation for assert */
172181834Sroberto	pps_timeu_t clear_off_tu;	/* offset compensation for clear */
173181834Sroberto} pps_params_t;
174181834Sroberto
175181834Sroberto#define assert_offset		assert_off_tu.tspec
176181834Sroberto#define clear_offset		clear_off_tu.tspec
177181834Sroberto
178181834Sroberto#define assert_offset_ntpfp	assert_off_tu.ntpfp
179181834Sroberto#define clear_offset_ntpfp	clear_off_tu.ntpfp
180181834Sroberto
181181834Sroberto/*
182181834Sroberto * The following definitions are architecture-dependent
183181834Sroberto */
184181834Sroberto
185181834Sroberto#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
186181834Sroberto#define PPS_RO	(PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
187181834Sroberto
188181834Srobertotypedef struct {
189181834Sroberto	int filedes;		/* file descriptor */
190181834Sroberto	pps_params_t params;	/* PPS parameters set by user */
191181834Sroberto} pps_unit_t;
192181834Sroberto
193181834Srobertotypedef pps_unit_t* pps_handle_t; /* pps handlebars */
194181834Sroberto
195181834Sroberto/*
196181834Sroberto *------ Here begins the implementation-specific part! ------
197181834Sroberto */
198181834Sroberto
199181834Sroberto#include <errno.h>
200181834Sroberto
201181834Sroberto/*
202181834Sroberto * create PPS handle from file descriptor
203181834Sroberto */
204181834Sroberto
205181834Srobertostatic inline int
206181834Srobertotime_pps_create(
207181834Sroberto	int filedes,		/* file descriptor */
208181834Sroberto	pps_handle_t *handle	/* returned handle */
209181834Sroberto	)
210181834Sroberto{
211181834Sroberto	/*
212181834Sroberto	 * Check for valid arguments and attach PPS signal.
213181834Sroberto	 */
214181834Sroberto
215181834Sroberto	if (!handle) {
216181834Sroberto		errno = EFAULT;
217181834Sroberto		return (-1);	/* null pointer */
218181834Sroberto	}
219181834Sroberto
220181834Sroberto	if (ioctl(filedes, I_PUSH, "ppsclock") < 0) {
221181834Sroberto		perror("time_pps_create: I_PUSH ppsclock failed");
222181834Sroberto		return (-1);
223181834Sroberto	}
224181834Sroberto
225181834Sroberto	/*
226181834Sroberto	 * Allocate and initialize default unit structure.
227181834Sroberto	 */
228181834Sroberto
229181834Sroberto	*handle = malloc(sizeof(pps_unit_t));
230181834Sroberto	if (!(*handle)) {
231181834Sroberto		errno = EBADF;
232181834Sroberto		return (-1);	/* what, no memory? */
233181834Sroberto	}
234181834Sroberto
235181834Sroberto	memset(*handle, 0, sizeof(pps_unit_t));
236181834Sroberto	(*handle)->filedes = filedes;
237181834Sroberto	(*handle)->params.api_version = PPS_API_VERS_1;
238181834Sroberto	(*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
239181834Sroberto	return (0);
240181834Sroberto}
241181834Sroberto
242181834Sroberto/*
243181834Sroberto * release PPS handle
244181834Sroberto */
245181834Sroberto
246181834Srobertostatic inline int
247181834Srobertotime_pps_destroy(
248181834Sroberto	pps_handle_t handle
249181834Sroberto	)
250181834Sroberto{
251181834Sroberto	/*
252181834Sroberto	 * Check for valid arguments and detach PPS signal.
253181834Sroberto	 */
254181834Sroberto
255181834Sroberto	if (!handle) {
256181834Sroberto		errno = EBADF;
257181834Sroberto		return (-1);	/* bad handle */
258181834Sroberto	}
259181834Sroberto	free(handle);
260181834Sroberto	return (0);
261181834Sroberto}
262181834Sroberto
263181834Sroberto/*
264181834Sroberto * set parameters for handle
265181834Sroberto */
266181834Sroberto
267181834Srobertostatic inline int
268181834Srobertotime_pps_setparams(
269181834Sroberto	pps_handle_t handle,
270181834Sroberto	const pps_params_t *params
271181834Sroberto	)
272181834Sroberto{
273181834Sroberto	int	mode, mode_in;
274181834Sroberto	/*
275181834Sroberto	 * Check for valid arguments and set parameters.
276181834Sroberto	 */
277181834Sroberto
278181834Sroberto	if (!handle) {
279181834Sroberto		errno = EBADF;
280181834Sroberto		return (-1);	/* bad handle */
281181834Sroberto	}
282181834Sroberto
283181834Sroberto	if (!params) {
284181834Sroberto		errno = EFAULT;
285181834Sroberto		return (-1);	/* bad argument */
286181834Sroberto	}
287181834Sroberto
288181834Sroberto	/*
289181834Sroberto	 * There was no reasonable consensu in the API working group.
290181834Sroberto	 * I require `api_version' to be set!
291181834Sroberto	 */
292181834Sroberto
293181834Sroberto	if (params->api_version != PPS_API_VERS_1) {
294181834Sroberto		errno = EINVAL;
295181834Sroberto		return(-1);
296181834Sroberto	}
297181834Sroberto
298181834Sroberto	/*
299181834Sroberto	 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
300181834Sroberto	 */
301181834Sroberto
302181834Sroberto	mode_in = params->mode;
303181834Sroberto
304181834Sroberto	/* turn off read-only bits */
305181834Sroberto
306181834Sroberto	mode_in &= ~PPS_RO;
307181834Sroberto
308181834Sroberto	/* test remaining bits, should only have captureassert and/or offsetassert */
309181834Sroberto
310181834Sroberto	if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) {
311181834Sroberto		errno = EOPNOTSUPP;
312181834Sroberto		return(-1);
313181834Sroberto	}
314181834Sroberto
315181834Sroberto	/*
316181834Sroberto	 * ok, ready to go.
317181834Sroberto	 */
318181834Sroberto
319181834Sroberto	mode = handle->params.mode;
320181834Sroberto	memcpy(&handle->params, params, sizeof(pps_params_t));
321181834Sroberto	handle->params.api_version = PPS_API_VERS_1;
322181834Sroberto	handle->params.mode = mode | mode_in;
323181834Sroberto	return (0);
324181834Sroberto}
325181834Sroberto
326181834Sroberto/*
327181834Sroberto * get parameters for handle
328181834Sroberto */
329181834Sroberto
330181834Srobertostatic inline int
331181834Srobertotime_pps_getparams(
332181834Sroberto	pps_handle_t handle,
333181834Sroberto	pps_params_t *params
334181834Sroberto	)
335181834Sroberto{
336181834Sroberto	/*
337181834Sroberto	 * Check for valid arguments and get parameters.
338181834Sroberto	 */
339181834Sroberto
340181834Sroberto	if (!handle) {
341181834Sroberto		errno = EBADF;
342181834Sroberto		return (-1);	/* bad handle */
343181834Sroberto	}
344181834Sroberto
345181834Sroberto	if (!params) {
346181834Sroberto		errno = EFAULT;
347181834Sroberto		return (-1);	/* bad argument */
348181834Sroberto	}
349181834Sroberto
350181834Sroberto	memcpy(params, &handle->params, sizeof(pps_params_t));
351181834Sroberto	return (0);
352181834Sroberto}
353181834Sroberto
354181834Sroberto/* (
355181834Sroberto * get capabilities for handle
356181834Sroberto */
357181834Sroberto
358181834Srobertostatic inline int
359181834Srobertotime_pps_getcap(
360181834Sroberto	pps_handle_t handle,
361181834Sroberto	int *mode
362181834Sroberto	)
363181834Sroberto{
364181834Sroberto	/*
365181834Sroberto	 * Check for valid arguments and get capabilities.
366181834Sroberto	 */
367181834Sroberto
368181834Sroberto	if (!handle) {
369181834Sroberto		errno = EBADF;
370181834Sroberto		return (-1);	/* bad handle */
371181834Sroberto	}
372181834Sroberto
373181834Sroberto	if (!mode) {
374181834Sroberto		errno = EFAULT;
375181834Sroberto		return (-1);	/* bad argument */
376181834Sroberto	}
377181834Sroberto	*mode = PPS_CAP;
378181834Sroberto	return (0);
379181834Sroberto}
380181834Sroberto
381181834Sroberto/*
382181834Sroberto * Fetch timestamps
383181834Sroberto */
384181834Sroberto
385181834Srobertostatic inline int
386181834Srobertotime_pps_fetch(
387181834Sroberto	pps_handle_t handle,
388181834Sroberto	const int tsformat,
389181834Sroberto	pps_info_t *ppsinfo,
390181834Sroberto	const struct timespec *timeout
391181834Sroberto	)
392181834Sroberto{
393181834Sroberto	struct ppsclockev {
394181834Sroberto		struct timeval tv;
395181834Sroberto		u_int serial;
396181834Sroberto	} ev;
397181834Sroberto	pps_info_t infobuf;
398181834Sroberto
399181834Sroberto	/*
400181834Sroberto	 * Check for valid arguments and fetch timestamps
401181834Sroberto	 */
402181834Sroberto
403181834Sroberto	if (!handle) {
404181834Sroberto		errno = EBADF;
405181834Sroberto		return (-1);	/* bad handle */
406181834Sroberto	}
407181834Sroberto
408181834Sroberto	if (!ppsinfo) {
409181834Sroberto		errno = EFAULT;
410181834Sroberto		return (-1);	/* bad argument */
411181834Sroberto	}
412181834Sroberto
413181834Sroberto	/*
414181834Sroberto	 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
415181834Sroberto	 * ignore the timeout variable.
416181834Sroberto	 */
417181834Sroberto
418181834Sroberto	memset(&infobuf, 0, sizeof(infobuf));
419181834Sroberto
420181834Sroberto	/*
421181834Sroberto	 * if not captureassert, nothing to return.
422181834Sroberto	 */
423181834Sroberto
424181834Sroberto	if (!handle->params.mode & PPS_CAPTUREASSERT) {
425181834Sroberto		memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
426181834Sroberto		return (0);
427181834Sroberto	}
428181834Sroberto
429181834Sroberto#if defined(__STDC__)
430181834Sroberto#define CIOGETEV	_IOR('C', 0, struct ppsclockev) /* get last pps event */
431181834Sroberto#else
432181834Sroberto#define CIOGETEV	_IOR(C, 0, struct ppsclockev)	/* get last pps event */
433181834Sroberto#endif
434181834Sroberto
435181834Sroberto	if (ioctl(handle->filedes, CIOGETEV, (caddr_t) &ev) < 0) {
436181834Sroberto		perror("time_pps_fetch:");
437181834Sroberto		errno = EOPNOTSUPP;
438181834Sroberto		return(-1);
439181834Sroberto	}
440181834Sroberto
441181834Sroberto	/*
442181834Sroberto	 * Apply offsets as specified. Note that only assert timestamps
443181834Sroberto	 * are captured by this interface.
444181834Sroberto	 */
445181834Sroberto
446181834Sroberto	infobuf.assert_sequence = ev.serial;
447181834Sroberto	infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec;
448181834Sroberto	infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000;
449181834Sroberto
450181834Sroberto	if (handle->params.mode & PPS_OFFSETASSERT) {
451181834Sroberto		infobuf.assert_timestamp.tv_sec  += handle->params.assert_offset.tv_sec;
452181834Sroberto		infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec;
453181834Sroberto		PPS_NORMALIZE(infobuf.assert_timestamp);
454181834Sroberto	}
455181834Sroberto
456181834Sroberto	/*
457181834Sroberto	 * Translate to specified format
458181834Sroberto	 */
459181834Sroberto
460181834Sroberto	switch (tsformat) {
461181834Sroberto	case PPS_TSFMT_TSPEC:
462181834Sroberto		break;		 /* timespec format requires no translation */
463181834Sroberto
464181834Sroberto	case PPS_TSFMT_NTPFP:	/* NTP format requires conversion to fraction form */
465181834Sroberto		PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
466181834Sroberto		break;
467181834Sroberto
468181834Sroberto	default:
469181834Sroberto		errno = EINVAL;
470181834Sroberto		return (-1);
471181834Sroberto	}
472181834Sroberto
473181834Sroberto	infobuf.current_mode = handle->params.mode;
474181834Sroberto	memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
475181834Sroberto	return (0);
476181834Sroberto}
477181834Sroberto
478181834Sroberto/*
479181834Sroberto * specify kernel consumer
480181834Sroberto */
481181834Sroberto
482181834Srobertostatic inline int
483181834Srobertotime_pps_kcbind(
484181834Sroberto	pps_handle_t handle,
485181834Sroberto	const int kernel_consumer,
486181834Sroberto	const int edge, const int tsformat
487181834Sroberto	)
488181834Sroberto{
489181834Sroberto	/*
490181834Sroberto	 * Check for valid arguments and bind kernel consumer
491181834Sroberto	 */
492181834Sroberto	if (!handle) {
493181834Sroberto		errno = EBADF;
494181834Sroberto		return (-1);	/* bad handle */
495181834Sroberto	}
496181834Sroberto	if (geteuid() != 0) {
497181834Sroberto		errno = EPERM;
498181834Sroberto		return (-1);	/* must be superuser */
499181834Sroberto	}
500181834Sroberto	errno = EOPNOTSUPP;
501181834Sroberto	return(-1);
502181834Sroberto}
503181834Sroberto
504181834Sroberto#endif /* _SYS_TIMEPPS_H_ */
505