summaryrefslogtreecommitdiff
path: root/ports/winnt/ppsapi/loopback/src
diff options
context:
space:
mode:
Diffstat (limited to 'ports/winnt/ppsapi/loopback/src')
-rw-r--r--ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c465
-rw-r--r--ports/winnt/ppsapi/loopback/src/loopback-ppsapi.def9
-rw-r--r--ports/winnt/ppsapi/loopback/src/loopback-ppsapi.h49
-rw-r--r--ports/winnt/ppsapi/loopback/src/sys/time.h18
-rw-r--r--ports/winnt/ppsapi/loopback/src/timepps.h805
5 files changed, 1346 insertions, 0 deletions
diff --git a/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c
new file mode 100644
index 0000000..4a708f4
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c
@@ -0,0 +1,465 @@
+/*
+ * loopback-ppsapi-provider.c - derived from monolithic timepps.h
+ * for usermode PPS by Juergen Perlinger
+ */
+
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1999-2009 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and *
+ * its documentation for any purpose and 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 serialpps-ppsapi-provider.dll implements the PPSAPI provider *
+ * for serialpps.sys, which is a very lightly patched Windows *
+ * serial.sys with CD timestamping support.
+ * *
+ * This Windows version was derived by Dave Hart *
+ * <davehart@davehart.com> from David L. 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 *
+ * *
+ **********************************************************************/
+
+/*
+ * Implementation note: the logical states ``assert'' and ``clear''
+ * are implemented in terms of the UART register, i.e. ``assert''
+ * means the bit is set. This follows the sense of the serial driver
+ * of the Windows OS, and is opposite of the RS-232 spec for the
+ * CD/DCD logical state.
+ */
+
+
+#define PPSAPI_PROVIDER_EXPORTS
+#include "loopback-ppsapi.h"
+
+/*
+** global stuff
+*/
+
+pcreate_pps_handle p_create_pps_handle;
+
+#define SERIALPPS_CAPS (PPS_CAPTUREBOTH | PPS_OFFSETBOTH | PPS_TSFMT_BOTH)
+#define SERIALPPS_RO (PPS_CANWAIT | PPS_CANPOLL)
+
+
+/*
+ * The ntp_timestamp_from_counter callback into timepps.h routines in
+ * the host is saved in each unit separately, so that binaries that
+ * inline timepps.h into multiple source files (such as refclock_atom.c
+ * and a number of other ntpd refclocks including refclock_nmea.c) will
+ * get called back in the correct instance for each unit. This assumes
+ * that ppsapi_prov_init for subsequent instances happens only after the
+ * first instance has completed all time_pps_create() calls it will
+ * invoke, which is a safe assumption at least for ntpd.
+ */
+struct loopback_unit {
+ HANDLE hnd_dev; /* true device handle */
+ HANDLE hnd_pps; /* loopback handle */
+ ntp_fp_t ofs_assert; /* correction for assert*/
+ ntp_fp_t ofs_clear; /* correction for clear */
+};
+typedef struct loopback_unit loopback_unit;
+
+/*
+ * --------------------------------------------------------------------
+ * DllMain - DLL entrypoint, no-op.
+ * --------------------------------------------------------------------
+ */
+BOOL APIENTRY DllMain(
+ HMODULE hModule,
+ DWORD why,
+ LPVOID lpReserved
+ )
+{
+ UNUSED(hModule);
+ UNUSED(lpReserved);
+ UNUSED(why);
+
+ return TRUE;
+}
+
+/*
+ * --------------------------------------------------------------------
+ * time conversion and other helpers
+ * --------------------------------------------------------------------
+ */
+
+/* --------------------------------------------------------------------
+ * convert fixed point time stamp to struct timespec, with proper
+ * Epoch/Era unfolding around the current time.
+ */
+static struct timespec
+fp_stamp_to_tspec(
+ ntp_fp_t x,
+ time_t p
+ )
+{
+ struct timespec out;
+ u_int64 tmp;
+ u_int32 ntp;
+
+ ntp = x.I.u;
+ tmp = p;
+ tmp -= 0x80000000u; /* unshift of half range */
+ ntp -= (u_int32)PPS_JAN_1970; /* warp into UN*X domain */
+ ntp -= (u_int32)tmp; /* cycle difference */
+ tmp += (u_int64)ntp; /* get expanded time */
+ out.tv_sec = (time_t)tmp;
+ out.tv_nsec = ((long)(((u_int64)x.F.u * PPS_NANOSECOND + 0x80000000u) >> 32));
+ if (out.tv_nsec >= PPS_NANOSECOND) {
+ out.tv_nsec -= PPS_NANOSECOND;
+ out.tv_sec++;
+ }
+
+ return out;
+}
+
+/* --------------------------------------------------------------------
+ * convert a duration in struct timespec format to
+ * fixed point representation.
+ */
+static ntp_fp_t
+tspec_to_fp(
+ const struct timespec * ts
+ )
+{
+ ntp_fp_t out;
+ long tmp;
+
+ out.I.u = (u_int32)ts->tv_sec;
+ tmp = ts->tv_nsec;
+ if (tmp < 0)
+ do {
+ tmp += PPS_NANOSECOND;
+ out.I.u--;
+ } while (tmp < 0);
+ else if (tmp >= PPS_NANOSECOND)
+ do {
+ tmp -= PPS_NANOSECOND;
+ out.I.u++;
+ } while (tmp >= PPS_NANOSECOND);
+ out.F.u = (u_int32)((u_int64)tmp << 32) + (PPS_NANOSECOND / 2) / PPS_NANOSECOND;
+ return out;
+}
+
+/* --------------------------------------------------------------------
+ * count number of '1' bits in a u_long
+ */
+static size_t
+popcount(
+ u_long val
+ )
+{
+ size_t res;
+
+ for (res = 0; val; res++)
+ val &= (val - 1);
+ return res;
+}
+
+/*
+ * --------------------------------------------------------------------
+ * API function implementation
+ * --------------------------------------------------------------------
+ */
+
+/* --------------------------------------------------------------------
+ * prov_time_pps_create - create PPS handle given underlying device
+ */
+int WINAPI
+prov_time_pps_create(
+ HANDLE device, /* underlying device */
+ pps_handle_t * handle /* returned handle */
+ )
+{
+ loopback_unit * loopunit;
+ pps_unit_t * punit;
+
+ /*
+ * Allocate and initialize loopback unit structure.
+ */
+ loopunit = (loopback_unit*)calloc(1, sizeof(loopback_unit));
+ if (NULL == loopunit)
+ return ENOMEM;
+
+ /* Try to attach to NTPD internal data with the device handle.
+ * Free unit buffer on failure.
+ */
+ loopunit->hnd_dev = device;
+ loopunit->hnd_pps = ntp_pps_attach_device(device);
+ if (NULL == loopunit->hnd_pps) {
+ free(loopunit);
+ return ENXIO;
+ }
+
+ /* create the outer PPS handle structure. Undo work done so far
+ * if things go wrong.
+ */
+ *handle = (*p_create_pps_handle)(loopunit);
+ if (!*handle) {
+ ntp_pps_detach_device(loopunit->hnd_pps);
+ free(loopunit);
+ return ENOMEM;
+ }
+
+ /* All good so far. Store things to remember. */
+ punit = (pps_unit_t *)*handle;
+ punit->params.api_version = PPS_API_VERS_1;
+ punit->params.mode = PPS_CAPTUREBOTH | PPS_TSFMT_BOTH;
+ return 0;
+}
+
+
+/* --------------------------------------------------------------------
+ * prov_time_pps_destroy - release PPS handle
+ */
+int WINAPI
+prov_time_pps_destroy(
+ pps_unit_t * unit,
+ void * context
+ )
+{
+ loopback_unit * loopunit;
+
+ loopunit = (loopback_unit*)context;
+ if (unit->context == context)
+ unit->context = NULL;
+ if (NULL != loopunit)
+ ntp_pps_detach_device(loopunit->hnd_pps);
+ free(loopunit);
+ return 0;
+}
+
+
+/* --------------------------------------------------------------------
+ * prov_time_pps_setparams - set parameters for handle
+ */
+int WINAPI
+prov_time_pps_setparams(
+ pps_unit_t * unit,
+ void * context,
+ const pps_params_t * params
+ )
+{
+ loopback_unit * loopunit;
+ int mode;
+
+ loopunit = (loopback_unit*)context;
+ mode = params->mode;
+
+ /*
+ * There was no reasonable consensus in the API working group.
+ * I require `api_version' to be set!
+ */
+ if (params->api_version != PPS_API_VERS_1)
+ return EINVAL;
+
+ /*
+ * We support all edges and formats plus offsets, but not
+ * POLL or WAIT. And we are strict on the time stamp format:
+ * Only one is permitted if we set offsets!
+ */
+
+ /*
+ * Only one of the time formats may be selected.
+ */
+ if ((mode & PPS_OFFSETBOTH) != 0 &&
+ popcount(mode & PPS_TSFMT_BOTH) != 1 )
+ return EINVAL;
+
+ /* turn off read-only bits */
+ mode &= ~SERIALPPS_RO;
+
+ /*
+ * test remaining bits.
+ */
+ if (mode & ~(PPS_CAPTUREBOTH | PPS_OFFSETBOTH | PPS_TSFMT_BOTH))
+ return EOPNOTSUPP;
+
+ /*
+ * ok, ready to go.
+ *
+ * calculate offsets as ntp_fp_t's and store them in unit as ntp_fp_t. They will
+ * be always applied, since fetching the time stamps is not critical.
+ */
+ if (mode & PPS_OFFSETASSERT) {
+ if (mode & PPS_TSFMT_TSPEC)
+ loopunit->ofs_assert = tspec_to_fp(&params->assert_offset);
+ else
+ loopunit->ofs_assert = params->assert_offset_ntpfp;
+ }
+ if (mode & PPS_OFFSETCLEAR) {
+ if (mode & PPS_TSFMT_TSPEC)
+ loopunit->ofs_clear = tspec_to_fp(&params->clear_offset);
+ else
+ loopunit->ofs_clear = params->clear_offset_ntpfp;
+ }
+ /* save remaining bits */
+ mode |= unit->params.mode;
+ unit->params = *params;
+ unit->params.mode = mode;
+
+ return 0;
+}
+
+
+/* --------------------------------------------------------------------
+ * prov_time_pps_fetch - Fetch timestamps
+ */
+
+int WINAPI
+prov_time_pps_fetch(
+ pps_unit_t * unit,
+ void * context,
+ const int tsformat,
+ pps_info_t * pinfo,
+ const struct timespec * timeout
+ )
+{
+ loopback_unit * loopunit;
+ PPSData_t pps_stamps;
+ pps_info_t infobuf;
+ BOOL rc;
+ time_t tnow;
+
+ /*
+ * nb. PPS_CANWAIT is NOT set by the implementation, we can totally
+ * ignore the timeout variable.
+ */
+ UNUSED(timeout);
+ loopunit = (loopback_unit*)context;
+
+ /* read & check raw data from daemon */
+ memset(&infobuf, 0, sizeof(infobuf));
+ rc = ntp_pps_read(loopunit->hnd_pps, &pps_stamps, sizeof(pps_stamps));
+ if (!rc) {
+ switch (GetLastError())
+ {
+ case ERROR_INVALID_HANDLE:
+ return EINVAL;
+ case ERROR_INVALID_PARAMETER:
+ return EOPNOTSUPP;
+ case ERROR_INVALID_DATA:
+ return ENXIO;
+ default:
+ return EINVAL;
+ }
+ }
+
+ /* add offsets on raw data */
+ NTPFP_L_ADDS(&pps_stamps.ts_assert, &loopunit->ofs_assert);
+ NTPFP_L_ADDS(&pps_stamps.ts_clear , &loopunit->ofs_clear);
+
+ /* store sequence numbers */
+ infobuf.assert_sequence = pps_stamps.cc_assert;
+ infobuf.clear_sequence = pps_stamps.cc_clear;
+
+ /*
+ * Translate or copy to specified format
+ */
+ switch (tsformat) {
+ case PPS_TSFMT_NTPFP: /* NTP format requires no translation */
+ infobuf.assert_timestamp_ntpfp = pps_stamps.ts_assert;
+ infobuf.clear_timestamp_ntpfp = pps_stamps.ts_clear;
+ break;
+
+ case PPS_TSFMT_TSPEC: /* timespec format requires conversion to nsecs form */
+ time(&tnow);
+ infobuf.assert_timestamp = fp_stamp_to_tspec(pps_stamps.ts_assert, tnow);
+ infobuf.clear_timestamp = fp_stamp_to_tspec(pps_stamps.ts_clear, tnow);
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ infobuf.current_mode = unit->params.mode;
+ *pinfo = infobuf;
+
+ return 0;
+}
+
+
+/* --------------------------------------------------------------------
+ * prov_time_pps_kcbind - specify kernel consumer
+ *
+ * Not supported so far by Windows.
+ */
+int WINAPI
+prov_time_pps_kcbind(
+ pps_unit_t * punit,
+ void * context,
+ const int kernel_consumer,
+ const int edge,
+ const int tsformat
+ )
+{
+ UNUSED(punit);
+ UNUSED(context);
+ UNUSED(kernel_consumer);
+ UNUSED(edge);
+ UNUSED(tsformat);
+
+ return EOPNOTSUPP;
+}
+
+
+/* --------------------------------------------------------------------
+ * prov_init - returns capabilities and provider name
+ */
+int WINAPI
+ppsapi_prov_init(
+ int ppsapi_timepps_prov_ver,
+ pcreate_pps_handle create_pps_handle,
+ ppps_ntp_timestamp_from_counter ntp_timestamp_from_counter,
+ char * short_name_buf,
+ size_t short_name_size,
+ char * full_name_buf,
+ size_t full_name_size
+ )
+{
+ UNUSED(ntp_timestamp_from_counter);
+
+ if (ppsapi_timepps_prov_ver < PPSAPI_TIMEPPS_PROV_VER)
+ return 0;
+
+ p_create_pps_handle = create_pps_handle;
+
+ strncpy(short_name_buf, "loopback", short_name_size);
+ short_name_buf[short_name_size - 1] ='\0'; /* ensure ASCIIZ */
+ strncpy(full_name_buf,
+ "loopback user mode DCD change detection",
+ full_name_size);
+ full_name_buf[full_name_size - 1] ='\0'; /* ensure ASCIIZ */
+
+ return SERIALPPS_CAPS;
+}
diff --git a/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.def b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.def
new file mode 100644
index 0000000..14573a2
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.def
@@ -0,0 +1,9 @@
+LIBRARY "loopback-ppsapi-provider.dll"
+
+EXPORTS
+ ppsapi_prov_init
+ prov_time_pps_create
+ prov_time_pps_destroy
+ prov_time_pps_fetch
+ prov_time_pps_kcbind
+ prov_time_pps_setparams \ No newline at end of file
diff --git a/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.h b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.h
new file mode 100644
index 0000000..5541e33
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.h
@@ -0,0 +1,49 @@
+//
+// serialpps-ppsapi-provider.h
+//
+// For this tiny project the single header serves as a precompiled header
+// as well, meaning all the bulky headers are included before or within it.
+// Within, in this case.
+//
+
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <windows.h>
+typedef __int32 int32;
+typedef unsigned __int32 u_int32;
+typedef __int64 int64;
+typedef unsigned __int64 u_int64;
+#include "timepps.h"
+
+#ifndef UNUSED
+#define UNUSED(item) ((void)(item))
+#endif
+
+/* PPS data structure as captured by the serial line I/O system. This
+ * must match the local definition in 'ntp_iocompletionport.c' or
+ * 'Bad Things (tm)' are bound to happen.
+ */
+struct PpsData {
+ u_long cc_assert;
+ u_long cc_clear;
+ ntp_fp_t ts_assert;
+ ntp_fp_t ts_clear;
+};
+typedef struct PpsData PPSData_t;
+
+/* prototypes imported from the NTPD executable */
+__declspec(dllimport) HANDLE WINAPI ntp_pps_attach_device(HANDLE hndIo);
+__declspec(dllimport) void WINAPI ntp_pps_detach_device(HANDLE ppsHandle);
+__declspec(dllimport) BOOL WINAPI ntp_pps_read(HANDLE ppsHandle, void*, size_t);
+
+/* prototypes exported to the NTPD executable */
+__declspec(dllexport) int WINAPI prov_time_pps_create(HANDLE, pps_handle_t*);
+__declspec(dllexport) int WINAPI prov_time_pps_destroy(pps_unit_t*, void*);
+__declspec(dllexport) int WINAPI prov_time_pps_setparams(pps_unit_t*, void*,
+ const pps_params_t*);
+__declspec(dllexport) int WINAPI prov_time_pps_fetch(pps_unit_t*, void*,
+ const int, pps_info_t*, const struct timespec*);
+__declspec(dllexport) int WINAPI prov_time_pps_kcbind(pps_unit_t*, void*, const int, const int, const int);
+__declspec(dllexport) int WINAPI ppsapi_prov_init(int, pcreate_pps_handle,
+ ppps_ntp_timestamp_from_counter, char*, size_t,
+ char*, size_t);
diff --git a/ports/winnt/ppsapi/loopback/src/sys/time.h b/ports/winnt/ppsapi/loopback/src/sys/time.h
new file mode 100644
index 0000000..079e5b6
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/src/sys/time.h
@@ -0,0 +1,18 @@
+/*
+ * This sys/time.h is part of ppsapi-prov skeleton sample source code
+ * for a Windows PPSAPI provider DLL. It was adapted from
+ * ports/winnt/include/sys/time.h in ntpd.
+ */
+
+#ifndef SYS_TIME_H
+#define SYS_TIME_H
+
+#include <windows.h>
+#include <time.h>
+
+typedef struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+} timespec_t;
+
+#endif /* SYS_TIME_H */
diff --git a/ports/winnt/ppsapi/loopback/src/timepps.h b/ports/winnt/ppsapi/loopback/src/timepps.h
new file mode 100644
index 0000000..3a7e806
--- /dev/null
+++ b/ports/winnt/ppsapi/loopback/src/timepps.h
@@ -0,0 +1,805 @@
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1999-2009 *
+ * *
+ * 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 one or more backend provider DLLs *
+ * implementing the provider interfaces defined herein. *
+ * *
+ * 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 TIMEPPS_H
+#define TIMEPPS_H
+
+#include "sys/time.h" /* in ntp ref source declares struct timespec */
+
+/*
+ * 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 { \
+ while ((x).tv_nsec >= PPS_NANOSECOND) { \
+ (x).tv_nsec -= PPS_NANOSECOND; \
+ (x).tv_sec++; \
+ } \
+ while ((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_frac; \
+ \
+ d_frac = ((struct timespec)&(x))->tv_nsec \
+ * PPS_FRAC / PPS_NANOSECOND; \
+ (x).integral = ((struct timespec)&(x))->tv_sec \
+ + PPS_JAN_1970; \
+ (x).fractional = (unsigned int)d_frac; \
+ if (d_frac >= PPS_FRAC) \
+ (x).integral++; \
+ } while (0)
+
+#define PPS_NTPTOTSPEC(x) /* convert ntp_fp to timespec */ \
+ do { \
+ double d_frac; \
+ \
+ /* careful, doing in place and tv_sec may be 64bit */ \
+ d_frac = (double)((ntp_fp_t *)&(x))->F.u \
+ * PPS_NANOSECOND / PPS_FRAC; \
+ (x).tv_sec = ((ntp_fp_t *)&(x))->I.u \
+ - (time_t)PPS_JAN_1970; \
+ (x).tv_nsec = (long)d_frac; \
+ } 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 */
+#define PPS_TSFMT_BOTH (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
+
+/*
+ * 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 {
+ int32 s;
+ u_int32 u;
+ } I;
+ union {
+ int32 s;
+ u_int32 u;
+ } F;
+} 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 { \
+ r_f = (u_int32)(r_f) + (u_int32)(a_f); \
+ r_i = (u_int32)(r_i) + (u_int32)(a_i) + \
+ ((u_int32)(r_f) < (u_int32)(a_f)); \
+ } while (0)
+
+#define NTPFP_L_ADDS(r, a) NTPFP_M_ADD((r)->I.u, (r)->F.u, (a)->I.u, (a)->F.u)
+
+
+/*
+ * 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
+
+/*
+ *------ Here begins the implementation-specific part! ------
+ */
+
+#include <windows.h>
+#include <errno.h>
+#include <stddef.h> /* offsetof() */
+#include <io.h> /* _get_osfhandle() */
+
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP 45
+#endif
+
+typedef UINT_PTR pps_handle_t; /* pps handlebars */
+
+#ifndef inline
+#define inline __inline
+#endif
+
+/*
+ * ntpd on Windows is typically distributed as a binary as few users
+ * have the tools needed to build from source. Rather than build
+ * a single timepps.h for Windows which knows how to talk to all
+ * PPS implementations frozen in time as of compiling, this timepps.h
+ * allows one or more backend providers to be used by naming a DLL
+ * which exports the provider interfaces defined here.
+ */
+typedef enum ppsapi_magic_tag {
+ PPSAPI_MAGIC_UNIT = 0x70707355, /* ppsU */
+} ppsapi_magic;
+
+typedef struct {
+ struct pps_provider_tag *provider;
+ void * context;/* provider's unit pointer */
+ ppsapi_magic magic; /* to detect invalid handles */
+ pps_params_t params; /* PPS parameters set by user */
+} pps_unit_t;
+
+typedef void (*ppps_ntp_timestamp_from_counter)(
+ ntp_fp_t *result,
+ ULONGLONG Timestamp,
+ ULONGLONG Counterstamp
+ );
+
+typedef pps_handle_t (*pcreate_pps_handle)(
+ void * prov_context
+ );
+
+/*
+ * ppsapi_prov_init() - exported by backend DLLs
+ *
+ * Return value is pps capabilities available to PPSAPI consumers
+ * via time_pps_getcaps().
+ */
+#define PPSAPI_TIMEPPS_PROV_VER 2
+
+typedef int (WINAPI *pppsapi_prov_init)(
+ int ppsapi_timepps_prov_ver,
+ pcreate_pps_handle create_pps_handle,
+ ppps_ntp_timestamp_from_counter ntp_timestamp_from_counter,
+ char * short_name_buf,
+ size_t short_name_size,
+ char * full_name_buf,
+ size_t full_name_size
+ );
+
+typedef int (WINAPI *provtime_pps_create)(
+ HANDLE winhandle, /* user device handle */
+ pps_handle_t *phandle /* returned handle */
+ );
+
+typedef int (WINAPI *provtime_pps_destroy)(
+ pps_unit_t * unit,
+ void * context
+ );
+
+typedef int (WINAPI *provtime_pps_setparams)(
+ pps_unit_t * unit,
+ void * context,
+ const pps_params_t * params
+ );
+
+typedef int (WINAPI *provtime_pps_fetch)(
+ pps_unit_t * unit,
+ void * context,
+ const int tsformat,
+ pps_info_t * pinfo,
+ const struct timespec * timeout
+ );
+
+typedef int (WINAPI *provtime_pps_kcbind)(
+ pps_unit_t * unit,
+ void * context,
+ const int kernel_consumer,
+ const int edge,
+ const int tsformat
+ );
+
+typedef struct pps_provider_tag {
+ struct pps_provider_tag *next;
+ int caps;
+ char * short_name;
+ char * full_name;
+ provtime_pps_create ptime_pps_create;
+ provtime_pps_destroy ptime_pps_destroy;
+ provtime_pps_setparams ptime_pps_setparams;
+ provtime_pps_fetch ptime_pps_fetch;
+ provtime_pps_kcbind ptime_pps_kcbind;
+} ppsapi_provider;
+
+static ppsapi_provider * g_provider_list;
+static ppsapi_provider * g_curr_provider;
+
+
+static inline pps_handle_t
+internal_create_pps_handle(
+ void * prov_context
+ )
+{
+ pps_unit_t * punit;
+
+ if (NULL == g_curr_provider) {
+ fprintf(stderr, "create_pps_handle: provider backend called me outside time_pps_create\n");
+ punit = NULL;
+ } else
+ punit = malloc(sizeof(*punit));
+ if (punit != NULL) {
+ punit->provider = g_curr_provider;
+ punit->context = prov_context;
+ punit->magic = PPSAPI_MAGIC_UNIT;
+ memset(&punit->params, 0, sizeof(punit->params));
+ }
+ return (pps_handle_t)punit;
+}
+
+static inline pps_unit_t *
+unit_from_ppsapi_handle(
+ pps_handle_t handle
+ )
+{
+ pps_unit_t *punit;
+
+ punit = (pps_unit_t *)handle;
+ if (PPSAPI_MAGIC_UNIT != punit->magic)
+ punit = NULL;
+ return punit;
+}
+
+/*
+ * 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 rely 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.
+ *
+ * You will probably want to supply your own and #define
+ * OWN_PPS_NTP_TIMESTAMP_FROM_COUNTER, as this stub
+ * converts only the low-resolution system timestamp.
+ *
+ * When implementing a provider, use the pointer to this
+ * conversion function supplied to your prov_init(), as
+ * the copy in your DLL will likely be the stub below,
+ * where you want the one provided by the PPSAPI client
+ * such as ntpd.
+ */
+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->I.u = PPS_JAN_1970 +
+ (unsigned)(BiasedTimestamp / PPS_HECTONANOSECONDS);
+ result->F.u =
+ (unsigned) ((BiasedTimestamp % PPS_HECTONANOSECONDS) *
+ (PPS_FRAC / PPS_HECTONANOSECONDS));
+}
+#endif
+
+
+static inline int
+load_pps_provider(
+ char * dllpath
+ )
+{
+ char short_name[16];
+ char full_name[64];
+ ppsapi_provider * prov;
+ HMODULE hmod;
+ pppsapi_prov_init pprov_init;
+
+ prov = malloc(sizeof(*prov));
+ if (NULL == prov)
+ return ENOMEM;
+
+ hmod = LoadLibrary(dllpath);
+ if (NULL == hmod) {
+ fprintf(stderr, "load_pps_provider: LoadLibrary(%s) error %u\n", dllpath, GetLastError());
+ free(prov);
+ return ENOENT;
+ }
+
+ pprov_init = (pppsapi_prov_init)GetProcAddress(hmod, "ppsapi_prov_init");
+ if (NULL == pprov_init) {
+ fprintf(stderr, "load_pps_provider: entrypoint ppsapi_prov_init not found in %s\n", dllpath);
+ free(prov);
+ FreeLibrary(hmod);
+ return EFAULT;
+ }
+
+ prov->caps = (*pprov_init)(PPSAPI_TIMEPPS_PROV_VER,
+ &internal_create_pps_handle,
+ &pps_ntp_timestamp_from_counter,
+ short_name, sizeof(short_name),
+ full_name, sizeof(full_name));
+
+ if (!prov->caps) {
+ free(prov);
+ FreeLibrary(hmod);
+ return EACCES;
+ }
+
+ prov->short_name = _strdup(short_name);
+ prov->full_name = _strdup(full_name);
+
+ if (NULL == prov->short_name || !prov->short_name[0]
+ || NULL == prov->full_name || !prov->full_name[0]) {
+
+ if (prov->short_name)
+ free(prov->short_name);
+ if (prov->full_name)
+ free(prov->full_name);
+ free(prov);
+ FreeLibrary(hmod);
+ return EINVAL;
+ }
+
+ prov->ptime_pps_create = (provtime_pps_create)
+ GetProcAddress(hmod, "prov_time_pps_create");
+ prov->ptime_pps_destroy = (provtime_pps_destroy)
+ GetProcAddress(hmod, "prov_time_pps_destroy");
+ prov->ptime_pps_setparams = (provtime_pps_setparams)
+ GetProcAddress(hmod, "prov_time_pps_setparams");
+ prov->ptime_pps_fetch = (provtime_pps_fetch)
+ GetProcAddress(hmod, "prov_time_pps_fetch");
+ prov->ptime_pps_kcbind = (provtime_pps_kcbind)
+ GetProcAddress(hmod, "prov_time_pps_kcbind");
+
+ if (NULL == prov->ptime_pps_create
+ || NULL == prov->ptime_pps_destroy
+ || NULL == prov->ptime_pps_setparams
+ || NULL == prov->ptime_pps_fetch
+ || NULL == prov->ptime_pps_kcbind) {
+
+ fprintf(stderr, "PPSAPI provider %s missing entrypoint\n",
+ prov->short_name);
+ free(prov->short_name);
+ free(prov->full_name);
+ free(prov);
+ FreeLibrary(hmod);
+ return EINVAL;
+ }
+
+ fprintf(stderr, "loaded PPSAPI provider %s caps 0x%x provider %p\n",
+ prov->full_name, prov->caps, prov);
+
+ prov->next = g_provider_list;
+ g_provider_list = prov;
+
+ return 0;
+}
+
+
+/*
+ * time_pps_create - create PPS handle from file descriptor
+ *
+ * This is the initial entrypoint of PPSAPI from the client. Note
+ * to maintain source compatibility with Unix, the input file
+ * descriptor really is a descriptor from the C runtime low-numbered
+ * descriptor namespace, though it may have been converted from a
+ * native Windows HANDLE using _open_osfhandle().
+ */
+static inline int
+time_pps_create(
+ int filedes,/* device file descriptor */
+ pps_handle_t * phandle /* returned handle */
+ )
+{
+ HANDLE winhandle;
+ char * dlls;
+ char * dll;
+ char * pch;
+ ppsapi_provider * prov;
+ pps_handle_t ppshandle;
+ int err;
+
+ if (NULL == phandle)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ winhandle = (HANDLE)_get_osfhandle(filedes);
+ fprintf(stderr, "time_pps_create(%d) got winhandle %p\n", filedes, winhandle);
+ if (INVALID_HANDLE_VALUE == winhandle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ /*
+ * For initial testing the list of PPSAPI backend
+ * providers is provided by the environment variable
+ * PPSAPI_DLLS, separated by semicolons such as
+ * PPSAPI_DLLS=c:\ntp\serial_ppsapi.dll;..\parport_ppsapi.dll
+ * There are a million better ways, such as a well-known
+ * registry key under which a value is created for each
+ * provider DLL installed, or even a platform-specific
+ * ntp.conf directive or command-line switch.
+ */
+ dlls = getenv("PPSAPI_DLLS");
+ if (dlls != NULL && NULL == g_provider_list) {
+ dlls = dll = _strdup(dlls);
+ fprintf(stderr, "getenv(PPSAPI_DLLS) gives %s\n", dlls);
+ } else
+ dlls = dll = NULL;
+
+ while (dll != NULL && dll[0]) {
+ pch = strchr(dll, ';');
+ if (pch != NULL)
+ *pch = 0;
+ err = load_pps_provider(dll);
+ if (err) {
+ fprintf(stderr, "load_pps_provider(%s) got errno %d\n", dll, err);
+ RETURN_PPS_ERRNO(err);
+ }
+ dll = (NULL == pch)
+ ? NULL
+ : pch + 1;
+ }
+
+ if (NULL != dlls)
+ free(dlls);
+ dlls = dll = NULL;
+
+ /*
+ * Hand off to each provider in turn until one returns a PPS
+ * handle or they've all declined.
+ */
+ for (prov = g_provider_list; prov != NULL; prov = prov->next) {
+ ppshandle = 0;
+ g_curr_provider = prov;
+ err = (*prov->ptime_pps_create)(winhandle, &ppshandle);
+ g_curr_provider = NULL;
+ fprintf(stderr, "%s prov_time_pps_create(%p) returned %d\n",
+ prov->short_name, winhandle, err);
+ if (!err && ppshandle) {
+ *phandle = ppshandle;
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "PPSAPI provider list %p\n", g_provider_list);
+
+ RETURN_PPS_ERRNO(ENOEXEC);
+}
+
+
+/*
+ * release PPS handle
+ */
+
+static inline int
+time_pps_destroy(
+ pps_handle_t handle
+ )
+{
+ pps_unit_t * punit;
+ int err;
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ err = (*punit->provider->ptime_pps_destroy)(punit, punit->context);
+
+ free(punit);
+
+ if (err)
+ RETURN_PPS_ERRNO(err);
+ else
+ return 0;
+}
+
+/*
+ * set parameters for handle
+ */
+
+static inline int
+time_pps_setparams(
+ pps_handle_t handle,
+ const pps_params_t *params
+ )
+{
+ pps_unit_t * punit;
+ int err;
+
+ /*
+ * Check for valid arguments and set parameters.
+ */
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (NULL == params)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ err = (*punit->provider->ptime_pps_setparams)(punit, punit->context, params);
+
+ if (err)
+ RETURN_PPS_ERRNO(err);
+ else
+ return 0;
+}
+
+/*
+ * get parameters for handle
+ */
+
+static inline int
+time_pps_getparams(
+ pps_handle_t handle,
+ pps_params_t *params_buf
+ )
+{
+ pps_unit_t * punit;
+
+ /*
+ * Check for valid arguments and get parameters.
+ */
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (NULL == params_buf)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ *params_buf = punit->params;
+ return 0;
+}
+
+
+/*
+ * time_pps_getcap - get capabilities for handle
+ */
+static inline int
+time_pps_getcap(
+ pps_handle_t handle,
+ int *pmode
+ )
+{
+ pps_unit_t * punit;
+
+ /*
+ * Check for valid arguments and get capabilities.
+ */
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (NULL == pmode)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ *pmode = punit->provider->caps;
+ return 0;
+}
+
+/*
+ * Fetch timestamps
+ */
+
+static inline int
+time_pps_fetch(
+ pps_handle_t handle,
+ const int tsformat,
+ pps_info_t * pinfo,
+ const struct timespec * ptimeout
+ )
+{
+ pps_unit_t * punit;
+ int err;
+
+ /*
+ * Check for valid arguments and fetch timestamps
+ */
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ if (NULL == pinfo)
+ RETURN_PPS_ERRNO(EFAULT);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ err = (*punit->provider->ptime_pps_fetch)(punit,
+ punit->context,
+ tsformat,
+ pinfo,
+ ptimeout);
+
+ if (err)
+ RETURN_PPS_ERRNO(err);
+ else
+ 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
+ )
+{
+ pps_unit_t * punit;
+ int err;
+
+ if (!handle)
+ RETURN_PPS_ERRNO(EBADF);
+
+ punit = unit_from_ppsapi_handle(handle);
+
+ if (NULL == punit)
+ RETURN_PPS_ERRNO(EBADF);
+
+ err = (*punit->provider->ptime_pps_kcbind)(
+ punit,
+ punit->context,
+ kernel_consumer,
+ edge,
+ tsformat);
+
+ if (err)
+ RETURN_PPS_ERRNO(err);
+ else
+ return 0;
+}
+
+
+#endif /* TIMEPPS_H */