/*
* skeleton-ppsapi-provider.c - structure but no useful function
*/
/***********************************************************************
* *
* 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 skeleton-ppsapi-provider.c implements the PPSAPI provider DLL *
* interface but has no actual timestamp-fetching code. It is *
* derived from serialpps-ppsapi-provider.c which was derived from *
* David L. Mills' timepps.h for Solaris. *
* *
***********************************************************************
* *
* 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 *
* *
**********************************************************************/
#include "skeleton-ppsapi-provider.h"
pcreate_pps_handle p_create_pps_handle;
ppps_ntp_timestamp_from_counter p_ntp_timestamp_from_counter;
#define SKELPPS_CAPS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT \
| PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)
#define SKELPPS_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.
*/
typedef struct skel_unit_tag {
HANDLE device;
ppps_ntp_timestamp_from_counter p_ntp_timestamp_from_counter;
} skel_unit;
/*
* DllMain - DLL entrypoint, no-op.
*/
BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
UNUSED(hModule);
UNUSED(ul_reason_for_call);
UNUSED(lpReserved);
return TRUE;
}
/*
* 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 */
)
{
skel_unit * pskelunit;
pps_unit_t * punit;
/*
* Allocate and initialize unit structure.
*/
pskelunit = malloc(sizeof(*pskelunit));
if (NULL == pskelunit)
return ENOMEM;
pskelunit->device = device;
pskelunit->p_ntp_timestamp_from_counter = p_ntp_timestamp_from_counter;
*handle = (*p_create_pps_handle)(pskelunit);
if (*handle) {
punit = (pps_unit_t *)*handle;
punit->params.api_version = PPS_API_VERS_1;
punit->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
}
return (*handle)
? 0
: ENOMEM;
}
/*
* prov_time_pps_destroy - release PPS handle
*/
int WINAPI
prov_time_pps_destroy(
pps_unit_t * unit,
void * context
)
{
skel_unit *pskelunit;
UNUSED(unit);
pskelunit = context;
free(pskelunit);
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
)
{
skel_unit *pskelunit;
int mode, mode_in;
pskelunit = context;
/*
* 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;
/*
* The only settable modes are PPS_CAPTUREASSERT,
* PPS_OFFSETASSERT, and the timestamp formats.
*/
mode_in = params->mode;
/*
* Only one of the time formats may be selected
* if a nonzero assert offset is supplied.
*/
if ((mode_in & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP))
== (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) {
if (params->assert_offset.tv_sec ||
params->assert_offset.tv_nsec)
return EINVAL;
/*
* If no offset was specified but both time
* format flags are used consider it harmless
* but turn off PPS_TSFMT_NTPFP so getparams
* will not show both formats lit.
*/
mode_in &= ~PPS_TSFMT_NTPFP;
}
/* turn off read-only bits */
mode_in &= ~SKELPPS_RO;
/*
* test remaining bits, should only have captureassert,
* offsetassert, and/or timestamp format bits.
*/
if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP))
return EOPNOTSUPP;
/*
* ok, ready to go.
*/
mode = unit->params.mode;
unit->params = *params;
unit->params.mode = mode | mode_in;
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
)
{
ULONGLONG WindowsTimestamp;
ULONGLONG Counterstamp;
skel_unit *pskelunit;
pps_info_t infobuf;
/*
* nb. PPS_CANWAIT is NOT set by the implementation, we can totally
* ignore the timeout variable.
*/
UNUSED(timeout);
pskelunit = context;
memset(&infobuf, 0, sizeof(infobuf));
/*
* if not captureassert, nothing to return.
*/
if (!(unit->params.mode & PPS_CAPTUREASSERT)) {
*pinfo = infobuf;
return 0;
}
/*
* ADD CODE to retrieve timestamp here.
*/
WindowsTimestamp = Counterstamp = 0;
/*
* ADD CODE to retrieve timestamp here.
*/
/*
* pps_ntp_timestamp_from_counter takes the two flavors
* of timestamp we have (counter and system time) and
* uses whichever it can to give the best NTP fixed-point
* conversion. In ntpd the Counterstamp is typically
* used. A stub implementation in timepps.h simply
* converts from Windows timestamp to NTP fixed-point.
* We call through a pointer to get ntpd's version.
*/
(*pskelunit->p_ntp_timestamp_from_counter)(
&infobuf.assert_timestamp_ntpfp,
WindowsTimestamp,
Counterstamp);
/*
* Note that only assert timestamps
* are captured by this interface.
*/
infobuf.assert_sequence = 0; /* ADD CODE */
/*
* Apply offset and translate to specified format
*/
switch (tsformat) {
case PPS_TSFMT_NTPFP: /* NTP format requires no translation */
if (unit->params.mode & PPS_OFFSETASSERT) {
NTPFP_L_ADDS(&infobuf.assert_timestamp_ntpfp,
&unit->params.assert_offset_ntpfp);
}
break;
case PPS_TSFMT_TSPEC: /* timespec format requires conversion to nsecs form */
PPS_NTPTOTSPEC(infobuf.assert_timestamp);
if (unit->params.mode & PPS_OFFSETASSERT) {
infobuf.assert_timestamp.tv_sec +=
unit->params.assert_offset.tv_sec;
infobuf.assert_timestamp.tv_nsec +=
unit->params.assert_offset.tv_nsec;
PPS_NORMALIZE(infobuf.assert_timestamp);
}
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
)
{
ppsapi_provider test_prov;
if (ppsapi_timepps_prov_ver < PPSAPI_TIMEPPS_PROV_VER)
return 0;
p_create_pps_handle = create_pps_handle;
p_ntp_timestamp_from_counter = ntp_timestamp_from_counter;
strncpy(short_name_buf, "skeleton", short_name_size);
strncpy(full_name_buf,
"skeleton, ADD CODE to make useful",
full_name_size);
/*
* Use function pointer prototypes from timepps.h to verify
* our prototypes match with some otherwise pointless code.
*/
test_prov.ptime_pps_create = &prov_time_pps_create;
test_prov.ptime_pps_destroy = &prov_time_pps_destroy;
test_prov.ptime_pps_fetch = &prov_time_pps_fetch;
test_prov.ptime_pps_kcbind = &prov_time_pps_kcbind;
test_prov.ptime_pps_setparams = &prov_time_pps_setparams;
return SKELPPS_CAPS;
}