diff options
Diffstat (limited to 'ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c')
-rw-r--r-- | ports/winnt/ppsapi/loopback/src/loopback-ppsapi.c | 465 |
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(¶ms->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(¶ms->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; +} |