summaryrefslogtreecommitdiff
path: root/daemons/gptp/windows
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/gptp/windows')
-rw-r--r--daemons/gptp/windows/daemon_cl/daemon_cl.cpp129
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.cpp424
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.hpp187
-rw-r--r--daemons/gptp/windows/daemon_cl/tsc.hpp13
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.cpp180
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.hpp114
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.cpp106
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.hpp74
-rw-r--r--daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp2
9 files changed, 1185 insertions, 44 deletions
diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
index 5bb2f3c5..8da3ddfa 100644
--- a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
+++ b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
@@ -44,6 +44,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <tchar.h>
#include <iphlpapi.h>
+#include <ether_port.hpp>
+#include <wireless_port.hpp>
+#include <intel_wireless.hpp>
+
/* Generic PCH delays */
#define PHY_DELAY_GB_TX_PCH 7750 //1G delay
#define PHY_DELAY_GB_RX_PCH 7750 //1G delay
@@ -125,9 +129,11 @@ int _tmain(int argc, _TCHAR* argv[])
CommonPort::NEIGHBOR_PROP_DELAY_THRESH;
bool syntonize = false;
+ bool wireless = false;
uint8_t priority1 = 248;
int i;
int phy_delays[4] = { -1, -1, -1, -1 };
+ uint8_t addr_ostr[ETHER_ADDR_OCTETS];
// Register default network interface
WindowsPCAPNetworkInterfaceFactory *default_factory = new WindowsPCAPNetworkInterfaceFactory();
@@ -140,6 +146,8 @@ int _tmain(int argc, _TCHAR* argv[])
portInit.condition_factory = new WindowsConditionFactory();
WindowsNamedPipeIPC *ipc = new WindowsNamedPipeIPC();
WindowsTimerQueueFactory *timerq_factory = new WindowsTimerQueueFactory();
+ CommonPort *port;
+ WindowsWirelessAdapter *wl_adapter = NULL;
if( !ipc->init() ) {
delete ipc;
@@ -155,49 +163,106 @@ int _tmain(int argc, _TCHAR* argv[])
/* Process optional arguments */
for( i = 1; i < argc; ++i ) {
- if( ispunct(argv[i][0]) ) {
- if( toupper( argv[i][1] ) == 'H' ) {
- print_usage( argv[0] );
+ if (ispunct(argv[i][0]))
+ {
+ if (toupper(argv[i][1]) == 'H') {
+ print_usage(argv[0]);
return -1;
}
- else if( toupper( argv[i][1] ) == 'R' ) {
- if( i+1 >= argc ) {
- printf( "Priority 1 value must be specified on "
- "command line, using default value\n" );
- } else {
- unsigned long tmp = strtoul( argv[i+1], NULL, 0 ); ++i;
- if( tmp > 254 ) {
- printf( "Invalid priority 1 value, using "
- "default value\n" );
- } else {
- priority1 = (uint8_t) tmp;
+ if (toupper(argv[i][1]) == 'W')
+ {
+ wireless = true;
+ }
+ else if (toupper(argv[i][1]) == 'R') {
+ if (i + 1 >= argc) {
+ printf("Priority 1 value must be specified on "
+ "command line, using default value\n");
+ }
+ else {
+ unsigned long tmp = strtoul(argv[i + 1], NULL, 0); ++i;
+ if (tmp > 255) {
+ printf("Invalid priority 1 value, using "
+ "default value\n");
+ }
+ else {
+ priority1 = (uint8_t)tmp;
}
}
}
+ } else
+ {
+ break;
}
}
- // the last argument is supposed to be a MAC address, so decrement argv index to read it
- i--;
+ // Parse local HW MAC address
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ portInit.net_label = new LinkLayerAddress(addr_ostr);
+ } else
+ {
+ printf("Local hardware MAC address required");
+ return -1;
+ }
+
+ if( wireless )
+ {
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ portInit.virtual_label = new LinkLayerAddress(addr_ostr);
+ } else
+ {
+ printf("Wireless operation requires local virtual MAC address");
+ return -1;
+ }
+ }
+
+ if (!wireless)
+ {
+ // Create HWTimestamper object
+ portInit.timestamper = new WindowsEtherTimestamper();
+ } else
+ {
+ portInit.timestamper = new WindowsWirelessTimestamper();
+ (static_cast<WindowsWirelessTimestamper *> (portInit.timestamper))->setAdapter(new IntelWirelessAdapter());
+ }
- // Create Low level network interface object
- uint8_t local_addr_ostr[ETHER_ADDR_OCTETS];
- parseMacAddr( argv[i], local_addr_ostr );
- LinkLayerAddress local_addr(local_addr_ostr);
- portInit.net_label = &local_addr;
- // Create HWTimestamper object
- portInit.timestamper = new WindowsTimestamper();
// Create Clock object
- portInit.clock = new IEEE1588Clock( false, false, priority1, timerq_factory, ipc, portInit.lock_factory ); // Do not force slave
- // Create Port Object linked to clock and low level
- portInit.phy_delay = &ether_phy_delay;
- EtherPort *port = new EtherPort( &portInit );
- port->setLinkSpeed(findLinkSpeed(&local_addr));
- if ( !port->init_port() ) {
- printf( "Failed to initialize port\n" );
- return -1;
+ portInit.clock = new IEEE1588Clock(false, false, priority1, timerq_factory, ipc, portInit.lock_factory); // Do not force slave
+
+ if (!wireless)
+ {
+ // Create Port Object linked to clock and low level
+ portInit.phy_delay = &ether_phy_delay;
+ EtherPort *eport = new EtherPort(&portInit);
+ eport->setLinkSpeed( findLinkSpeed( static_cast <LinkLayerAddress *> ( portInit.net_label )));
+ port = eport;
+ if (!eport->init_port()) {
+ printf("Failed to initialize port\n");
+ return -1;
+ }
+ port->processEvent(POWERUP);
+ } else
+ {
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ LinkLayerAddress peer_addr(addr_ostr);
+ port = new WirelessPort(&portInit, peer_addr);
+ (static_cast <WirelessTimestamper *> (portInit.timestamper))->setPort( static_cast<WirelessPort *> ( port ));
+ if (!port->init_port()) {
+ printf("Failed to initialize port\n");
+ return -1;
+ }
+ port->processEvent(POWERUP);
+ } else
+ {
+ printf("Wireless operation requires remote MAC address");
+ return -1;
+ }
}
- port->processEvent( POWERUP );
// Wait for Ctrl-C
if( !SetConsoleCtrlHandler( ctrl_handler, true )) {
diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.cpp b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp
new file mode 100644
index 00000000..a6f8f99f
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp
@@ -0,0 +1,424 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <intel_wireless.hpp>
+#include <windows_hal.hpp>
+#include <work_queue.hpp>
+#include <memory>
+#include <map>
+
+#define INTEL_EVENT_OFFSET 0x1141
+#define GP2_ROLLOVER 4294967296ULL
+
+GetAdapterList_t GetAdapterList;
+WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest;
+WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa;
+RegisterIntelCallback_t RegisterIntelCallback;
+DeregisterIntelCallback_t DeregisterIntelCallback;
+
+struct TimestamperContext
+{
+ WindowsWorkQueue work_queue;
+ WindowsWirelessTimestamper *timestamper;
+ ~TimestamperContext()
+ {
+ work_queue.stop();
+ }
+};
+
+typedef std::map< LinkLayerAddress, TimestamperContext > TimestamperContextMap;
+
+class LockedTimestamperContextMap
+{
+public:
+ LockedTimestamperContextMap()
+ {
+ InitializeSRWLock(&lock);
+ }
+ TimestamperContextMap map;
+ SRWLOCK lock;
+};
+
+LockedTimestamperContextMap timestamperContextMap;
+HANDLE IntelTimestamperThread;
+bool IntelTimestamperThreadStop = false;
+
+void IntelWirelessTimestamperEventHandler( INTEL_EVENT iEvent, void *pContext )
+{
+ WindowsWirelessTimestamper *timestamper;
+ IntelWirelessAdapter *adapter;
+ LockedTimestamperContextMap *timestamperContextMap =
+ (LockedTimestamperContextMap *)pContext;
+ WirelessTimestamperCallbackArg *arg =
+ new WirelessTimestamperCallbackArg();
+ int vendor_extension_copy_length;
+ bool error = false;
+ bool submit = false; // If true submit the event for later processing
+
+ // We share driver callback with other events, subtract the offset for
+ // timing measurement related events and check this is time sync event
+ iEvent.eType =
+ (WIRELESS_EVENT_TYPE) (iEvent.eType - INTEL_EVENT_OFFSET);
+ switch( iEvent.eType )
+ {
+ default:
+ return;
+ case TIMINGMSMT_CONFIRM_EVENT:
+ case TIMINGMSMT_EVENT:
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ break;
+ }
+
+ LinkLayerAddress event_source( iEvent.BtMacAddress );
+ arg->iEvent_type = (WIRELESS_EVENT_TYPE) iEvent.eType;
+ AcquireSRWLockShared( &timestamperContextMap->lock );
+ if( timestamperContextMap->map.find( event_source )
+ != timestamperContextMap->map.cend())
+ {
+ arg->timestamper =
+ timestamperContextMap->map[event_source].timestamper;
+ } else
+ {
+ goto bail;
+ }
+
+ timestamper = dynamic_cast <WindowsWirelessTimestamper *>
+ (arg->timestamper);
+ if( timestamper == NULL )
+ {
+ GPTP_LOG_ERROR( "Unexpected timestamper type encountered" );
+ goto bail;
+ }
+ adapter = dynamic_cast <IntelWirelessAdapter *>
+ (timestamper->getAdapter( ));
+ if( adapter == NULL )
+ {
+ GPTP_LOG_ERROR( "Unexpected adapter type encountered" );
+ goto bail;
+ }
+
+ // The driver implementation makes no guarantee of the lifetime of the
+ // iEvent object we make a copy and delete the copy when processing is
+ // complete
+ switch( arg->iEvent_type )
+ {
+ case TIMINGMSMT_CONFIRM_EVENT:
+ arg->event_data.confirm =
+ *(TIMINGMSMT_CONFIRM_EVENT_DATA *)iEvent.data;
+ arg->event_data.confirm.T1 =
+ adapter->calc_rollover( arg->event_data.confirm.T1 );
+ arg->event_data.confirm.T4 =
+ adapter->calc_rollover( arg->event_data.confirm.T4 );
+ submit = true;
+
+ break;
+
+ case TIMINGMSMT_EVENT:
+ arg->event_data.indication.indication =
+ *(TIMINGMSMT_EVENT_DATA *)iEvent.data;
+ arg->event_data.indication.indication.T2 =
+ adapter->calc_rollover
+ ( arg->event_data.indication.indication.T2/100 );
+ arg->event_data.indication.indication.T3 =
+ adapter->calc_rollover
+ ( arg->event_data.indication.indication.T3/100 );
+ // Calculate copy length, at most followup message
+ // (See S8021AS indication)
+ vendor_extension_copy_length = arg->event_data.
+ indication.indication.WiFiVSpecHdr.Length - 1;
+ vendor_extension_copy_length -= vendor_extension_copy_length -
+ sizeof(arg->event_data.indication.followup) > 0 ?
+ vendor_extension_copy_length -
+ sizeof(arg->event_data.indication.followup) : 0;
+ // Copy the rest of the vendor specific field
+ memcpy( arg->event_data.indication.followup,
+ ((BYTE *)iEvent.data) +
+ sizeof( arg->event_data.indication.indication ),
+ vendor_extension_copy_length );
+ submit = true;
+ break;
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ AcquireSRWLockExclusive(&adapter->ct_lock);
+ if (adapter->ct_miss > 0)
+ {
+ --adapter->ct_miss;
+ } else
+ {
+ arg->event_data.ptm_wa =
+ *(WIRELESS_CORRELATEDTIME *)iEvent.data;
+ arg->event_data.ptm_wa.LocalClk =
+ adapter->calc_rollover
+ (arg->event_data.ptm_wa.LocalClk);
+ adapter->ct_done = true;
+ // No need to schedule this, it returns immediately
+ WirelessTimestamperCallback(arg);
+ WakeAllConditionVariable(&adapter->ct_cond);
+ }
+ ReleaseSRWLockExclusive(&adapter->ct_lock);
+
+ break;
+ }
+
+ if (submit &&
+ !timestamperContextMap->map[event_source].work_queue.submit
+ ( WirelessTimestamperCallback, arg ))
+ GPTP_LOG_ERROR("Failed to submit WiFi event");
+bail:
+ ReleaseSRWLockShared(&timestamperContextMap->lock);
+}
+
+DWORD WINAPI IntelWirelessLoop(LPVOID arg)
+{
+ // Register for callback
+ INTEL_CALLBACK tempCallback;
+ tempCallback.fnIntelCallback = IntelWirelessTimestamperEventHandler;
+ tempCallback.pContext = arg;
+
+ if (RegisterIntelCallback(&tempCallback) != S_OK) {
+ GPTP_LOG_ERROR( "Failed to register WiFi callback" );
+ return 0;
+ }
+
+ while (!IntelTimestamperThreadStop)
+ {
+ SleepEx(320, true);
+ }
+
+ DeregisterIntelCallback(tempCallback.fnIntelCallback);
+
+ return 0;
+}
+
+bool IntelWirelessAdapter::initialize(void)
+{
+ HMODULE MurocApiDLL = LoadLibrary("MurocApi.dll");
+
+ GetAdapterList = (GetAdapterList_t)
+ GetProcAddress(MurocApiDLL, "GetAdapterList");
+ if( GetAdapterList == NULL )
+ return false;
+
+ RegisterIntelCallback = (RegisterIntelCallback_t)
+ GetProcAddress(MurocApiDLL, "RegisterIntelCallback");
+ if( RegisterIntelCallback == NULL )
+ return false;
+
+ DeregisterIntelCallback = (DeregisterIntelCallback_t)
+ GetProcAddress(MurocApiDLL, "DeregisterIntelCallback");
+ if( DeregisterIntelCallback == NULL )
+ return false;
+
+ WifiCmdTimingPtmWa = (WifiCmdTimingPtmWa_t)
+ GetProcAddress(MurocApiDLL, "WifiCmdTimingPtmWa");
+ if( WifiCmdTimingPtmWa == NULL )
+ return false;
+
+ WifiCmdTimingMeasurementRequest = (WifiCmdTimingMeasurementRequest_t)
+ GetProcAddress(MurocApiDLL, "WifiCmdTimingMeasurementRequest");
+ if (WifiCmdTimingMeasurementRequest == NULL)
+ return false;
+
+ // Initialize crosstimestamp condition
+ InitializeConditionVariable(&ct_cond);
+ InitializeSRWLock(&ct_lock);
+ ct_miss = 0;
+
+ // Spawn thread
+ IntelTimestamperThread = CreateThread
+ ( NULL, 0, IntelWirelessLoop, &timestamperContextMap, 0, NULL);
+ return true;
+}
+
+void IntelWirelessAdapter::shutdown(void) {
+ // Signal thread exit
+ IntelTimestamperThreadStop = true;
+ // Join thread
+ WaitForSingleObject(IntelTimestamperThread, INFINITE);
+}
+
+bool IntelWirelessAdapter::refreshCrossTimestamp(void) {
+ INTEL_WIFI_HEADER header;
+ WIRELESS_CORRELATEDTIME ptm_wa;
+ DWORD wait_return = TRUE;
+ DWORD start = 0;
+
+ AcquireSRWLockExclusive(&ct_lock);
+ ct_done = false;
+ header.dwSize = sizeof(ptm_wa);
+ header.dwStructVersion = INTEL_STRUCT_VER_LATEST;
+ HRESULT hres = WifiCmdTimingPtmWa(hAdapter, &header, &ptm_wa);
+ if (hres != S_OK)
+ return false;
+
+ while (!ct_done && wait_return == TRUE) {
+ DWORD now = GetTickCount();
+ start = start == 0 ? now : start;
+ DWORD wait = now - start < CORRELATEDTIME_TRANSACTION_TIMEOUT ?
+ CORRELATEDTIME_TRANSACTION_TIMEOUT -
+ (now - start) : 0;
+ wait_return = SleepConditionVariableSRW
+ ( &ct_cond, &ct_lock, wait, 0 );
+ }
+ ct_miss += wait_return != TRUE ? 1 : 0;
+ ReleaseSRWLockExclusive(&ct_lock);
+
+ return wait_return == TRUE ? true : false;
+}
+
+bool IntelWirelessAdapter::initiateTimingRequest
+( TIMINGMSMT_REQUEST *tm_request )
+{
+ INTEL_WIFI_HEADER header;
+ HRESULT err;
+
+ memset(&header, 0, sizeof(header));
+ // Proset wants an equivalent, but slightly different struct definition
+ header.dwSize = sizeof(*tm_request) - sizeof(PTP_SPEC) + 1;
+ header.dwStructVersion = INTEL_STRUCT_VER_LATEST;
+ if(( err = WifiCmdTimingMeasurementRequest
+ (hAdapter, &header, tm_request)) != S_OK )
+ return false;
+
+ return true;
+}
+
+// Find Intel adapter given MAC address and return adapter ID
+//
+// @adapter(out): Adapter identifier used for all driver interaction
+// @mac_addr: HW address of the adapter
+// @return: *false* if adapter is not found or driver communication failure
+// occurs, otherwise *true*
+//
+bool IntelWirelessAdapter::attachAdapter(uint8_t *mac_addr)
+{
+ PINTEL_ADAPTER_LIST adapter_list;
+ int i;
+
+ if (GetAdapterList(&adapter_list) != S_OK)
+ return false;
+
+ GPTP_LOG_VERBOSE("Driver query returned %d adapters\n",
+ adapter_list->count);
+ for (i = 0; i < adapter_list->count; ++i) {
+ if( memcmp(adapter_list->adapter[i].btMACAddress,
+ mac_addr, ETHER_ADDR_OCTETS) == 0 )
+ break;
+ }
+
+ if (i == adapter_list->count)
+ {
+ GPTP_LOG_ERROR("Driver failed to find the requested adapter");
+ return false;
+ }
+ hAdapter = adapter_list->adapter[i].hAdapter;
+
+ return true;
+}
+
+// Register timestamper with Intel wireless subsystem.
+//
+// @timestamper: timestamper object that should receive events
+// @source: MAC address of local interface
+// @return: *false* if the MAC address has already been registered, *true* if
+//
+bool IntelWirelessAdapter::registerTimestamper
+(WindowsWirelessTimestamper *timestamper)
+{
+ bool ret = false;
+
+ AcquireSRWLockExclusive(&timestamperContextMap.lock);
+ // Do not "re-add" the same timestamper
+ if( timestamperContextMap.map.find
+ ( *timestamper->getPort()->getLocalAddr() )
+ == timestamperContextMap.map.cend())
+ {
+ // Initialize per timestamper context and add
+ timestamperContextMap.map
+ [*timestamper->getPort()->getLocalAddr()].
+ work_queue.init(0);
+ timestamperContextMap.map
+ [*timestamper->getPort()->getLocalAddr()].
+ timestamper = timestamper;
+ ret = true;
+ }
+ ReleaseSRWLockExclusive(&timestamperContextMap.lock);
+
+ return ret;
+}
+
+bool IntelWirelessAdapter::deregisterTimestamper
+( WindowsWirelessTimestamper *timestamper )
+{
+ bool ret;
+
+ TimestamperContextMap::iterator iter;
+ AcquireSRWLockExclusive(&timestamperContextMap.lock);
+ // Find timestamper
+ iter = timestamperContextMap.map.find
+ ( *timestamper->getPort()->getLocalAddr( ));
+ if( iter != timestamperContextMap.map.end( ))
+ {
+ // Shutdown work queue
+ iter->second.work_queue.stop();
+ // Remove timestamper from list
+ timestamperContextMap.map.erase(iter);
+ ret = true;
+ }
+ ReleaseSRWLockExclusive(&timestamperContextMap.lock);
+
+ return ret;
+}
+
+uint64_t IntelWirelessAdapter::calc_rollover( uint64_t gp2 )
+{
+ gp2 = gp2 & 0xFFFFFFFF;
+ uint64_t l_gp2_ext;
+
+ if (gp2_last == ULLONG_MAX)
+ {
+ gp2_last = gp2;
+
+ return gp2;
+ }
+
+ if (gp2 < GP2_ROLLOVER/4 && gp2_last > (GP2_ROLLOVER*3)/4)
+ ++gp2_ext;
+
+ l_gp2_ext = gp2_ext;
+ if( gp2_last < GP2_ROLLOVER / 4 && gp2 >(GP2_ROLLOVER * 3) / 4 )
+ --l_gp2_ext;
+ else
+ gp2_last = gp2;
+
+ return gp2 + ( l_gp2_ext * GP2_ROLLOVER );
+}
diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.hpp b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp
new file mode 100644
index 00000000..bad97d71
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp
@@ -0,0 +1,187 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#ifndef INTEL_WIRELESS_HPP
+#define INTEL_WIRELESS_HPP
+
+#include <windows_hal.hpp>
+#include <Windows.h>
+#include <stdint.h>
+
+#define INTEL_MAC_ADDR_LENGTH 6
+#define INTEL_MAX_ADAPTERS 20
+#define CORRELATEDTIME_TRANSACTION_TIMEOUT 110/*ms*/
+
+enum _WIRELESS_EVENT_TYPE;
+
+typedef struct INTEL_EVENT
+{
+ BYTE BtMacAddress[INTEL_MAC_ADDR_LENGTH + 1];
+ ///< Source of event (MAC address)
+ _WIRELESS_EVENT_TYPE eType; ///< Event type
+ HRESULT hErrCode; ///< Error code
+ DWORD dwSize;
+ BYTE* data;
+} INTEL_EVENT, *PINTEL_EVENT;
+
+typedef void(*INTEL_EVENT_CALLBACK) ( INTEL_EVENT iEvent, void *pContext );
+
+typedef struct INTEL_CALLBACK
+{
+ INTEL_EVENT_CALLBACK fnIntelCallback;
+ void *pContext;
+
+} INTEL_CALLBACK, *PINTEL_CALLBACK;
+
+/// Adapter handle
+typedef DWORD HADAPTER;
+
+//intel Header structure
+typedef struct _INTEL_WIFI_HEADER
+{
+ DWORD dwStructVersion;
+ //Structure version for which this header is created
+ DWORD dwSize;
+ //size of the structure for which this header is created
+ DWORD dwFlags; //For future use
+ DWORD dwReserved; //For future use
+} INTEL_WIFI_HEADER, *PINTEL_WIFI_HEADER;
+
+typedef enum INTEL_ADAPTER_TYPE
+{
+ eStone, // Stone Peak - without BT Support
+ eStoneBT, // Stone Peak - with BT Support
+ eSnowfield // Snowfield Peak
+} INTEL_ADAPTER_TYPE;
+
+#define INTEL_ADAPTER_NAME_SIZE 64
+#define INTEL_ADAPTER_DESCRIPTION_SIZE 64
+#define INTEL_ADAPTER_CLSGUID_SIZE 64
+#define INTEL_REGPATH_SIZE 512
+
+#define INTEL_STRUCT_VER_LATEST 156
+
+typedef enum INTEL_ADAPTER_PROTOCOL
+{
+ eUnknownProtocol, ///< Unknown adapter.
+ e802_11, ///< WiFi
+} INTEL_ADAPTER_PROTOCOL;
+
+typedef struct INTEL_ADAPTER_LIST_ENTRY
+{
+ HADAPTER hAdapter;
+ ///< Adapter handle
+ UCHAR btMACAddress[INTEL_MAC_ADDR_LENGTH];
+ ///< Adapter MAC address
+ CHAR szAdapterName[INTEL_ADAPTER_NAME_SIZE];
+ ///< Adapter OS CLSGUID
+ CHAR szAdapterDescription[INTEL_ADAPTER_DESCRIPTION_SIZE];
+ ///< Display name
+ CHAR szAdapterClsguid[INTEL_ADAPTER_CLSGUID_SIZE];
+ ///< Muroc CLSGUID
+ CHAR szRegistryPath[INTEL_REGPATH_SIZE];
+ ///< Adapter registry root
+ CHAR szPnPId[INTEL_REGPATH_SIZE]; ///< Plug-and-Play ID
+ BOOL bEnabled; ///< enabled in driver
+ INTEL_ADAPTER_TYPE eAdapterType; ///< Adapter type
+ INTEL_ADAPTER_PROTOCOL eAdapterProtocol; ///< Adapter type
+} INTEL_ADAPTER_LIST_ENTRY, *PINTEL_ADAPTER_LIST_ENTRY;
+
+typedef struct INTEL_ADAPTER_LIST
+{
+ int count; ///< Number of entries
+ INTEL_ADAPTER_LIST_ENTRY adapter[INTEL_MAX_ADAPTERS];
+ ///< Array of adapter entries
+} INTEL_ADAPTER_LIST, *PINTEL_ADAPTER_LIST;
+
+
+typedef HRESULT ( APIENTRY *GetAdapterList_t )
+ ( PINTEL_ADAPTER_LIST* ppAdapterList );
+typedef HRESULT ( APIENTRY *WifiCmdTimingMeasurementRequest_t )
+ ( HADAPTER, PINTEL_WIFI_HEADER, void * );
+typedef HRESULT ( APIENTRY *WifiCmdTimingPtmWa_t )
+ (HADAPTER, PINTEL_WIFI_HEADER, void *);
+typedef HRESULT ( APIENTRY *RegisterIntelCallback_t ) ( PINTEL_CALLBACK );
+typedef HRESULT ( APIENTRY *DeregisterIntelCallback_t )
+ ( INTEL_EVENT_CALLBACK );
+
+extern GetAdapterList_t GetAdapterList;
+extern WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest;
+extern WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa;
+extern RegisterIntelCallback_t RegisterIntelCallback;
+extern DeregisterIntelCallback_t DeregisterIntelCallback;
+
+typedef struct _TIMINGMSMT_REQUEST *tm_request_t;
+typedef struct WirelessTimestamperCallbackArg
+*WirelessTimestamperCallbackArg_t;
+typedef void( *WirelessTimestamperCallback_t )
+( WirelessTimestamperCallbackArg_t arg );
+
+class IntelWirelessAdapter : public WindowsWirelessAdapter
+{
+private:
+ HADAPTER hAdapter;
+
+ // Get correlated time operation may timeout
+ // Use condition wait to manage this
+ SRWLOCK ct_lock;
+ CONDITION_VARIABLE ct_cond;
+ bool ct_done;
+ int ct_miss;
+
+ uint64_t gp2_last;
+ unsigned gp2_ext;
+
+public:
+ bool initialize( void );
+ void shutdown( void );
+ bool refreshCrossTimestamp();
+ bool initiateTimingRequest( TIMINGMSMT_REQUEST *tm_request );
+ bool attachAdapter( uint8_t *mac_addr );
+ bool registerTimestamper( WindowsWirelessTimestamper *timestamper );
+ bool deregisterTimestamper( WindowsWirelessTimestamper *timestamper );
+ IntelWirelessAdapter();
+
+ uint64_t calc_rollover( uint64_t gp2 );
+
+ friend void IntelWirelessTimestamperEventHandler
+ ( INTEL_EVENT iEvent, void *pContext );
+};
+
+inline IntelWirelessAdapter::IntelWirelessAdapter()
+{
+ gp2_ext = 0;
+ gp2_last = ULLONG_MAX;
+}
+
+#endif
diff --git a/daemons/gptp/windows/daemon_cl/tsc.hpp b/daemons/gptp/windows/daemon_cl/tsc.hpp
index ed22752c..425205d3 100644
--- a/daemons/gptp/windows/daemon_cl/tsc.hpp
+++ b/daemons/gptp/windows/daemon_cl/tsc.hpp
@@ -95,13 +95,14 @@ inline uint32_t FindFrequencyByModel(uint8_t model_query) {
/**
* @brief Gets the TSC frequnecy
- * @param millis time in miliseconds
+ * @param builtin whether device is connected to the Intel bus (not PCIE)
* @return TSC frequency, or 0 on error
*/
-inline uint64_t getTSCFrequency( unsigned millis ) {
+inline uint64_t getTSCFrequency( bool builtin ) {
int max_cpuid_level;
int tmp[4];
BOOL is_windows_10;
+ LARGE_INTEGER freq;
// Find the max cpuid level, and if possible find clock info
__cpuid(tmp, 0);
@@ -124,9 +125,11 @@ inline uint64_t getTSCFrequency( unsigned millis ) {
// clock will be returned, *else* use QPC for everything else
//
// EAX (tmp[0]) must be >= 1, See Intel SDM 17.15.4 "Invariant Time-keeping"
- if (is_windows_10 &&
+ if (!is_windows_10 &&
max_cpuid_level >= CLOCK_INFO_CPUID_LEAF &&
- tmp[0] >= 1) {
+ tmp[0] >= 1 &&
+ builtin )
+ {
SYSTEM_INFO info;
GetSystemInfo(&info);
@@ -135,8 +138,6 @@ inline uint64_t getTSCFrequency( unsigned millis ) {
return FindFrequencyByModel(info.wProcessorRevision >> 8);
}
- LARGE_INTEGER freq;
-
if (QueryPerformanceFrequency(&freq))
return freq.QuadPart;
diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.cpp b/daemons/gptp/windows/daemon_cl/windows_hal.cpp
index a05d473c..3d034e39 100644
--- a/daemons/gptp/windows/daemon_cl/windows_hal.cpp
+++ b/daemons/gptp/windows/daemon_cl/windows_hal.cpp
@@ -67,8 +67,184 @@ VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ) {
ReleaseSRWLockExclusive( &arg->queue->retiredTimersLock );
}
+inline uint64_t scale64(uint64_t i, uint32_t m, uint32_t n)
+{
+ uint64_t tmp, res, rem;
+
+ rem = i % n;
+ i /= n;
+
+ res = i * m;
+ tmp = rem * m;
+
+ tmp /= n;
+
+ return res + tmp;
+}
+
+void WirelessTimestamperCallback( LPVOID arg )
+{
+ WirelessTimestamperCallbackArg *larg =
+ (WirelessTimestamperCallbackArg *)arg;
+ WindowsWirelessTimestamper *timestamper =
+ dynamic_cast<WindowsWirelessTimestamper *> (larg->timestamper);
+ WirelessDialog tmp1, tmp2;
+ LinkLayerAddress *peer_addr = NULL;
+
+ if (timestamper == NULL)
+ {
+ GPTP_LOG_ERROR( "Wrong timestamper type: %p",
+ larg->timestamper );
+ return;
+ }
+
+ switch( larg->iEvent_type )
+ {
+ default:
+ case TIMINGMSMT_CONFIRM_EVENT:
+ tmp1.action_devclk = larg->event_data.confirm.T1;
+ tmp1.ack_devclk = larg->event_data.confirm.T4;
+ tmp1.dialog_token = (BYTE)larg->event_data.confirm.DialogToken;
+ GPTP_LOG_VERBOSE
+ ( "Got confirm, %hhu(%llu,%llu)", tmp1.dialog_token,
+ tmp1.action_devclk, tmp1.ack_devclk );
+ peer_addr = new LinkLayerAddress
+ ( larg->event_data.confirm.PeerMACAddress );
+ larg->timestamper->
+ timingMeasurementConfirmCB( *peer_addr, &tmp1 );
+
+ break;
+
+ case TIMINGMSMT_EVENT:
+ tmp1/*prev*/.action_devclk = larg->event_data.indication.
+ indication.T1;
+ tmp1/*prev*/.ack_devclk = larg->event_data.indication.
+ indication.T4;
+ tmp1/*prev*/.dialog_token = (BYTE)larg->event_data.indication.
+ indication.FollowUpDialogToken;
+ tmp2/*curr*/.action_devclk = larg->event_data.indication.
+ indication.T2;
+ tmp2/*curr*/.ack_devclk = larg->event_data.indication.
+ indication.T3;
+ tmp2/*curr*/.dialog_token = (BYTE)larg->event_data.indication.
+ indication.DialogToken;
+ GPTP_LOG_VERBOSE
+ ("Got indication, %hhu(%llu,%llu) %hhu(%llu,%llu)",
+ tmp1.dialog_token, tmp1.action_devclk,
+ tmp1.ack_devclk, tmp2.dialog_token,
+ tmp2.action_devclk, tmp2.ack_devclk);
+ peer_addr = new LinkLayerAddress(larg->event_data.indication.
+ indication.PeerMACAddress);
+
+ larg->timestamper->timeMeasurementIndicationCB
+ ( *peer_addr, &tmp2, &tmp1,
+ larg->event_data.indication.
+ indication.PtpSpec.fwup_data,
+ larg->event_data.indication.
+ indication.WiFiVSpecHdr.Length - (sizeof(PTP_SPEC) -
+ 1));
+
+ break;
+
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ timestamper->system_counter =
+ scale64( larg->event_data.ptm_wa.TSC, NS_PER_SECOND,
+ (uint32_t)timestamper->tsc_hz.QuadPart );
+ timestamper->system_time.set64(timestamper->system_counter);
+ // Scale from TM timescale to nanoseconds
+ larg->event_data.ptm_wa.LocalClk *= 10;
+ timestamper->device_time.set64
+ (larg->event_data.ptm_wa.LocalClk*10);
+
+ break;
+ }
+
+ delete peer_addr;
+}
+
+net_result WindowsWirelessTimestamper::_requestTimingMeasurement
+(TIMINGMSMT_REQUEST *timingmsmt_req)
+{
+ net_result ret = net_succeed;
+
+ if (!adapter->initiateTimingRequest(timingmsmt_req)) {
+ GPTP_LOG_ERROR("Failed to send timing measurement request\n");
+ ret = net_fatal;
+ }
+
+ return ret;
+}
+
+bool WindowsWirelessTimestamper::HWTimestamper_gettime
+( Timestamp *system_time,
+ Timestamp * device_time,
+ uint32_t * local_clock,
+ uint32_t * nominal_clock_rate ) const
+{
+ bool refreshed = adapter->refreshCrossTimestamp();
+ if (refreshed)
+ {
+ // We have a fresh cross-timestamp just use it
+ *system_time = this->system_time;
+ *device_time = this->device_time;
+ } else
+ {
+ // We weren't able to get a fresh timestamp,
+ // extrapolate from the last
+ LARGE_INTEGER tsc_now;
+ QueryPerformanceCounter(&tsc_now);
+ unsigned device_delta = (unsigned)
+ (((long double) (tsc_now.QuadPart - system_counter)) /
+ (((long double)tsc_hz.QuadPart) / 1000000000));
+ device_delta = (unsigned)(device_delta*getPort()->
+ getLocalSystemFreqOffset());
+ system_time->set64((uint64_t)
+ (((long double)tsc_now.QuadPart) /
+ ((long double)tsc_hz.QuadPart /
+ 1000000000)));
+ device_time->set64(device_delta);
+ *device_time = *device_time + this->device_time;
+ }
+
+ return true;
+}
+
+bool WindowsWirelessTimestamper::HWTimestamper_init
+(InterfaceLabel *iface_label, OSNetworkInterface *iface)
+{
+ uint8_t mac_addr_local[ETHER_ADDR_OCTETS];
+
+ if (!initialized) {
+ if (!adapter->initialize()) return false;
+ if (getPort()->getLocalAddr() == NULL)
+ return false;
+
+ getPort()->getLocalAddr()->toOctetArray(mac_addr_local);
+ if (!adapter->attachAdapter(mac_addr_local)) {
+ return false;
+ }
+
+ tsc_hz.QuadPart = getTSCFrequency(false);
+ if (tsc_hz.QuadPart == 0) {
+ return false;
+ }
+
+ if (!adapter->registerTimestamper(this))
+ return false;
+ }
+
+ initialized = true;
+ return true;
+}
+
+WindowsWirelessTimestamper::~WindowsWirelessTimestamper() {
+ if (adapter->deregisterTimestamper(this))
+ adapter->shutdown();
+ else
+ GPTP_LOG_INFO("Failed to shutdown time sync on adapter");
+}
-bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) {
+bool WindowsEtherTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) {
char network_card_id[64];
LinkLayerAddress *addr = dynamic_cast<LinkLayerAddress *>(iface_label);
if( addr == NULL ) return false;
@@ -112,7 +288,7 @@ bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetw
NULL, OPEN_EXISTING, 0, NULL );
if( miniport == INVALID_HANDLE_VALUE ) return false;
- tsc_hz.QuadPart = getTSCFrequency( 1000 );
+ tsc_hz.QuadPart = getTSCFrequency( true );
if( tsc_hz.QuadPart == 0 ) {
return false;
}
diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.hpp b/daemons/gptp/windows/daemon_cl/windows_hal.hpp
index f5192910..5f1a6f61 100644
--- a/daemons/gptp/windows/daemon_cl/windows_hal.hpp
+++ b/daemons/gptp/windows/daemon_cl/windows_hal.hpp
@@ -36,7 +36,6 @@
/**@file*/
-#include <minwindef.h>
#include <IPCListener.hpp>
#include "avbts_osnet.hpp"
#include "avbts_oslock.hpp"
@@ -47,6 +46,7 @@
#include "packet.hpp"
#include "ieee1588.hpp"
#include "ether_tstamper.hpp"
+#include "wireless_tstamper.hpp"
#include "iphlpapi.h"
#include "windows_ipc.hpp"
#include "tsc.hpp"
@@ -590,6 +590,114 @@ public:
}
};
+void WirelessTimestamperCallback(LPVOID arg);
+
+class WindowsWirelessAdapter;
+
+/**
+* @brief Windows Wireless (802.11) HWTimestamper implementation
+*/
+class WindowsWirelessTimestamper : public WirelessTimestamper
+{
+private:
+ WindowsWirelessAdapter *adapter;
+
+ uint64_t system_counter;
+ Timestamp system_time;
+ Timestamp device_time;
+ LARGE_INTEGER tsc_hz;
+ bool initialized;
+
+public:
+ WindowsWirelessTimestamper()
+ {
+ initialized = false;
+ }
+
+ net_result _requestTimingMeasurement
+ ( TIMINGMSMT_REQUEST *timingmsmt_req );
+
+ bool HWTimestamper_gettime
+ ( Timestamp *system_time, Timestamp * device_time,
+ uint32_t * local_clock, uint32_t * nominal_clock_rate ) const;
+
+ virtual bool HWTimestamper_init
+ ( InterfaceLabel *iface_label, OSNetworkInterface *iface );
+
+ /**
+ * @brief attach adapter to timestamper
+ * @param adapter [in] adapter to attach
+ */
+ void setAdapter( WindowsWirelessAdapter *adapter )
+ {
+ this->adapter = adapter;
+ }
+
+ /**
+ * @brief get attached adapter
+ * @return attached adapter
+ */
+ WindowsWirelessAdapter *getAdapter(void)
+ {
+ return adapter;
+ }
+
+ ~WindowsWirelessTimestamper();
+
+ friend void WirelessTimestamperCallback( LPVOID arg );
+};
+
+class WindowsWirelessAdapter
+{
+public:
+ /**
+ * @brief initiate wireless TM request (completion is asynchronous)
+ * @param tm_request [in] pointer to TM request object
+ * @return true on success
+ */
+ virtual bool initiateTimingRequest(TIMINGMSMT_REQUEST *tm_request) = 0;
+
+ /**
+ * @brief attempt to refresh cross timestamp (extrapolate on failure)
+ * @return true on success
+ */
+ virtual bool refreshCrossTimestamp() = 0;
+
+ /**
+ * @brief register timestamper with adapter
+ * @param timestamper [in] timestamper object
+ * @return true on success
+ */
+ virtual bool registerTimestamper
+ ( WindowsWirelessTimestamper *timestamper ) = 0;
+
+ /**
+ * @brief deregister timestamper
+ * @param timestamper [in] timestamper object
+ * @return true on success
+ */
+ virtual bool deregisterTimestamper
+ ( WindowsWirelessTimestamper *timestamper ) = 0;
+
+ /**
+ * @brief initialize adapter object
+ * @return true on success
+ */
+ virtual bool initialize() = 0;
+
+ /**
+ * @brief shutdown adapter
+ */
+ virtual void shutdown() = 0;
+
+ /**
+ * @brief attach adapter to MAC address
+ * @param mac_addr [in] MAC address to attach to
+ * @return true on success
+ */
+ virtual bool attachAdapter( uint8_t *mac_addr ) = 0;
+};
+
#define I217_DESC "I217-LM"
#define I219_DESC "I219-V"
@@ -616,9 +724,9 @@ static DeviceClockRateMapping DeviceClockRateMap[] =
};
/**
- * @brief Windows HWTimestamper implementation
+ * @brief Windows Ethernet HWTimestamper implementation
*/
-class WindowsTimestamper : public EtherTimestamper {
+class WindowsEtherTimestamper : public EtherTimestamper {
private:
// No idea whether the underlying implementation is thread safe
HANDLE miniport;
diff --git a/daemons/gptp/windows/daemon_cl/work_queue.cpp b/daemons/gptp/windows/daemon_cl/work_queue.cpp
new file mode 100644
index 00000000..461d6923
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/work_queue.cpp
@@ -0,0 +1,106 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <work_queue.hpp>
+#include <stdio.h>
+
+
+struct WWQueueThreadState {
+ bool running;
+ bool stop;
+ WWQueueCallback task;
+ LPVOID arg;
+};
+
+DWORD WINAPI WindowsWorkQueueLoop(LPVOID arg) {
+ WWQueueThreadState *state = (WWQueueThreadState *)arg;
+ state->running = true;
+ while (!state->stop) {
+ if (state->task != NULL) {
+ state->task(state->arg);
+ delete state->arg;
+ state->task = NULL;
+ }
+ Sleep(1);
+ }
+ state->running = false;
+
+ return 0;
+}
+
+bool WindowsWorkQueue::init(int number_threads)
+{
+ if (number_threads == 0) number_threads = DEFAULT_THREAD_COUNT;
+ state = new WWQueueThreadState[number_threads];
+ for (int i = 0; i < number_threads; ++i) {
+ state[i].running = false;
+ state[i].stop = false;
+ state[i].task = NULL;
+ }
+ workers = new HANDLE[number_threads];
+ for (int i = 0; i < number_threads; ++i) {
+ workers[i] = CreateThread(NULL, 0, WindowsWorkQueueLoop, state + i, 0, NULL);
+ if (workers[i] == INVALID_HANDLE_VALUE)
+ return false;
+ while (!state[i].running)
+ Sleep(1);
+ }
+ this->number_threads = number_threads;
+ return true;
+}
+
+bool WindowsWorkQueue::submit(WWQueueCallback cb, LPVOID arg)
+{
+ int i;
+
+ for (i = 0; i < number_threads; ++i) {
+ if (state[i].task == NULL) {
+ state[i].arg = arg;
+ state[i].task = cb;
+ break;
+ }
+ }
+ if (i == number_threads)
+ return false;
+
+ return true;
+}
+
+void WindowsWorkQueue::stop()
+{
+ for (int i = 0; i < number_threads; ++i) {
+ state[i].stop = true;
+ while (state[i].running)
+ Sleep(1);
+ }
+}
diff --git a/daemons/gptp/windows/daemon_cl/work_queue.hpp b/daemons/gptp/windows/daemon_cl/work_queue.hpp
new file mode 100644
index 00000000..4ac750c5
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/work_queue.hpp
@@ -0,0 +1,74 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#ifndef WORK_QUEUE_HPP
+#define WORK_QUEUE_HPP
+
+#include <WTypesbase.h>
+
+#define DEFAULT_THREAD_COUNT (5)
+
+struct WWQueueThreadState;
+
+typedef void(*WWQueueCallback)(LPVOID arg);
+
+class WindowsWorkQueue
+{
+private:
+ HANDLE *workers;
+ WWQueueThreadState *state;
+ int number_threads;
+
+public:
+ /**
+ * @brief initialize work queue
+ * @param number_threads [in] number of threads (0 = default)
+ * @return true on success
+ */
+ bool init( int number_threads );
+
+ /**
+ * @brief submit job to work queue
+ * @param cb [in] function to call
+ * @param arg [in] parameter provided to callback
+ * @return true on success
+ */
+ bool submit( WWQueueCallback cb, LPVOID arg );
+
+ /**
+ * @brief stop work queue
+ */
+ void stop();
+};
+
+#endif/*WORK_QUEUE_HPP*/
diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
index 713a2b4a..9a34a4ef 100644
--- a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
+++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
@@ -60,7 +60,7 @@ int _tmain(int argc, _TCHAR* argv[])
strcpy_s( pipename, 64, PIPE_PREFIX );
strcat_s( pipename, 64-strlen(pipename), P802_1AS_PIPENAME );
HANDLE pipe;
- uint64_t tsc_frequency = getTSCFrequency( 1000 );
+ uint64_t tsc_frequency = getTSCFrequency( true );
// Wait for Ctrl-C
if( !SetConsoleCtrlHandler( ctrl_handler, true )) {