blob: 25d32fd175f781af04ce5c2fdc4cc8da424ddb99 [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 "filter.tmh"
// Helper function to query the CompartmentID of a Network Interface
COMPARTMENT_ID
GetInterfaceCompartmentID(
_In_ PIF_LUID pNetLuid
)
{
COMPARTMENT_ID CompartmentID = UNSPECIFIED_COMPARTMENT_ID;
NTSTATUS Status =
NsiGetParameter(
NsiActive,
&NPI_MS_NDIS_MODULEID,
NdisNsiObjectInterfaceInformation,
pNetLuid, sizeof(*pNetLuid),
NsiStructRoDynamic,
&CompartmentID, sizeof(CompartmentID),
FIELD_OFFSET(NDIS_NSI_INTERFACE_INFORMATION_ROD, CompartmentId)
);
return (NT_SUCCESS(Status) ? CompartmentID : DEFAULT_COMPARTMENT_ID);
}
_Use_decl_annotations_
NDIS_STATUS
FilterAttach(
NDIS_HANDLE NdisFilterHandle,
NDIS_HANDLE FilterDriverContext,
PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters
)
/*++
Routine Description:
Filter attach routine.
Create filter's context, allocate NetBufferLists and NetBuffer pools and any
other resources, and read configuration if needed.
Arguments:
NdisFilterHandle - Specify a handle identifying this instance of the filter. FilterAttach
should save this handle. It is a required parameter in subsequent calls
to NdisFxxx functions.
FilterDriverContext - Filter driver context passed to NdisFRegisterFilterDriver.
AttachParameters - attach parameters
Return Value:
NDIS_STATUS_SUCCESS: FilterAttach successfully allocated and initialize data structures
for this filter instance.
NDIS_STATUS_RESOURCES: FilterAttach failed due to insufficient resources.
NDIS_STATUS_FAILURE: FilterAttach could not set up this instance of this filter and it has called
NdisWriteErrorLogEntry with parameters specifying the reason for failure.
N.B.: FILTER can use NdisRegisterDeviceEx to create a device, so the upper
layer can send Irps to the filter.
--*/
{
PMS_FILTER pFilter = NULL;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
NTSTATUS NtStatus;
NDIS_FILTER_ATTRIBUTES FilterAttributes;
ULONG Size;
COMPARTMENT_ID OriginalCompartmentID;
OBJECT_ATTRIBUTES ObjectAttributes = {0};
const ULONG RegKeyOffset = ARRAYSIZE(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\") - 1;
DECLARE_CONST_UNICODE_STRING(RegKeyPath, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\{00000000-0000-0000-0000-000000000000}");
RtlCopyMemory(RegKeyPath.Buffer + RegKeyOffset, AttachParameters->BaseMiniportName->Buffer + 8, sizeof(L"{00000000-0000-0000-0000-000000000000}"));
LogFuncEntry(DRIVER_DEFAULT);
do
{
ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject);
if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject)
{
Status = NDIS_STATUS_INVALID_PARAMETER;
break;
}
// Verify the media type is supported. This is a last resort; the
// the filter should never have been bound to an unsupported miniport
// to begin with.
if (AttachParameters->MiniportMediaType != NdisMediumIP)
{
LogError(DRIVER_DEFAULT, "Unsupported media type, 0x%x.", (ULONG)AttachParameters->MiniportMediaType);
Status = NDIS_STATUS_INVALID_PARAMETER;
break;
}
Size = sizeof(MS_FILTER) + AttachParameters->BaseMiniportInstanceName->Length;
pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size);
if (pFilter == NULL)
{
LogWarning(DRIVER_DEFAULT, "Failed to allocate context structure, 0x%x bytes", Size);
Status = NDIS_STATUS_RESOURCES;
break;
}
NdisZeroMemory(pFilter, sizeof(MS_FILTER));
LogVerbose(DRIVER_DEFAULT, "Opening interface registry key %S", RegKeyPath.Buffer);
InitializeObjectAttributes(
&ObjectAttributes,
(PUNICODE_STRING)&RegKeyPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
// Open the registry key
NtStatus = ZwOpenKey(&pFilter->InterfaceRegKey, KEY_ALL_ACCESS, &ObjectAttributes);
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "ZwOpenKey failed to open %S, %!STATUS!", RegKeyPath.Buffer, NtStatus);
Status = NDIS_STATUS_FAILURE;
break;
}
// Format of "\DEVICE\{5BA90C49-0D7E-455B-8D3B-614F6714A212}"
AttachParameters->BaseMiniportName->Buffer += 8;
AttachParameters->BaseMiniportName->Length -= 8 * sizeof(WCHAR);
NtStatus = RtlGUIDFromString(AttachParameters->BaseMiniportName, &pFilter->InterfaceGuid);
AttachParameters->BaseMiniportName->Buffer -= 8;
AttachParameters->BaseMiniportName->Length += 8 * sizeof(WCHAR);
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "Failed to convert FilterModuleGuidName to a GUID, %!STATUS!", NtStatus);
Status = NDIS_STATUS_FAILURE;
break;
}
pFilter->InterfaceFriendlyName.Length = pFilter->InterfaceFriendlyName.MaximumLength = AttachParameters->BaseMiniportInstanceName->Length;
pFilter->InterfaceFriendlyName.Buffer = (PWSTR)((PUCHAR)pFilter + sizeof(MS_FILTER));
NdisMoveMemory(pFilter->InterfaceFriendlyName.Buffer,
AttachParameters->BaseMiniportInstanceName->Buffer,
pFilter->InterfaceFriendlyName.Length);
pFilter->InterfaceIndex = AttachParameters->BaseMiniportIfIndex;
pFilter->InterfaceLuid = AttachParameters->BaseMiniportNetLuid;
pFilter->InterfaceCompartmentID = UNSPECIFIED_COMPARTMENT_ID;
pFilter->FilterHandle = NdisFilterHandle;
NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
FilterAttributes.Flags = 0;
NDIS_DECLARE_FILTER_MODULE_CONTEXT(MS_FILTER);
Status = NdisFSetAttributes(NdisFilterHandle, pFilter, &FilterAttributes);
if (Status != NDIS_STATUS_SUCCESS)
{
LogError(DRIVER_DEFAULT, "Failed to set attributes, %!NDIS_STATUS!", Status);
break;
}
// Filter initially in Paused state
pFilter->State = FilterPaused;
// Initialize rundowns to disabled with no active references
pFilter->ExternalRefs.Count = EX_RUNDOWN_ACTIVE;
pFilter->cmdRundown.Count = EX_RUNDOWN_ACTIVE;
// Query the compartment ID for this interface to use for the IP stack
pFilter->InterfaceCompartmentID = GetInterfaceCompartmentID(&pFilter->InterfaceLuid);
LogVerbose(DRIVER_DEFAULT, "Interface %!GUID! is in Compartment %u", &pFilter->InterfaceGuid, (ULONG)pFilter->InterfaceCompartmentID);
// Make sure we are in the right compartment
(VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID);
// Register for address changed notifications
NtStatus =
NotifyUnicastIpAddressChange(
AF_INET6,
otLwfAddressChangeCallback,
pFilter,
FALSE,
&pFilter->AddressChangeHandle
);
// Revert the compartment, now that we have the table
otLwfRevertCompartment(OriginalCompartmentID);
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "NotifyUnicastIpAddressChange failed, %!STATUS!", NtStatus);
Status = NDIS_STATUS_FAILURE;
break;
}
// Add Filter to global list of Thread Filters
NdisAcquireSpinLock(&FilterListLock);
InsertTailList(&FilterModuleList, &pFilter->FilterModuleLink);
NdisReleaseSpinLock(&FilterListLock);
LogVerbose(DRIVER_DEFAULT, "Created Filter: %p", pFilter);
} while (FALSE);
// Clean up on failure
if (Status != NDIS_STATUS_SUCCESS)
{
if (pFilter != NULL)
{
if (pFilter->AddressChangeHandle != NULL)
{
CancelMibChangeNotify2(pFilter->AddressChangeHandle);
pFilter->AddressChangeHandle = NULL;
}
NdisFreeMemory(pFilter, 0, 0);
}
}
LogFuncExitNDIS(DRIVER_DEFAULT, Status);
return Status;
}
_Use_decl_annotations_
VOID
FilterDetach(
NDIS_HANDLE FilterModuleContext
)
/*++
Routine Description:
Filter detach routine.
This is a required function that will deallocate all the resources allocated during
FilterAttach. NDIS calls FilterAttach to remove a filter instance from a filter stack.
Arguments:
FilterModuleContext - pointer to the filter context area.
Return Value:
None.
NOTE: Called at PASSIVE_LEVEL and the filter is in paused state
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
// Filter must be in paused state and pretty much inactive
NT_ASSERT(pFilter->State == FilterPaused);
NT_ASSERT(pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_UNINTIALIZED);
//
// Detach must not fail, so do not put any code here that can possibly fail.
//
// Remove this Filter from the global list
NdisAcquireSpinLock(&FilterListLock);
RemoveEntryList(&pFilter->FilterModuleLink);
NdisReleaseSpinLock(&FilterListLock);
// Unregister from address change notifications
CancelMibChangeNotify2(pFilter->AddressChangeHandle);
pFilter->AddressChangeHandle = NULL;
// Close the registry key
if (pFilter->InterfaceRegKey)
{
ZwClose(pFilter->InterfaceRegKey);
pFilter->InterfaceRegKey = NULL;
}
// Free the memory allocated
NdisFreeMemory(pFilter, 0, 0);
LogFuncExit(DRIVER_DEFAULT);
}
// Indicates an interface state change has taken place (used for interface arrival/removal)
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
otLwfNotifyDeviceAvailabilityChange(
_In_ PMS_FILTER pFilter,
_In_ BOOLEAN fAvailable
)
{
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_DEVICE_AVAILABILITY;
NotifEntry->Notif.DeviceAvailabilityPayload.Available = fAvailable;
otLwfIndicateNotification(NotifEntry);
}
}
PAGED
NTSTATUS
GetRegDWORDValue(
_In_ PMS_FILTER pFilter,
_In_ PCWSTR ValueName,
_Out_ PULONG ValueData
)
{
NTSTATUS status;
ULONG resultLength;
UCHAR keybuf[128] = {0};
UNICODE_STRING UValueName;
PAGED_CODE();
RtlInitUnicodeString(&UValueName, ValueName);
status = ZwQueryValueKey(
pFilter->InterfaceRegKey,
&UValueName,
KeyValueFullInformation,
keybuf,
sizeof(keybuf),
&resultLength);
if (NT_SUCCESS(status))
{
PKEY_VALUE_FULL_INFORMATION keyInfo = (PKEY_VALUE_FULL_INFORMATION)keybuf;
if (keyInfo->Type != REG_DWORD)
{
status = STATUS_INVALID_PARAMETER_MIX;
}
else
{
*ValueData = *((ULONG UNALIGNED *)(keybuf + keyInfo->DataOffset));
}
}
return status;
}
PAGED
NTSTATUS
SetRegDWORDValue(
_In_ PMS_FILTER pFilter,
_In_ PCWSTR ValueName,
_In_ ULONG ValueData
)
{
NTSTATUS status;
UNICODE_STRING UValueName;
PAGED_CODE();
RtlInitUnicodeString(&UValueName, ValueName);
status = ZwSetValueKey(
pFilter->InterfaceRegKey,
&UValueName,
0,
REG_DWORD,
(PVOID)&ValueData,
sizeof(ValueData));
return status;
}
_Use_decl_annotations_
NDIS_STATUS
FilterRestart(
NDIS_HANDLE FilterModuleContext,
PNDIS_FILTER_RESTART_PARAMETERS RestartParameters
)
/*++
Routine Description:
Filter restart routine.
Start the datapath - begin sending and receiving NBLs.
Arguments:
FilterModuleContext - pointer to the filter context stucture.
RestartParameters - additional information about the restart operation.
Return Value:
NDIS_STATUS_SUCCESS: if filter restarts successfully
NDIS_STATUS_XXX: Otherwise.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
PVOID SpinelCapsDataBuffer = NULL;
const uint8_t* SpinelCapsPtr = NULL;
spinel_size_t SpinelCapsLen = 0;
NL_INTERFACE_KEY key = {0};
NL_INTERFACE_RW interfaceRw;
ULONG ThreadOnHost = TRUE;
PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes;
PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes;
LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
NT_ASSERT(pFilter->State == FilterPaused);
NdisRestartAttributes = RestartParameters->RestartAttributes;
//
// If NdisRestartAttributes is not NULL, then the filter can modify generic
// attributes and add new media specific info attributes at the end.
// Otherwise, if NdisRestartAttributes is NULL, the filter should not try to
// modify/add attributes.
//
if (NdisRestartAttributes != NULL)
{
ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES);
NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data;
//
// Check to see if we need to change any attributes. For example, the
// driver can change the current MAC address here. Or the driver can add
// media specific info attributes.
//
NdisGeneralAttributes->LookaheadSize = 128;
}
// Initialize the Spinel command processing
NdisStatus = otLwfCmdInitialize(pFilter);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
LogError(DRIVER_DEFAULT, "otLwfCmdInitialize failed, %!NDIS_STATUS!", NdisStatus);
goto exit;
}
// Query the device capabilities
NtStatus = otLwfCmdGetProp(pFilter, &SpinelCapsDataBuffer, SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, &SpinelCapsPtr, &SpinelCapsLen);
if (!NT_SUCCESS(NtStatus))
{
NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_CAPS, %!STATUS!", NtStatus);
goto exit;
}
// Iterate and process returned capabilities
NT_ASSERT(SpinelCapsDataBuffer);
while (SpinelCapsLen > 0)
{
ULONG SpinelCap = 0;
spinel_ssize_t len = spinel_datatype_unpack(SpinelCapsPtr, SpinelCapsLen, SPINEL_DATATYPE_UINT_PACKED_S, &SpinelCap);
if (len < 1) break;
SpinelCapsLen -= (spinel_size_t)len;
SpinelCapsPtr += len;
switch (SpinelCap)
{
case SPINEL_CAP_MAC_RAW:
pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO;
pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ACK_TIMEOUT;
pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_MAC_RETRY_AND_COLLISION_AVOIDANCE;
pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ENERGY_SCAN;
break;
case SPINEL_CAP_NET_THREAD_1_0:
pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_THREAD_1_0;
break;
default:
break;
}
}
// Set the state indicating where we should be running the Thread logic (Host or Device).
if (!NT_SUCCESS(GetRegDWORDValue(pFilter, L"RunOnHost", &ThreadOnHost)))
{
// Default to running on the host if the key isn't present
ThreadOnHost = TRUE;
SetRegDWORDValue(pFilter, L"RunOnHost", ThreadOnHost);
}
LogInfo(DRIVER_DEFAULT, "Filter: %p initializing ThreadOnHost=%d", FilterModuleContext, ThreadOnHost);
// Initialize the processing logic
if (ThreadOnHost)
{
// Ensure the device has the capabilities to support raw radio commands
if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO) == 0)
{
LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support raw radio commands");
NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
goto exit;
}
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_RADIO_MODE;
NtStatus = otLwfInitializeThreadMode(pFilter);
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "otLwfInitializeThreadMode failed, %!STATUS!", NtStatus);
NdisStatus = NDIS_STATUS_FAILURE;
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
goto exit;
}
}
else
{
// Ensure the device has the capabilities to support Thread commands
if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_THREAD_1_0) == 0)
{
LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support thread commands");
NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
goto exit;
}
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_THREAD_MODE;
NtStatus = otLwfTunInitialize(pFilter);
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "otLwfInitializeTunnelMode failed, %!STATUS!", NtStatus);
NdisStatus = NDIS_STATUS_FAILURE;
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
goto exit;
}
}
//
// Disable DAD and Neighbor advertisements
//
key.Luid = pFilter->InterfaceLuid;
NlInitializeInterfaceRw(&interfaceRw);
interfaceRw.DadTransmits = 0;
interfaceRw.SendUnsolicitedNeighborAdvertisementOnDad = FALSE;
NtStatus =
NsiSetAllParameters(
NsiActive,
NsiSetDefault,
&NPI_MS_IPV6_MODULEID,
NlInterfaceObject,
&key,
sizeof(key),
&interfaceRw,
sizeof(interfaceRw));
if (!NT_SUCCESS(NtStatus))
{
LogError(DRIVER_DEFAULT, "NsiSetAllParameters (NlInterfaceObject) failed, %!STATUS!", NtStatus);
NdisStatus = NDIS_STATUS_FAILURE;
goto exit;
}
//
// Enable the external references to the filter
//
ExReInitializeRundownProtection(&pFilter->ExternalRefs);
//
// If everything is OK, set the filter in running state.
//
pFilter->State = FilterRunning; // when successful
otLwfNotifyDeviceAvailabilityChange(pFilter, TRUE);
LogInfo(DRIVER_DEFAULT, "Interface %!GUID! arrival, Filter=%p", &pFilter->InterfaceGuid, pFilter);
exit:
//
// Ensure the state is Paused if restart failed.
//
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
pFilter->State = FilterPaused;
if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
{
otLwfUninitializeThreadMode(pFilter);
}
else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
{
otLwfTunUninitialize(pFilter);
}
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
// Clean up Spinel command processing
otLwfCmdUninitialize(pFilter);
}
// Free the buffer for the capabilities we queried
if (SpinelCapsDataBuffer != NULL)
{
FILTER_FREE_MEM(SpinelCapsDataBuffer);
}
LogFuncExitNDIS(DRIVER_DEFAULT, NdisStatus);
return NdisStatus;
}
_Use_decl_annotations_
NDIS_STATUS
FilterPause(
NDIS_HANDLE FilterModuleContext,
PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters
)
/*++
Routine Description:
Filter pause routine.
Complete all the outstanding sends and queued sends,
wait for all the outstanding recvs to be returned
and return all the queued receives.
Arguments:
FilterModuleContext - pointer to the filter context stucture
PauseParameters - additional information about the pause
Return Value:
NDIS_STATUS_SUCCESS if filter pauses successfully, NDIS_STATUS_PENDING
if not. No other return value is allowed (pause must succeed, eventually).
N.B.: When the filter is in Pausing state, it can still process OID requests,
complete sending, and returning packets to NDIS, and also indicate status.
After this function completes, the filter must not attempt to send or
receive packets, but it may still process OID requests and status
indications.
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)(FilterModuleContext);
NDIS_STATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(PauseParameters);
LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
//
// Set the flag that the filter is going to pause
//
NT_ASSERT(pFilter->State == FilterRunning);
NdisAcquireSpinLock(&FilterListLock);
pFilter->State = FilterPausing;
NdisReleaseSpinLock(&FilterListLock);
//
// Send final notification of interface removal
//
otLwfNotifyDeviceAvailabilityChange(pFilter, FALSE);
LogInfo(DRIVER_DEFAULT, "Interface %!GUID! removal.", &pFilter->InterfaceGuid);
//
// Disable external references and wait for existing calls to complete
//
LogInfo(DRIVER_DEFAULT, "Disabling and waiting for external references to release");
ExWaitForRundownProtectionRelease(&pFilter->ExternalRefs);
LogInfo(DRIVER_DEFAULT, "External references released.");
//
// Clean up based on the device mode
//
if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
{
otLwfUninitializeThreadMode(pFilter);
}
else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
{
otLwfTunUninitialize(pFilter);
}
pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
//
// Clean up the Spinel command processing
//
otLwfCmdUninitialize(pFilter);
//
// Set the state back to Paused now that we are done
//
pFilter->State = FilterPaused;
LogFuncExitNDIS(DRIVER_DEFAULT, Status);
return Status;
}
_Use_decl_annotations_
VOID
FilterStatus(
NDIS_HANDLE FilterModuleContext,
PNDIS_STATUS_INDICATION StatusIndication
)
/*++
Routine Description:
Status indication handler
Arguments:
FilterModuleContext - our filter context
StatusIndication - the status being indicated
NOTE: called at <= DISPATCH_LEVEL
FILTER driver may call NdisFIndicateStatus to generate a status indication to
all higher layer modules.
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p, IndicateStatus: %8x", FilterModuleContext, StatusIndication->StatusCode);
if (StatusIndication->StatusCode == NDIS_STATUS_LINK_STATE)
{
PNDIS_LINK_STATE LinkState = (PNDIS_LINK_STATE)StatusIndication->StatusBuffer;
LogInfo(DRIVER_DEFAULT, "Filter: %p, MediaConnectState: %u", FilterModuleContext, LinkState->MediaConnectState);
// Cache the link state from the miniport
memcpy(&pFilter->MiniportLinkState, LinkState, sizeof(NDIS_LINK_STATE));
}
NdisFIndicateStatus(pFilter->FilterHandle, StatusIndication);
LogFuncExit(DRIVER_DEFAULT);
}
// Indicate a change of the link state
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
otLwfIndicateLinkState(
_In_ PMS_FILTER pFilter,
_In_ NDIS_MEDIA_CONNECT_STATE MediaState
)
{
// If we are already in the correct state, just return
if (pFilter->MiniportLinkState.MediaConnectState == MediaState)
{
return;
}
NDIS_STATUS_INDICATION StatusIndication = {0};
StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION);
StatusIndication.SourceHandle = pFilter->FilterHandle;
StatusIndication.StatusCode = NDIS_STATUS_LINK_STATE;
StatusIndication.StatusBuffer = &pFilter->MiniportLinkState;
StatusIndication.StatusBufferSize = sizeof(pFilter->MiniportLinkState);
pFilter->MiniportLinkState.MediaConnectState = MediaState;
LogInfo(DRIVER_DEFAULT, "Interface %!GUID! new media state: %u", &pFilter->InterfaceGuid, MediaState);
NdisFIndicateStatus(pFilter->FilterHandle, &StatusIndication);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
otLwfSetCompartment(
_In_ PMS_FILTER pFilter,
_Out_ COMPARTMENT_ID* pOriginalCompartment
)
/*++
Routine Description:
Sets the current thread's compartment ID to match the filter instance.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
// Make sure we are in the right compartment
*pOriginalCompartment = NdisGetCurrentThreadCompartmentId();
if (*pOriginalCompartment != pFilter->InterfaceCompartmentID)
{
status = NdisSetCurrentThreadCompartmentId(pFilter->InterfaceCompartmentID);
if (!NT_SUCCESS(status))
{
LogError(DRIVER_DEFAULT, "NdisSetCurrentThreadCompartmentId failed, %!STATUS!", status);
*pOriginalCompartment = 0;
}
}
else
{
*pOriginalCompartment = 0;
}
return status;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
otLwfRevertCompartment(
_In_ COMPARTMENT_ID OriginalCompartment
)
/*++
Routine Description:
Resets the current thread's compartment ID.
--*/
{
// Revert the compartment if it is set
if (OriginalCompartment != 0)
{
(VOID)NdisSetCurrentThreadCompartmentId(OriginalCompartment);
}
}