summaryrefslogtreecommitdiff
path: root/ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c')
-rw-r--r--ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c465
1 files changed, 465 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;
+}