summaryrefslogtreecommitdiff
path: root/ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h
diff options
context:
space:
mode:
Diffstat (limited to 'ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h')
-rw-r--r--ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h714
1 files changed, 714 insertions, 0 deletions
diff --git a/ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h b/ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h
new file mode 100644
index 0000000..1007ccd
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/monolithic-serialpps-timepps.h
@@ -0,0 +1,714 @@
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1999-2000 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and *
+ * its documentation for any purpose and with or without fee is hereby *
+ * granted, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission *
+ * notice appear in supporting documentation, and that the name *
+ * University of Delaware not be used in advertising or publicity *
+ * pertaining to distribution of the software without specific, *
+ * written prior permission. The University of Delaware makes no *
+ * representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied *
+ * warranty. *
+ * *
+ ***********************************************************************
+ * *
+ * This header file complies with "Pulse-Per-Second API for UNIX-like *
+ * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul *
+ * and Marc Brett, from whom much of this code was shamelessly stolen. *
+ * *
+ * This modified timepps.h can be used to provide a PPSAPI interface *
+ * to a machine running Windows with a suitably modified *
+ * serialpps.sys being used in place of serial.sys. It can *
+ * be extended to support a modified parallel port driver once *
+ * available. *
+ * *
+ * This Windows version was derived by Dave Hart *
+ * <davehart@davehart.com> from Mills' timepps-Solaris.h *
+ * *
+ ***********************************************************************
+ * *
+ * Some of this include file *
+ * Copyright (c) 1999 by Ulrich Windl, *
+ * based on code by Reg Clemens <reg@dwf.com> *
+ * based on code by Poul-Henning Kamp <phk@FreeBSD.org> *
+ * *
+ ***********************************************************************
+ * *
+ * "THE BEER-WARE LICENSE" (Revision 42): *
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this *
+ * notice you can do whatever you want with this stuff. If we meet some*
+ * day, and you think this stuff is worth it, you can buy me a beer *
+ * in return. Poul-Henning Kamp *
+ * *
+ **********************************************************************/
+
+#ifndef _SYS_TIMEPPS_H_
+#define _SYS_TIMEPPS_H_
+
+/* Implementation note: the logical states ``assert'' and ``clear''
+ * are implemented in terms of the UART register, i.e. ``assert''
+ * means the bit is set.
+ */
+
+/*
+ * The following definitions are architecture independent
+ */
+
+#define PPS_API_VERS_1 1 /* API version number */
+#define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */
+#define PPS_FRAC 4294967296. /* 2^32 as a double */
+#define PPS_HECTONANOSECONDS 10000000 /* 100ns units in a second */
+#define PPS_FILETIME_1970 0x019db1ded53e8000 /* unix epoch to Windows */
+
+#define PPS_NORMALIZE(x) /* normalize timespec */ \
+ do { \
+ if ((x).tv_nsec >= PPS_NANOSECOND) { \
+ (x).tv_nsec -= PPS_NANOSECOND; \
+ (x).tv_sec++; \
+ } else if ((x).tv_nsec < 0) { \
+ (x).tv_nsec += PPS_NANOSECOND; \
+ (x).tv_sec--; \
+ } \
+ } while (0)
+
+#define PPS_TSPECTONTP(x) /* convert timespec to ntp_fp */ \
+ do { \
+ double d_temp; \
+ \
+ (x).integral += (unsigned int)PPS_JAN_1970; \
+ d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \
+ if (d_temp >= PPS_FRAC) \
+ (x).integral++; \
+ (x).fractional = (unsigned int)d_temp; \
+ } while (0)
+
+#define PPS_NTPTOTSPEC(x) /* convert ntp_fp to timespec */ \
+ do { \
+ double d_temp; \
+ \
+ (x).tv_sec -= (time_t)PPS_JAN_1970; \
+ d_temp = (double)((x).tv_nsec); \
+ d_temp *= PPS_NANOSECOND; \
+ d_temp /= PPS_FRAC; \
+ (x).tv_nsec = (long)d_temp; \
+ } while (0)
+
+
+/*
+ * Device/implementation parameters (mode)
+ */
+
+#define PPS_CAPTUREASSERT 0x01 /* capture assert events */
+#define PPS_CAPTURECLEAR 0x02 /* capture clear events */
+#define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */
+
+#define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */
+#define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */
+#define PPS_OFFSETBOTH 0x30 /* apply compensation for both */
+
+#define PPS_CANWAIT 0x100 /* Can we wait for an event? */
+#define PPS_CANPOLL 0x200 /* "This bit is reserved for */
+
+/*
+ * Kernel actions (mode)
+ */
+
+#define PPS_ECHOASSERT 0x40 /* feed back assert event to output */
+#define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */
+
+/*
+ * Timestamp formats (tsformat)
+ */
+
+#define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */
+#define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */
+
+/*
+ * Kernel discipline actions (not used in Windows yet)
+ */
+
+#define PPS_KC_HARDPPS 0 /* enable kernel consumer */
+#define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */
+#define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */
+
+/*
+ * Type definitions
+ */
+
+typedef unsigned long pps_seq_t; /* sequence number */
+
+#pragma warning(push)
+#pragma warning(disable: 201) /* nonstd extension nameless union */
+
+typedef struct ntp_fp {
+ union ntp_fp_sec {
+ unsigned int integral;
+ int s_integral;
+ };
+ unsigned int fractional;
+} ntp_fp_t; /* NTP-compatible time stamp */
+
+#pragma warning(pop)
+
+typedef union pps_timeu { /* timestamp format */
+ struct timespec tspec;
+ ntp_fp_t ntpfp;
+ unsigned long longpad[3];
+} pps_timeu_t; /* generic data type to represent time stamps */
+
+/* addition of NTP fixed-point format */
+
+#define NTPFP_M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \
+ do { \
+ register u_int32 lo_tmp; \
+ register u_int32 hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += (a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } while (0)
+
+#define NTPFP_L_ADDS(r, a) NTPFP_M_ADD((r)->integral, (r)->fractional, \
+ (a)->s_integral, (a)->fractional)
+
+
+/*
+ * Timestamp information structure
+ */
+
+typedef struct pps_info {
+ pps_seq_t assert_sequence; /* seq. num. of assert event */
+ pps_seq_t clear_sequence; /* seq. num. of clear event */
+ pps_timeu_t assert_tu; /* time of assert event */
+ pps_timeu_t clear_tu; /* time of clear event */
+ int current_mode; /* current mode bits */
+} pps_info_t;
+
+#define assert_timestamp assert_tu.tspec
+#define clear_timestamp clear_tu.tspec
+
+#define assert_timestamp_ntpfp assert_tu.ntpfp
+#define clear_timestamp_ntpfp clear_tu.ntpfp
+
+/*
+ * Parameter structure
+ */
+
+typedef struct pps_params {
+ int api_version; /* API version # */
+ int mode; /* mode bits */
+ pps_timeu_t assert_off_tu; /* offset compensation for assert */
+ pps_timeu_t clear_off_tu; /* offset compensation for clear */
+} pps_params_t;
+
+#define assert_offset assert_off_tu.tspec
+#define clear_offset clear_off_tu.tspec
+
+#define assert_offset_ntpfp assert_off_tu.ntpfp
+#define clear_offset_ntpfp clear_off_tu.ntpfp
+
+/*
+ * The following definitions are architecture-dependent
+ */
+
+#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
+#define PPS_RO (PPS_CANWAIT | PPS_CANPOLL)
+
+typedef struct {
+ int filedes; /* file descriptor */
+ OVERLAPPED ol; /* caches ol.hEvent for DeviceIoControl */
+ pps_params_t params; /* PPS parameters set by user */
+} pps_unit_t;
+
+typedef pps_unit_t* pps_handle_t; /* pps handlebars */
+
+/*
+ *------ Here begins the implementation-specific part! ------
+ */
+
+#include <windows.h>
+#include <errno.h>
+
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP 45
+#endif
+
+typedef struct _OLD_SERIAL_PPS_STAMPS {
+ LARGE_INTEGER Timestamp;
+ LARGE_INTEGER Counterstamp;
+} OLD_SERIAL_PPS_STAMPS, *POLDSERIAL_PPS_STAMPS;
+
+typedef struct _SERIAL_PPS_STAMPS {
+ LARGE_INTEGER Timestamp;
+ LARGE_INTEGER Counterstamp;
+ DWORD SeqNum;
+} SERIAL_PPS_STAMPS, *PSERIAL_PPS_STAMPS;
+
+#define IOCTL_SERIAL_GET_PPS_STAMPS CTL_CODE(FILE_DEVICE_SERIAL_PORT,114,METHOD_BUFFERED,FILE_ANY_ACCESS)
+
+/* byte offset of member m in struct typedef s */
+#define PPS_OFFSETOF(m,s) (size_t)(&((s *)0)->m)
+
+/*
+ * ntpd on Windows only looks to errno after finding
+ * GetLastError returns NO_ERROR. To accomodate its
+ * use of msyslog in portable code such as refclock_atom.c,
+ * this implementation always clears the Windows
+ * error code using SetLastError(NO_ERROR) when
+ * returning an errno. This is also a good idea
+ * for any non-ntpd clients as they should use only
+ * the errno for PPSAPI functions.
+ */
+#define RETURN_PPS_ERRNO(e) \
+do { \
+ SetLastError(NO_ERROR); \
+ errno = (e); \
+ return -1; \
+} while (0)
+
+
+#ifdef OWN_PPS_NTP_TIMESTAMP_FROM_COUNTER
+extern void pps_ntp_timestamp_from_counter(ntp_fp_t *, ULONGLONG, ULONGLONG);
+#else
+/*
+ * helper routine for serialpps.sys ioctl which returns
+ * performance counter "timestamp" as well as a system
+ * FILETIME timestamp. Converts one of the inputs to
+ * NTP fixed-point format.
+ */
+static inline void
+pps_ntp_timestamp_from_counter(
+ ntp_fp_t *result,
+ ULONGLONG Timestamp,
+ ULONGLONG Counterstamp)
+{
+ ULONGLONG BiasedTimestamp;
+
+ /* convert from 100ns units to NTP fixed point format */
+
+ BiasedTimestamp = Timestamp - PPS_FILETIME_1970;
+ result->integral = PPS_JAN_1970 +
+ (unsigned)(BiasedTimestamp / PPS_HECTONANOSECONDS);
+ result->fractional =
+ (unsigned) ((BiasedTimestamp % PPS_HECTONANOSECONDS) *
+ (PPS_FRAC / PPS_HECTONANOSECONDS));
+}
+#endif
+
+
+/*
+ * create PPS handle from file descriptor
+ */
+
+static inline int
+time_pps_create(
+ int filedes, /* file descriptor */
+ pps_handle_t *handle /* returned handle */
+ )
+{
+ OLD_SERIAL_PPS_STAMPS old_pps_stamps;
+ DWORD bytes;
+ OVERLAPPED ol;
+
+ /*
+ * Check for valid arguments and attach PPS signal.
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ if (PPS_OFFSETOF(tspec.tv_nsec, pps_timeu_t) !=
+ PPS_OFFSETOF(ntpfp.fractional, pps_timeu_t)) {
+ fprintf(stderr,
+ "timepps.h needs work, union of \n"
+ "unsigned int ntp_fp.integral and\n"
+ "time_t timespec.tv_sec accessed\n"
+ "interchangeably.\n");
+ RETURN_PPS_ERRNO(EFAULT);
+ }
+
+ /*
+ * For this ioctl which will never block, we don't want to go
+ * through the overhead of a completion port, so we use an
+ * event handle in the overlapped structure with its 1 bit set.
+ *
+ * From GetQueuedCompletionStatus docs:
+ * Even if you have passed the function a file handle associated
+ * with a completion port and a valid OVERLAPPED structure, an
+ * application can prevent completion port notification. This is
+ * done by specifying a valid event handle for the hEvent member
+ * of the OVERLAPPED structure, and setting its low-order bit. A
+ * valid event handle whose low-order bit is set keeps I/O
+ * completion from being queued to the completion port.
+ */
+
+ ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ol.hEvent = (HANDLE) ((ULONG_PTR)ol.hEvent | 1);
+
+ if (FALSE == DeviceIoControl(
+ (HANDLE)_get_osfhandle(filedes),
+ IOCTL_SERIAL_GET_PPS_STAMPS,
+ NULL,
+ 0,
+ &old_pps_stamps,
+ sizeof(old_pps_stamps),
+ &bytes,
+ &ol)
+ || sizeof(old_pps_stamps) != bytes) {
+
+ /*
+ * If you want to write some dead code this could detect the
+ * IOCTL being pended, but the driver always has the info
+ * instantly, so ERROR_IO_PENDING isn't a concern.
+ */
+
+ CloseHandle(ol.hEvent);
+ fprintf(stderr,
+ "time_pps_create: IOCTL_SERIAL_GET_PPS_STAMPS: %d %d\n",
+ bytes,
+ GetLastError());
+ RETURN_PPS_ERRNO(ENXIO);
+ }
+
+ /*
+ * Allocate and initialize default unit structure.
+ */
+
+ *handle = malloc(sizeof(pps_unit_t));
+ if (!(*handle))
+ RETURN_PPS_ERRNO(ENOMEM);
+
+ memset(*handle, 0, sizeof(pps_unit_t));
+ (*handle)->filedes = filedes;
+ (*handle)->ol.hEvent = ol.hEvent;
+ (*handle)->params.api_version = PPS_API_VERS_1;
+ (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
+ return (0);
+}
+
+/*
+ * release PPS handle
+ */
+
+static inline int
+time_pps_destroy(
+ pps_handle_t handle
+ )
+{
+ /*
+ * Check for valid arguments and detach PPS signal.
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ CloseHandle(handle->ol.hEvent);
+ free(handle);
+ return (0);
+}
+
+/*
+ * set parameters for handle
+ */
+
+static inline int
+time_pps_setparams(
+ pps_handle_t handle,
+ const pps_params_t *params
+ )
+{
+ int mode, mode_in;
+ /*
+ * Check for valid arguments and set parameters.
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (!params)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ /*
+ * There was no reasonable consensu in the API working group.
+ * I require `api_version' to be set!
+ */
+
+ if (params->api_version != PPS_API_VERS_1)
+ RETURN_PPS_ERRNO(EINVAL);
+
+ /*
+ * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT
+ */
+
+ mode_in = params->mode;
+
+ /*
+ * Only one of the time formats may be selected
+ * if a nonzero assert offset is supplied.
+ */
+ if ((mode_in & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) ==
+ (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
+
+ if (handle->params.assert_offset.tv_sec ||
+ handle->params.assert_offset.tv_nsec)
+
+ RETURN_PPS_ERRNO(EINVAL);
+
+ /*
+ * If no offset was specified but both time
+ * format flags are used consider it harmless
+ * but turn off PPS_TSFMT_NTPFP so getparams
+ * will not show both formats lit.
+ */
+ mode_in &= ~PPS_TSFMT_NTPFP;
+ }
+
+ /* turn off read-only bits */
+
+ mode_in &= ~PPS_RO;
+
+ /*
+ * test remaining bits, should only have captureassert,
+ * offsetassert, and/or timestamp format bits.
+ */
+
+ if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
+ PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP))
+ RETURN_PPS_ERRNO(EOPNOTSUPP);
+
+ /*
+ * ok, ready to go.
+ */
+
+ mode = handle->params.mode;
+ handle->params = *params;
+ handle->params.mode = mode | mode_in;
+ handle->params.api_version = PPS_API_VERS_1;
+ return (0);
+}
+
+/*
+ * get parameters for handle
+ */
+
+static inline int
+time_pps_getparams(
+ pps_handle_t handle,
+ pps_params_t *params
+ )
+{
+ /*
+ * Check for valid arguments and get parameters.
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (!params)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ *params = handle->params;
+ return (0);
+}
+
+/* (
+ * get capabilities for handle
+ */
+
+static inline int
+time_pps_getcap(
+ pps_handle_t handle,
+ int *mode
+ )
+{
+ /*
+ * Check for valid arguments and get capabilities.
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (!mode)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ *mode = PPS_CAP;
+ return (0);
+}
+
+/*
+ * Fetch timestamps
+ */
+
+static inline int
+time_pps_fetch(
+ pps_handle_t handle,
+ const int tsformat,
+ pps_info_t *ppsinfo,
+ const struct timespec *timeout
+ )
+{
+ SERIAL_PPS_STAMPS pps_stamps;
+ pps_info_t infobuf;
+ BOOL rc;
+ DWORD bytes;
+ DWORD lasterr;
+
+ /*
+ * Check for valid arguments and fetch timestamps
+ */
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (!ppsinfo)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ /*
+ * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
+ * ignore the timeout variable.
+ */
+
+ memset(&infobuf, 0, sizeof(infobuf));
+
+ /*
+ * if not captureassert, nothing to return.
+ */
+
+ if (!handle->params.mode & PPS_CAPTUREASSERT) {
+ *ppsinfo = infobuf;
+ return (0);
+ }
+
+ /*
+ * First rev of serialpps.sys didn't support the SeqNum field,
+ * support it by simply returning constant 0 for serial in that case.
+ */
+ pps_stamps.SeqNum = 0;
+
+ /*
+ * interrogate (hopefully) serialpps.sys
+ * if it's the standard serial.sys or another driver,
+ * IOCTL_SERIAL_GET_PPS_STAMPS is most likely unknown
+ * and will result in ERROR_INVALID_PARAMETER.
+ */
+ bytes = 0;
+
+ rc = DeviceIoControl(
+ (HANDLE)_get_osfhandle(handle->filedes),
+ IOCTL_SERIAL_GET_PPS_STAMPS,
+ NULL,
+ 0,
+ &pps_stamps,
+ sizeof(pps_stamps),
+ &bytes,
+ &handle->ol);
+
+ if (!rc) {
+
+ lasterr = GetLastError();
+ if (ERROR_INVALID_PARAMETER != lasterr)
+ fprintf(stderr, "time_pps_fetch: ioctl err %d\n",
+ lasterr);
+ RETURN_PPS_ERRNO(EOPNOTSUPP);
+
+ } else if (bytes != sizeof(pps_stamps) &&
+ bytes != sizeof(OLD_SERIAL_PPS_STAMPS)) {
+
+ fprintf(stderr,
+ "time_pps_fetch: wanted %d or %d bytes got %d from "
+ "IOCTL_SERIAL_GET_PPS_STAMPS 0x%x\n" ,
+ sizeof(OLD_SERIAL_PPS_STAMPS),
+ sizeof(SERIAL_PPS_STAMPS),
+ bytes,
+ IOCTL_SERIAL_GET_PPS_STAMPS);
+ RETURN_PPS_ERRNO(ENXIO);
+ }
+
+ /*
+ * pps_ntp_timestamp_from_counter takes the two flavors
+ * of timestamp we have (counter and system time) and
+ * uses whichever it can to give the best NTP fixed-point
+ * conversion. In ntpd the Counterstamp is typically
+ * used. A stub implementation in this file simply
+ * converts from Windows Timestamp to NTP fixed-point.
+ */
+ pps_ntp_timestamp_from_counter(
+ &infobuf.assert_timestamp_ntpfp,
+ pps_stamps.Timestamp.QuadPart,
+ pps_stamps.Counterstamp.QuadPart);
+
+ /*
+ * Note that only assert timestamps
+ * are captured by this interface.
+ */
+
+ infobuf.assert_sequence = pps_stamps.SeqNum;
+
+ /*
+ * Apply offset and translate to specified format
+ */
+
+ switch (tsformat) {
+ case PPS_TSFMT_NTPFP: /* NTP format requires no translation */
+ if (handle->params.mode & PPS_OFFSETASSERT) {
+ NTPFP_L_ADDS(&infobuf.assert_timestamp_ntpfp,
+ &handle->params.assert_offset_ntpfp);
+ }
+ break;
+
+ case PPS_TSFMT_TSPEC: /* timespec format requires conversion to nsecs form */
+ PPS_NTPTOTSPEC(infobuf.assert_timestamp);
+ if (handle->params.mode & PPS_OFFSETASSERT) {
+ infobuf.assert_timestamp.tv_sec +=
+ handle->params.assert_offset.tv_sec;
+ infobuf.assert_timestamp.tv_nsec +=
+ handle->params.assert_offset.tv_nsec;
+ PPS_NORMALIZE(infobuf.assert_timestamp);
+ }
+ break;
+
+ default:
+ RETURN_PPS_ERRNO(EINVAL);
+ }
+
+ infobuf.current_mode = handle->params.mode;
+ *ppsinfo = infobuf;
+ return (0);
+}
+
+/*
+ * time_pps_kcbind - specify kernel consumer
+ *
+ * Not supported so far by Windows.
+ */
+
+static inline int
+time_pps_kcbind(
+ pps_handle_t handle,
+ const int kernel_consumer,
+ const int edge, const int tsformat
+ )
+{
+ /*
+ * Check for valid arguments before revealing the ugly truth
+ */
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ RETURN_PPS_ERRNO(EOPNOTSUPP);
+}
+
+
+
+#endif /* _SYS_TIMEPPS_H_ */