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