/***********************************************************************
* *
* 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 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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>