blob: 2139f456f6fd36953a7771daa6cc6daf7f3d089d [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.
*/
/**
* @file
* @brief
* This file implements the Thread mode (Radio Miniport) functions required for the OpenThread library.
*/
#include "precomp.h"
#include "thread.tmh"
_IRQL_requires_max_(PASSIVE_LEVEL)
NDIS_STATUS
otLwfInitializeThreadMode(
_In_ PMS_FILTER pFilter
)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
LogFuncEntry(DRIVER_DEFAULT);
NT_ASSERT(pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO);
do
{
KeInitializeEvent(
&pFilter->SendNetBufferListComplete,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
// Initialize the event processing
pFilter->EventWorkerThread = NULL;
NdisAllocateSpinLock(&pFilter->EventsLock);
InitializeListHead(&pFilter->AddressChangesHead);
InitializeListHead(&pFilter->NBLsHead);
InitializeListHead(&pFilter->MacFramesHead);
InitializeListHead(&pFilter->EventIrpListHead);
KeInitializeEvent(
&pFilter->EventWorkerThreadStopEvent,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadWaitTimeUpdated,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadProcessTasklets,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadProcessAddressChanges,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadProcessNBLs,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadProcessMacFrames,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadProcessIrp,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
KeInitializeEvent(
&pFilter->EventWorkerThreadEnergyScanComplete,
SynchronizationEvent, // auto-clearing event
FALSE // event initially non-signalled
);
pFilter->EventHighPrecisionTimer =
ExAllocateTimer(
otLwfEventProcessingTimer,
pFilter,
EX_TIMER_HIGH_RESOLUTION
);
if (pFilter->EventHighPrecisionTimer == NULL)
{
LogError(DRIVER_DEFAULT, "Failed to allocate timer!");
break;
}
// Query the interface state (best effort, since it might not be supported)
BOOLEAN IfUp = FALSE;
Status = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_NET_IF_UP, SPINEL_DATATYPE_BOOL_S, &IfUp);
if (!NT_SUCCESS(Status))
{
LogVerbose(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_INTERFACE_TYPE, %!STATUS!", Status);
Status = NDIS_STATUS_SUCCESS;
}
else
{
NT_ASSERT(IfUp == FALSE);
}
// Initialize the event processing thread
if (!NT_SUCCESS(otLwfEventProcessingStart(pFilter)))
{
Status = NDIS_STATUS_RESOURCES;
break;
}
} while (FALSE);
if (Status != NDIS_STATUS_SUCCESS)
{
// Stop event processing thread
otLwfEventProcessingStop(pFilter);
// Stop and free the timer
if (pFilter->EventHighPrecisionTimer)
{
ExDeleteTimer(pFilter->EventHighPrecisionTimer, TRUE, FALSE, NULL);
}
}
LogFuncExitNDIS(DRIVER_DEFAULT, Status);
return Status;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
void
otLwfUninitializeThreadMode(
_In_ PMS_FILTER pFilter
)
{
LogFuncEntry(DRIVER_DEFAULT);
// Stop event processing thread
otLwfEventProcessingStop(pFilter);
// Free timer
if (pFilter->EventHighPrecisionTimer)
{
ExDeleteTimer(pFilter->EventHighPrecisionTimer, TRUE, FALSE, NULL);
pFilter->EventHighPrecisionTimer = NULL;
}
// Close handle to settings registry key
if (pFilter->otSettingsRegKey)
{
ZwClose(pFilter->otSettingsRegKey);
pFilter->otSettingsRegKey = NULL;
}
LogFuncExit(DRIVER_DEFAULT);
}
#if DEBUG_ALLOC
PMS_FILTER
otLwfFindFromCurrentThread()
{
PMS_FILTER pOutput = NULL;
HANDLE CurThreadId = PsGetCurrentThreadId();
NdisAcquireSpinLock(&FilterListLock);
for (PLIST_ENTRY Link = FilterModuleList.Flink; Link != &FilterModuleList; Link = Link->Flink)
{
PMS_FILTER pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
if (pFilter->otThreadId == CurThreadId)
{
pOutput = pFilter;
break;
}
}
NdisReleaseSpinLock(&FilterListLock);
NT_ASSERT(pOutput);
return pOutput;
}
#endif
#define OTPLAT_CALLOC_TAG 'OTDM'
#define BUFFER_POOL_TAG 'OTBP'
_IRQL_requires_max_(PASSIVE_LEVEL)
void
otLwfReleaseInstance(
_In_ PMS_FILTER pFilter
)
{
LogFuncEntry(DRIVER_DEFAULT);
if (pFilter->otCtx != NULL)
{
otInstanceFinalize(pFilter->otCtx);
pFilter->otCtx = NULL;
#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
// Free all the pools as there should be no outstanding
// references to the buffers any more.
BufferPool *curPool = pFilter->otBufferPoolHead;
while (curPool != NULL)
{
BufferPool *nextPool = curPool->Next;
ExFreePoolWithTag(curPool, BUFFER_POOL_TAG);
curPool = nextPool;
}
#endif
#if DEBUG_ALLOC
NT_ASSERT(pFilter->otOutstandingAllocationCount == 0);
NT_ASSERT(pFilter->otOutstandingMemoryAllocated == 0);
PLIST_ENTRY Link = pFilter->otOutStandingAllocations.Flink;
while (Link != &pFilter->otOutStandingAllocations)
{
OT_ALLOC* AllocHeader = CONTAINING_RECORD(Link, OT_ALLOC, Link);
Link = Link->Flink;
LogVerbose(DRIVER_DEFAULT, "Leaked Alloc ID:%u", AllocHeader->ID);
ExFreePoolWithTag(AllocHeader, OTPLAT_CALLOC_TAG);
}
#endif
}
LogFuncExit(DRIVER_DEFAULT);
}
//
// OpenThread Platform functions
//
_IRQL_requires_max_(PASSIVE_LEVEL)
void *otPlatCAlloc(size_t aNum, size_t aSize)
{
size_t totalSize = aNum * aSize;
#if DEBUG_ALLOC
totalSize += sizeof(OT_ALLOC);
#endif
PVOID mem = ExAllocatePoolWithTag(PagedPool, totalSize, OTPLAT_CALLOC_TAG);
if (mem)
{
RtlZeroMemory(mem, totalSize);
#if DEBUG_ALLOC
PMS_FILTER pFilter = otLwfFindFromCurrentThread();
//LogVerbose(DRIVER_DEFAULT, "otPlatAlloc(%u) = ID:%u %p", (ULONG)totalSize, pFilter->otAllocationID, mem);
OT_ALLOC* AllocHeader = (OT_ALLOC*)mem;
AllocHeader->Length = (LONG)totalSize;
AllocHeader->ID = pFilter->otAllocationID++;
InsertTailList(&pFilter->otOutStandingAllocations, &AllocHeader->Link);
InterlockedIncrement(&pFilter->otOutstandingAllocationCount);
InterlockedAdd(&pFilter->otOutstandingMemoryAllocated, AllocHeader->Length);
mem = (PUCHAR)(mem) + sizeof(OT_ALLOC);
#endif
}
return mem;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
void otPlatFree(_In_opt_ void *aPtr)
{
if (aPtr == NULL) return;
#if DEBUG_ALLOC
aPtr = (PUCHAR)(aPtr) - sizeof(OT_ALLOC);
//LogVerbose(DRIVER_DEFAULT, "otPlatFree(%p)", aPtr);
OT_ALLOC* AllocHeader = (OT_ALLOC*)aPtr;
PMS_FILTER pFilter = otLwfFindFromCurrentThread();
InterlockedDecrement(&pFilter->otOutstandingAllocationCount);
InterlockedAdd(&pFilter->otOutstandingMemoryAllocated, -AllocHeader->Length);
RemoveEntryList(&AllocHeader->Link);
#endif
ExFreePoolWithTag(aPtr, OTPLAT_CALLOC_TAG);
}
#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
_IRQL_requires_max_(PASSIVE_LEVEL)
BufferPool* AllocBufferPool(_In_ PMS_FILTER pFilter)
{
// Allocate the memory
BufferPool* bufPool = (BufferPool*)ExAllocatePoolWithTag(PagedPool, pFilter->otBufferPoolByteSize, BUFFER_POOL_TAG);
if (bufPool == NULL)
{
LogWarning(DRIVER_DEFAULT, "Failed to allocate new buffer pool!");
return NULL;
}
// Zero out the memory
RtlZeroMemory(bufPool, pFilter->otBufferPoolByteSize);
// Set all mNext for the buffers
otMessage* prevBuf = (otMessage*)bufPool->Buffers;
for (uint16_t i = 1; i < pFilter->otBufferPoolBufferCount; i++)
{
otMessage* curBuf =
(otMessage*)&bufPool->Buffers[i * pFilter->otBufferSize];
prevBuf->mNext = curBuf;
prevBuf = curBuf;
}
LogVerbose(DRIVER_DEFAULT, "Allocated new buffer pool (%d bytes)!", pFilter->otBufferPoolByteSize);
return bufPool;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
otMessage* GetNextFreeBufferFromPool(_In_ PMS_FILTER pFilter)
{
// Immediately return if we have hit our limit
if (pFilter->otBuffersLeft == 0) return NULL;
// If we don't have any free buffers left, allocate another pool
if (pFilter->otFreeBuffers == NULL)
{
BufferPool *newPool = AllocBufferPool(pFilter);
if (newPool == NULL) return NULL; // Out of physical memory
// Push on top of the pool list
newPool->Next = pFilter->otBufferPoolHead;
pFilter->otBufferPoolHead = newPool;
// Set the free buffer list
pFilter->otFreeBuffers = (otMessage*)newPool->Buffers;
}
// Pop the top free buffer
otMessage* buffer = pFilter->otFreeBuffers;
pFilter->otFreeBuffers = pFilter->otFreeBuffers->mNext;
pFilter->otBuffersLeft--;
buffer->mNext = NULL;
return buffer;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
void otPlatMessagePoolInit(_In_ otInstance *otCtx, uint16_t aMinNumFreeBuffers, size_t aBufferSize)
{
NT_ASSERT(otCtx);
PMS_FILTER pFilter = otCtxToFilter(otCtx);
LogFuncEntry(DRIVER_DEFAULT);
UNREFERENCED_PARAMETER(aMinNumFreeBuffers);
// Initialize parameters
pFilter->otBufferSize = (uint16_t)aBufferSize;
pFilter->otBufferPoolByteSize = (uint16_t)(kPageSize * kPagesPerBufferPool);
pFilter->otBufferPoolBufferCount = (uint16_t)((pFilter->otBufferPoolByteSize - sizeof(BufferPool)) / aBufferSize);
pFilter->otBuffersLeft = kMaxPagesForBufferPools * pFilter->otBufferPoolBufferCount;
// Allocate first pool
pFilter->otBufferPoolHead = AllocBufferPool(pFilter);
ASSERT(pFilter->otBufferPoolHead); // Should this API allow for failure ???
// Set initial free buffer list
pFilter->otFreeBuffers = (otMessage*)pFilter->otBufferPoolHead->Buffers;
LogFuncExit(DRIVER_DEFAULT);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
otMessage *otPlatMessagePoolNew(_In_ otInstance *otCtx)
{
NT_ASSERT(otCtx);
PMS_FILTER pFilter = otCtxToFilter(otCtx);
return GetNextFreeBufferFromPool(pFilter);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
void otPlatMessagePoolFree(_In_ otInstance *otCtx, _In_ otMessage *aBuffer)
{
NT_ASSERT(otCtx);
PMS_FILTER pFilter = otCtxToFilter(otCtx);
// Put buffer back on the list
aBuffer->mNext = pFilter->otFreeBuffers;
pFilter->otFreeBuffers = aBuffer;
pFilter->otBuffersLeft++;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
uint16_t otPlatMessagePoolNumFreeBuffers(_In_ otInstance *otCtx)
{
NT_ASSERT(otCtx);
PMS_FILTER pFilter = otCtxToFilter(otCtx);
return pFilter->otBuffersLeft;
}
#endif
uint32_t otPlatRandomGet()
{
LARGE_INTEGER Counter = KeQueryPerformanceCounter(NULL);
return (uint32_t)RtlRandomEx(&Counter.LowPart);
}
otError otPlatRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength)
{
// Just use the system-preferred random number generator algorithm
NTSTATUS status =
BCryptGenRandom(
NULL,
aOutput,
(ULONG)aOutputLength,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
);
NT_ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
{
LogError(DRIVER_DEFAULT, "BCryptGenRandom failed, %!STATUS!", status);
return OT_ERROR_FAILED;
}
return OT_ERROR_NONE;
}
void otTaskletsSignalPending(_In_ otInstance *otCtx)
{
LogVerbose(DRIVER_DEFAULT, "otTaskletsSignalPending");
PMS_FILTER pFilter = otCtxToFilter(otCtx);
otLwfEventProcessingIndicateNewTasklet(pFilter);
}
// Process a role state change
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
otLwfProcessRoleStateChange(
_In_ PMS_FILTER pFilter
)
{
otDeviceRole prevRole = pFilter->otCachedRole;
pFilter->otCachedRole = otThreadGetDeviceRole(pFilter->otCtx);
if (prevRole == pFilter->otCachedRole) return;
LogInfo(DRIVER_DEFAULT, "Interface %!GUID! new role: %!otDeviceRole!", &pFilter->InterfaceGuid, pFilter->otCachedRole);
// Make sure we are in the correct media connect state
otLwfIndicateLinkState(
pFilter,
IsAttached(pFilter->otCachedRole) ?
MediaConnectStateConnected :
MediaConnectStateDisconnected);
}
void otLwfStateChangedCallback(uint32_t aFlags, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
//
// Process the notification internally
//
if ((aFlags & OT_CHANGED_IP6_ADDRESS_ADDED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_IP6_ADDRESS_ADDED", pFilter);
otLwfRadioAddressesUpdated(pFilter);
}
if ((aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_IP6_ADDRESS_REMOVED", pFilter);
otLwfRadioAddressesUpdated(pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_RLOC_ADDED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_RLOC_ADDED", pFilter);
otLwfRadioAddressesUpdated(pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_RLOC_REMOVED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_RLOC_REMOVED", pFilter);
otLwfRadioAddressesUpdated(pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_ROLE", pFilter);
otLwfProcessRoleStateChange(pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_PARTITION_ID) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_PARTITION_ID", pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER", pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_CHILD_ADDED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_CHILD_ADDED", pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_CHILD_REMOVED) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_CHILD_REMOVED", pFilter);
}
if ((aFlags & OT_CHANGED_THREAD_NETDATA) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_NETDATA", pFilter);
otIp6SlaacUpdate(pFilter->otCtx, pFilter->otAutoAddresses, ARRAYSIZE(pFilter->otAutoAddresses), otIp6CreateRandomIid, NULL);
#if OPENTHREAD_ENABLE_DHCP6_SERVER
otDhcp6ServerUpdate(pFilter->otCtx);
#endif // OPENTHREAD_ENABLE_DHCP6_SERVER
#if OPENTHREAD_ENABLE_DHCP6_CLIENT
otDhcp6ClientUpdate(pFilter->otCtx, pFilter->otDhcpAddresses, ARRAYSIZE(pFilter->otDhcpAddresses), NULL);
#endif // OPENTHREAD_ENABLE_DHCP6_CLIENT
}
if ((aFlags & OT_CHANGED_THREAD_ML_ADDR) != 0)
{
LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_ML_ADDR", pFilter);
}
//
// Queue the notification for clients
//
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_STATE_CHANGE;
NotifEntry->Notif.StateChangePayload.Flags = aFlags;
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfActiveScanCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_ACTIVE_SCAN;
if (aResult)
{
NotifEntry->Notif.ActiveScanPayload.Valid = TRUE;
NotifEntry->Notif.ActiveScanPayload.Results = *aResult;
}
else
{
NotifEntry->Notif.ActiveScanPayload.Valid = FALSE;
}
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfEnergyScanCallback(_In_ otEnergyScanResult *aResult, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_ENERGY_SCAN;
if (aResult)
{
NotifEntry->Notif.EnergyScanPayload.Valid = TRUE;
NotifEntry->Notif.EnergyScanPayload.Results = *aResult;
}
else
{
NotifEntry->Notif.EnergyScanPayload.Valid = FALSE;
}
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfDiscoverCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_DISCOVER;
if (aResult)
{
NotifEntry->Notif.DiscoverPayload.Valid = TRUE;
NotifEntry->Notif.DiscoverPayload.Results = *aResult;
}
else
{
NotifEntry->Notif.DiscoverPayload.Valid = FALSE;
}
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfCommissionerEnergyReportCallback(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT;
// Limit the number of reports if necessary
if (aEnergyListLength > MAX_ENERGY_REPORT_LENGTH) aEnergyListLength = MAX_ENERGY_REPORT_LENGTH;
NotifEntry->Notif.CommissionerEnergyReportPayload.ChannelMask = aChannelMask;
NotifEntry->Notif.CommissionerEnergyReportPayload.EnergyListLength = aEnergyListLength;
memcpy(NotifEntry->Notif.CommissionerEnergyReportPayload.EnergyList, aEnergyList, aEnergyListLength);
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfCommissionerPanIdConflictCallback(uint16_t aPanId, uint32_t aChannelMask, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_COMMISSIONER_PANID_QUERY;
NotifEntry->Notif.CommissionerPanIdQueryPayload.PanId = aPanId;
NotifEntry->Notif.CommissionerPanIdQueryPayload.ChannelMask = aChannelMask;
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
void otLwfJoinerCallback(otError aError, _In_ void *aContext)
{
LogFuncEntry(DRIVER_DEFAULT);
PMS_FILTER pFilter = (PMS_FILTER)aContext;
PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
if (NotifEntry)
{
RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
NotifEntry->Notif.NotifType = OTLWF_NOTIF_JOINER_COMPLETE;
NotifEntry->Notif.JoinerCompletePayload.Error = aError;
otLwfIndicateNotification(NotifEntry);
}
LogFuncExit(DRIVER_DEFAULT);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void
otLwfThreadValueIs(
_In_ PMS_FILTER pFilter,
_In_ BOOLEAN DispatchLevel,
_In_ spinel_prop_key_t key,
_In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
_In_ spinel_size_t value_data_len
)
{
LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value for %s", pFilter, spinel_prop_key_to_cstr(key));
if (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)
{
uint8_t scanChannel;
int8_t maxRssi;
spinel_ssize_t ret;
ret = spinel_datatype_unpack(
value_data_ptr,
value_data_len,
"Cc",
&scanChannel,
&maxRssi);
NT_ASSERT(ret > 0);
if (ret > 0)
{
LogInfo(DRIVER_DEFAULT, "Filter: %p, completed energy scan: Rssi:%d", pFilter, maxRssi);
otLwfEventProcessingIndicateEnergyScanResult(pFilter, maxRssi);
}
}
else if (key == SPINEL_PROP_STREAM_RAW)
{
if (value_data_len < 256)
{
otLwfEventProcessingIndicateNewMacFrameCommand(
pFilter,
DispatchLevel,
value_data_ptr,
(uint8_t)value_data_len);
}
}
else if (key == SPINEL_PROP_STREAM_DEBUG)
{
const uint8_t* output = NULL;
UINT output_len = 0;
spinel_ssize_t ret;
ret = spinel_datatype_unpack(
value_data_ptr,
value_data_len,
SPINEL_DATATYPE_DATA_S,
&output,
&output_len);
NT_ASSERT(ret > 0);
if (ret > 0 && output && output_len <= (UINT)ret)
{
if (strnlen((char*)output, output_len) != output_len)
{
LogInfo(DRIVER_DEFAULT, "DEVICE: %s", (char*)output);
}
else if (output_len < 128)
{
char strOutput[128] = { 0 };
memcpy(strOutput, output, output_len);
LogInfo(DRIVER_DEFAULT, "DEVICE: %s", strOutput);
}
}
}
LogFuncExit(DRIVER_DEFAULT);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void
otLwfThreadValueInserted(
_In_ PMS_FILTER pFilter,
_In_ BOOLEAN DispatchLevel,
_In_ spinel_prop_key_t key,
_In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
_In_ spinel_size_t value_data_len
)
{
LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value Inserted for %s", pFilter, spinel_prop_key_to_cstr(key));
UNREFERENCED_PARAMETER(pFilter);
UNREFERENCED_PARAMETER(DispatchLevel);
UNREFERENCED_PARAMETER(key);
UNREFERENCED_PARAMETER(value_data_ptr);
UNREFERENCED_PARAMETER(value_data_len);
LogFuncExit(DRIVER_DEFAULT);
}