diff options
Diffstat (limited to 'ports/winnt/include/timepps.h')
-rw-r--r-- | ports/winnt/include/timepps.h | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/ports/winnt/include/timepps.h b/ports/winnt/include/timepps.h new file mode 100644 index 000000000000..76b1bfada1ab --- /dev/null +++ b/ports/winnt/include/timepps.h @@ -0,0 +1,811 @@ +/*********************************************************************** + * * + * 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 { \ + 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_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))->fractional \ + * PPS_NANOSECOND / PPS_FRAC; \ + (x).tv_sec = ((ntp_fp_t *)&(x))->integral \ + - (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 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 + +/* + *------ 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->integral = PPS_JAN_1970 + + (unsigned)(BiasedTimestamp / PPS_HECTONANOSECONDS); + result->fractional = + (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 */ |