#ifndef TIMEPPS_H
#define TIMEPPS_H
#include "sys/time.h"
#define PPS_API_VERS_1 1
#define PPS_JAN_1970 2208988800UL
#define PPS_NANOSECOND 1000000000L
#define PPS_FRAC 4294967296.
#define PPS_HECTONANOSECONDS 10000000
#define PPS_FILETIME_1970 0x019db1ded53e8000
#define PPS_NORMALIZE(x) \
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) \
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) \
do { \
double d_frac; \
\
\
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)
#define PPS_CAPTUREASSERT 0x01
#define PPS_CAPTURECLEAR 0x02
#define PPS_CAPTUREBOTH 0x03
#define PPS_OFFSETASSERT 0x10
#define PPS_OFFSETCLEAR 0x20
#define PPS_OFFSETBOTH 0x30
#define PPS_CANWAIT 0x100
#define PPS_CANPOLL 0x200
#define PPS_ECHOASSERT 0x40
#define PPS_ECHOCLEAR 0x80
#define PPS_TSFMT_TSPEC 0x1000
#define PPS_TSFMT_NTPFP 0x2000
#define PPS_TSFMT_BOTH (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
#define PPS_KC_HARDPPS 0
#define PPS_KC_HARDPPS_PLL 1
#define PPS_KC_HARDPPS_FLL 2
typedef unsigned long pps_seq_t;
#pragma warning(push)
typedef struct ntp_fp {
union {
int32 s;
u_int32 u;
} I;
union {
int32 s;
u_int32 u;
} F;
} ntp_fp_t;
#pragma warning(pop)
typedef union pps_timeu {
struct timespec tspec;
ntp_fp_t ntpfp;
unsigned long longpad[3];
} pps_timeu_t;
static void
ntpfp_add(
ntp_fp_t *op1r,
const ntp_fp_t *op2 )
{
op1r->F.u += op2->F.u;
op1r->I.u += op2->I.u + (op1r->F.u < op2->F.u);
}
#define NTPFP_L_ADDS ntpfp_add
typedef struct pps_info {
pps_seq_t assert_sequence;
pps_seq_t clear_sequence;
pps_timeu_t assert_tu;
pps_timeu_t clear_tu;
int current_mode;
} 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
typedef struct pps_params {
int api_version;
int mode;
pps_timeu_t assert_off_tu;
pps_timeu_t clear_off_tu;
} 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
#include <windows.h>
#include <errno.h>
#include <stddef.h>
#include <io.h>
#ifndef EOPNOTSUPP
#define EOPNOTSUPP 45
#endif
typedef UINT_PTR pps_handle_t;
#ifndef inline
#define inline __inline
#endif
typedef enum ppsapi_magic_tag {
PPSAPI_MAGIC_UNIT = 0x70707355,
} ppsapi_magic;
typedef struct {
struct pps_provider_tag *provider;
void * context;
ppsapi_magic magic;
pps_params_t params;
} 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
);
#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,
pps_handle_t *phandle
);
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;
}
static __inline int
pps_set_errno(
int e)
{
SetLastError(NO_ERROR);
errno = e;
return -1;
}
#define RETURN_PPS_ERRNO(e) return pps_set_errno(e)
#ifdef OWN_PPS_NTP_TIMESTAMP_FROM_COUNTER
extern void pps_ntp_timestamp_from_counter(ntp_fp_t *, ULONGLONG, ULONGLONG);
#else
static inline void
pps_ntp_timestamp_from_counter(
ntp_fp_t *result,
ULONGLONG Timestamp,
ULONGLONG Counterstamp)
{
ULONGLONG BiasedTimestamp;
(void)Counterstamp;
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;
}
static inline int
time_pps_create(
int filedes,
pps_handle_t * phandle
)
{
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);
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;
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);
}
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;
}
static inline int
time_pps_setparams(
pps_handle_t handle,
const pps_params_t *params
)
{
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);
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;
}
static inline int
time_pps_getparams(
pps_handle_t handle,
pps_params_t *params_buf
)
{
pps_unit_t * punit;
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;
}
static inline int
time_pps_getcap(
pps_handle_t handle,
int *pmode
)
{
pps_unit_t * punit;
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;
}
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;
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;
}
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