blob: f3bdc0e9d30e37c157e223e9b7adb7b07a3d7790 [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* 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 copyright holder 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 HOLDER 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 "precomp.h"
#include "otApi.tmh"
//#define DEBUG_ASYNC_IO
using namespace std;
// The maximum time we will wait for an overlapped result. Essentially, the maximum
// amount of time each synchronous IOCTL should take.
const DWORD c_MaxOverlappedWaitTimeMS = 5 * 1000;
// Version string returned by the API
const char c_Version[] = "Windows"; // TODO - What should we really put here?
template<class CallbackT>
class otCallback
{
public:
RTL_REFERENCE_COUNT CallbackRefCount;
HANDLE CallbackCompleteEvent;
GUID InterfaceGuid;
CallbackT Callback;
PVOID CallbackContext;
otCallback(
CallbackT _Callback,
PVOID _CallbackContext
) :
CallbackRefCount(1),
CallbackCompleteEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
Callback(_Callback),
CallbackContext(_CallbackContext)
{
}
otCallback(
const GUID& _InterfaceGuid,
CallbackT _Callback,
PVOID _CallbackContext
) :
CallbackRefCount(1),
CallbackCompleteEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
InterfaceGuid(_InterfaceGuid),
Callback(_Callback),
CallbackContext(_CallbackContext)
{
}
~otCallback()
{
if (CallbackCompleteEvent) CloseHandle(CallbackCompleteEvent);
}
void AddRef()
{
RtlIncrementReferenceCount(&CallbackRefCount);
}
void Release(bool waitForShutdown = false)
{
if (RtlDecrementReferenceCount(&CallbackRefCount))
{
// Set completion event if there are no more refs
SetEvent(CallbackCompleteEvent);
}
if (waitForShutdown)
{
WaitForSingleObject(CallbackCompleteEvent, INFINITE);
}
}
};
typedef otCallback<otDeviceAvailabilityChangedCallback> otApiDeviceAvailabilityCallback;
typedef otCallback<otHandleActiveScanResult> otApiActiveScanCallback;
typedef otCallback<otHandleEnergyScanResult> otApiEnergyScanCallback;
typedef otCallback<otStateChangedCallback> otApiStateChangeCallback;
typedef otCallback<otCommissionerEnergyReportCallback> otApiCommissionerEnergyReportCallback;
typedef otCallback<otCommissionerPanIdConflictCallback> otApiCommissionerPanIdConflictCallback;
typedef otCallback<otJoinerCallback> otApiJoinerCallback;
typedef struct otApiInstance
{
// Handle to the driver
HANDLE DeviceHandle;
// Async IO variables
OVERLAPPED Overlapped;
PTP_WAIT ThreadpoolWait;
// Notification variables
CRITICAL_SECTION CallbackLock;
OTLWF_NOTIFICATION NotificationBuffer;
// Callbacks
otApiDeviceAvailabilityCallback* DeviceAvailabilityCallbacks;
vector<otApiActiveScanCallback*> ActiveScanCallbacks;
vector<otApiEnergyScanCallback*> EnergyScanCallbacks;
vector<otApiActiveScanCallback*> DiscoverCallbacks;
vector<otApiStateChangeCallback*> StateChangedCallbacks;
vector<otApiCommissionerEnergyReportCallback*> CommissionerEnergyReportCallbacks;
vector<otApiCommissionerPanIdConflictCallback*> CommissionerPanIdConflictCallbacks;
vector<otApiJoinerCallback*> JoinerCallbacks;
// Constructor
otApiInstance() :
DeviceHandle(INVALID_HANDLE_VALUE),
Overlapped({0}),
ThreadpoolWait(nullptr),
DeviceAvailabilityCallbacks(nullptr)
{
InitializeCriticalSection(&CallbackLock);
}
~otApiInstance()
{
DeleteCriticalSection(&CallbackLock);
}
// Helper function to set a callback
template<class CallbackT>
bool
SetCallback(
vector<otCallback<CallbackT>*> &Callbacks,
const GUID& InterfaceGuid,
CallbackT Callback,
PVOID CallbackContext
)
{
bool alreadyExists = false;
otCallback<CallbackT>* CallbackToRelease = nullptr;
EnterCriticalSection(&CallbackLock);
if (Callback == nullptr)
{
for (size_t i = 0; i < Callbacks.size(); i++)
{
if (Callbacks[i]->InterfaceGuid == InterfaceGuid)
{
CallbackToRelease = Callbacks[i];
Callbacks.erase(Callbacks.begin() + i);
break;
}
}
}
else
{
for (size_t i = 0; i < Callbacks.size(); i++)
{
if (Callbacks[i]->InterfaceGuid == InterfaceGuid)
{
alreadyExists = true;
break;
}
}
if (!alreadyExists)
{
Callbacks.push_back(new otCallback<CallbackT>(InterfaceGuid, Callback, CallbackContext));
}
}
LeaveCriticalSection(&CallbackLock);
if (CallbackToRelease)
{
CallbackToRelease->Release(true);
delete CallbackToRelease;
}
return !alreadyExists;
}
} otApiInstance;
typedef struct otInstance
{
otApiInstance *ApiHandle; // Pointer to the Api handle
NET_IFINDEX InterfaceIndex; // Interface Index
NET_LUID InterfaceLuid; // Interface Luid
GUID InterfaceGuid; // Interface guid
ULONG CompartmentID; // Interface Compartment ID
} otInstance;
// otpool wait callback for async IO completion
VOID CALLBACK
otIoComplete(
_Inout_ PTP_CALLBACK_INSTANCE Instance,
_Inout_opt_ PVOID Context,
_Inout_ PTP_WAIT Wait,
_In_ TP_WAIT_RESULT WaitResult
);
OTAPI
otApiInstance *
OTCALL
otApiInit(
)
{
DWORD dwError = ERROR_SUCCESS;
otApiInstance *aApitInstance = nullptr;
LogFuncEntry(API_DEFAULT);
aApitInstance = new(std::nothrow)otApiInstance();
if (aApitInstance == nullptr)
{
dwError = GetLastError();
LogWarning(API_DEFAULT, "Failed to allocate otApiInstance");
goto error;
}
// Open the pipe to the OpenThread driver
aApitInstance->DeviceHandle =
CreateFile(
OTLWF_IOCLT_PATH,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
FILE_FLAG_OVERLAPPED, // Allow asynchronous requests
nullptr
);
if (aApitInstance->DeviceHandle == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
LogError(API_DEFAULT, "CreateFile failed, %!WINERROR!", dwError);
goto error;
}
// Create event for completion of async IO
aApitInstance->Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (aApitInstance->Overlapped.hEvent == nullptr)
{
dwError = GetLastError();
LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
goto error;
}
// Create the otpool wait
aApitInstance->ThreadpoolWait =
CreateThreadpoolWait(
otIoComplete,
aApitInstance,
nullptr
);
if (aApitInstance->ThreadpoolWait == nullptr)
{
dwError = GetLastError();
LogError(API_DEFAULT, "CreateThreadpoolWait failed, %!WINERROR!", dwError);
goto error;
}
// Start the otpool waiting on the overlapped event
SetThreadpoolWait(aApitInstance->ThreadpoolWait, aApitInstance->Overlapped.hEvent, nullptr);
#ifdef DEBUG_ASYNC_IO
LogVerbose(API_DEFAULT, "Querying for 1st notification");
#endif
// Request first notification asynchronously
if (!DeviceIoControl(
aApitInstance->DeviceHandle,
IOCTL_OTLWF_QUERY_NOTIFICATION,
nullptr, 0,
&aApitInstance->NotificationBuffer, sizeof(OTLWF_NOTIFICATION),
nullptr,
&aApitInstance->Overlapped))
{
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
LogError(API_DEFAULT, "DeviceIoControl for first notification failed, %!WINERROR!", dwError);
goto error;
}
dwError = ERROR_SUCCESS;
}
error:
if (dwError != ERROR_SUCCESS)
{
otApiFinalize(aApitInstance);
aApitInstance = nullptr;
}
LogFuncExit(API_DEFAULT);
return aApitInstance;
}
OTAPI
void
OTCALL
otApiFinalize(
_In_ otApiInstance *aApitInstance
)
{
if (aApitInstance == nullptr) return;
LogFuncEntry(API_DEFAULT);
// If we never got the handle, nothing left to clean up
if (aApitInstance->DeviceHandle != INVALID_HANDLE_VALUE)
{
//
// Make sure we unregister callbacks
//
EnterCriticalSection(&aApitInstance->CallbackLock);
otApiDeviceAvailabilityCallback* DeviceAvailabilityCallbacks = aApitInstance->DeviceAvailabilityCallbacks;
aApitInstance->DeviceAvailabilityCallbacks = nullptr;
vector<otApiActiveScanCallback*> ActiveScanCallbacks(aApitInstance->ActiveScanCallbacks);
aApitInstance->ActiveScanCallbacks.clear();
vector<otApiEnergyScanCallback*> EnergyScanCallbacks(aApitInstance->EnergyScanCallbacks);
aApitInstance->EnergyScanCallbacks.clear();
vector<otApiActiveScanCallback*> DiscoverCallbacks(aApitInstance->DiscoverCallbacks);
aApitInstance->DiscoverCallbacks.clear();
vector<otApiStateChangeCallback*> StateChangedCallbacks(aApitInstance->StateChangedCallbacks);
aApitInstance->StateChangedCallbacks.clear();
vector<otApiCommissionerEnergyReportCallback*> CommissionerEnergyReportCallbacks(aApitInstance->CommissionerEnergyReportCallbacks);
aApitInstance->CommissionerEnergyReportCallbacks.clear();
vector<otApiCommissionerPanIdConflictCallback*> CommissionerPanIdConflictCallbacks(aApitInstance->CommissionerPanIdConflictCallbacks);
aApitInstance->CommissionerPanIdConflictCallbacks.clear();
vector<otApiJoinerCallback*> JoinerCallbacks(aApitInstance->JoinerCallbacks);
aApitInstance->JoinerCallbacks.clear();
#ifdef DEBUG_ASYNC_IO
LogVerbose(API_DEFAULT, "Clearing Threadpool Wait");
#endif
// Clear the threadpool wait to prevent further waits from being scheduled
PTP_WAIT tpWait = aApitInstance->ThreadpoolWait;
aApitInstance->ThreadpoolWait = nullptr;
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Clear all callbacks
if (DeviceAvailabilityCallbacks)
{
DeviceAvailabilityCallbacks->Release(true);
delete DeviceAvailabilityCallbacks;
}
for (size_t i = 0; i < ActiveScanCallbacks.size(); i++)
{
ActiveScanCallbacks[i]->Release(true);
delete ActiveScanCallbacks[i];
}
for (size_t i = 0; i < EnergyScanCallbacks.size(); i++)
{
EnergyScanCallbacks[i]->Release(true);
delete EnergyScanCallbacks[i];
}
for (size_t i = 0; i < DiscoverCallbacks.size(); i++)
{
DiscoverCallbacks[i]->Release(true);
delete DiscoverCallbacks[i];
}
for (size_t i = 0; i < StateChangedCallbacks.size(); i++)
{
StateChangedCallbacks[i]->Release(true);
delete StateChangedCallbacks[i];
}
for (size_t i = 0; i < CommissionerEnergyReportCallbacks.size(); i++)
{
CommissionerEnergyReportCallbacks[i]->Release(true);
delete CommissionerEnergyReportCallbacks[i];
}
for (size_t i = 0; i < CommissionerPanIdConflictCallbacks.size(); i++)
{
CommissionerPanIdConflictCallbacks[i]->Release(true);
delete CommissionerPanIdConflictCallbacks[i];
}
for (size_t i = 0; i < JoinerCallbacks.size(); i++)
{
JoinerCallbacks[i]->Release(true);
delete JoinerCallbacks[i];
}
// Clean up threadpool wait
if (tpWait)
{
#ifdef DEBUG_ASYNC_IO
LogVerbose(API_DEFAULT, "Waiting for outstanding threadpool callbacks to compelte");
#endif
// Cancel any queued waits and wait for any outstanding calls to compelte
WaitForThreadpoolWaitCallbacks(tpWait, TRUE);
#ifdef DEBUG_ASYNC_IO
LogVerbose(API_DEFAULT, "Cancelling any pending IO");
#endif
// Cancel any async IO
CancelIoEx(aApitInstance->DeviceHandle, &aApitInstance->Overlapped);
// Free the threadpool wait
CloseThreadpoolWait(tpWait);
}
// Clean up overlapped event
if (aApitInstance->Overlapped.hEvent)
{
CloseHandle(aApitInstance->Overlapped.hEvent);
}
// Close the device handle
CloseHandle(aApitInstance->DeviceHandle);
}
delete aApitInstance;
LogFuncExit(API_DEFAULT);
}
OTAPI
void
OTCALL
otFreeMemory(
_In_ const void *mem
)
{
free((void*)mem);
}
// Handles cleanly invoking the register callback
VOID
ProcessNotification(
_In_ otApiInstance *aApitInstance,
_In_ POTLWF_NOTIFICATION Notif
)
{
if (Notif->NotifType == OTLWF_NOTIF_DEVICE_AVAILABILITY)
{
otCallback<otDeviceAvailabilityChangedCallback>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
if (aApitInstance->DeviceAvailabilityCallbacks != nullptr)
{
aApitInstance->DeviceAvailabilityCallbacks->AddRef();
Callback = aApitInstance->DeviceAvailabilityCallbacks;
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->DeviceAvailabilityPayload.Available != FALSE,
&Notif->InterfaceGuid,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_STATE_CHANGE)
{
otCallback<otStateChangedCallback>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->StateChangedCallbacks.size(); i++)
{
if (aApitInstance->StateChangedCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->StateChangedCallbacks[i]->AddRef();
Callback = aApitInstance->StateChangedCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->StateChangePayload.Flags,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_DISCOVER)
{
otCallback<otHandleActiveScanResult>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->DiscoverCallbacks.size(); i++)
{
if (aApitInstance->DiscoverCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->DiscoverCallbacks[i]->AddRef();
Callback = aApitInstance->DiscoverCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->DiscoverPayload.Valid ? &Notif->DiscoverPayload.Results : nullptr,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_ACTIVE_SCAN)
{
otCallback<otHandleActiveScanResult>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->ActiveScanCallbacks.size(); i++)
{
if (aApitInstance->ActiveScanCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->ActiveScanCallbacks[i]->AddRef();
Callback = aApitInstance->ActiveScanCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->ActiveScanPayload.Valid ? &Notif->ActiveScanPayload.Results : nullptr,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_ENERGY_SCAN)
{
otCallback<otHandleEnergyScanResult>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->EnergyScanCallbacks.size(); i++)
{
if (aApitInstance->EnergyScanCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->EnergyScanCallbacks[i]->AddRef();
Callback = aApitInstance->EnergyScanCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->EnergyScanPayload.Valid ? &Notif->EnergyScanPayload.Results : nullptr,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT)
{
otCallback<otCommissionerEnergyReportCallback>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->CommissionerEnergyReportCallbacks.size(); i++)
{
if (aApitInstance->CommissionerEnergyReportCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->CommissionerEnergyReportCallbacks[i]->AddRef();
Callback = aApitInstance->CommissionerEnergyReportCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->CommissionerEnergyReportPayload.ChannelMask,
Notif->CommissionerEnergyReportPayload.EnergyList,
Notif->CommissionerEnergyReportPayload.EnergyListLength,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_COMMISSIONER_PANID_QUERY)
{
otCallback<otCommissionerPanIdConflictCallback>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->CommissionerPanIdConflictCallbacks.size(); i++)
{
if (aApitInstance->CommissionerPanIdConflictCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->CommissionerPanIdConflictCallbacks[i]->AddRef();
Callback = aApitInstance->CommissionerPanIdConflictCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
Callback->Callback(
Notif->CommissionerPanIdQueryPayload.PanId,
Notif->CommissionerPanIdQueryPayload.ChannelMask,
Callback->CallbackContext);
Callback->Release();
}
}
else if (Notif->NotifType == OTLWF_NOTIF_JOINER_COMPLETE)
{
otCallback<otJoinerCallback>* Callback = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
for (size_t i = 0; i < aApitInstance->JoinerCallbacks.size(); i++)
{
if (aApitInstance->JoinerCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
{
aApitInstance->JoinerCallbacks[i]->AddRef();
Callback = aApitInstance->JoinerCallbacks[i];
break;
}
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
// Invoke the callback outside the lock and release ref when done
if (Callback)
{
aApitInstance->SetCallback(
aApitInstance->JoinerCallbacks,
Notif->InterfaceGuid, (otJoinerCallback)nullptr, (PVOID)nullptr
);
Callback->Callback(
Notif->JoinerCompletePayload.Error,
Callback->CallbackContext);
Callback->Release();
}
}
else
{
// Unexpected notif type
}
}
// Threadpool wait callback for async IO completion
VOID CALLBACK
otIoComplete(
_Inout_ PTP_CALLBACK_INSTANCE /* Instance */,
_Inout_opt_ PVOID Context,
_Inout_ PTP_WAIT /* Wait */,
_In_ TP_WAIT_RESULT /* WaitResult */
)
{
#ifdef DEBUG_ASYNC_IO
LogFuncEntry(API_DEFAULT);
#endif
otApiInstance *aApitInstance = (otApiInstance*)Context;
if (aApitInstance == nullptr) return;
// Get the result of the IO operation
DWORD dwBytesTransferred = 0;
if (!GetOverlappedResult(
aApitInstance->DeviceHandle,
&aApitInstance->Overlapped,
&dwBytesTransferred,
FALSE))
{
DWORD dwError = GetLastError();
LogError(API_DEFAULT, "GetOverlappedResult for notification failed, %!WINERROR!", dwError);
}
else
{
LogVerbose(API_DEFAULT, "Received successful callback for notification, type=%d",
aApitInstance->NotificationBuffer.NotifType);
// Invoke the callback if set
ProcessNotification(aApitInstance, &aApitInstance->NotificationBuffer);
// Try to get the threadpool wait to see if we are allowed to continue processing notifications
EnterCriticalSection(&aApitInstance->CallbackLock);
PTP_WAIT tpWait = aApitInstance->ThreadpoolWait;
LeaveCriticalSection(&aApitInstance->CallbackLock);
if (tpWait)
{
// Start waiting for next notification
SetThreadpoolWait(tpWait, aApitInstance->Overlapped.hEvent, nullptr);
#ifdef DEBUG_ASYNC_IO
LogVerbose(API_DEFAULT, "Querying for next notification");
#endif
// Request next notification
if (!DeviceIoControl(
aApitInstance->DeviceHandle,
IOCTL_OTLWF_QUERY_NOTIFICATION,
nullptr, 0,
&aApitInstance->NotificationBuffer, sizeof(OTLWF_NOTIFICATION),
nullptr,
&aApitInstance->Overlapped))
{
DWORD dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
LogError(API_DEFAULT, "DeviceIoControl for new notification failed, %!WINERROR!", dwError);
}
}
}
}
#ifdef DEBUG_ASYNC_IO
LogFuncExit(API_DEFAULT);
#endif
}
DWORD
SendIOCTL(
_In_ otApiInstance *aApitInstance,
_In_ DWORD dwIoControlCode,
_In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_writes_bytes_opt_(nOutBufferSize) LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize
)
{
DWORD dwError = ERROR_SUCCESS;
OVERLAPPED Overlapped = { 0 };
DWORD dwBytesReturned = 0;
Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (Overlapped.hEvent == nullptr)
{
dwError = GetLastError();
LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
goto error;
}
// Send the IOCTL the OpenThread driver
if (!DeviceIoControl(
aApitInstance->DeviceHandle,
dwIoControlCode,
lpInBuffer, nInBufferSize,
lpOutBuffer, nOutBufferSize,
nullptr,
&Overlapped))
{
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
LogError(API_DEFAULT, "DeviceIoControl(0x%x) failed, %!WINERROR!", dwIoControlCode, dwError);
goto error;
}
dwError = ERROR_SUCCESS;
}
// Get the result of the IO operation
if (!GetOverlappedResultEx(
aApitInstance->DeviceHandle,
&Overlapped,
&dwBytesReturned,
c_MaxOverlappedWaitTimeMS,
FALSE
))
{
dwError = GetLastError();
if (dwError == WAIT_TIMEOUT)
{
dwError = ERROR_TIMEOUT;
CancelIoEx(aApitInstance->DeviceHandle, &Overlapped);
}
LogError(API_DEFAULT, "GetOverlappedResult failed, %!WINERROR!", dwError);
goto error;
}
if (dwBytesReturned != nOutBufferSize)
{
dwError = ERROR_INVALID_DATA;
LogError(API_DEFAULT, "GetOverlappedResult returned invalid output size, expected=%u actual=%u",
nOutBufferSize, dwBytesReturned);
goto error;
}
error:
if (Overlapped.hEvent)
{
CloseHandle(Overlapped.hEvent);
}
return dwError;
}
__pragma(pack(push,1))
template <class T1, class T2>
struct PackedBuffer2
{
T1 data1; T2 data2;
PackedBuffer2(const T1 &d1, const T2 &d2) : data1(d1), data2(d2) { }
};
template <class T1, class T2, class T3>
struct PackedBuffer3
{
T1 data1; T2 data2; T3 data3;
PackedBuffer3(const T1 &d1, const T2 &d2, const T3 &d3) : data1(d1), data2(d2), data3(d3) { }
};
template <class T1, class T2, class T3, class T4>
struct PackedBuffer4
{
T1 data1; T2 data2; T3 data3; T4 data4;
PackedBuffer4(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4) : data1(d1), data2(d2), data3(d3), data4(d4) { }
};
template <class T1, class T2, class T3, class T4, class T5>
struct PackedBuffer5
{
T1 data1; T2 data2; T3 data3; T4 data4; T5 data5;
PackedBuffer5(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4, const T5 &d5) : data1(d1), data2(d2), data3(d3), data4(d4), data5(d5) { }
};
template <class T1, class T2, class T3, class T4, class T5, class T6>
struct PackedBuffer6
{
T1 data1; T2 data2; T3 data3; T4 data4; T5 data5; T6 data6;
PackedBuffer6(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4, const T5 &d5, const T6 &d6) : data1(d1), data2(d2), data3(d3), data4(d4), data5(d5), data6(d6) { }
};
__pragma(pack(pop))
template <class in, class out>
DWORD
QueryIOCTL(
_In_ otInstance *aInstance,
_In_ DWORD dwIoControlCode,
_In_ const in *input,
_Out_ out* output
)
{
PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, *input);
return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), output, sizeof(out));
}
template <class out>
DWORD
QueryIOCTL(
_In_ otInstance *aInstance,
_In_ DWORD dwIoControlCode,
_Out_ out* output
)
{
return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &aInstance->InterfaceGuid, sizeof(GUID), output, sizeof(out));
}
template <class in>
DWORD
SetIOCTL(
_In_ otInstance *aInstance,
_In_ DWORD dwIoControlCode,
_In_ const in* input
)
{
PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, *input);
return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), nullptr, 0);
}
template <class in>
DWORD
SetIOCTL(
_In_ otInstance *aInstance,
_In_ DWORD dwIoControlCode,
_In_ const in input
)
{
PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, input);
return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), nullptr, 0);
}
DWORD
SetIOCTL(
_In_ otInstance *aInstance,
_In_ DWORD dwIoControlCode
)
{
return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &aInstance->InterfaceGuid, sizeof(GUID), nullptr, 0);
}
otError
DwordToThreadError(
DWORD dwError
)
{
if (((int)dwError) > 0)
{
return OT_ERROR_GENERIC;
}
else
{
return (otError)(-(int)dwError);
}
}
OTAPI
void
OTCALL
otSetDeviceAvailabilityChangedCallback(
_In_ otApiInstance *aApitInstance,
_In_ otDeviceAvailabilityChangedCallback aCallback,
_In_ void *aCallbackContext
)
{
otApiDeviceAvailabilityCallback* CallbackToRelease = nullptr;
EnterCriticalSection(&aApitInstance->CallbackLock);
if (aApitInstance->DeviceAvailabilityCallbacks != nullptr)
{
CallbackToRelease = aApitInstance->DeviceAvailabilityCallbacks;
aApitInstance->DeviceAvailabilityCallbacks = nullptr;
}
if (aCallback != nullptr)
{
aApitInstance->DeviceAvailabilityCallbacks =
new otApiDeviceAvailabilityCallback(aCallback, aCallbackContext);
}
LeaveCriticalSection(&aApitInstance->CallbackLock);
if (CallbackToRelease)
{
CallbackToRelease->Release(true);
delete CallbackToRelease;
}
}
OTAPI
otDeviceList*
OTCALL
otEnumerateDevices(
_In_ otApiInstance *aApitInstance
)
{
DWORD dwError = ERROR_SUCCESS;
OVERLAPPED Overlapped = { 0 };
DWORD dwBytesReturned = 0;
otDeviceList* pDeviceList = nullptr;
DWORD cbDeviceList = sizeof(otDeviceList);
LogFuncEntry(API_DEFAULT);
Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (Overlapped.hEvent == nullptr)
{
dwError = GetLastError();
LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
goto error;
}
pDeviceList = (otDeviceList*)malloc(cbDeviceList);
if (pDeviceList == nullptr)
{
LogWarning(API_DEFAULT, "Failed to allocate otDeviceList of %u bytes.", cbDeviceList);
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto error;
}
RtlZeroMemory(pDeviceList, cbDeviceList);
// Query in a loop to account for it changing between calls
while (true)
{
// Send the IOCTL to query the interfaces
if (!DeviceIoControl(
aApitInstance->DeviceHandle,
IOCTL_OTLWF_ENUMERATE_DEVICES,
nullptr, 0,
pDeviceList, cbDeviceList,
nullptr,
&Overlapped))
{
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
LogError(API_DEFAULT, "DeviceIoControl(IOCTL_OTLWF_ENUMERATE_DEVICES) failed, %!WINERROR!", dwError);
goto error;
}
dwError = ERROR_SUCCESS;
}
// Get the result of the IO operation
if (!GetOverlappedResultEx(
aApitInstance->DeviceHandle,
&Overlapped,
&dwBytesReturned,
c_MaxOverlappedWaitTimeMS,
TRUE))
{
dwError = GetLastError();
if (dwError == WAIT_TIMEOUT)
{
dwError = ERROR_TIMEOUT;
CancelIoEx(aApitInstance->DeviceHandle, &Overlapped);
}
LogError(API_DEFAULT, "GetOverlappedResult for notification failed, %!WINERROR!", dwError);
goto error;
}
// Calculate the expected size of the full buffer
cbDeviceList =
FIELD_OFFSET(otDeviceList, aDevices) +
pDeviceList->aDevicesLength * sizeof(otDeviceList::aDevices);
// Make sure they returned a complete buffer
if (dwBytesReturned != sizeof(otDeviceList::aDevicesLength)) break;
// If we get here that means we didn't have a big enough buffer
// Reallocate a new buffer
free(pDeviceList);
pDeviceList = (otDeviceList*)malloc(cbDeviceList);
if (pDeviceList == nullptr)
{
LogError(API_DEFAULT, "Failed to allocate otDeviceList of %u bytes.", cbDeviceList);
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto error;
}
RtlZeroMemory(pDeviceList, cbDeviceList);
}
error:
if (dwError != ERROR_SUCCESS)
{
free(pDeviceList);
pDeviceList = nullptr;
}
if (Overlapped.hEvent)
{
CloseHandle(Overlapped.hEvent);
}
LogFuncExitMsg(API_DEFAULT, "%d devices", pDeviceList == nullptr ? -1 : (int)pDeviceList->aDevicesLength);
return pDeviceList;
}
OTAPI
otInstance *
OTCALL
otInstanceInit(
_In_ otApiInstance *aApitInstance,
_In_ const GUID *aDeviceGuid
)
{
otInstance *aInstance = nullptr;
OTLWF_DEVICE Result = {0};
if (aApitInstance &&
SendIOCTL(
aApitInstance,
IOCTL_OTLWF_QUERY_DEVICE,
(LPVOID)aDeviceGuid,
sizeof(GUID),
&Result,
sizeof(Result)
) == ERROR_SUCCESS)
{
aInstance = (otInstance*)malloc(sizeof(otInstance));
if (aInstance)
{
aInstance->ApiHandle = aApitInstance;
aInstance->InterfaceGuid = *aDeviceGuid;
aInstance->CompartmentID = Result.CompartmentID;
if (ConvertInterfaceGuidToLuid(aDeviceGuid, &aInstance->InterfaceLuid) != ERROR_SUCCESS ||
ConvertInterfaceLuidToIndex(&aInstance->InterfaceLuid, &aInstance->InterfaceIndex) != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "Failed to convert interface guid to index!");
free(aInstance);
aInstance = nullptr;
}
}
}
return aInstance;
}
OTAPI
GUID
OTCALL
otGetDeviceGuid(
otInstance *aInstance
)
{
if (aInstance == nullptr) return {};
return aInstance->InterfaceGuid;
}
OTAPI
uint32_t
OTCALL
otGetDeviceIfIndex(
otInstance *aInstance
)
{
if (aInstance == nullptr) return (uint32_t)-1;
return aInstance->InterfaceIndex;
}
OTAPI
uint32_t
OTCALL
otGetCompartmentId(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return (uint32_t)-1;
return aInstance->CompartmentID;
}
OTAPI
const char *
OTCALL
otGetVersionString()
{
char* szVersion = (char*)malloc(sizeof(c_Version));
if (szVersion)
{
memcpy_s(szVersion, sizeof(c_Version), c_Version, sizeof(c_Version));
}
return szVersion;
}
OTAPI
otError
OTCALL
otIp6SetEnabled(
_In_ otInstance *aInstance,
bool aEnabled
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_INTERFACE, (BOOLEAN)aEnabled));
}
OTAPI
bool
OTCALL
otIp6IsEnabled(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_INTERFACE, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otThreadSetEnabled(
_In_ otInstance *aInstance,
bool aEnabled
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD, (BOOLEAN)aEnabled));
}
OTAPI
otError
OTCALL
otThreadSetAutoStart(
_In_ otInstance *aInstance,
bool aStartAutomatically
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD_AUTO_START, (BOOLEAN)(aStartAutomatically ? TRUE : FALSE)));
}
OTAPI
bool
OTCALL
otThreadGetAutoStart(
otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD_AUTO_START, &Result);
return Result != FALSE;
}
OTAPI
bool
OTCALL
otThreadIsSingleton(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_SINGLETON, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otLinkActiveScan(
_In_ otInstance *aInstance,
uint32_t aScanChannels,
uint16_t aScanDuration,
otHandleActiveScanResult aCallback,
_In_ void *aCallbackContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->ActiveScanCallbacks,
aInstance->InterfaceGuid, aCallback, aCallbackContext
);
PackedBuffer3<GUID,uint32_t,uint16_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aScanDuration);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ACTIVE_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
bool
OTCALL
otLinkIsActiveScanInProgress(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_SCAN, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otLinkEnergyScan(
_In_ otInstance *aInstance,
uint32_t aScanChannels,
uint16_t aScanDuration,
_In_ otHandleEnergyScanResult aCallback,
_In_ void *aCallbackContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->EnergyScanCallbacks,
aInstance->InterfaceGuid, aCallback, aCallbackContext
);
PackedBuffer3<GUID,uint32_t,uint16_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aScanDuration);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ENERGY_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
bool
OTCALL
otLinkIsEnergyScanInProgress(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ENERGY_SCAN, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otThreadDiscover(
_In_ otInstance *aInstance,
uint32_t aScanChannels,
uint16_t aPanid,
bool aJoiner,
bool aEnableEui64Filtering,
otHandleActiveScanResult aCallback,
void *aCallbackContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->DiscoverCallbacks,
aInstance->InterfaceGuid, aCallback, aCallbackContext
);
PackedBuffer5<GUID,uint32_t,uint16_t, uint8_t, uint8_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aPanid, aJoiner, aEnableEui64Filtering);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_DISCOVER, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
bool
OTCALL
otIsDiscoverInProgress(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = FALSE;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_DISCOVER, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otLinkSendDataRequest(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
UNREFERENCED_PARAMETER(aInstance);
return OT_ERROR_NOT_IMPLEMENTED; // TODO
}
OTAPI
uint8_t
OTCALL
otLinkGetChannel(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHANNEL, &Result);
return Result;
}
OTAPI
otError
OTCALL
otLinkSetChannel(
_In_ otInstance *aInstance,
uint8_t aChannel
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_CHANNEL, aChannel));
}
OTAPI
otError
OTCALL
otDatasetSetDelayTimerMinimal(
_In_ otInstance *aInstance,
uint32_t aDelayTimerMinimal
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
// TODO
UNREFERENCED_PARAMETER(aDelayTimerMinimal);
return OT_ERROR_NOT_IMPLEMENTED;
}
OTAPI
uint32_t
OTCALL
otDatasetGetDelayTimerMinimal(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return 0;
// TODO
return 0;
}
OTAPI
uint8_t
OTCALL
otThreadGetMaxAllowedChildren(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_CHILDREN, &Result);
return Result;
}
OTAPI
otError
OTCALL
otThreadSetMaxAllowedChildren(
_In_ otInstance *aInstance,
uint8_t aMaxChildren
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_CHILDREN, aMaxChildren));
}
OTAPI
uint32_t
OTCALL
otThreadGetChildTimeout(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_TIMEOUT, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetChildTimeout(
_In_ otInstance *aInstance,
uint32_t aTimeout
)
{
if (aInstance == nullptr) return;
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_TIMEOUT, aTimeout);
}
OTAPI
const
uint8_t *
OTCALL
otLinkGetExtendedAddress(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otExtAddress *Result = (otExtAddress*)malloc(sizeof(otExtAddress));
if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_ADDRESS, Result) != ERROR_SUCCESS)
{
free(Result);
Result = nullptr;
}
return (uint8_t*)Result;
}
OTAPI
otError
OTCALL
otLinkSetExtendedAddress(
_In_ otInstance *aInstance,
const otExtAddress *aExtendedAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_ADDRESS, aExtendedAddress));
}
OTAPI
const uint8_t *
OTCALL
otThreadGetExtendedPanId(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otExtendedPanId *Result = (otExtendedPanId*)malloc(sizeof(otExtendedPanId));
if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_PANID, Result) != ERROR_SUCCESS)
{
free(Result);
Result = nullptr;
}
return (uint8_t*)Result;
}
OTAPI
otError
OTCALL
otThreadSetExtendedPanId(
_In_ otInstance *aInstance,
const uint8_t *aExtendedPanId
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_PANID, (const otExtendedPanId*)aExtendedPanId));
}
OTAPI
void
OTCALL
otLinkGetFactoryAssignedIeeeEui64(
_In_ otInstance *aInstance,
_Out_ otExtAddress *aEui64
)
{
if (aInstance == nullptr) return;
(void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_FACTORY_EUI64, aEui64);
}
OTAPI
void
OTCALL
otLinkGetJoinerId(
_In_ otInstance *aInstance,
_Out_ otExtAddress *aHashMacAddress
)
{
if (aInstance == nullptr) return;
(void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_HASH_MAC_ADDRESS, aHashMacAddress);
}
OTAPI
otError
OTCALL
otThreadGetLeaderRloc(
_In_ otInstance *aInstance,
_Out_ otIp6Address *aLeaderRloc
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_RLOC, aLeaderRloc));
}
OTAPI
otLinkModeConfig
OTCALL
otThreadGetLinkMode(
_In_ otInstance *aInstance
)
{
otLinkModeConfig Result = {0};
static_assert(sizeof(otLinkModeConfig) == 4, "The size of otLinkModeConfig should be 4 bytes");
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LINK_MODE, &Result);
return Result;
}
OTAPI
otError
OTCALL
otThreadSetLinkMode(
_In_ otInstance *aInstance,
otLinkModeConfig aConfig
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
static_assert(sizeof(otLinkModeConfig) == 4, "The size of otLinkModeConfig should be 4 bytes");
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_LINK_MODE, aConfig));
}
OTAPI
const otMasterKey *
OTCALL
otThreadGetMasterKey(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otMasterKey *Result = (otMasterKey*)malloc(sizeof(otMasterKey));
if (Result == nullptr) return nullptr;
if (QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MASTER_KEY, Result) != ERROR_SUCCESS)
{
free(Result);
return nullptr;
}
return Result;
}
OTAPI
otError
OTCALL
otThreadSetMasterKey(
_In_ otInstance *aInstance,
const otMasterKey *aKey
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MASTER_KEY, aKey));
}
OTAPI
const uint8_t *
OTCALL
otThreadGetPSKc(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
uint8_t *Result = (uint8_t*)malloc(sizeof(otPSKc));
if (Result == nullptr) return nullptr;
if (QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PSKC, Result) != ERROR_SUCCESS)
{
free(Result);
return nullptr;
}
return Result;
}
OTAPI
otError
OTCALL
otThreadSetPSKc(
_In_ otInstance *aInstance,
const uint8_t *aPSKc
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
BYTE Buffer[sizeof(GUID) + sizeof(otPSKc)];
memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), aPSKc, sizeof(otPSKc));
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_PSKC, Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
int8_t
OTCALL
otLinkGetMaxTransmitPower(
_In_ otInstance *aInstance
)
{
int8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER, &Result);
return Result;
}
OTAPI
void
OTCALL
otLinkSetMaxTransmitPower(
_In_ otInstance *aInstance,
int8_t aPower
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER, aPower);
}
OTAPI
const otIp6Address *
OTCALL
otThreadGetMeshLocalEid(
_In_ otInstance *aInstance
)
{
otIp6Address *Result = (otIp6Address*)malloc(sizeof(otIp6Address));
if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_EID, Result) != ERROR_SUCCESS)
{
free(Result);
Result = nullptr;
}
return Result;
}
OTAPI
const uint8_t *
OTCALL
otThreadGetMeshLocalPrefix(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otMeshLocalPrefix *Result = (otMeshLocalPrefix*)malloc(sizeof(otMeshLocalPrefix));
if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX, Result) != ERROR_SUCCESS)
{
free(Result);
Result = nullptr;
}
return (uint8_t*)Result;
}
OTAPI
otError
OTCALL
otThreadSetMeshLocalPrefix(
_In_ otInstance *aInstance,
const uint8_t *aMeshLocalPrefix
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX, (const otMeshLocalPrefix*)aMeshLocalPrefix));
}
OTAPI
otError
OTCALL
otThreadGetNetworkDataLeader(
_In_ otInstance *aInstance,
bool aStable,
_Out_ uint8_t *aData,
_Out_ uint8_t *aDataLength
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
UNREFERENCED_PARAMETER(aInstance);
UNREFERENCED_PARAMETER(aStable);
UNREFERENCED_PARAMETER(aData);
UNREFERENCED_PARAMETER(aDataLength);
return OT_ERROR_NOT_IMPLEMENTED; // TODO
}
OTAPI
otError
OTCALL
otThreadGetNetworkDataLocal(
_In_ otInstance *aInstance,
bool aStable,
_Out_ uint8_t *aData,
_Out_ uint8_t *aDataLength
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
UNREFERENCED_PARAMETER(aInstance);
UNREFERENCED_PARAMETER(aStable);
UNREFERENCED_PARAMETER(aData);
UNREFERENCED_PARAMETER(aDataLength);
return OT_ERROR_NOT_IMPLEMENTED; // TODO
}
OTAPI
const char *
OTCALL
otThreadGetNetworkName(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otNetworkName *Result = (otNetworkName*)malloc(sizeof(otNetworkName));
if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_NAME, Result) != ERROR_SUCCESS)
{
free(Result);
Result = nullptr;
}
return (char*)Result;
}
OTAPI
otError
OTCALL
otThreadSetNetworkName(
_In_ otInstance *aInstance,
_In_ const char *aNetworkName
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
otNetworkName Buffer = {0};
strcpy_s(Buffer.m8, sizeof(Buffer), aNetworkName);
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_NAME, (const otNetworkName*)&Buffer));
}
OTAPI
otError
OTCALL
otNetDataGetNextOnMeshPrefix(
_In_ otInstance *aInstance,
_Inout_ otNetworkDataIterator *aIterator,
_Out_ otBorderRouterConfig *aConfig
)
{
if (aInstance == nullptr || aConfig == nullptr) return OT_ERROR_INVALID_ARGS;
BOOLEAN aLocal = FALSE;
PackedBuffer3<GUID,BOOLEAN,otNetworkDataIterator> InBuffer(aInstance->InterfaceGuid, aLocal, *aIterator);
BYTE OutBuffer[sizeof(uint8_t) + sizeof(otBorderRouterConfig)];
otError aError =
DwordToThreadError(
SendIOCTL(
aInstance->ApiHandle,
IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX,
&InBuffer, sizeof(InBuffer),
OutBuffer, sizeof(OutBuffer)));
if (aError == OT_ERROR_NONE)
{
memcpy(aIterator, OutBuffer, sizeof(uint8_t));
memcpy(aConfig, OutBuffer + sizeof(uint8_t), sizeof(otBorderRouterConfig));
}
else
{
ZeroMemory(aConfig, sizeof(otBorderRouterConfig));
}
return aError;
}
OTAPI
otError
OTCALL
otBorderRouterGetNextOnMeshPrefix(
_In_ otInstance *aInstance,
_Inout_ otNetworkDataIterator *aIterator,
_Out_ otBorderRouterConfig *aConfig
)
{
if (aInstance == nullptr || aConfig == nullptr) return OT_ERROR_INVALID_ARGS;
BOOLEAN aLocal = TRUE;
PackedBuffer3<GUID,BOOLEAN,otNetworkDataIterator> InBuffer(aInstance->InterfaceGuid, aLocal, *aIterator);
BYTE OutBuffer[sizeof(uint8_t) + sizeof(otBorderRouterConfig)];
otError aError =
DwordToThreadError(
SendIOCTL(
aInstance->ApiHandle,
IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX,
&InBuffer, sizeof(InBuffer),
OutBuffer, sizeof(OutBuffer)));
if (aError == OT_ERROR_NONE)
{
memcpy(aIterator, OutBuffer, sizeof(uint8_t));
memcpy(aConfig, OutBuffer + sizeof(uint8_t), sizeof(otBorderRouterConfig));
}
else
{
ZeroMemory(aConfig, sizeof(otBorderRouterConfig));
}
return aError;
}
OTAPI
otPanId
OTCALL
otLinkGetPanId(
_In_ otInstance *aInstance
)
{
otPanId Result = {0};
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, &Result);
return Result;
}
OTAPI
otError
OTCALL
otLinkSetPanId(
_In_ otInstance *aInstance,
otPanId aPanId
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, aPanId));
}
OTAPI
bool
OTCALL
otThreadIsRouterRoleEnabled(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = {0};
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED, &Result);
return Result != FALSE;
}
OTAPI
void
OTCALL
otThreadSetRouterRoleEnabled(
_In_ otInstance *aInstance,
bool aEnabled
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED, (BOOLEAN)aEnabled);
}
OTAPI
otError
OTCALL
otThreadSetPreferredRouterId(
_In_ otInstance *aInstance,
uint8_t aRouterId
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, aRouterId));
}
OTAPI
otShortAddress
OTCALL
otLinkGetShortAddress(
_In_ otInstance *aInstance
)
{
otShortAddress Result = {0};
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_SHORT_ADDRESS, &Result);
return Result;
}
BOOL
GetAdapterAddresses(
PIP_ADAPTER_ADDRESSES * ppIAA
)
{
PIP_ADAPTER_ADDRESSES pIAA = NULL;
DWORD len = 0;
DWORD flags;
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
if (GetAdaptersAddresses(AF_INET6, flags, NULL, NULL, &len) != ERROR_BUFFER_OVERFLOW)
return FALSE;
pIAA = (PIP_ADAPTER_ADDRESSES)malloc(len);
if (pIAA) {
GetAdaptersAddresses(AF_INET6, flags, NULL, pIAA, &len);
*ppIAA = pIAA;
return TRUE;
}
return FALSE;
}
OTAPI
const otNetifAddress *
OTCALL
otIp6GetUnicastAddresses(
_In_ otInstance *aInstance
)
{
LogFuncEntry(API_DEFAULT);
if (aInstance == nullptr)
{
LogFuncExit(API_DEFAULT);
return nullptr;
}
// Put the current thead in the correct compartment
bool RevertCompartmentOnExit = false;
ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
if (OriginalCompartmentID != aInstance->CompartmentID)
{
DWORD dwError = ERROR_SUCCESS;
if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
return nullptr;
}
RevertCompartmentOnExit = true;
}
otNetifAddress *addrs = nullptr;
ULONG AddrCount = 0;
// Query the current adapter addresses and format them in the proper output format
PIP_ADAPTER_ADDRESSES pIAAList;
if (GetAdapterAddresses(&pIAAList))
{
// Loop through all the interfaces
for (auto pIAA = pIAAList; pIAA != nullptr; pIAA = pIAA->Next)
{
// Look for the right interface
if (pIAA->Ipv6IfIndex != aInstance->InterfaceIndex) continue;
// Look through all unicast addresses
for (auto pUnicastAddr = pIAA->FirstUnicastAddress;
pUnicastAddr != nullptr;
pUnicastAddr = pUnicastAddr->Next)
{
AddrCount++;
}
break;
}
// If we didn't find any addresses, just break out
if (AddrCount == 0) goto error;
// Allocate the addresses
addrs = (otNetifAddress*)malloc(AddrCount * sizeof(otNetifAddress));
if (addrs == nullptr)
{
LogWarning(API_DEFAULT, "Not enough memory to alloc otNetifAddress array");
goto error;
}
ZeroMemory(addrs, AddrCount * sizeof(otNetifAddress));
// Initialize the next pointers
for (ULONG i = 0; i < AddrCount; i++)
{
addrs[i].mNext = (i + 1 == AddrCount) ? nullptr : &addrs[i + 1];
}
AddrCount = 0;
// Loop through all the interfaces
for (auto pIAA = pIAAList; pIAA != nullptr; pIAA = pIAA->Next)
{
// Look for the right interface
if (pIAA->Ipv6IfIndex != aInstance->InterfaceIndex) continue;
// Look through all unicast addresses
for (auto pUnicastAddr = pIAA->FirstUnicastAddress;
pUnicastAddr != nullptr;
pUnicastAddr = pUnicastAddr->Next)
{
LPSOCKADDR_IN6 pAddr = (LPSOCKADDR_IN6)pUnicastAddr->Address.lpSockaddr;
// Copy the necessary parameters
memcpy(&addrs[AddrCount].mAddress, &pAddr->sin6_addr, sizeof(pAddr->sin6_addr));
addrs[AddrCount].mPreferred = pUnicastAddr->PreferredLifetime != 0;
addrs[AddrCount].mValid = pUnicastAddr->ValidLifetime != 0;
addrs[AddrCount].mPrefixLength = pUnicastAddr->OnLinkPrefixLength;
AddrCount++;
}
break;
}
error:
free(pIAAList);
}
else
{
LogError(API_DEFAULT, "GetAdapterAddresses failed!");
}
// Revert the comparment if necessary
if (RevertCompartmentOnExit)
{
(VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
}
LogFuncExitMsg(API_DEFAULT, "%d addrs", AddrCount);
return addrs;
}
OTAPI
otError
OTCALL
otIp6AddUnicastAddress(
_In_ otInstance *aInstance,
const otNetifAddress *aAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
// Put the current thead in the correct compartment
bool RevertCompartmentOnExit = false;
ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
if (OriginalCompartmentID != aInstance->CompartmentID)
{
DWORD dwError = ERROR_SUCCESS;
if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
return OT_ERROR_FAILED;
}
RevertCompartmentOnExit = true;
}
MIB_UNICASTIPADDRESS_ROW newRow;
InitializeUnicastIpAddressEntry(&newRow);
newRow.InterfaceIndex = aInstance->InterfaceIndex;
newRow.InterfaceLuid = aInstance->InterfaceLuid;
newRow.Address.si_family = AF_INET6;
newRow.Address.Ipv6.sin6_family = AF_INET6;
static_assert(sizeof(IN6_ADDR) == sizeof(otIp6Address), "Windows and OpenThread IPv6 Addr Structs must be same size");
memcpy(&newRow.Address.Ipv6.sin6_addr, &aAddress->mAddress, sizeof(IN6_ADDR));
newRow.OnLinkPrefixLength = aAddress->mPrefixLength;
newRow.PreferredLifetime = aAddress->mPreferred ? 0xffffffff : 0;
newRow.ValidLifetime = aAddress->mValid ? 0xffffffff : 0;
newRow.PrefixOrigin = IpPrefixOriginOther; // Derived from network XPANID
newRow.SkipAsSource = FALSE; // Allow automatic binding to this address (default)
if (IN6_IS_ADDR_LINKLOCAL(&newRow.Address.Ipv6.sin6_addr))
{
newRow.SuffixOrigin = IpSuffixOriginLinkLayerAddress; // Derived from Extended MAC address
}
else
{
newRow.SuffixOrigin = IpSuffixOriginRandom; // Was created randomly
}
DWORD dwError = CreateUnicastIpAddressEntry(&newRow);
// Revert the comparment if necessary
if (RevertCompartmentOnExit)
{
(VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
}
if (dwError != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "CreateUnicastIpAddressEntry failed %!WINERROR!", dwError);
return OT_ERROR_FAILED;
}
return OT_ERROR_NONE;
}
OTAPI
otError
OTCALL
otIp6RemoveUnicastAddress(
_In_ otInstance *aInstance,
const otIp6Address *aAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
// Put the current thead in the correct compartment
bool RevertCompartmentOnExit = false;
ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
if (OriginalCompartmentID != aInstance->CompartmentID)
{
DWORD dwError = ERROR_SUCCESS;
if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
return OT_ERROR_FAILED;
}
RevertCompartmentOnExit = true;
}
MIB_UNICASTIPADDRESS_ROW deleteRow;
InitializeUnicastIpAddressEntry(&deleteRow);
deleteRow.InterfaceIndex = aInstance->InterfaceIndex;
deleteRow.InterfaceLuid = aInstance->InterfaceLuid;
deleteRow.Address.si_family = AF_INET6;
memcpy(&deleteRow.Address.Ipv6.sin6_addr, aAddress, sizeof(IN6_ADDR));
DWORD dwError = DeleteUnicastIpAddressEntry(&deleteRow);
// Revert the comparment if necessary
if (RevertCompartmentOnExit)
{
(VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
}
if (dwError != ERROR_SUCCESS)
{
LogError(API_DEFAULT, "DeleteUnicastIpAddressEntry failed %!WINERROR!", dwError);
return OT_ERROR_FAILED;
}
return OT_ERROR_NONE;
}
OTAPI
otError
OTCALL
otSetStateChangedCallback(
_In_ otInstance *aInstance,
_In_ otStateChangedCallback aCallback,
_In_ void *aContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
bool success =
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->StateChangedCallbacks,
aInstance->InterfaceGuid, aCallback, aContext
);
return success ? OT_ERROR_NONE : OT_ERROR_ALREADY;
}
OTAPI
void
OTCALL
otRemoveStateChangeCallback(
_In_ otInstance *aInstance,
_In_ otStateChangedCallback /* aCallback */,
_In_ void *aContext
)
{
if (aInstance == nullptr) return;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->StateChangedCallbacks,
aInstance->InterfaceGuid, (otStateChangedCallback)nullptr, aContext
);
}
OTAPI
otError
OTCALL
otDatasetGetActive(
_In_ otInstance *aInstance,
_Out_ otOperationalDataset *aDataset
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_DATASET, aDataset));
}
OTAPI
otError
OTCALL
otDatasetSetActive(
_In_ otInstance *aInstance,
const otOperationalDataset *aDataset
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_DATASET, aDataset));
}
OTAPI
otError
OTCALL
otDatasetGetPending(
_In_ otInstance *aInstance,
_Out_ otOperationalDataset *aDataset
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PENDING_DATASET, aDataset));
}
OTAPI
otError
OTCALL
otDatasetSetPending(
_In_ otInstance *aInstance,
const otOperationalDataset *aDataset
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PENDING_DATASET, aDataset));
}
OTAPI
otError
OTCALL
otDatasetSendMgmtActiveGet(
_In_ otInstance *aInstance,
const uint8_t *aTlvTypes,
uint8_t aLength,
_In_opt_ const otIp6Address *aAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvTypes == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
if (aAddress) BufferSize += sizeof(otIp6Address);
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvTypes, aLength);
if (aAddress)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + aLength, BufferSize - sizeof(GUID) - sizeof(uint8_t) - aLength, aAddress, sizeof(otIp6Address));
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_ACTIVE_GET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otDatasetSendMgmtActiveSet(
_In_ otInstance *aInstance,
const otOperationalDataset *aDataset,
const uint8_t *aTlvs,
uint8_t aLength
)
{
if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otOperationalDataset));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset) - sizeof(uint8_t), aTlvs, aLength);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_ACTIVE_SET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otDatasetSendMgmtPendingGet(
_In_ otInstance *aInstance,
const uint8_t *aTlvTypes,
uint8_t aLength,
_In_opt_ const otIp6Address *aAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvTypes == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
if (aAddress) BufferSize += sizeof(otIp6Address);
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvTypes, aLength);
if (aAddress)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + aLength, BufferSize - sizeof(GUID) - sizeof(uint8_t) - aLength, aAddress, sizeof(otIp6Address));
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_PENDING_GET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otDatasetSendMgmtPendingSet(
_In_ otInstance *aInstance,
const otOperationalDataset *aDataset,
const uint8_t *aTlvs,
uint8_t aLength
)
{
if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otOperationalDataset));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset) - sizeof(uint8_t), aTlvs, aLength);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_PENDING_SET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
uint32_t
OTCALL
otLinkGetPollPeriod(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_POLL_PERIOD, &Result);
return Result;
}
OTAPI
void
OTCALL
otLinkSetPollPeriod(
_In_ otInstance *aInstance,
uint32_t aPollPeriod
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_POLL_PERIOD, aPollPeriod);
}
OTAPI
uint8_t
OTCALL
otThreadGetLocalLeaderWeight(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetLocalLeaderWeight(
_In_ otInstance *aInstance,
uint8_t aWeight
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT, aWeight);
}
OTAPI
uint32_t
OTCALL
otThreadGetLocalLeaderPartitionId(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetLocalLeaderPartitionId(
_In_ otInstance *aInstance,
uint32_t aPartitionId
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID, aPartitionId);
}
OTAPI
uint16_t
OTCALL
otThreadGetJoinerUdpPort(
_In_ otInstance *aInstance
)
{
uint16_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_UDP_PORT, &Result);
return Result;
}
OTAPI
otError
OTCALL
otThreadSetJoinerUdpPort(
_In_ otInstance *aInstance,
uint16_t aJoinerUdpPort
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_UDP_PORT, aJoinerUdpPort));
}
OTAPI
otError
OTCALL
otBorderRouterAddOnMeshPrefix(
_In_ otInstance *aInstance,
const otBorderRouterConfig *aConfig
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_BORDER_ROUTER, aConfig));
}
OTAPI
otError
OTCALL
otBorderRouterRemoveOnMeshPrefix(
_In_ otInstance *aInstance,
const otIp6Prefix *aPrefix
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_BORDER_ROUTER, aPrefix));
}
OTAPI
otError
OTCALL
otBorderRouterAddRoute(
_In_ otInstance *aInstance,
const otExternalRouteConfig *aConfig
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_EXTERNAL_ROUTE, aConfig));
}
OTAPI
otError
OTCALL
otBorderRouterRemoveRoute(
_In_ otInstance *aInstance,
const otIp6Prefix *aPrefix
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_EXTERNAL_ROUTE, aPrefix));
}
OTAPI
otError
OTCALL
otBorderRouterRegister(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_SEND_SERVER_DATA));
}
OTAPI
uint32_t
OTCALL
otThreadGetContextIdReuseDelay(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetContextIdReuseDelay(
_In_ otInstance *aInstance,
uint32_t aDelay
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY, aDelay);
}
OTAPI
uint32_t
OTCALL
otThreadGetKeySequenceCounter(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetKeySequenceCounter(
_In_ otInstance *aInstance,
uint32_t aKeySequenceCounter
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER, aKeySequenceCounter);
}
OTAPI
uint32_t
OTCALL
otThreadGetKeySwitchGuardTime(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetKeySwitchGuardTime(
_In_ otInstance *aInstance,
uint32_t aKeySwitchGuardTime
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME, aKeySwitchGuardTime);
}
OTAPI
uint8_t
OTCALL
otThreadGetNetworkIdTimeout(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetNetworkIdTimeout(
_In_ otInstance *aInstance,
uint8_t aTimeout
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT, aTimeout);
}
OTAPI
uint8_t
OTCALL
otThreadGetRouterUpgradeThreshold(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetRouterUpgradeThreshold(
_In_ otInstance *aInstance,
uint8_t aThreshold
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD, aThreshold);
}
OTAPI
uint8_t
OTCALL
otThreadGetRouterDowngradeThreshold(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetRouterDowngradeThreshold(
_In_ otInstance *aInstance,
uint8_t aThreshold
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD, aThreshold);
}
OTAPI
uint8_t
OTCALL
otThreadGetRouterSelectionJitter(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER, &Result);
return Result;
}
OTAPI
void
OTCALL
otThreadSetRouterSelectionJitter(
_In_ otInstance *aInstance,
uint8_t aRouterJitter
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER, aRouterJitter);
}
OTAPI
otError
OTCALL
otThreadReleaseRouterId(
_In_ otInstance *aInstance,
uint8_t aRouterId
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_RELEASE_ROUTER_ID, aRouterId));
}
OTAPI
otError
OTCALL
otLinkAddWhitelist(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_MAC_WHITELIST, (const otExtAddress*)aExtAddr));
}
OTAPI
otError
OTCALL
otLinkAddWhitelistRssi(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr,
int8_t aRssi
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
PackedBuffer3<GUID,otExtAddress,int8_t> Buffer(aInstance->InterfaceGuid, *(otExtAddress*)aExtAddr, aRssi);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ADD_MAC_WHITELIST, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
void
OTCALL
otLinkRemoveWhitelist(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_MAC_WHITELIST, (const otExtAddress*)aExtAddr);
}
OTAPI
otError
OTCALL
otLinkGetWhitelistEntry(
_In_ otInstance *aInstance,
uint8_t aIndex,
_Out_ otMacWhitelistEntry *aEntry
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENTRY, &aIndex, aEntry));
}
OTAPI
void
OTCALL
otLinkClearWhitelist(
_In_ otInstance *aInstance
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CLEAR_MAC_WHITELIST);
}
OTAPI
void
OTCALL
otLinkSetWhitelistEnabled(
_In_ otInstance *aInstance,
bool aEnabled
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED, (BOOLEAN)aEnabled);
}
OTAPI
bool
OTCALL
otLinkIsWhitelistEnabled(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otThreadBecomeDetached(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_DETACHED));
}
OTAPI
otError
OTCALL
otThreadBecomeChild(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_CHILD));
}
OTAPI
otError
OTCALL
otThreadBecomeRouter(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_ROUTER));
}
OTAPI
otError
OTCALL
otThreadBecomeLeader(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_LEADER));
}
OTAPI
otError
OTCALL
otLinkAddBlacklist(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_MAC_BLACKLIST, (const otExtAddress*)aExtAddr));
}
OTAPI
void
OTCALL
otLinkRemoveBlacklist(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_MAC_BLACKLIST, (const otExtAddress*)aExtAddr);
}
OTAPI
otError
OTCALL
otLinkGetBlacklistEntry(
_In_ otInstance *aInstance,
uint8_t aIndex,
_Out_ otMacBlacklistEntry *aEntry
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENTRY, &aIndex, aEntry));
}
OTAPI
void
OTCALL
otLinkClearBlacklist(
_In_ otInstance *aInstance
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CLEAR_MAC_BLACKLIST);
}
OTAPI
void
OTCALL
otLinkSetBlacklistEnabled(
_In_ otInstance *aInstance,
bool aEnabled
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED, (BOOLEAN)aEnabled);
}
OTAPI
bool
OTCALL
otLinkIsBlacklistEnabled(
_In_ otInstance *aInstance
)
{
BOOLEAN Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED, &Result);
return Result != FALSE;
}
OTAPI
otError
OTCALL
otLinkGetAssignLinkQuality(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr,
_Out_ uint8_t *aLinkQuality
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY, (otExtAddress*)aExtAddr, aLinkQuality));
}
OTAPI
void
OTCALL
otLinkSetAssignLinkQuality(
_In_ otInstance *aInstance,
const uint8_t *aExtAddr,
uint8_t aLinkQuality
)
{
if (aInstance == nullptr) return;
PackedBuffer3<GUID,otExtAddress,uint8_t> Buffer(aInstance->InterfaceGuid, *(otExtAddress*)aExtAddr, aLinkQuality);
(void)SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY, &Buffer, sizeof(Buffer), nullptr, 0);
}
OTAPI
void
OTCALL
otInstanceReset(
_In_ otInstance *aInstance
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_PLATFORM_RESET);
}
OTAPI
void
OTCALL
otInstanceFactoryReset(
_In_ otInstance *aInstance
)
{
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_FACTORY_RESET);
}
OTAPI
otError
OTCALL
otThreadGetChildInfoById(
_In_ otInstance *aInstance,
uint16_t aChildId,
_Out_ otChildInfo *aChildInfo
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_INFO_BY_ID, &aChildId, aChildInfo));
}
OTAPI
otError
OTCALL
otThreadGetChildInfoByIndex(
_In_ otInstance *aInstance,
uint8_t aChildIndex,
_Out_ otChildInfo *aChildInfo
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_INFO_BY_INDEX, &aChildIndex, aChildInfo));
}
OTAPI
otError
OTCALL
otThreadGetNextNeighborInfo(
_In_ otInstance *aInstance,
_Inout_ otNeighborInfoIterator *aIterator,
_Out_ otNeighborInfo *aInfo
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
UNREFERENCED_PARAMETER(aIterator);
UNREFERENCED_PARAMETER(aInfo);
return OT_ERROR_NOT_IMPLEMENTED; // TODO
}
OTAPI
otDeviceRole
OTCALL
otThreadGetDeviceRole(
_In_ otInstance *aInstance
)
{
uint8_t Result = OT_DEVICE_ROLE_DISABLED;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, &Result);
return (otDeviceRole)Result;
}
OTAPI
otError
OTCALL
otThreadGetEidCacheEntry(
_In_ otInstance *aInstance,
uint8_t aIndex,
_Out_ otEidCacheEntry *aEntry
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EID_CACHE_ENTRY, &aIndex, aEntry));
}
OTAPI
otError
OTCALL
otThreadGetLeaderData(
_In_ otInstance *aInstance,
_Out_ otLeaderData *aLeaderData
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_DATA, aLeaderData));
}
OTAPI
uint8_t
OTCALL
otThreadGetLeaderRouterId(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_ROUTER_ID, &Result);
return Result;
}
OTAPI
uint8_t
OTCALL
otThreadGetLeaderWeight(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_WEIGHT, &Result);
return Result;
}
OTAPI
uint8_t
OTCALL
otNetDataGetVersion(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_DATA_VERSION, &Result);
return Result;
}
OTAPI
uint32_t
OTCALL
otThreadGetPartitionId(
_In_ otInstance *aInstance
)
{
uint32_t Result = 0xFFFFFFFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARTITION_ID, &Result);
return Result;
}
OTAPI
uint16_t
OTCALL
otThreadGetRloc16(
_In_ otInstance *aInstance
)
{
uint16_t Result = 0xFFFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_RLOC16, &Result);
return Result;
}
OTAPI
uint8_t
OTCALL
otThreadGetRouterIdSequence(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ID_SEQUENCE, &Result);
return Result;
}
OTAPI
otError
OTCALL
otThreadGetRouterInfo(
_In_ otInstance *aInstance,
uint16_t aRouterId,
_Out_ otRouterInfo *aRouterInfo
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_INFO, &aRouterId, aRouterInfo));
}
OTAPI
otError
OTCALL
otThreadGetParentInfo(
_In_ otInstance *aInstance,
_Out_ otRouterInfo *aParentInfo
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
static_assert(sizeof(otRouterInfo) == 20, "The size of otRouterInfo should be 20 bytes");
return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_INFO, aParentInfo));
}
OTAPI
uint8_t
OTCALL
otNetDataGetStableVersion(
_In_ otInstance *aInstance
)
{
uint8_t Result = 0xFF;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_STABLE_NETWORK_DATA_VERSION, &Result);
return Result;
}
OTAPI
const otMacCounters*
OTCALL
otLinkGetCounters(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return nullptr;
otMacCounters* aCounters = (otMacCounters*)malloc(sizeof(otMacCounters));
if (aCounters)
{
if (ERROR_SUCCESS != QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_COUNTERS, aCounters))
{
free(aCounters);
aCounters = nullptr;
}
}
return aCounters;
}
OTAPI
void
OTCALL
otMessageGetBufferInfo(
_In_ otInstance *,
_Out_ otBufferInfo *aBufferInfo
)
{
// Not supported on Windows
ZeroMemory(aBufferInfo, sizeof(otBufferInfo));
}
OTAPI
bool
OTCALL
otIsIp6AddressEqual(
const otIp6Address *a,
const otIp6Address *b
)
{
return memcmp(a->mFields.m8, b->mFields.m8, sizeof(otIp6Address)) == 0;
}
OTAPI
otError
OTCALL
otIp6AddressFromString(
const char *str,
otIp6Address *address
)
{
otError error = OT_ERROR_NONE;
uint8_t *dst = reinterpret_cast<uint8_t *>(address->mFields.m8);
uint8_t *endp = reinterpret_cast<uint8_t *>(address->mFields.m8 + 15);
uint8_t *colonp = NULL;
uint16_t val = 0;
uint8_t count = 0;
bool first = true;
char ch;
uint8_t d;
memset(address->mFields.m8, 0, 16);
dst--;
for (;;)
{
ch = *str++;
d = ch & 0xf;
if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'))
{
d += 9;
}
else if (ch == ':' || ch == '\0' || ch == ' ')
{
if (count)
{
if (dst + 2 > endp)
{
error = OT_ERROR_PARSE;
goto exit;
}
*(dst + 1) = static_cast<uint8_t>(val >> 8);
*(dst + 2) = static_cast<uint8_t>(val);
dst += 2;
count = 0;
val = 0;
}
else if (ch == ':')
{
if (!(colonp == nullptr || first))
{
error = OT_ERROR_PARSE;
goto exit;
}
colonp = dst;
}
if (ch == '\0' || ch == ' ')
{
break;
}
continue;
}
else
{
if (!('0' <= ch && ch <= '9'))
{
error = OT_ERROR_PARSE;
goto exit;
}
}
first = false;
val = static_cast<uint16_t>((val << 4) | d);
if (!(++count <= 4))
{
error = OT_ERROR_PARSE;
goto exit;
}
}
while (colonp && dst > colonp)
{
*endp-- = *dst--;
}
while (endp > dst)
{
*endp-- = 0;
}
exit:
return error;
}
OTAPI
uint8_t
OTCALL
otIp6PrefixMatch(
const otIp6Address *aFirst,
const otIp6Address *aSecond
)
{
uint8_t rval = 0;
uint8_t diff;
for (uint8_t i = 0; i < sizeof(otIp6Address); i++)
{
diff = aFirst->mFields.m8[i] ^ aSecond->mFields.m8[i];
if (diff == 0)
{
rval += 8;
}
else
{
while ((diff & 0x80) == 0)
{
rval++;
diff <<= 1;
}
break;
}
}
return rval;
}
OTAPI
const char *
OTCALL
otThreadErrorToString(
otError aError
)
{
const char *retval;
switch (aError)
{
case OT_ERROR_NONE:
retval = "None";
break;
case OT_ERROR_FAILED:
retval = "Failed";
break;
case OT_ERROR_DROP:
retval = "Drop";
break;
case OT_ERROR_NO_BUFS:
retval = "NoBufs";
break;
case OT_ERROR_NO_ROUTE:
retval = "NoRoute";
break;
case OT_ERROR_BUSY:
retval = "Busy";
break;
case OT_ERROR_PARSE:
retval = "Parse";
break;
case OT_ERROR_INVALID_ARGS:
retval = "InvalidArgs";
break;
case OT_ERROR_SECURITY:
retval = "Security";
break;
case OT_ERROR_ADDRESS_QUERY:
retval = "AddressQuery";
break;
case OT_ERROR_NO_ADDRESS:
retval = "NoAddress";
break;
case OT_ERROR_ABORT:
retval = "Abort";
break;
case OT_ERROR_NOT_IMPLEMENTED:
retval = "NotImplemented";
break;
case OT_ERROR_INVALID_STATE:
retval = "InvalidState";
break;
case OT_ERROR_NO_ACK:
retval = "NoAck";
break;
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
retval = "ChannelAccessFailure";
break;
case OT_ERROR_DETACHED:
retval = "Detached";
break;
case OT_ERROR_FCS:
retval = "FcsErr";
break;
case OT_ERROR_NO_FRAME_RECEIVED:
retval = "NoFrameReceived";
break;
case OT_ERROR_UNKNOWN_NEIGHBOR:
retval = "UnknownNeighbor";
break;
case OT_ERROR_INVALID_SOURCE_ADDRESS:
retval = "InvalidSourceAddress";
break;
case OT_ERROR_WHITELIST_FILTERED:
retval = "WhitelistFiltered";
break;
case OT_ERROR_DESTINATION_ADDRESS_FILTERED:
retval = "DestinationAddressFiltered";
break;
case OT_ERROR_NOT_FOUND:
retval = "NotFound";
break;
case OT_ERROR_ALREADY:
retval = "Already";
break;
case OT_ERROR_BLACKLIST_FILTERED:
retval = "BlacklistFiltered";
break;
case OT_ERROR_IP6_ADDRESS_CREATION_FAILURE:
retval = "Ipv6AddressCreationFailure";
break;
case OT_ERROR_NOT_CAPABLE:
retval = "NotCapable";
break;
case OT_ERROR_RESPONSE_TIMEOUT:
retval = "ResponseTimeout";
break;
case OT_ERROR_DUPLICATED:
retval = "Duplicated";
break;
case OT_ERROR_GENERIC:
retval = "GenericError";
break;
default:
retval = "UnknownErrorType";
break;
}
return retval;
}
OTAPI
otError
OTCALL
otThreadSendDiagnosticGet(
_In_ otInstance *aInstance,
const otIp6Address *aDestination,
const uint8_t aTlvTypes[],
uint8_t aCount
)
{
if (aInstance == nullptr || aDestination == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvTypes == nullptr && aCount != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t) + aCount;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDestination, sizeof(otIp6Address));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address), BufferSize - sizeof(GUID) - sizeof(otIp6Address), &aCount, sizeof(aCount));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otIp6Address) - sizeof(uint8_t), aTlvTypes, aCount);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_GET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otThreadSendDiagnosticReset(
_In_ otInstance *aInstance,
const otIp6Address *aDestination,
const uint8_t aTlvTypes[],
uint8_t aCount
)
{
if (aInstance == nullptr || aDestination == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvTypes == nullptr && aCount != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t) + aCount;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDestination, sizeof(otIp6Address));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address), BufferSize - sizeof(GUID) - sizeof(otIp6Address), &aCount, sizeof(aCount));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otIp6Address) - sizeof(uint8_t), aTlvTypes, aCount);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_RESET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otCommissionerStart(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_COMMISIONER_START));
}
OTAPI
otError
OTCALL
otCommissionerAddJoiner(
_In_ otInstance *aInstance,
const otExtAddress *aExtAddress,
const char *aPSKd,
uint32_t aTimeout
)
{
if (aInstance == nullptr || aPSKd == nullptr) return OT_ERROR_INVALID_ARGS;
size_t aPSKdLength = strnlen(aPSKd, OPENTHREAD_PSK_MAX_LENGTH + 1);
if (aPSKdLength > OPENTHREAD_PSK_MAX_LENGTH)
{
return OT_ERROR_INVALID_ARGS;
}
uint8_t aExtAddressValid = aExtAddress ? 1 : 0;
const ULONG BufferLength = sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + (ULONG)aPSKdLength + 1 + sizeof(aTimeout);
BYTE Buffer[sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + OPENTHREAD_PSK_MAX_LENGTH + 1 + sizeof(aTimeout)] = {0};
memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), &aExtAddressValid, sizeof(aExtAddressValid));
if (aExtAddressValid)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t), aExtAddress, sizeof(otExtAddress));
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t) - sizeof(otExtAddress), aPSKd, aPSKdLength);
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + aPSKdLength + 1, sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t) - sizeof(otExtAddress) - aPSKdLength - 1, &aTimeout, sizeof(aTimeout));
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_ADD_JOINER, Buffer, BufferLength, nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerRemoveJoiner(
_In_ otInstance *aInstance,
const otExtAddress *aExtAddress
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
uint8_t aExtAddressValid = aExtAddress ? 1 : 0;
BYTE Buffer[sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress)] = {0};
memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), &aExtAddressValid, sizeof(aExtAddressValid));
if (aExtAddressValid)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t), aExtAddress, sizeof(otExtAddress));
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_REMOVE_JOINER, Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerSetProvisioningUrl(
_In_ otInstance *aInstance,
const char *aProvisioningUrl
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
size_t aProvisioningUrlLength = aProvisioningUrl ? strnlen(aProvisioningUrl, OPENTHREAD_PROV_URL_MAX_LENGTH + 1) : 0;
if (aProvisioningUrlLength > OPENTHREAD_PROV_URL_MAX_LENGTH)
{
return OT_ERROR_INVALID_ARGS;
}
const ULONG BufferLength = sizeof(GUID) + (ULONG)aProvisioningUrlLength + 1;
BYTE Buffer[sizeof(GUID) + OPENTHREAD_PROV_URL_MAX_LENGTH + 1] = {0};
memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
if (aProvisioningUrlLength > 0)
memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), aProvisioningUrl, aProvisioningUrlLength);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_PROVISIONING_URL, Buffer, BufferLength, nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerAnnounceBegin(
otInstance *aInstance,
uint32_t aChannelMask,
uint8_t aCount,
uint16_t aPeriod,
const otIp6Address *aAddress
)
{
if (aInstance == nullptr || aAddress == nullptr) return OT_ERROR_INVALID_ARGS;
PackedBuffer5<GUID,uint32_t,uint8_t,uint16_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aChannelMask, aCount, aPeriod, *aAddress);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_ANNOUNCE_BEGIN, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerStop(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_COMMISIONER_STOP));
}
OTAPI
otError
OTCALL
otCommissionerEnergyScan(
_In_ otInstance *aInstance,
uint32_t aChannelMask,
uint8_t aCount,
uint16_t aPeriod,
uint16_t aScanDuration,
const otIp6Address *aAddress,
_In_ otCommissionerEnergyReportCallback aCallback,
_In_ void *aContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->CommissionerEnergyReportCallbacks,
aInstance->InterfaceGuid, aCallback, aContext
);
PackedBuffer6<GUID,uint32_t,uint8_t,uint16_t,uint16_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aChannelMask, aCount, aPeriod, aScanDuration, *aAddress);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISSIONER_ENERGY_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerPanIdQuery(
_In_ otInstance *aInstance,
uint16_t aPanId,
uint32_t aChannelMask,
const otIp6Address *aAddress,
_In_ otCommissionerPanIdConflictCallback aCallback,
_In_ void *aContext
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->CommissionerPanIdConflictCallbacks,
aInstance->InterfaceGuid, aCallback, aContext
);
PackedBuffer4<GUID,uint16_t,uint32_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aPanId, aChannelMask, *aAddress);
return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISSIONER_PANID_QUERY, &Buffer, sizeof(Buffer), nullptr, 0));
}
OTAPI
otError
OTCALL
otCommissionerSendMgmtGet(
_In_ otInstance *aInstance,
const uint8_t *aTlvs,
uint8_t aLength
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvs, aLength);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_GET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
otError
OTCALL
otCommissionerSendMgmtSet(
_In_ otInstance *aInstance,
const otCommissioningDataset *aDataset,
const uint8_t *aTlvs,
uint8_t aLength
)
{
if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
DWORD BufferSize = sizeof(GUID) + sizeof(otCommissioningDataset) + sizeof(uint8_t) + aLength;
PBYTE Buffer = (PBYTE)malloc(BufferSize);
if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otCommissioningDataset));
memcpy_s(Buffer + sizeof(GUID) + sizeof(otCommissioningDataset), BufferSize - sizeof(GUID) - sizeof(otCommissioningDataset), &aLength, sizeof(aLength));
if (aLength > 0)
memcpy_s(Buffer + sizeof(GUID) + sizeof(otCommissioningDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otCommissioningDataset) - sizeof(uint8_t), aTlvs, aLength);
otError result =
DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_SET, Buffer, BufferSize, nullptr, 0));
free(Buffer);
return result;
}
OTAPI
uint16_t
OTCALL
otCommissionerGetSessionId(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return 0;
// TODO
return 0;
}
OTAPI
otError
OTCALL
otJoinerStart(
_In_ otInstance *aInstance,
_Null_terminated_ const char *aPSKd,
_Null_terminated_ const char *aProvisioningUrl,
_Null_terminated_ const char *aVendorName,
_Null_terminated_ const char *aVendorModel,
_Null_terminated_ const char *aVendorSwVersion,
_Null_terminated_ const char *aVendorData,
_In_ otJoinerCallback aCallback,
_In_ void *aCallbackContext
)
{
if (aInstance == nullptr || aPSKd == nullptr) return OT_ERROR_INVALID_ARGS;
otCommissionConfig config = {0};
size_t aPSKdLength = strlen(aPSKd);
size_t aProvisioningUrlLength = aProvisioningUrl == nullptr ? 0 : strlen(aProvisioningUrl);
size_t aVendorNameLength = aVendorName == nullptr ? 0 : strlen(aVendorName);
size_t aVendorModelLength = aVendorModel == nullptr ? 0 : strlen(aVendorModel);
size_t aVendorSwVersionLength = aVendorSwVersion == nullptr ? 0 : strlen(aVendorSwVersion);
size_t aVendorDataLength = aVendorData == nullptr ? 0 : strlen(aVendorData);
if (aPSKdLength > OPENTHREAD_PSK_MAX_LENGTH ||
aProvisioningUrlLength > OPENTHREAD_PROV_URL_MAX_LENGTH ||
aVendorNameLength > OPENTHREAD_VENDOR_NAME_MAX_LENGTH ||
aVendorModelLength > OPENTHREAD_VENDOR_MODEL_MAX_LENGTH ||
aVendorSwVersionLength > OPENTHREAD_VENDOR_SW_VERSION_MAX_LENGTH ||
aVendorDataLength > OPENTHREAD_VENDOR_DATA_MAX_LENGTH)
{
return OT_ERROR_INVALID_ARGS;
}
memcpy_s(config.PSKd, sizeof(config.PSKd), aPSKd, aPSKdLength);
memcpy_s(config.ProvisioningUrl, sizeof(config.ProvisioningUrl), aProvisioningUrl, aProvisioningUrlLength);
memcpy_s(config.VendorName, sizeof(config.VendorName), aVendorName, aVendorNameLength);
memcpy_s(config.VendorModel, sizeof(config.VendorModel), aVendorModel, aVendorModelLength);
memcpy_s(config.VendorSwVersion, sizeof(config.VendorSwVersion), aVendorSwVersion, aVendorSwVersionLength);
memcpy_s(config.VendorData, sizeof(config.VendorData), aVendorData, aVendorDataLength);
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->JoinerCallbacks,
aInstance->InterfaceGuid, aCallback, aCallbackContext
);
auto ret = DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_START, (const otCommissionConfig*)&config));
if (ret != OT_ERROR_NONE)
{
aInstance->ApiHandle->SetCallback(
aInstance->ApiHandle->JoinerCallbacks,
aInstance->InterfaceGuid, (otJoinerCallback)nullptr, (PVOID)nullptr
);
}
return ret;
}
OTAPI
otError
OTCALL
otJoinerStop(
_In_ otInstance *aInstance
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_STOP));
}
OTAPI
int8_t
OTCALL
otThreadGetParentPriority(
_In_ otInstance *aInstance
)
{
int8_t Result = 0;
if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_PRIORITY, &Result);
return Result;
}
OTAPI
otError
OTCALL
otThreadSetParentPriority(
_In_ otInstance *aInstance,
int8_t aParentPriority
)
{
if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_PRIORITY, aParentPriority));
}