1181834Sroberto/***********************************************************************
2181834Sroberto *								       *
3181834Sroberto * Copyright (c) David L. Mills 1999-2000			       *
4181834Sroberto *								       *
5181834Sroberto * Permission to use, copy, modify, and distribute this software and   *
6280849Scy * its documentation for any purpose and with or 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 SCO Unix.				       *
25181834Sroberto *								       *
26181834Sroberto ***********************************************************************
27181834Sroberto *								       *
28181834Sroberto * A full PPSAPI interface to the SCO Unix 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/*SCO UNIX version, TIOCDCDTIMESTAMP assumed to exist. */
50181834Sroberto
51181834Sroberto#ifndef _SYS_TIMEPPS_H_
52181834Sroberto#define _SYS_TIMEPPS_H_
53181834Sroberto
54181834Sroberto#include <termios.h>	/* to get TIOCDCDTIMESTAMP */
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 Solaris)
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	struct timeval	tv_save;
192181834Sroberto	pps_seq_t serial;
193181834Sroberto} pps_unit_t;
194181834Sroberto
195181834Srobertotypedef pps_unit_t* pps_handle_t; /* pps handlebars */
196181834Sroberto
197181834Sroberto/*
198181834Sroberto *------ Here begins the implementation-specific part! ------
199181834Sroberto */
200181834Sroberto
201181834Sroberto#include <errno.h>
202181834Sroberto
203181834Sroberto/*
204181834Sroberto * create PPS handle from file descriptor
205181834Sroberto */
206181834Sroberto
207181834Srobertostatic inline int
208181834Srobertotime_pps_create(
209181834Sroberto	int filedes,		/* file descriptor */
210181834Sroberto	pps_handle_t *handle	/* returned handle */
211181834Sroberto	)
212181834Sroberto{
213181834Sroberto	int one = 1;
214181834Sroberto
215181834Sroberto	/*
216181834Sroberto	 * Check for valid arguments and attach PPS signal.
217181834Sroberto	 */
218181834Sroberto
219181834Sroberto	if (!handle) {
220181834Sroberto		errno = EFAULT;
221181834Sroberto		return (-1);	/* null pointer */
222181834Sroberto	}
223181834Sroberto
224181834Sroberto	/*
225181834Sroberto	 * Allocate and initialize default unit structure.
226181834Sroberto	 */
227181834Sroberto
228181834Sroberto	*handle = malloc(sizeof(pps_unit_t));
229181834Sroberto	if (!(*handle)) {
230181834Sroberto		errno = EBADF;
231181834Sroberto		return (-1);	/* what, no memory? */
232181834Sroberto	}
233181834Sroberto
234181834Sroberto	memset(*handle, 0, sizeof(pps_unit_t));
235181834Sroberto	(*handle)->filedes = filedes;
236181834Sroberto	(*handle)->params.api_version = PPS_API_VERS_1;
237181834Sroberto	(*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
238181834Sroberto	return (0);
239181834Sroberto}
240181834Sroberto
241181834Sroberto/*
242181834Sroberto * release PPS handle
243181834Sroberto */
244181834Sroberto
245181834Srobertostatic inline int
246181834Srobertotime_pps_destroy(
247181834Sroberto	pps_handle_t handle
248181834Sroberto	)
249181834Sroberto{
250181834Sroberto	/*
251181834Sroberto	 * Check for valid arguments and detach PPS signal.
252181834Sroberto	 */
253181834Sroberto
254181834Sroberto	if (!handle) {
255181834Sroberto		errno = EBADF;
256181834Sroberto		return (-1);	/* bad handle */
257181834Sroberto	}
258181834Sroberto	free(handle);
259181834Sroberto	return (0);
260181834Sroberto}
261181834Sroberto
262181834Sroberto/*
263181834Sroberto * set parameters for handle
264181834Sroberto */
265181834Sroberto
266181834Srobertostatic inline int
267181834Srobertotime_pps_setparams(
268181834Sroberto	pps_handle_t handle,
269181834Sroberto	const pps_params_t *params
270181834Sroberto	)
271181834Sroberto{
272181834Sroberto	int	mode, mode_in;
273181834Sroberto	/*
274181834Sroberto	 * Check for valid arguments and set parameters.
275181834Sroberto	 */
276181834Sroberto
277181834Sroberto	if (!handle) {
278181834Sroberto		errno = EBADF;
279181834Sroberto		return (-1);	/* bad handle */
280181834Sroberto	}
281181834Sroberto
282181834Sroberto	if (!params) {
283181834Sroberto		errno = EFAULT;
284181834Sroberto		return (-1);	/* bad argument */
285181834Sroberto	}
286181834Sroberto
287181834Sroberto	/*
288181834Sroberto	 * There was no reasonable consensu in the API working group.
289181834Sroberto	 * I require `api_version' to be set!
290181834Sroberto	 */
291181834Sroberto
292181834Sroberto	if (params->api_version != PPS_API_VERS_1) {
293181834Sroberto		errno = EINVAL;
294181834Sroberto		return(-1);
295181834Sroberto	}
296181834Sroberto
297181834Sroberto	/*
298181834Sroberto	 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
299181834Sroberto	 */
300181834Sroberto
301181834Sroberto	mode_in = params->mode;
302181834Sroberto
303181834Sroberto	/* turn off read-only bits */
304181834Sroberto
305181834Sroberto	mode_in &= ~PPS_RO;
306181834Sroberto
307181834Sroberto	/* test remaining bits, should only have captureassert and/or offsetassert */
308181834Sroberto
309181834Sroberto	if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) {
310181834Sroberto		errno = EOPNOTSUPP;
311181834Sroberto		return(-1);
312181834Sroberto	}
313181834Sroberto
314181834Sroberto	/*
315181834Sroberto	 * ok, ready to go.
316181834Sroberto	 */
317181834Sroberto
318181834Sroberto	mode = handle->params.mode;
319181834Sroberto	memcpy(&handle->params, params, sizeof(pps_params_t));
320181834Sroberto	handle->params.api_version = PPS_API_VERS_1;
321181834Sroberto	handle->params.mode = mode | mode_in;
322181834Sroberto	return (0);
323181834Sroberto}
324181834Sroberto
325181834Sroberto/*
326181834Sroberto * get parameters for handle
327181834Sroberto */
328181834Sroberto
329181834Srobertostatic inline int
330181834Srobertotime_pps_getparams(
331181834Sroberto	pps_handle_t handle,
332181834Sroberto	pps_params_t *params
333181834Sroberto	)
334181834Sroberto{
335181834Sroberto	/*
336181834Sroberto	 * Check for valid arguments and get parameters.
337181834Sroberto	 */
338181834Sroberto
339181834Sroberto	if (!handle) {
340181834Sroberto		errno = EBADF;
341181834Sroberto		return (-1);	/* bad handle */
342181834Sroberto	}
343181834Sroberto
344181834Sroberto	if (!params) {
345181834Sroberto		errno = EFAULT;
346181834Sroberto		return (-1);	/* bad argument */
347181834Sroberto	}
348181834Sroberto
349181834Sroberto	memcpy(params, &handle->params, sizeof(pps_params_t));
350181834Sroberto	return (0);
351181834Sroberto}
352181834Sroberto
353181834Sroberto/* (
354181834Sroberto * get capabilities for handle
355181834Sroberto */
356181834Sroberto
357181834Srobertostatic inline int
358181834Srobertotime_pps_getcap(
359181834Sroberto	pps_handle_t handle,
360181834Sroberto	int *mode
361181834Sroberto	)
362181834Sroberto{
363181834Sroberto	/*
364181834Sroberto	 * Check for valid arguments and get capabilities.
365181834Sroberto	 */
366181834Sroberto
367181834Sroberto	if (!handle) {
368181834Sroberto		errno = EBADF;
369181834Sroberto		return (-1);	/* bad handle */
370181834Sroberto	}
371181834Sroberto
372181834Sroberto	if (!mode) {
373181834Sroberto		errno = EFAULT;
374181834Sroberto		return (-1);	/* bad argument */
375181834Sroberto	}
376181834Sroberto	*mode = PPS_CAP;
377181834Sroberto	return (0);
378181834Sroberto}
379181834Sroberto
380181834Sroberto/*
381181834Sroberto * Fetch timestamps
382181834Sroberto */
383181834Sroberto
384181834Srobertostatic inline int
385181834Srobertotime_pps_fetch(
386181834Sroberto	pps_handle_t handle,
387181834Sroberto	const int tsformat,
388181834Sroberto	pps_info_t *ppsinfo,
389181834Sroberto	const struct timespec *timeout
390181834Sroberto	)
391181834Sroberto{
392181834Sroberto	struct timeval tv;
393181834Sroberto	pps_info_t infobuf;
394181834Sroberto
395181834Sroberto	/*
396181834Sroberto	 * Check for valid arguments and fetch timestamps
397181834Sroberto	 */
398181834Sroberto
399181834Sroberto	if (!handle) {
400181834Sroberto		errno = EBADF;
401181834Sroberto		return (-1);	/* bad handle */
402181834Sroberto	}
403181834Sroberto
404181834Sroberto	if (!ppsinfo) {
405181834Sroberto		errno = EFAULT;
406181834Sroberto		return (-1);	/* bad argument */
407181834Sroberto	}
408181834Sroberto
409181834Sroberto	/*
410181834Sroberto	 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
411181834Sroberto	 * ignore the timeout variable.
412181834Sroberto	 */
413181834Sroberto
414181834Sroberto	memset(&infobuf, 0, sizeof(infobuf));
415181834Sroberto
416181834Sroberto	/*
417181834Sroberto	 * if not captureassert, nothing to return.
418181834Sroberto	 */
419181834Sroberto
420181834Sroberto	if (!handle->params.mode & PPS_CAPTUREASSERT) {
421181834Sroberto		memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
422181834Sroberto		return (0);
423181834Sroberto	}
424181834Sroberto
425181834Sroberto	if (ioctl(instance->filedes, TIOCDCDTIMESTAMP, &tv) < 0) {
426181834Sroberto		perror("time_pps_fetch:");
427181834Sroberto		errno = EOPNOTSUPP;
428181834Sroberto		return(-1);
429181834Sroberto	}
430181834Sroberto
431181834Sroberto	/*
432181834Sroberto	 * fake serial here
433181834Sroberto	 */
434181834Sroberto
435181834Sroberto	if (tv.tv_sec != handle->tv_save.tv_sec || tv.tv_usec != handle->tv_save.tv_usec) {
436181834Sroberto		handle->tv_save = tv;
437181834Sroberto		handle->serial++;
438181834Sroberto	}
439181834Sroberto
440181834Sroberto	/*
441181834Sroberto	 * Apply offsets as specified. Note that only assert timestamps
442181834Sroberto	 * are captured by this interface.
443181834Sroberto	 */
444181834Sroberto
445181834Sroberto	infobuf.assert_sequence = handle->serial;
446181834Sroberto	infobuf.assert_timestamp.tv_sec = tv.tv_sec;
447181834Sroberto	infobuf.assert_timestamp.tv_nsec = tv.tv_usec * 1000;
448181834Sroberto
449181834Sroberto	if (handle->params.mode & PPS_OFFSETASSERT) {
450181834Sroberto		infobuf.assert_timestamp.tv_sec  += handle->params.assert_offset.tv_sec;
451181834Sroberto		infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec;
452181834Sroberto		PPS_NORMALIZE(infobuf.assert_timestamp);
453181834Sroberto	}
454181834Sroberto
455181834Sroberto	/*
456181834Sroberto	 * Translate to specified format
457181834Sroberto	 */
458181834Sroberto
459181834Sroberto	switch (tsformat) {
460181834Sroberto	case PPS_TSFMT_TSPEC:
461181834Sroberto		break;		 /* timespec format requires no translation */
462181834Sroberto
463181834Sroberto	case PPS_TSFMT_NTPFP:	/* NTP format requires conversion to fraction form */
464181834Sroberto		PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp);
465181834Sroberto		break;
466181834Sroberto
467181834Sroberto	default:
468181834Sroberto		errno = EINVAL;
469181834Sroberto		return (-1);
470181834Sroberto	}
471181834Sroberto
472181834Sroberto	infobuf.current_mode = handle->params.mode;
473181834Sroberto	memcpy(ppsinfo, &infobuf, sizeof(pps_info_t));
474181834Sroberto	return (0);
475181834Sroberto}
476181834Sroberto
477181834Sroberto/*
478181834Sroberto * specify kernel consumer
479181834Sroberto */
480181834Sroberto
481181834Srobertostatic inline int
482181834Srobertotime_pps_kcbind(
483181834Sroberto	pps_handle_t handle,
484181834Sroberto	const int kernel_consumer,
485181834Sroberto	const int edge, const int tsformat
486181834Sroberto	)
487181834Sroberto{
488181834Sroberto	/*
489181834Sroberto	 * Check for valid arguments and bind kernel consumer
490181834Sroberto	 */
491181834Sroberto	if (!handle) {
492181834Sroberto		errno = EBADF;
493181834Sroberto		return (-1);	/* bad handle */
494181834Sroberto	}
495181834Sroberto	if (geteuid() != 0) {
496181834Sroberto		errno = EPERM;
497181834Sroberto		return (-1);	/* must be superuser */
498181834Sroberto	}
499181834Sroberto	errno = EOPNOTSUPP;
500181834Sroberto	return(-1);
501181834Sroberto}
502181834Sroberto
503181834Sroberto#endif /* _SYS_TIMEPPS_H_ */
504