2020-09-24 20:51:15 +00:00
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
|
|
|
|
|
|
FxPkgPnp.cpp
|
|
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
|
|
This module implements the pnp IRP handlers for the driver framework.
|
|
|
|
|
|
|
|
|
|
Author:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
|
|
|
|
|
|
Both kernel and user mode
|
|
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
#include "pnppriv.hpp"
|
|
|
|
|
|
|
|
|
|
#include <initguid.h>
|
|
|
|
|
#include <wdmguid.h>
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
|
|
#if defined(EVENT_TRACING)
|
|
|
|
|
#include "FxPkgPnp.tmh"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* dc7a8e51-49b3-4a3a-9e81-625205e7d729 */
|
|
|
|
|
const GUID FxPkgPnp::GUID_POWER_THREAD_INTERFACE = {
|
|
|
|
|
0xdc7a8e51, 0x49b3, 0x4a3a, { 0x9e, 0x81, 0x62, 0x52, 0x05, 0xe7, 0xd7, 0x29 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FxPkgPnp::FxPkgPnp(
|
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in WDFTYPE Type
|
|
|
|
|
) :
|
|
|
|
|
FxPackage(FxDriverGlobals, Device, Type)
|
|
|
|
|
{
|
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
|
|
m_DmaEnablerList = NULL;
|
|
|
|
|
m_RemovalDeviceList = NULL;
|
|
|
|
|
m_UsageDependentDeviceList = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Initialize the structures to the default state and then override the
|
|
|
|
|
// non WDF std default values to the unsupported / off values.
|
|
|
|
|
//
|
|
|
|
|
m_PnpStateAndCaps.Value =
|
|
|
|
|
FxPnpStateDisabledUseDefault |
|
|
|
|
|
FxPnpStateDontDisplayInUIUseDefault |
|
|
|
|
|
FxPnpStateFailedUseDefault |
|
|
|
|
|
FxPnpStateNotDisableableUseDefault |
|
|
|
|
|
FxPnpStateRemovedUseDefault |
|
|
|
|
|
FxPnpStateResourcesChangedUseDefault |
|
|
|
|
|
|
|
|
|
|
FxPnpCapLockSupportedUseDefault |
|
|
|
|
|
FxPnpCapEjectSupportedUseDefault |
|
|
|
|
|
FxPnpCapRemovableUseDefault |
|
|
|
|
|
FxPnpCapDockDeviceUseDefault |
|
|
|
|
|
FxPnpCapUniqueIDUseDefault |
|
|
|
|
|
FxPnpCapSilentInstallUseDefault |
|
|
|
|
|
FxPnpCapSurpriseRemovalOKUseDefault |
|
|
|
|
|
FxPnpCapHardwareDisabledUseDefault |
|
|
|
|
|
FxPnpCapNoDisplayInUIUseDefault
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
m_PnpCapsAddress = (ULONG) -1;
|
|
|
|
|
m_PnpCapsUINumber = (ULONG) -1;
|
|
|
|
|
|
|
|
|
|
RtlZeroMemory(&m_PowerCaps, sizeof(m_PowerCaps));
|
|
|
|
|
m_PowerCaps.Caps =
|
|
|
|
|
FxPowerCapDeviceD1UseDefault |
|
|
|
|
|
FxPowerCapDeviceD2UseDefault |
|
|
|
|
|
FxPowerCapWakeFromD0UseDefault |
|
|
|
|
|
FxPowerCapWakeFromD1UseDefault |
|
|
|
|
|
FxPowerCapWakeFromD2UseDefault |
|
|
|
|
|
FxPowerCapWakeFromD3UseDefault
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.DeviceWake = PowerDeviceMaximum;
|
|
|
|
|
m_PowerCaps.SystemWake = PowerSystemMaximum;
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.D1Latency = (ULONG) -1;
|
|
|
|
|
m_PowerCaps.D2Latency = (ULONG) -1;
|
|
|
|
|
m_PowerCaps.D3Latency = (ULONG) -1;
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.States = 0;
|
|
|
|
|
for (i = 0; i < PowerSystemMaximum; i++) {
|
|
|
|
|
_SetPowerCapState(i, PowerDeviceMaximum, &m_PowerCaps.States);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface));
|
|
|
|
|
RtlZeroMemory(&m_SpecialSupport[0], sizeof(m_SpecialSupport));
|
|
|
|
|
RtlZeroMemory(&m_SpecialFileCount[0], sizeof(m_SpecialFileCount));
|
|
|
|
|
|
|
|
|
|
m_PowerThreadInterface.Interface.Size = sizeof(m_PowerThreadInterface);
|
|
|
|
|
m_PowerThreadInterface.Interface.Version = 1;
|
|
|
|
|
m_PowerThreadInterface.Interface.Context = this;
|
|
|
|
|
m_PowerThreadInterface.Interface.InterfaceReference = &FxPkgPnp::_PowerThreadInterfaceReference;
|
|
|
|
|
m_PowerThreadInterface.Interface.InterfaceDereference = &FxPkgPnp::_PowerThreadInterfaceDereference;
|
|
|
|
|
m_PowerThreadInterface.PowerThreadEnqueue = &FxPkgPnp::_PowerThreadEnqueue;
|
|
|
|
|
m_PowerThread = NULL;
|
|
|
|
|
m_HasPowerThread = FALSE;
|
|
|
|
|
m_PowerThreadInterfaceReferenceCount = 1;
|
|
|
|
|
m_PowerThreadEvent = NULL;
|
|
|
|
|
|
|
|
|
|
m_DeviceStopCount = 0;
|
|
|
|
|
m_CapsQueried = FALSE;
|
|
|
|
|
m_InternalFailure = FALSE;
|
|
|
|
|
m_FailedAction = WdfDeviceFailedUndefined;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We only set the pending child count to 1 once we know we have successfully
|
|
|
|
|
// created an FxDevice and initialized it fully. If we delete an FxDevice
|
|
|
|
|
// which is half baked, it cannot have any FxChildLists which have any
|
|
|
|
|
// pending children on them.
|
|
|
|
|
//
|
|
|
|
|
m_PendingChildCount = 0;
|
|
|
|
|
|
|
|
|
|
m_QueryInterfaceHead.Next = NULL;
|
|
|
|
|
|
|
|
|
|
m_DeviceInterfaceHead.Next = NULL;
|
|
|
|
|
m_DeviceInterfacesCanBeEnabled = FALSE;
|
|
|
|
|
|
|
|
|
|
m_Failed = FALSE;
|
|
|
|
|
m_SetDeviceRemoveProcessed = FALSE;
|
|
|
|
|
|
|
|
|
|
m_SystemPowerState = PowerSystemWorking;
|
|
|
|
|
m_DevicePowerState = WdfPowerDeviceD3Final;
|
|
|
|
|
m_DevicePowerStateOld = WdfPowerDeviceD3Final;
|
|
|
|
|
|
|
|
|
|
m_PendingPnPIrp = NULL;
|
|
|
|
|
m_PendingSystemPowerIrp = NULL;
|
|
|
|
|
m_PendingDevicePowerIrp = NULL;
|
|
|
|
|
m_SystemPowerAction = (UCHAR) PowerActionNone;
|
|
|
|
|
|
|
|
|
|
m_PnpStateCallbacks = NULL;
|
|
|
|
|
m_PowerStateCallbacks = NULL;
|
|
|
|
|
m_PowerPolicyStateCallbacks = NULL;
|
|
|
|
|
|
|
|
|
|
m_SelfManagedIoMachine = NULL;
|
|
|
|
|
|
|
|
|
|
m_EnumInfo = NULL;
|
|
|
|
|
|
|
|
|
|
m_Resources = NULL;
|
|
|
|
|
m_ResourcesRaw = NULL;
|
|
|
|
|
|
|
|
|
|
InitializeListHead(&m_InterruptListHead);
|
|
|
|
|
m_InterruptObjectCount = 0;
|
|
|
|
|
m_WakeInterruptCount = 0;
|
|
|
|
|
m_WakeInterruptPendingAckCount = 0;
|
|
|
|
|
m_SystemWokenByWakeInterrupt = FALSE;
|
|
|
|
|
m_WakeInterruptsKeepConnected = FALSE;
|
|
|
|
|
m_AchievedStart = FALSE;
|
|
|
|
|
|
|
|
|
|
m_SharedPower.m_WaitWakeIrp = NULL;
|
|
|
|
|
m_SharedPower.m_WaitWakeOwner = FALSE;
|
|
|
|
|
m_SharedPower.m_ExtendWatchDogTimer = FALSE;
|
|
|
|
|
|
|
|
|
|
m_DeviceRemoveProcessed = NULL;
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
|
|
|
|
//
|
|
|
|
|
// Interrupt APIs for Vista and forward
|
|
|
|
|
//
|
|
|
|
|
m_IoConnectInterruptEx = FxLibraryGlobals.IoConnectInterruptEx;
|
|
|
|
|
m_IoDisconnectInterruptEx = FxLibraryGlobals.IoDisconnectInterruptEx;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Interrupt APIs for Windows 8 and forward
|
|
|
|
|
//
|
|
|
|
|
m_IoReportInterruptActive = FxLibraryGlobals.IoReportInterruptActive;
|
|
|
|
|
m_IoReportInterruptInactive = FxLibraryGlobals.IoReportInterruptInactive;
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
m_ReleaseHardwareAfterDescendantsOnFailure = FALSE;
|
|
|
|
|
|
|
|
|
|
MarkDisposeOverride(ObjectDoNotLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FxPkgPnp::~FxPkgPnp()
|
|
|
|
|
{
|
|
|
|
|
PSINGLE_LIST_ENTRY ple;
|
|
|
|
|
|
|
|
|
|
Mx::MxAssert(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We should either have zero pending children or we never made it out of
|
|
|
|
|
// the init state during a failed WDFDEVICE create or failed EvtDriverDeviceAdd
|
|
|
|
|
//
|
|
|
|
|
Mx::MxAssert(m_PendingChildCount == 0 ||
|
|
|
|
|
m_Device->GetDevicePnpState() == WdfDevStatePnpInit);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ple = m_DeviceInterfaceHead.Next;
|
|
|
|
|
while (ple != NULL) {
|
|
|
|
|
FxDeviceInterface* pDI;
|
|
|
|
|
|
|
|
|
|
pDI = FxDeviceInterface::_FromEntry(ple);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Advance to the next before deleting the current
|
|
|
|
|
//
|
|
|
|
|
ple = ple->Next;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// No longer in the list
|
|
|
|
|
//
|
|
|
|
|
pDI->m_Entry.Next = NULL;
|
|
|
|
|
|
|
|
|
|
delete pDI;
|
|
|
|
|
}
|
|
|
|
|
m_DeviceInterfaceHead.Next = NULL;
|
|
|
|
|
|
|
|
|
|
if (m_DmaEnablerList != NULL) {
|
|
|
|
|
delete m_DmaEnablerList;
|
|
|
|
|
m_DmaEnablerList = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_RemovalDeviceList != NULL) {
|
|
|
|
|
delete m_RemovalDeviceList;
|
|
|
|
|
m_RemovalDeviceList = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_UsageDependentDeviceList != NULL) {
|
|
|
|
|
delete m_UsageDependentDeviceList;
|
|
|
|
|
m_UsageDependentDeviceList = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PnpStateCallbacks != NULL) {
|
|
|
|
|
delete m_PnpStateCallbacks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerStateCallbacks != NULL) {
|
|
|
|
|
delete m_PowerStateCallbacks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyStateCallbacks != NULL) {
|
|
|
|
|
delete m_PowerPolicyStateCallbacks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_SelfManagedIoMachine != NULL) {
|
|
|
|
|
delete m_SelfManagedIoMachine;
|
|
|
|
|
m_SelfManagedIoMachine = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
|
|
|
delete m_EnumInfo;
|
|
|
|
|
m_EnumInfo = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_Resources != NULL) {
|
|
|
|
|
m_Resources->RELEASE(this);
|
|
|
|
|
m_Resources = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_ResourcesRaw != NULL) {
|
|
|
|
|
m_ResourcesRaw->RELEASE(this);
|
|
|
|
|
m_ResourcesRaw = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSERT(IsListEmpty(&m_InterruptListHead));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
|
FxPkgPnp::Dispose(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
PSINGLE_LIST_ENTRY ple;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// All of the interrupts were freed during this object's dispose path. This
|
|
|
|
|
// is because all of the interrupts were attached as children to this object.
|
|
|
|
|
//
|
|
|
|
|
// It is safe to just reinitialize the list head.
|
|
|
|
|
//
|
|
|
|
|
InitializeListHead(&m_InterruptListHead);
|
|
|
|
|
|
|
|
|
|
m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// A derived class can insert an embedded FxQueryInterface into the QI list,
|
|
|
|
|
// so clear out the list before the destructor chain runs. (The derived
|
|
|
|
|
// class will be destructed first, as such, the embedded FxQueryInterface
|
|
|
|
|
// will also destruct first and complain it is still in a list.
|
|
|
|
|
//
|
|
|
|
|
ple = m_QueryInterfaceHead.Next;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Clear out the head while holding the lock so that we synchronize against
|
|
|
|
|
// processing a QI while deleting the list.
|
|
|
|
|
//
|
|
|
|
|
m_QueryInterfaceHead.Next = NULL;
|
|
|
|
|
|
|
|
|
|
m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
while (ple != NULL) {
|
|
|
|
|
FxQueryInterface* pQI;
|
|
|
|
|
|
|
|
|
|
pQI = FxQueryInterface::_FromEntry(ple);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Advance before we potentiall free the structure
|
|
|
|
|
//
|
|
|
|
|
ple = ple->Next;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FxQueryInterface's destructor requires Next be NULL
|
|
|
|
|
//
|
|
|
|
|
pQI->m_Entry.Next = NULL;
|
|
|
|
|
|
|
|
|
|
if (pQI->m_EmbeddedInterface == FALSE) {
|
|
|
|
|
delete pQI;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
|
|
|
|
DropD3ColdInterface();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Call up the hierarchy
|
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
|
return FxPackage::Dispose(); // __super call
|
2020-09-24 20:51:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::Initialize(
|
|
|
|
|
__in PWDFDEVICE_INIT DeviceInit
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This function initializes state associated with an instance of FxPkgPnp.
|
|
|
|
|
This differs from the constructor because it can do operations which
|
|
|
|
|
will fail, and can return failure. (Constructors can't fail, they can
|
|
|
|
|
only throw exceptions, which we can't deal with in this kernel mode
|
|
|
|
|
environment.)
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
DeviceInit - Struct that the driver initialized that contains defaults.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
pFxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
|
|
|
|
|
m_ReleaseHardwareAfterDescendantsOnFailure = (DeviceInit->ReleaseHardwareOrderOnFailure ==
|
|
|
|
|
WdfReleaseHardwareOrderOnFailureAfterDescendants);
|
|
|
|
|
|
|
|
|
|
status = m_QueryInterfaceLock.Initialize();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
|
|
|
|
|
TRACINGPNP,
|
|
|
|
|
"Could not initialize QueryInterfaceLock for "
|
|
|
|
|
"WDFDEVICE %p, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_DeviceInterfaceLock.Initialize();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
|
|
|
|
|
TRACINGPNP,
|
|
|
|
|
"Could not initialize DeviceInterfaceLock for "
|
|
|
|
|
"WDFDEVICE %p, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Initialize preallocated events for UM
|
|
|
|
|
// (For KM, events allocated on stack are used since event initialization
|
|
|
|
|
// doesn't fail in KM)
|
|
|
|
|
//
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_USER_MODE)
|
|
|
|
|
|
|
|
|
|
status = m_CleanupEventUm.Initialize();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
|
|
|
|
|
TRACINGPNP,
|
|
|
|
|
"Could not initialize cleanup event for "
|
|
|
|
|
"WDFDEVICE %p, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_RemoveEventUm.Initialize(SynchronizationEvent, FALSE);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
|
|
|
|
|
TRACINGPNP,
|
|
|
|
|
"Could not initialize remove event for "
|
|
|
|
|
"WDFDEVICE %p, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (DeviceInit->IsPwrPolOwner()) {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner = new (pFxDriverGlobals)
|
|
|
|
|
FxPowerPolicyOwnerSettings(this);
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner == NULL) {
|
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_PowerPolicyMachine.m_Owner->Init();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
|
|
|
|
QueryForD3ColdInterface();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// we will change the access flags on the object later on when we build up
|
|
|
|
|
// the list from the wdm resources
|
|
|
|
|
//
|
|
|
|
|
status = FxCmResList::_CreateAndInit(&m_Resources,
|
|
|
|
|
pFxDriverGlobals,
|
|
|
|
|
m_Device,
|
|
|
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
|
|
|
FxResourceNoAccess);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_Resources->Commit(WDF_NO_OBJECT_ATTRIBUTES,
|
|
|
|
|
WDF_NO_HANDLE,
|
|
|
|
|
m_Device);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This should never fail
|
|
|
|
|
//
|
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
m_Resources->DeleteFromFailedCreate();
|
|
|
|
|
m_Resources = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_Resources->ADDREF(this);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// we will change the access flags on the object later on when we build up
|
|
|
|
|
// the list from the wdm resources
|
|
|
|
|
//
|
|
|
|
|
status = FxCmResList::_CreateAndInit(&m_ResourcesRaw,
|
|
|
|
|
pFxDriverGlobals,
|
|
|
|
|
m_Device,
|
|
|
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
|
|
|
FxResourceNoAccess);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_ResourcesRaw->Commit(WDF_NO_OBJECT_ATTRIBUTES,
|
|
|
|
|
WDF_NO_HANDLE,
|
|
|
|
|
m_Device);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This should never fail
|
|
|
|
|
//
|
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
m_ResourcesRaw->DeleteFromFailedCreate();
|
|
|
|
|
m_ResourcesRaw = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_ResourcesRaw->ADDREF(this);
|
|
|
|
|
|
|
|
|
|
status = RegisterCallbacks(&DeviceInit->PnpPower.PnpPowerEventCallbacks);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
RegisterPowerPolicyCallbacks(&DeviceInit->PnpPower.PolicyEventCallbacks);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::Dispatch(
|
|
|
|
|
__in MdIrp Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This is the main dispatch handler for the pnp package. This method is
|
|
|
|
|
called by the framework manager when a PNP or Power IRP enters the driver.
|
|
|
|
|
This function will dispatch the IRP to a function designed to handle the
|
|
|
|
|
specific IRP.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Device - a pointer to the FxDevice
|
|
|
|
|
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
FxIrp irp(Irp);
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
|
|
|
|
FX_TRACK_DRIVER(GetDriverGlobals());
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (irp.GetMajorFunction() == IRP_MJ_PNP) {
|
|
|
|
|
|
|
|
|
|
switch (irp.GetMinorFunction()) {
|
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
|
case IRP_MN_EJECT:
|
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! IRP 0x%p",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(), irp.GetIrp());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! "
|
|
|
|
|
"type %!DEVICE_RELATION_TYPE! IRP 0x%p",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(),
|
|
|
|
|
irp.GetParameterQDRType(), irp.GetIrp());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! IRP 0x%p",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(), irp.GetIrp());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (irp.GetMinorFunction() <= IRP_MN_SURPRISE_REMOVAL) {
|
|
|
|
|
status = (*GetDispatchPnp()[irp.GetMinorFunction()])(this, &irp);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// For Pnp IRPs we don't understand, just forget about them
|
|
|
|
|
//
|
|
|
|
|
status = FireAndForgetIrp(&irp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// If this isn't a PnP Irp, it must be a power irp.
|
|
|
|
|
//
|
|
|
|
|
switch (irp.GetMinorFunction()) {
|
|
|
|
|
case IRP_MN_WAIT_WAKE:
|
|
|
|
|
case IRP_MN_SET_POWER:
|
|
|
|
|
if (irp.GetParameterPowerType() == SystemPowerState) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! "
|
|
|
|
|
"IRP 0x%p for %!SYSTEM_POWER_STATE! (S%d)",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(), irp.GetIrp(),
|
|
|
|
|
irp.GetParameterPowerStateSystemState(),
|
|
|
|
|
irp.GetParameterPowerStateSystemState() - 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! "
|
|
|
|
|
"IRP 0x%p for %!DEVICE_POWER_STATE!",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(), irp.GetIrp(),
|
|
|
|
|
irp.GetParameterPowerStateDeviceState());
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! IRP 0x%p",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
irp.GetMinorFunction(), irp.GetIrp());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mx::MxAssert(irp.GetMajorFunction() == IRP_MJ_POWER);
|
|
|
|
|
|
|
|
|
|
if (irp.GetMinorFunction() <= IRP_MN_QUERY_POWER) {
|
|
|
|
|
status = (*GetDispatchPower()[irp.GetMinorFunction()])(this, &irp);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// For Power IRPs we don't understand, just forget about them
|
|
|
|
|
//
|
|
|
|
|
status = FireAndForgetIrp(&irp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PNP_DEVICE_STATE
|
|
|
|
|
FxPkgPnp::HandleQueryPnpDeviceState(
|
|
|
|
|
__in PNP_DEVICE_STATE PnpDeviceState
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This function handled IRP_MN_QUERY_DEVICE_STATE. Most of the bits are
|
|
|
|
|
just copied from internal Framework state.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
PnpDeviceState - Bitfield that will be returned to the sender of the IRP.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
LONG state;
|
|
|
|
|
|
|
|
|
|
state = GetPnpStateInternal();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Return device state set by driver.
|
|
|
|
|
//
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_DISABLED,
|
|
|
|
|
state,
|
|
|
|
|
Disabled);
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_DONT_DISPLAY_IN_UI,
|
|
|
|
|
state,
|
|
|
|
|
DontDisplayInUI);
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_FAILED,
|
|
|
|
|
state,
|
|
|
|
|
Failed);
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_NOT_DISABLEABLE,
|
|
|
|
|
state,
|
|
|
|
|
NotDisableable);
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_REMOVED,
|
|
|
|
|
state,
|
|
|
|
|
Removed);
|
|
|
|
|
SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
|
|
|
|
|
PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED,
|
|
|
|
|
state,
|
|
|
|
|
ResourcesChanged);
|
|
|
|
|
|
|
|
|
|
if ((state & FxPnpStateDontDisplayInUIMask) == FxPnpStateDontDisplayInUIUseDefault) {
|
|
|
|
|
LONG caps;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Mask off all caps except for NoDispalyInUI
|
|
|
|
|
//
|
|
|
|
|
caps = GetPnpCapsInternal() & FxPnpCapNoDisplayInUIMask;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the driver didn't specify pnp state, see if they specified no
|
|
|
|
|
// display as a capability. For raw PDOs and just usability, it is not
|
|
|
|
|
// always clear to the driver writer that they must set both the pnp cap
|
|
|
|
|
// and the pnp state for the no display in UI property to stick after
|
|
|
|
|
// the device has been started.
|
|
|
|
|
//
|
|
|
|
|
if (caps == FxPnpCapNoDisplayInUITrue) {
|
|
|
|
|
PnpDeviceState |= PNP_DEVICE_DONT_DISPLAY_IN_UI;
|
|
|
|
|
}
|
|
|
|
|
else if (caps == FxPnpCapNoDisplayInUIFalse) {
|
|
|
|
|
PnpDeviceState &= ~PNP_DEVICE_DONT_DISPLAY_IN_UI;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Return device state maintained by frameworks.
|
|
|
|
|
//
|
|
|
|
|
if (IsInSpecialUse()) {
|
|
|
|
|
PnpDeviceState |= PNP_DEVICE_NOT_DISABLEABLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If there is an internal failure, then indicate that up to pnp.
|
|
|
|
|
//
|
|
|
|
|
if (m_InternalFailure || m_Failed) {
|
|
|
|
|
PnpDeviceState |= PNP_DEVICE_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PnpDeviceState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::HandleQueryBusRelations(
|
|
|
|
|
__inout FxIrp* Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Handles a query device relations for the bus relations type (all other types
|
|
|
|
|
are handled by HandleQueryDeviceRelations). This function will call into
|
|
|
|
|
each of the device's FxChildList objects to process the request.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Irp - the request contain the query device relations
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxWaitLockTransactionedList* pList;
|
|
|
|
|
PDEVICE_RELATIONS pRelations;
|
|
|
|
|
FxTransactionedEntry* ple;
|
|
|
|
|
NTSTATUS status, listStatus;
|
|
|
|
|
BOOLEAN changed;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Before we do anything, callback into the driver
|
|
|
|
|
//
|
|
|
|
|
m_DeviceRelationsQuery.Invoke(m_Device->GetHandle(), BusRelations);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Default to success unless list processing fails
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Keep track of changes made by any list object. If anything changes,
|
|
|
|
|
// remember it for post-processing.
|
|
|
|
|
//
|
|
|
|
|
changed = FALSE;
|
|
|
|
|
|
|
|
|
|
pRelations = (PDEVICE_RELATIONS) Irp->GetInformation();
|
|
|
|
|
|
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
|
|
|
pList = &m_EnumInfo->m_ChildListList;
|
|
|
|
|
|
|
|
|
|
pList->LockForEnum(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pList = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ple = NULL;
|
|
|
|
|
while (pList != NULL && (ple = pList->GetNextEntry(ple)) != NULL) {
|
|
|
|
|
FxChildList* pChildList;
|
|
|
|
|
|
|
|
|
|
pChildList = FxChildList::_FromEntry(ple);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// ProcessBusRelations will free and reallocate pRelations if necessary
|
|
|
|
|
//
|
|
|
|
|
listStatus = pChildList->ProcessBusRelations(&pRelations);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// STATUS_NOT_SUPPORTED is a special value. It indicates that the call
|
|
|
|
|
// to ProcessBusRelations did not modify pRelations in any way.
|
|
|
|
|
//
|
|
|
|
|
if (listStatus == STATUS_NOT_SUPPORTED) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(listStatus)) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, WDFCHILDLIST %p returned %!STATUS! from "
|
|
|
|
|
"processing bus relations",
|
|
|
|
|
m_Device->GetHandle(), pChildList->GetHandle(), listStatus);
|
|
|
|
|
status = listStatus;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We updated pRelations, change the status later
|
|
|
|
|
//
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// By checking for NT_SUCCESS(status) below we account for
|
|
|
|
|
// both the cases - list changed, as well as list unchanged but possibly
|
|
|
|
|
// children being reported missing (that doesn't involve list change).
|
|
|
|
|
//
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
|
|
|
|
|
ple = NULL;
|
|
|
|
|
while (pList != NULL && (ple = pList->GetNextEntry(ple)) != NULL) {
|
|
|
|
|
FxChildList* pChildList;
|
|
|
|
|
|
|
|
|
|
pChildList = FxChildList::_FromEntry(ple);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// invoke the ReportedMissing callback for for children that are
|
|
|
|
|
// being reporte missing
|
|
|
|
|
//
|
|
|
|
|
pChildList->InvokeReportedMissingCallback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pList != NULL) {
|
|
|
|
|
pList->UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status) && changed == FALSE) {
|
|
|
|
|
//
|
|
|
|
|
// Went through the entire list of FxChildList objects, but no object
|
|
|
|
|
// modified the pRelations, so restore the caller's NTSTATUS.
|
|
|
|
|
//
|
|
|
|
|
status = Irp->GetStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Re-set the relations into the structure so that any changes that any call
|
|
|
|
|
// to FxChildList::ProcessBusRelations takes effect and is reported.
|
|
|
|
|
//
|
|
|
|
|
Irp->SetInformation((ULONG_PTR) pRelations);
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, returning %!STATUS! from processing bus relations",
|
|
|
|
|
m_Device->GetHandle(), status
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status) && pRelations != NULL) {
|
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p returning %d devices in relations %p",
|
|
|
|
|
m_Device->GetHandle(), pRelations->Count, pRelations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Try to not consume an IFR entry per DO reported. Instead, report up
|
|
|
|
|
// to 4 at a time.
|
|
|
|
|
//
|
|
|
|
|
for (i = 0; i < pRelations->Count && GetDriverGlobals()->FxVerboseOn; i += 4) {
|
|
|
|
|
if (i + 3 < pRelations->Count) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"PDO %p PDO %p PDO %p PDO %p",
|
|
|
|
|
pRelations->Objects[i],
|
|
|
|
|
pRelations->Objects[i+1],
|
|
|
|
|
pRelations->Objects[i+2],
|
|
|
|
|
pRelations->Objects[i+3]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else if (i + 2 < pRelations->Count) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"PDO %p PDO %p PDO %p",
|
|
|
|
|
pRelations->Objects[i],
|
|
|
|
|
pRelations->Objects[i+1],
|
|
|
|
|
pRelations->Objects[i+2]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else if (i + 1 < pRelations->Count) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"PDO %p PDO %p",
|
|
|
|
|
pRelations->Objects[i],
|
|
|
|
|
pRelations->Objects[i+1]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"PDO %p",
|
|
|
|
|
pRelations->Objects[i]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::HandleQueryBusInformation(
|
|
|
|
|
__inout FxIrp* Irp
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Probably is a better check then this to see if the driver set the bus
|
|
|
|
|
// information
|
|
|
|
|
//
|
|
|
|
|
if (m_BusInformation.BusTypeGuid.Data1 != 0x0) {
|
|
|
|
|
PPNP_BUS_INFORMATION pBusInformation;
|
|
|
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
|
|
|
|
|
|
|
|
pFxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
pBusInformation = (PPNP_BUS_INFORMATION) MxMemory::MxAllocatePoolWithTag(
|
|
|
|
|
PagedPool, sizeof(PNP_BUS_INFORMATION), pFxDriverGlobals->Tag);
|
|
|
|
|
|
|
|
|
|
if (pBusInformation != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Initialize the PNP_BUS_INFORMATION structure with the data
|
|
|
|
|
// from PDO properties.
|
|
|
|
|
//
|
|
|
|
|
RtlCopyMemory(pBusInformation,
|
|
|
|
|
&m_BusInformation,
|
|
|
|
|
sizeof(PNP_BUS_INFORMATION));
|
|
|
|
|
|
|
|
|
|
Irp->SetInformation((ULONG_PTR) pBusInformation);
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Irp->SetInformation(NULL);
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p could not allocate PNP_BUS_INFORMATION string, "
|
|
|
|
|
" %!STATUS!", m_Device->GetHandle(), status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = Irp->GetStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::HandleQueryDeviceRelations(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__inout FxRelatedDeviceList* List
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Handles the query device relations request for all relation types *except*
|
|
|
|
|
for bus relations (HandleQueryBusRelations handles that type exclusively).
|
|
|
|
|
|
|
|
|
|
This function will allocate a PDEVICE_RELATIONS structure if the passed in
|
|
|
|
|
FxRelatedDeviceList contains any devices to add to the relations list.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Irp - the request
|
|
|
|
|
|
|
|
|
|
List - list containing devices to report in the relations
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
PDEVICE_RELATIONS pPriorRelations, pNewRelations;
|
|
|
|
|
FxRelatedDevice* entry;
|
|
|
|
|
DEVICE_RELATION_TYPE type;
|
|
|
|
|
ULONG count;
|
|
|
|
|
size_t size;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
BOOLEAN retry;
|
|
|
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
|
|
|
|
|
|
|
|
if (List == NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Indicate that we didn't modify the irp at all since we have no list
|
|
|
|
|
//
|
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pFxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
type = Irp->GetParameterQDRType();
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Notify driver that he should re-scan for device relations.
|
|
|
|
|
//
|
|
|
|
|
m_DeviceRelationsQuery.Invoke(m_Device->GetHandle(), type);
|
|
|
|
|
|
|
|
|
|
pPriorRelations = (PDEVICE_RELATIONS) Irp->GetInformation();
|
|
|
|
|
retry = FALSE;
|
|
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
|
|
List->LockForEnum(pFxDriverGlobals);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Count how many entries there are in the list
|
|
|
|
|
//
|
|
|
|
|
for (entry = NULL; (entry = List->GetNextEntry(entry)) != NULL; count++) {
|
|
|
|
|
DO_NOTHING();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If we have
|
|
|
|
|
// 1) no devices in the list AND
|
|
|
|
|
// a) we have nothing to report OR
|
|
|
|
|
// b) we have something to report and there are previous relations (which
|
|
|
|
|
// if left unchanged will be used to report our missing devices)
|
|
|
|
|
//
|
|
|
|
|
// THEN we have nothing else to do, just return
|
|
|
|
|
//
|
|
|
|
|
if (count == 0 &&
|
|
|
|
|
(List->m_NeedReportMissing == 0 || pPriorRelations != NULL)) {
|
|
|
|
|
List->UnlockFromEnum(pFxDriverGlobals);
|
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pPriorRelations != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Looks like another driver in the stack has already added some
|
|
|
|
|
// entries. Make sure we allocate space for these additional entries.
|
|
|
|
|
//
|
|
|
|
|
count += pPriorRelations->Count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Allocate space for the device relations structure (which includes
|
|
|
|
|
// space for one PDEVICE_OBJECT, and then allocate enough additional
|
|
|
|
|
// space for the extra PDEVICE_OBJECTS we need.
|
|
|
|
|
//
|
|
|
|
|
// (While no FxChildList objects are used in this function, this static
|
|
|
|
|
// function from the class computes what we need.)
|
|
|
|
|
//
|
|
|
|
|
size = FxChildList::_ComputeRelationsSize(count);
|
|
|
|
|
|
|
|
|
|
pNewRelations = (PDEVICE_RELATIONS) MxMemory::MxAllocatePoolWithTag(
|
|
|
|
|
PagedPool, size, pFxDriverGlobals->Tag);
|
|
|
|
|
|
|
|
|
|
if (pNewRelations == NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Dereference any previously reported relations before exiting. They
|
|
|
|
|
// are dereferenced here because the PNP manager will see error and not
|
|
|
|
|
// do anything while the driver which added these objects expects the
|
|
|
|
|
// pnp manager to do the dereference. Since this device is changing the
|
|
|
|
|
// status, it must act like the pnp manager.
|
|
|
|
|
//
|
|
|
|
|
if (pPriorRelations != NULL) {
|
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < pPriorRelations->Count; i++) {
|
|
|
|
|
Mx::MxDereferenceObject(pPriorRelations->Objects[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (List->IncrementRetries() < 3) {
|
|
|
|
|
retry = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p could not allocate device relations for type %d string, "
|
|
|
|
|
" %!STATUS!", m_Device->GetHandle(), type, status);
|
|
|
|
|
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtlZeroMemory(pNewRelations, size);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If there was an existing device relations structure, copy
|
|
|
|
|
// the entries to the new structure.
|
|
|
|
|
//
|
|
|
|
|
if (pPriorRelations != NULL && pPriorRelations->Count > 0) {
|
|
|
|
|
RtlCopyMemory(
|
|
|
|
|
pNewRelations,
|
|
|
|
|
pPriorRelations,
|
|
|
|
|
FxChildList::_ComputeRelationsSize(pPriorRelations->Count)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Walk the list and return the relations here
|
|
|
|
|
//
|
|
|
|
|
for (entry = NULL;
|
|
|
|
|
(entry = List->GetNextEntry(entry)) != NULL;
|
|
|
|
|
pNewRelations->Count++) {
|
|
|
|
|
MdDeviceObject pdo;
|
|
|
|
|
|
|
|
|
|
pdo = entry->GetDevice();
|
|
|
|
|
|
|
|
|
|
if (entry->m_State == RelatedDeviceStateNeedsReportPresent) {
|
|
|
|
|
entry->m_State = RelatedDeviceStateReportedPresent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Add it to the DEVICE_RELATIONS structure. Pnp dictates that each
|
|
|
|
|
// PDO in the list be referenced.
|
|
|
|
|
//
|
|
|
|
|
pNewRelations->Objects[pNewRelations->Count] = reinterpret_cast<PDEVICE_OBJECT>(pdo);
|
|
|
|
|
Mx::MxReferenceObject(pdo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Done:
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
List->ZeroRetries();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List->UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
if (pPriorRelations != NULL) {
|
|
|
|
|
MxMemory::MxFreePool(pPriorRelations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (retry) {
|
|
|
|
|
MxDeviceObject physicalDeviceObject(
|
|
|
|
|
m_Device->GetPhysicalDevice()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
physicalDeviceObject.InvalidateDeviceRelations(type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
Irp->SetInformation((ULONG_PTR) pNewRelations);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PostCreateDeviceInitialize(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This function does any initialization to this object which must be done
|
|
|
|
|
after the underlying device object has been attached to the device stack,
|
|
|
|
|
i.e. you can send IRPs down this stack now.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
none
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
status = m_PnpMachine.Init(this, &FxPkgPnp::_PnpProcessEventInner);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"PnP State Machine init failed, %!STATUS!",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_PowerMachine.Init(this, &FxPkgPnp::_PowerProcessEventInner);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Power State Machine init failed, %!STATUS!",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_PowerPolicyMachine.Init(this, &FxPkgPnp::_PowerPolicyProcessEventInner);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Power Policy State Machine init failed, %!STATUS!",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::FinishInitialize(
|
|
|
|
|
__inout PWDFDEVICE_INIT DeviceInit
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Finish initializing the object. All initialization up until this point
|
|
|
|
|
could fail. This function cannot fail, so all it can do is assign field
|
|
|
|
|
values and take allocations from DeviceInit.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
DeviceInit - device initialization structure that the driver writer has
|
|
|
|
|
initialized
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Reassign the state callback arrays away from the init struct.
|
|
|
|
|
// Set the init field to NULL so that it does not attempt to free the array
|
|
|
|
|
// when it is destroyed.
|
|
|
|
|
//
|
|
|
|
|
m_PnpStateCallbacks = DeviceInit->PnpPower.PnpStateCallbacks;
|
|
|
|
|
DeviceInit->PnpPower.PnpStateCallbacks = NULL;
|
|
|
|
|
|
|
|
|
|
m_PowerStateCallbacks = DeviceInit->PnpPower.PowerStateCallbacks;
|
|
|
|
|
DeviceInit->PnpPower.PowerStateCallbacks = NULL;
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyStateCallbacks = DeviceInit->PnpPower.PowerPolicyStateCallbacks;
|
|
|
|
|
DeviceInit->PnpPower.PowerPolicyStateCallbacks = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Bias the count towards one so that we can optimize the synchronous
|
|
|
|
|
// cleanup case when the device is being destroyed.
|
|
|
|
|
//
|
|
|
|
|
m_PendingChildCount = 1;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now "Add" this device in the terms that the PnP state machine uses. This
|
|
|
|
|
// will be in the context of an actual AddDevice function for FDOs, and
|
|
|
|
|
// something very much like it for PDOs.
|
|
|
|
|
//
|
|
|
|
|
// Important that the posting of the event is after setting of the state
|
|
|
|
|
// callback arrays so that we can guarantee that any state transition
|
|
|
|
|
// callback will be made.
|
|
|
|
|
//
|
|
|
|
|
PnpProcessEvent(PnpEventAddDevice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::ProcessDelayedDeletion(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p processing delayed deletion from pnp state "
|
|
|
|
|
"machine", m_Device->GetHandle(), m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
CleanupStateMachines(FALSE);
|
|
|
|
|
DeleteDevice();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetSpecialFileSupport(
|
|
|
|
|
__in WDF_SPECIAL_FILE_TYPE FileType,
|
|
|
|
|
__in BOOLEAN Supported
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This function marks the device as capable of handling the paging path,
|
|
|
|
|
hibernation or crashdumps. Any device that is necessary for one of these
|
|
|
|
|
three things will get notification. It is then responsible for forwarding
|
|
|
|
|
the notification to its parent. The Framework handles that. This
|
|
|
|
|
function just allows a driver to tell the Framework how to respond.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
FileType - identifies which of the special paths the device is in
|
|
|
|
|
Supported - Yes or No
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
switch (FileType) {
|
|
|
|
|
case WdfSpecialFilePaging:
|
|
|
|
|
case WdfSpecialFileHibernation:
|
|
|
|
|
case WdfSpecialFileDump:
|
|
|
|
|
case WdfSpecialFileBoot:
|
|
|
|
|
SetUsageSupport(_SpecialTypeToUsage(FileType), Supported);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Invalid special file type %x", FileType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
PnpPassThroughQI(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__inout FxIrp* Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This driver may be sent IRP_MN_QUERY_INTERFACE, which is a way of getting
|
|
|
|
|
a direct-call table from some driver in a device stack. In some cases, the
|
|
|
|
|
right response is to turn around and send a similar query to the device's
|
|
|
|
|
parent. This function does that.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Device - This WDFDEVICE.
|
|
|
|
|
Irp - The IRP that was sent to us.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
MdIrp pFwdIrp;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
NTSTATUS prevStatus;
|
|
|
|
|
MxDeviceObject pTopOfStack;
|
|
|
|
|
|
|
|
|
|
prevStatus = Irp->GetStatus();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pTopOfStack.SetObject(Device->m_ParentDevice->GetAttachedDeviceReference());
|
|
|
|
|
|
|
|
|
|
pFwdIrp = FxIrp::AllocateIrp(pTopOfStack.GetStackSize());
|
|
|
|
|
|
|
|
|
|
if (pFwdIrp != NULL) {
|
|
|
|
|
FxAutoIrp fxFwdIrp(pFwdIrp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The worker routine copies stack parameters to forward-Irp, sends it
|
|
|
|
|
// down the stack synchronously, then copies back the stack parameters
|
|
|
|
|
// from forward-irp to original-irp
|
|
|
|
|
//
|
|
|
|
|
PnpPassThroughQIWorker(&pTopOfStack, Irp, &fxFwdIrp);
|
|
|
|
|
|
|
|
|
|
if (fxFwdIrp.GetStatus() != STATUS_NOT_SUPPORTED) {
|
|
|
|
|
status = fxFwdIrp.GetStatus();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = prevStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
Irp->SetInformation(fxFwdIrp.GetInformation());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
Device->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p could not allocate IRP to send QI to parent !devobj "
|
|
|
|
|
"%p, %!STATUS!", Device->GetHandle(), pTopOfStack.GetObject(),
|
|
|
|
|
status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pTopOfStack.DereferenceObject();
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::HandleQueryInterfaceForPowerThread(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__out PBOOLEAN CompleteRequest
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
*CompleteRequest = TRUE;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Send the request down the stack first. If someone lower handles it
|
|
|
|
|
// or failed trying to, just return their status
|
|
|
|
|
//
|
|
|
|
|
status = SendIrpSynchronously(Irp);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status) || status != STATUS_NOT_SUPPORTED) {
|
|
|
|
|
//
|
|
|
|
|
// Success or failure trying to handle it
|
|
|
|
|
//
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The semantic of this QI is that it sent down while processing start or
|
|
|
|
|
// a device usage notification on the way *up* the stack. That means that
|
|
|
|
|
// by the time the QI gets to the lower part of the stack, the power thread
|
|
|
|
|
// will have already been allocated and exported.
|
|
|
|
|
//
|
|
|
|
|
ASSERT(HasPowerThread());
|
|
|
|
|
|
|
|
|
|
if (Irp->GetParameterQueryInterfaceVersion() == 1 &&
|
|
|
|
|
Irp->GetParameterQueryInterfaceSize() >=
|
|
|
|
|
m_PowerThreadInterface.Interface.Size) {
|
|
|
|
|
//
|
|
|
|
|
// Expose the interface to the requesting driver.
|
|
|
|
|
//
|
|
|
|
|
CopyQueryInterfaceToIrpStack(&m_PowerThreadInterface, Irp);
|
|
|
|
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Caller assumes a reference has been taken.
|
|
|
|
|
//
|
|
|
|
|
m_PowerThreadInterface.Interface.InterfaceReference(
|
|
|
|
|
m_PowerThreadInterface.Interface.Context
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::HandleQueryInterface(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__out PBOOLEAN CompleteRequest
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This driver may be sent IRP_MN_QUERY_INTERFACE, which is a way of getting
|
|
|
|
|
a direct-call table from some driver in a device stack. This function
|
|
|
|
|
looks into a list of interfaces that the driver has registered and, if
|
|
|
|
|
the interface that is being sought is present, it answers the IRP.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Irp - The IRP that was sent to us.
|
|
|
|
|
CompleteRequest - tells the caller whether the IRP should be completed
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
FxDeviceProcessQueryInterfaceRequest callback;
|
|
|
|
|
const GUID* pInterfaceType;
|
|
|
|
|
PSINGLE_LIST_ENTRY ple;
|
|
|
|
|
FxQueryInterface *pQI;
|
|
|
|
|
PVOID pFound;
|
|
|
|
|
PINTERFACE pExposedInterface;
|
|
|
|
|
PVOID pExposedInterfaceSpecificData;
|
|
|
|
|
BOOLEAN sendToParent;
|
|
|
|
|
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
*CompleteRequest = FALSE;
|
|
|
|
|
|
|
|
|
|
pFound = NULL;
|
|
|
|
|
pQI = NULL;
|
|
|
|
|
pExposedInterface = NULL;
|
|
|
|
|
pExposedInterfaceSpecificData = NULL;
|
|
|
|
|
sendToParent = FALSE;
|
|
|
|
|
|
|
|
|
|
pInterfaceType = Irp->GetParameterQueryInterfaceType();
|
|
|
|
|
//
|
|
|
|
|
// The power thread is special cased because it has a different semantic
|
|
|
|
|
// then the usual QI irp that we expose to the driver writer. In this case,
|
|
|
|
|
// we want to fill in the interface if the lower stack does not support it.
|
|
|
|
|
//
|
|
|
|
|
if (FxIsEqualGuid(pInterfaceType, &GUID_POWER_THREAD_INTERFACE)) {
|
|
|
|
|
return HandleQueryInterfaceForPowerThread(Irp, CompleteRequest);
|
|
|
|
|
}
|
|
|
|
|
else if (FxIsEqualGuid(pInterfaceType, &GUID_REENUMERATE_SELF_INTERFACE_STANDARD)) {
|
|
|
|
|
if (m_Device->IsPdo()) {
|
|
|
|
|
return ((FxPkgPdo*) this)->HandleQueryInterfaceForReenumerate(
|
|
|
|
|
Irp, CompleteRequest);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = Irp->GetStatus();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Walk the interface collection and return the appropriate interface.
|
|
|
|
|
//
|
|
|
|
|
m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
for (ple = m_QueryInterfaceHead.Next; ple != NULL; ple = ple->Next) {
|
|
|
|
|
pQI = FxQueryInterface::_FromEntry(ple);
|
|
|
|
|
|
|
|
|
|
if (FxIsEqualGuid(Irp->GetParameterQueryInterfaceType(),
|
|
|
|
|
&pQI->m_InterfaceType)) {
|
|
|
|
|
|
|
|
|
|
pExposedInterface = Irp->GetParameterQueryInterfaceInterface();
|
|
|
|
|
pExposedInterfaceSpecificData =
|
|
|
|
|
Irp->GetParameterQueryInterfaceInterfaceSpecificData();
|
|
|
|
|
|
|
|
|
|
if (pQI->m_Interface != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// NOTE: If a driver has exposed the same interface GUID with
|
|
|
|
|
// different sizes as a ways of versioning, then the driver
|
|
|
|
|
// writer can specify the minimum size and version number
|
|
|
|
|
// and then fill in the remaining fields in the callback
|
|
|
|
|
// below.
|
|
|
|
|
//
|
|
|
|
|
if (pQI->m_Interface->Size <= Irp->GetParameterQueryInterfaceSize() &&
|
|
|
|
|
pQI->m_Interface->Version <= Irp->GetParameterQueryInterfaceVersion()) {
|
|
|
|
|
|
|
|
|
|
if (pQI->m_ImportInterface == FALSE) {
|
|
|
|
|
//
|
|
|
|
|
// Expose the interface to the requesting driver.
|
|
|
|
|
//
|
|
|
|
|
RtlCopyMemory(pExposedInterface,
|
|
|
|
|
pQI->m_Interface,
|
|
|
|
|
pQI->m_Interface->Size);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// The interface contains data which the driver wants
|
|
|
|
|
// before copying over its information, so don't do a
|
|
|
|
|
// copy and let the event callback do the copy
|
|
|
|
|
//
|
|
|
|
|
DO_NOTHING();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
callback.m_Method = pQI->m_ProcessRequest.m_Method;
|
|
|
|
|
sendToParent = pQI->m_SendQueryToParentStack;
|
|
|
|
|
pFound = pQI;
|
|
|
|
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status) || pFound == NULL) {
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Let the driver see the interface before it is handed out.
|
|
|
|
|
//
|
|
|
|
|
status = callback.Invoke(m_Device->GetHandle(),
|
|
|
|
|
(LPGUID) Irp->GetParameterQueryInterfaceType(),
|
|
|
|
|
pExposedInterface,
|
|
|
|
|
pExposedInterfaceSpecificData);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// STATUS_NOT_SUPPORTED is a special cased error code which indicates that
|
|
|
|
|
// the QI should travel down the rest of the stack.
|
|
|
|
|
//
|
|
|
|
|
if (!NT_SUCCESS(status) && status != STATUS_NOT_SUPPORTED) {
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If it is meant for the parent, send it down the parent stack
|
|
|
|
|
//
|
|
|
|
|
if (sendToParent) {
|
|
|
|
|
status = PnpPassThroughQI(m_Device, Irp);
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Reference the interface before returning it to the requesting driver.
|
|
|
|
|
// If this is an import interface, the event callback is free to not fill
|
|
|
|
|
// in the InterfaceReference function pointer.
|
|
|
|
|
//
|
|
|
|
|
if (pExposedInterface->InterfaceReference != NULL) {
|
|
|
|
|
pExposedInterface->InterfaceReference(pExposedInterface->Context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If we are not a PDO in the stack, then send the fully formatted QI request
|
|
|
|
|
// down the stack to allow others to filter the interface.
|
|
|
|
|
//
|
|
|
|
|
if (m_Device->IsPdo() == FALSE) {
|
|
|
|
|
ASSERT(NT_SUCCESS(status) || status == STATUS_NOT_SUPPORTED);
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
Irp->CopyCurrentIrpStackLocationToNext();
|
|
|
|
|
status = Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Done:
|
|
|
|
|
if (pFound != NULL) {
|
|
|
|
|
*CompleteRequest = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::QueryForCapabilities(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
STACK_DEVICE_CAPABILITIES caps;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
MxDeviceObject deviceObject;
|
|
|
|
|
|
|
|
|
|
deviceObject.SetObject(m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
status = GetStackCapabilities(GetDriverGlobals(),
|
|
|
|
|
&deviceObject,
|
|
|
|
|
&m_D3ColdInterface,
|
|
|
|
|
&caps);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
ULONG states, i;
|
|
|
|
|
|
|
|
|
|
ASSERT(caps.DeviceCaps.DeviceWake <= 0xFF && caps.DeviceCaps.SystemWake <= 0xFF);
|
|
|
|
|
|
|
|
|
|
m_SystemWake = (BYTE) caps.DeviceCaps.SystemWake;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Initialize the array of wakeable D-states to say that all system
|
|
|
|
|
// states down to the one identified in the caps can generate wake.
|
|
|
|
|
// This will be overridden below if the BIOS supplied more information.
|
|
|
|
|
//
|
|
|
|
|
// Compatibility Note: Non-ACPI bus drivers (root-enumerated drivers)
|
|
|
|
|
// or other bus drivers that haven't set the power settings correctly
|
|
|
|
|
// for their PDO may end up with a valid value for DeviceWake in the
|
|
|
|
|
// device capabilities but a value of PowerSystemUnspecified for
|
|
|
|
|
// SystemWake, in which case a call to WdfDeviceAssignS0IdleSettings or
|
|
|
|
|
// WdfDeviceAssignSxWakeSettings DDIs will fail on 1.11+ resulting in
|
|
|
|
|
// device compat issues. The failure is expected and right thing to do
|
|
|
|
|
// but has compatibility implications - drivers that worked earlier now
|
|
|
|
|
// fail on 1.11. Note that earlier versions of WDF did not have
|
|
|
|
|
// m_DeviceWake as an array and stored just the capabilities->DeviceWake
|
|
|
|
|
// value without regard to the SystemWake but the current implementation
|
|
|
|
|
// introduces dependency on systemWake value). So for compat reasons,
|
|
|
|
|
// for pre-1.11 compiled drivers we initilaize the array with DeviceWake
|
|
|
|
|
// value ignoring SystemWake, removing any dependency of DeviceWake
|
|
|
|
|
// on SystemWake value and thus preserving previous behavior for
|
|
|
|
|
// pre-1.11 compiled drivers.
|
|
|
|
|
//
|
|
|
|
|
if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
|
|
|
|
|
|
|
|
|
|
RtlFillMemory(m_DeviceWake, DeviceWakeStates, DeviceWakeDepthNotWakeable);
|
|
|
|
|
|
|
|
|
|
for (i = PowerSystemWorking; i <= m_SystemWake; i++) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Note that this cast is hiding a conversion between two slightly
|
|
|
|
|
// incompatible types. DeviceWake is in terms of DEVICE_POWER_STATE
|
|
|
|
|
// which is defined this way:
|
|
|
|
|
//
|
|
|
|
|
// typedef enum _DEVICE_POWER_STATE {
|
|
|
|
|
// PowerDeviceUnspecified = 0,
|
|
|
|
|
// PowerDeviceD0,
|
|
|
|
|
// PowerDeviceD1,
|
|
|
|
|
// PowerDeviceD2,
|
|
|
|
|
// PowerDeviceD3,
|
|
|
|
|
// PowerDeviceMaximum
|
|
|
|
|
// } DEVICE_POWER_STATE, *PDEVICE_POWER_STATE;
|
|
|
|
|
//
|
|
|
|
|
// m_DeviceWake is defined in terms of DEVICE_WAKE_DEPTH which is
|
|
|
|
|
// defined this way:
|
|
|
|
|
//
|
|
|
|
|
// typedef enum _DEVICE_WAKE_DEPTH {
|
|
|
|
|
// DeviceWakeDepthNotWakeable = 0,
|
|
|
|
|
// DeviceWakeDepthD0,
|
|
|
|
|
// DeviceWakeDepthD1,
|
|
|
|
|
// DeviceWakeDepthD2,
|
|
|
|
|
// DeviceWakeDepthD3hot,
|
|
|
|
|
// DeviceWakeDepthD3cold,
|
|
|
|
|
// DeviceWakeDepthMaximum
|
|
|
|
|
// } DEVICE_WAKE_DEPTH, *PDEVICE_WAKE_DEPTH;
|
|
|
|
|
//
|
|
|
|
|
// The result is that the conversion below will map D3 onto D3hot,
|
|
|
|
|
// which is a safe assumption to start with, one which may be
|
|
|
|
|
// overridden later.
|
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
|
C_ASSERT(PowerDeviceD0 == static_cast<DEVICE_POWER_STATE>(DeviceWakeDepthD0));
|
2020-09-24 20:51:15 +00:00
|
|
|
|
m_DeviceWake[i - PowerSystemWorking] = (BYTE) caps.DeviceCaps.DeviceWake;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// See comments above for information on mapping of device power
|
|
|
|
|
// state to device wake depth.
|
|
|
|
|
//
|
|
|
|
|
RtlFillMemory(m_DeviceWake,
|
|
|
|
|
DeviceWakeStates,
|
|
|
|
|
(BYTE) caps.DeviceCaps.DeviceWake);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Capture the S -> D state mapping table as a ULONG for use in the
|
|
|
|
|
// power policy owner state machine when the machine moves into Sx and
|
|
|
|
|
// the device is not armed for wake and has set an IdealDxStateForSx
|
|
|
|
|
// value
|
|
|
|
|
//
|
|
|
|
|
states = 0x0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(caps.DeviceCaps.DeviceState); i++) {
|
|
|
|
|
_SetPowerCapState(i, caps.DeviceCaps.DeviceState[i], &states);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_SystemToDeviceStateMap = states;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Query for the D3cold support interface. If present, it will tell
|
|
|
|
|
// us specifically which D-states will work for generating wake signals
|
|
|
|
|
// from specific S-states.
|
|
|
|
|
//
|
|
|
|
|
// Earlier versions of WDF didn't make this query, so for compatibility,
|
|
|
|
|
// we only make it now if the driver was built against WDF 1.11 or
|
|
|
|
|
// later. In truth, this just shifts the failure from initialization
|
|
|
|
|
// time to run time, because the information that we're presumably
|
|
|
|
|
// getting from the BIOS with this interrogation is saying that the
|
|
|
|
|
// code in earlier verisions of WDF would have blindly enabled a device
|
|
|
|
|
// for wake which simply wasn't capable of generating its wake signal
|
|
|
|
|
// from the chosen D-state. Thus the device would have been put into
|
|
|
|
|
// a low power state and then failed to resume in response to its wake
|
|
|
|
|
// signal.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Cycle through all the system states that this device can wake
|
|
|
|
|
// from. There's no need to look at deeper sleep states than
|
|
|
|
|
// m_SystemWake because the driver will not arm for wake in
|
|
|
|
|
// those states.
|
|
|
|
|
//
|
|
|
|
|
for (i = PowerSystemWorking; i <= m_SystemWake; i++) {
|
|
|
|
|
if (caps.DeepestWakeableDstate[i] != DeviceWakeDepthMaximum) {
|
|
|
|
|
m_DeviceWake[i - PowerSystemWorking] = (BYTE)caps.DeepestWakeableDstate[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpStartDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This method is called in response to a PnP StartDevice IRP coming down the
|
|
|
|
|
stack.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
This - device instance
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
This->PnpProcessEvent(PnpEventStartDevice);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpQueryStopDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Pnp callback querying to see if the device can be stopped.
|
|
|
|
|
|
|
|
|
|
The Framework philosophy surrounding Query Stop (and Query Remove) is that
|
|
|
|
|
it's impossible to really know if you can stop unless you've tried to stop.
|
|
|
|
|
This may not always be true, but it's hard to find a general strategy that
|
|
|
|
|
works that is less conservative. Furthermore, I couldn't find good examples
|
|
|
|
|
of drivers that would really benefit from continuing to handle requests
|
|
|
|
|
until the actual Stop IRP arrived, particularly when you consider that
|
|
|
|
|
most QueryStops are followed immediately by Stops.
|
|
|
|
|
|
|
|
|
|
So this function sends an event to the PnP State machine that begins the
|
|
|
|
|
stopping process. If it is successful, then ultimately the QueryStop IRP
|
|
|
|
|
will be successfully completed.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - a pointer to the PnP package
|
|
|
|
|
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Keep this IRP around, since we're going to deal with it later.
|
|
|
|
|
//
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now run the state machine on this thread.
|
|
|
|
|
//
|
|
|
|
|
This->PnpProcessEvent(PnpEventQueryStop);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpCancelStopDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine is invoked in response to a query stop failing, somewhere in
|
|
|
|
|
the stack. Note that we can receive a cancel stop without being in the
|
|
|
|
|
query stop state if a driver above us in the stack failed the query stop.
|
|
|
|
|
|
|
|
|
|
Again, this function just exists to bridge the gap between the WDM IRPs
|
|
|
|
|
and the PnP state machine. This function does little more than send an
|
|
|
|
|
event to the machine.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the package
|
|
|
|
|
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Seed the irp with success
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Pend it and transition the state machine
|
|
|
|
|
//
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
This->PnpProcessEvent(PnpEventCancelStop);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpStopDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This method is invoked in response to a Pnp StopDevice IRP.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Seed the irp with success
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Pend and transition the state machine
|
|
|
|
|
//
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
This->PnpProcessEvent(PnpEventStop);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpQueryRemoveDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Again, the Framework handles QueryRemove by stopping everything going on
|
|
|
|
|
related to the device and then asking the driver whether it can be
|
|
|
|
|
removed. This function just kicks the state machine. Final completion
|
|
|
|
|
of the IRP will come (much) later.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the package
|
|
|
|
|
|
|
|
|
|
Irp - a pointer to the FxIrp
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// By default we handle this state.
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Keep this IRP around, since we're going to deal with it later.
|
|
|
|
|
//
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now run the state machine on this thread.
|
|
|
|
|
//
|
|
|
|
|
This->PnpProcessEvent(PnpEventQueryRemove);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpCancelRemoveDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Notification of a previous remove being canceled. Kick the state machine.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the package
|
|
|
|
|
|
|
|
|
|
Irp - FxIrp representing the notification
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Seed the irp with success
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Pend it and transition the state machine
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
This->SetPendingPnpIrp(Irp);
|
|
|
|
|
This->PnpProcessEvent(PnpEventCancelRemove);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::CleanupStateMachines(
|
|
|
|
|
__in BOOLEAN CleanupPnp
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_USER_MODE)
|
|
|
|
|
FxCREvent * event = m_CleanupEventUm.GetSelfPointer();
|
|
|
|
|
#else
|
|
|
|
|
FxCREvent eventOnStack;
|
|
|
|
|
eventOnStack.Initialize();
|
|
|
|
|
FxCREvent * event = eventOnStack.GetSelfPointer();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Order of shutdown is important here.
|
|
|
|
|
// o Pnp initiates events to power policy.
|
|
|
|
|
// o Power policy initiates events to power and device-power-requirement
|
|
|
|
|
// o Power does not initiate any events
|
|
|
|
|
// o Device-power-requirement does not initiate any events
|
|
|
|
|
//
|
|
|
|
|
// By shutting them down in the order in which they send events, we can
|
|
|
|
|
// guarantee that no new events will be posted into the subsidiary state
|
|
|
|
|
// machines.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This will shut off the pnp state machine and synchronize any outstanding
|
|
|
|
|
// threads of execution.
|
|
|
|
|
//
|
|
|
|
|
if (CleanupPnp && m_PnpMachine.SetFinished(
|
|
|
|
|
event
|
|
|
|
|
) == FALSE) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p waiting for pnp state machine to finish",
|
|
|
|
|
m_Device->GetHandle(), m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Process the event *before* completing the irp so that this event is in
|
|
|
|
|
// the queue before the device remove event which will be processed
|
|
|
|
|
// right after the start irp has been completed.
|
|
|
|
|
//
|
|
|
|
|
event->EnterCRAndWaitAndLeave();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Even though event is a SynchronizationEvent, so we need to reset it for
|
|
|
|
|
// the next wait because SetFinished will set it if even if the transition
|
|
|
|
|
// to the finished state is immediate
|
|
|
|
|
//
|
|
|
|
|
event->Clear();
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.SetFinished(event) == FALSE) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p waiting for pwr pol state machine to finish",
|
|
|
|
|
m_Device->GetHandle(), m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
event->EnterCRAndWaitAndLeave();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// See previous comment about why we Clear()
|
|
|
|
|
//
|
|
|
|
|
event->Clear();
|
|
|
|
|
|
|
|
|
|
if (m_PowerMachine.SetFinished(event) == FALSE) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p waiting for pwr state machine to finish",
|
|
|
|
|
m_Device->GetHandle(), m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
event->EnterCRAndWaitAndLeave();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
//
|
|
|
|
|
// See previous comment about why we Clear()
|
|
|
|
|
//
|
|
|
|
|
event->Clear();
|
|
|
|
|
|
|
|
|
|
if (NULL != m_PowerPolicyMachine.m_Owner->m_PoxInterface.
|
|
|
|
|
m_DevicePowerRequirementMachine) {
|
|
|
|
|
|
|
|
|
|
if (FALSE == m_PowerPolicyMachine.m_Owner->m_PoxInterface.
|
|
|
|
|
m_DevicePowerRequirementMachine->SetFinished(event)) {
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p waiting for device power "
|
|
|
|
|
"requirement state machine to finish",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
event->EnterCRAndWaitAndLeave();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->CleanupPowerCallback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Release the power thread if we have one either through creation or query.
|
|
|
|
|
// Since the power policy state machine is off, we should no longer need
|
|
|
|
|
// a dedicated thread.
|
|
|
|
|
//
|
|
|
|
|
// *** NOTE ***
|
|
|
|
|
// The power thread must be released *BEFORE* sending the irp down the stack
|
|
|
|
|
// because this can happen
|
|
|
|
|
// 1) this driver is not the power thread owner, but the last client
|
|
|
|
|
// 2) we send the pnp irp first
|
|
|
|
|
// 3) the power thread owner waits on this thread for all the clients to go
|
|
|
|
|
// away, but this device still has a reference on it
|
|
|
|
|
// 4) this device will not release the reference b/c the owner is waiting
|
|
|
|
|
// in the same thread.
|
|
|
|
|
//
|
|
|
|
|
ReleasePowerThread();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Deref the reenumeration interface
|
|
|
|
|
//
|
|
|
|
|
ReleaseReenumerationInterface();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::CleanupDeviceFromFailedCreate(
|
|
|
|
|
__in MxEvent * WaitEvent
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
The device failed creation in some stage. It is assumed that the device has
|
|
|
|
|
enough state that it can survive a transition through the pnp state machine
|
|
|
|
|
(which means that pointers like m_PkgIo are valid and != NULL). When this
|
|
|
|
|
function returns, it will have deleted the owning FxDevice.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
WaitEvent - Event on which RemoveProcessed wait will be performed
|
|
|
|
|
|
|
|
|
|
We can't initialize this event on stack as the initialization
|
|
|
|
|
can fail in user-mode. We can't have Initialize method
|
|
|
|
|
preinitailize this event either as this function may get called
|
|
|
|
|
before initialize (or in case of initialization failure).
|
|
|
|
|
|
|
|
|
|
Hence the caller preallocates the event and passes to this
|
|
|
|
|
function.
|
|
|
|
|
|
|
|
|
|
Caller must initialize this event as SynchronizationEvent
|
|
|
|
|
and it must be unsignalled.
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
Mx::MxAssert(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Caller must initialize the event as Synchronization event and it should
|
|
|
|
|
// be passed as non-signalled. But we Clear it just to be sure.
|
|
|
|
|
//
|
|
|
|
|
WaitEvent->Clear();
|
|
|
|
|
|
|
|
|
|
ADDREF(WaitEvent);
|
|
|
|
|
|
|
|
|
|
ASSERT(m_DeviceRemoveProcessed == NULL);
|
|
|
|
|
m_DeviceRemoveProcessed = WaitEvent;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Simulate a remove event coming to the device. After this call returns
|
|
|
|
|
// m_Device is still valid and must be deleted.
|
|
|
|
|
//
|
|
|
|
|
PnpProcessEvent(PnpEventRemove);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// No need to wait in a critical region because we are in the context of a
|
|
|
|
|
// pnp request which is in the system context.
|
|
|
|
|
//
|
|
|
|
|
WaitEvent->WaitFor(Executive, KernelMode, FALSE, NULL);
|
|
|
|
|
m_DeviceRemoveProcessed = NULL;
|
|
|
|
|
|
|
|
|
|
RELEASE(WaitEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::DeleteDevice(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine will detach and delete the device object and free the memory
|
|
|
|
|
for the device if there are no other references to it. Before calling this
|
|
|
|
|
routine, the state machines should have been cleaned up and the power thread
|
|
|
|
|
released.
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// This will detach and delete the device object
|
|
|
|
|
//
|
|
|
|
|
m_Device->Destroy();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If this is the last reference, this will free the memory for the device
|
|
|
|
|
//
|
|
|
|
|
m_Device->DeleteObject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpRemoveDevice(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Notification of a remove. Kick the state machine.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the package
|
|
|
|
|
|
|
|
|
|
Irp - FxIrp representing the notification
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
status
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
#if (FX_CORE_MODE==FX_CORE_USER_MODE)
|
|
|
|
|
MxEvent * event = This->m_RemoveEventUm.GetSelfPointer();
|
|
|
|
|
#else
|
|
|
|
|
MxEvent eventOnStack;
|
|
|
|
|
eventOnStack.Initialize(SynchronizationEvent, FALSE);
|
|
|
|
|
MxEvent * event = eventOnStack.GetSelfPointer();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
status = Mx::MxAcquireRemoveLock(
|
|
|
|
|
This->m_Device->GetRemoveLock(),
|
|
|
|
|
Irp->GetIrp());
|
|
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
|
#else
|
|
|
|
|
UNREFERENCED_PARAMETER(status);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Keep this object around after m_Device has RELEASE'ed its reference to
|
|
|
|
|
// this package.
|
|
|
|
|
//
|
|
|
|
|
This->ADDREF(Irp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Removes are always success
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
|
|
|
|
|
ASSERT(This->m_DeviceRemoveProcessed == NULL);
|
|
|
|
|
This->m_DeviceRemoveProcessed = event;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Post the event and wait for the FxDevice to destroy itself or determine
|
|
|
|
|
// it has not been reported missing yet (for PDOs and bus filters).
|
|
|
|
|
//
|
|
|
|
|
This->PnpProcessEvent(PnpEventRemove);
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p, !devobj %p waiting for remove event to finish processing",
|
|
|
|
|
This->m_Device->GetHandle(), This->m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// No need to wait in a critical region because we are in the context of a
|
|
|
|
|
// pnp request which is in the system context.
|
|
|
|
|
//
|
|
|
|
|
event->WaitFor(Executive, KernelMode, FALSE, NULL);
|
|
|
|
|
|
|
|
|
|
This->m_DeviceRemoveProcessed = NULL;
|
|
|
|
|
|
|
|
|
|
status = This->ProcessRemoveDeviceOverload(Irp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Release the reference added at the top. This is most likely going to be
|
|
|
|
|
// the last reference on the package for KMDF. For UMDF, host manages the
|
|
|
|
|
// lifetime of FxDevice so this may not be the last release for UMDF.
|
|
|
|
|
//
|
|
|
|
|
This->RELEASE(Irp);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PnpSurpriseRemoval(
|
|
|
|
|
__inout FxIrp* Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Notification that the device has been surprise removed. Kick the state
|
|
|
|
|
machine.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Irp - pointer to FxIrp representing this notification
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
STATUS_PENDING
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Package specific handling
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_SUCCESS);
|
|
|
|
|
SetPendingPnpIrp(Irp);
|
|
|
|
|
PnpProcessEvent(PnpEventSurpriseRemove);
|
|
|
|
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_DispatchWaitWake(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This the first-level dispatch routine for IRP_MN_WAIT_WAKE. What one
|
|
|
|
|
does with a WaitWake IRP depends very much on whether one is an FDO, a PDO
|
|
|
|
|
or a filter. So dispatch immediately to a subclassable function.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the package
|
|
|
|
|
|
|
|
|
|
Irp - pointer to FxIrp representing this notification
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
status
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
return This->DispatchWaitWake(Irp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::DispatchWaitWake(
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Handles wait wake requests in a generic fashion
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
PIRP oldIrp;
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_RequestedWaitWakeIrp == FALSE) {
|
|
|
|
|
//
|
|
|
|
|
// A power irp arrived, but we did not request it. log and bugcheck
|
|
|
|
|
//
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Received wait wake power irp %p on device %p, but the irp was "
|
|
|
|
|
"not requested by the device (the power policy owner)",
|
|
|
|
|
Irp->GetIrp(), m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
FxVerifierBugCheck(GetDriverGlobals(), // globals
|
|
|
|
|
WDF_POWER_MULTIPLE_PPO, // specific type
|
|
|
|
|
(ULONG_PTR)m_Device->GetDeviceObject(), //parm 2
|
|
|
|
|
(ULONG_PTR)Irp->GetIrp()); // parm 3
|
|
|
|
|
|
|
|
|
|
/* NOTREACHED */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We are no longer requesting a power irp because we received the one
|
|
|
|
|
// we requested.
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_RequestedWaitWakeIrp = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The Framework has the concept of a "Wait/Wake Owner." This is the layer
|
|
|
|
|
// in the stack that is enabling and disabling wake at the bus level. This
|
|
|
|
|
// is probably the PDO or a bus filter like ACPI.sys. This is distinct
|
|
|
|
|
// from being the "Power Policy Owner," which would mean that this driver
|
|
|
|
|
// is deciding what D-state is appropriate for the device, and also
|
|
|
|
|
// sending Wait/Wake IRPs.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if (m_SharedPower.m_WaitWakeOwner) {
|
|
|
|
|
m_PowerMachine.m_WaitWakeLock.Acquire(&irql);
|
|
|
|
|
|
|
|
|
|
if (m_SharedPower.m_WaitWakeIrp != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// We only allow one pended wait wake irp in the stack at a time.
|
|
|
|
|
// Fail this secondary wait wake request.
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Failing wait wake irp %p with %!STATUS! because wait wake irp "
|
|
|
|
|
"%p already pended",
|
|
|
|
|
Irp->GetIrp(), status, m_SharedPower.m_WaitWakeIrp);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
MdCancelRoutine pRoutine;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// No wait wake irp is currently in the stack, so attempt to set
|
|
|
|
|
// a cancel routine and transition the power state machine into a
|
|
|
|
|
// a state where it can arm the device at the bus level for this
|
|
|
|
|
// child.
|
|
|
|
|
//
|
|
|
|
|
// The power state machine expects the wait wake irp to have a cancel
|
|
|
|
|
// routine set in all states. For those states which require a
|
|
|
|
|
// non cancelable irp, those states clear the cancel routine as
|
|
|
|
|
// appropriate.
|
|
|
|
|
//
|
|
|
|
|
pRoutine = Irp->SetCancelRoutine(_PowerWaitWakeCancelRoutine);
|
|
|
|
|
#if DBG
|
|
|
|
|
ASSERT(pRoutine == NULL);
|
|
|
|
|
#else
|
|
|
|
|
UNREFERENCED_PARAMETER(pRoutine);
|
|
|
|
|
#endif
|
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
|
|
|
|
|
|
if (Irp->IsCanceled()) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
|
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"wait wake irp %p already canceled", Irp->GetIrp());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This IRP has already been cancelled, we must clear the cancel
|
|
|
|
|
// routine before completing the IRP.
|
|
|
|
|
//
|
|
|
|
|
pRoutine = Irp->SetCancelRoutine(NULL);
|
|
|
|
|
|
|
|
|
|
if (pRoutine != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Our cancel routine will not be called
|
|
|
|
|
//
|
|
|
|
|
#if DBG
|
|
|
|
|
ASSERT(pRoutine == _PowerWaitWakeCancelRoutine);
|
|
|
|
|
#else
|
|
|
|
|
UNREFERENCED_PARAMETER(pRoutine);
|
|
|
|
|
#endif
|
|
|
|
|
Irp->SetStatus(STATUS_CANCELLED);
|
|
|
|
|
status = STATUS_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
|
//
|
|
|
|
|
// Either we successfully set the cancel routine or the irp
|
|
|
|
|
// was canceled and the cancel routine is about to run when
|
|
|
|
|
// we drop the lock. If the routine is about to run, we still
|
|
|
|
|
// need to setup m_SharedPower to values that it expects.
|
|
|
|
|
//
|
|
|
|
|
Irp->MarkIrpPending();
|
|
|
|
|
m_SharedPower.m_WaitWakeIrp = Irp->GetIrp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_PowerMachine.m_WaitWakeLock.Release(irql);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// Post to the appropriate matchines
|
|
|
|
|
//
|
|
|
|
|
PowerProcessEvent(PowerWakeArrival);
|
|
|
|
|
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
PowerPolicyProcessEvent(PwrPolWakeArrived);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CompletePowerRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
else if (IsPowerPolicyOwner()) {
|
|
|
|
|
//
|
|
|
|
|
// Try to set m_WaitWakeIrp to the new IRP value only if the current
|
|
|
|
|
// value of m_WaitWakeIrp is NULL because there can only be one
|
|
|
|
|
// active wait wake irp in the stack at a time. Since the power policy
|
|
|
|
|
// state machine never sends more then one, this is really a guard
|
|
|
|
|
// against some other device in the stack sending a wait wake irp to
|
|
|
|
|
// this device in the wrong state.
|
|
|
|
|
//
|
|
|
|
|
oldIrp = (PIRP) InterlockedCompareExchangePointer(
|
|
|
|
|
(PVOID*) &m_SharedPower.m_WaitWakeIrp,
|
|
|
|
|
Irp->GetIrp(),
|
|
|
|
|
NULL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If oldIrp is NULL then there was no previous irp and we successfully
|
|
|
|
|
// exchanged the new PIRP value into m_WaitWakeIrp
|
|
|
|
|
//
|
|
|
|
|
if (oldIrp == NULL) {
|
|
|
|
|
m_PowerPolicyMachine.SetWaitWakeUnclaimed();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// NOTE: There is a built in race condition here that WDF cannot
|
|
|
|
|
// solve with the given WDM primitives. After arming the
|
|
|
|
|
// device for wake, there is a window where the wait wake irp
|
|
|
|
|
// has not yet been processed by the wait wake owner. Until
|
|
|
|
|
// the wake request is processed, wake events could be generated
|
|
|
|
|
// and lost. There is nothing we can do about this until we
|
|
|
|
|
// have synchronous "goto Dx and arm" command available.
|
|
|
|
|
//
|
|
|
|
|
Irp->CopyCurrentIrpStackLocationToNext();
|
|
|
|
|
Irp->SetCompletionRoutineEx(m_Device->GetDeviceObject(),
|
|
|
|
|
_PowerPolicyWaitWakeCompletionRoutine,
|
|
|
|
|
this);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Technically, there should be a PDO vs FDO overload here which
|
|
|
|
|
// does the right thing w/regard to this request and if it is at the
|
|
|
|
|
// bottom of the stack or not. But, by design, we check for
|
|
|
|
|
// m_WaitWakeOwner first which has the direct side affect of making
|
|
|
|
|
// it impossible for a PDO to get to this point.
|
|
|
|
|
//
|
|
|
|
|
ASSERT(m_Device->IsFdo());
|
|
|
|
|
|
|
|
|
|
status = Irp->PoCallDriver(m_Device->GetAttachedDevice());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Send the wake arrived after sending the request as commented above.
|
|
|
|
|
// This window between sending the request and sending the event to
|
|
|
|
|
// the state machine allows the wait owner to complete the request
|
|
|
|
|
// immediately. When completed synchronously, it has an effect on
|
|
|
|
|
// both wake scenarios:
|
|
|
|
|
//
|
|
|
|
|
// 1) wake from S0: the device never transitions to Dx and the idle
|
|
|
|
|
// timer is resumed if no i/o is present
|
|
|
|
|
//
|
|
|
|
|
// 2) wake from sx: the device is disarmed for wake from Sx and
|
|
|
|
|
// put into Dx with being armed for wake.
|
|
|
|
|
//
|
|
|
|
|
PowerPolicyProcessEvent(PwrPolWakeArrived);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"already have a ww irp %p, failing new ww irp %p with %!STATUS!",
|
|
|
|
|
oldIrp, Irp->GetIrp(), status);
|
|
|
|
|
|
|
|
|
|
CompletePowerRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// Not the power policy owner, not the wait wake irp owner, ignore the
|
|
|
|
|
// irp
|
|
|
|
|
//
|
|
|
|
|
// This will release the remove lock.
|
|
|
|
|
//
|
|
|
|
|
status = FireAndForgetIrp(Irp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::RegisterCallbacks(
|
|
|
|
|
__in PWDF_PNPPOWER_EVENT_CALLBACKS DispatchTable
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Update the callback table.
|
|
|
|
|
//
|
|
|
|
|
m_DeviceD0Entry.m_Method = DispatchTable->EvtDeviceD0Entry;
|
|
|
|
|
m_DeviceD0EntryPostInterruptsEnabled.m_Method =
|
|
|
|
|
DispatchTable->EvtDeviceD0EntryPostInterruptsEnabled;
|
|
|
|
|
m_DeviceD0ExitPreInterruptsDisabled.m_Method =
|
|
|
|
|
DispatchTable->EvtDeviceD0ExitPreInterruptsDisabled;
|
|
|
|
|
m_DeviceD0Exit.m_Method = DispatchTable->EvtDeviceD0Exit;
|
|
|
|
|
|
|
|
|
|
m_DevicePrepareHardware.m_Method = DispatchTable->EvtDevicePrepareHardware;
|
|
|
|
|
m_DeviceReleaseHardware.m_Method = DispatchTable->EvtDeviceReleaseHardware;
|
|
|
|
|
|
|
|
|
|
m_DeviceQueryStop.m_Method = DispatchTable->EvtDeviceQueryStop;
|
|
|
|
|
m_DeviceQueryRemove.m_Method = DispatchTable->EvtDeviceQueryRemove;
|
|
|
|
|
|
|
|
|
|
m_DeviceSurpriseRemoval.m_Method = DispatchTable->EvtDeviceSurpriseRemoval;
|
|
|
|
|
|
|
|
|
|
m_DeviceUsageNotification.m_Method = DispatchTable->EvtDeviceUsageNotification;
|
|
|
|
|
m_DeviceUsageNotificationEx.m_Method = DispatchTable->EvtDeviceUsageNotificationEx;
|
|
|
|
|
m_DeviceRelationsQuery.m_Method = DispatchTable->EvtDeviceRelationsQuery;
|
|
|
|
|
|
|
|
|
|
if (DispatchTable->EvtDeviceSelfManagedIoCleanup != NULL ||
|
|
|
|
|
DispatchTable->EvtDeviceSelfManagedIoFlush != NULL ||
|
|
|
|
|
DispatchTable->EvtDeviceSelfManagedIoInit != NULL ||
|
|
|
|
|
DispatchTable->EvtDeviceSelfManagedIoSuspend != NULL ||
|
|
|
|
|
DispatchTable->EvtDeviceSelfManagedIoRestart != NULL) {
|
|
|
|
|
|
|
|
|
|
status = FxSelfManagedIoMachine::_CreateAndInit(&m_SelfManagedIoMachine,
|
|
|
|
|
this);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_SelfManagedIoMachine->InitializeMachine(DispatchTable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RegisterPowerPolicyCallbacks(
|
|
|
|
|
__in PWDF_POWER_POLICY_EVENT_CALLBACKS Callbacks
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromS0.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceArmWakeFromS0;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromSx.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceArmWakeFromSx;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromSx.m_MethodWithReason =
|
|
|
|
|
Callbacks->EvtDeviceArmWakeFromSxWithReason;
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceDisarmWakeFromS0.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceDisarmWakeFromS0;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceDisarmWakeFromSx.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceDisarmWakeFromSx;
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceWakeFromS0Triggered.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceWakeFromS0Triggered;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_DeviceWakeFromSxTriggered.m_Method =
|
|
|
|
|
Callbacks->EvtDeviceWakeFromSxTriggered;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::RegisterPowerPolicyWmiInstance(
|
|
|
|
|
__in const GUID* Guid,
|
|
|
|
|
__in FxWmiInstanceInternalCallbacks* Callbacks,
|
|
|
|
|
__out FxWmiInstanceInternal** Instance
|
|
|
|
|
)
|
|
|
|
|
{
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// WDF_WMI_PROVIDER_CONFIG config;
|
|
|
|
|
// NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
// WDF_WMI_PROVIDER_CONFIG_INIT(&config, Guid);
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // We are assuming we are registering either for the wait wake or device
|
|
|
|
|
// // timeout GUIDs which both operate on BOOLEANs. If we expand this API in
|
|
|
|
|
// // the future, have the caller pass in a config structure for the provider
|
|
|
|
|
// // GUID.
|
|
|
|
|
// //
|
|
|
|
|
// config.MinInstanceBufferSize = sizeof(BOOLEAN);
|
|
|
|
|
|
|
|
|
|
// status = m_Device->m_PkgWmi->AddPowerPolicyProviderAndInstance(
|
|
|
|
|
// &config,
|
|
|
|
|
// Callbacks,
|
|
|
|
|
// Instance);
|
|
|
|
|
|
|
|
|
|
// if (status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
|
// status = STATUS_SUCCESS;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if (!NT_SUCCESS(status)) {
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
// "Failed to register WMI power GUID %!STATUS!", status);
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return status;
|
|
|
|
|
ROSWDFNOTIMPLEMENTED;
|
|
|
|
|
return STATUS_SUCCESS;
|
2020-09-24 20:51:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PowerPolicySetS0IdleSettings(
|
|
|
|
|
__in PWDF_DEVICE_POWER_POLICY_IDLE_SETTINGS Settings
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Updates the S0 Idle settings for the device and then posts an update event
|
|
|
|
|
to the power policy state machine. The first this function is called, the
|
|
|
|
|
ability to allow the user to control this setting is set.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Settings - The settings to apply.
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
DEVICE_POWER_STATE dxState;
|
|
|
|
|
ULONG idleTimeout;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
BOOLEAN enabled, s0Capable, overridable, firstTime;
|
|
|
|
|
WDF_TRI_STATE powerUpOnSystemWake;
|
|
|
|
|
const LONGLONG negliblySmallIdleTimeout = -1; // 100 nanoseconds
|
|
|
|
|
|
|
|
|
|
s0Capable = FALSE;
|
|
|
|
|
dxState = PowerDeviceD3;
|
|
|
|
|
overridable = FALSE;
|
|
|
|
|
firstTime = TRUE;
|
|
|
|
|
|
|
|
|
|
if (Settings->Enabled == WdfTrue) {
|
|
|
|
|
enabled = TRUE;
|
|
|
|
|
|
|
|
|
|
} else if (Settings->Enabled == WdfUseDefault) {
|
|
|
|
|
enabled = TRUE;
|
|
|
|
|
|
|
|
|
|
if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
DECLARE_CONST_UNICODE_STRING(valueName, WDF_S0_IDLE_DEFAULT_VALUE_NAME);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Read registry. If registry value is not found, the value of "enabled"
|
|
|
|
|
// remains unchanged
|
|
|
|
|
//
|
|
|
|
|
ReadRegistryS0Idle(&valueName, &enabled);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
|
"If registry value WdfDefaultIdleInWorkingState was present, "
|
|
|
|
|
"it was not read because DDI WdfDeviceAssignS0IdleSettings "
|
|
|
|
|
"was not called at PASSIVE_LEVEL");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
enabled = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Set) {
|
|
|
|
|
firstTime = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_CapsQueried == FALSE && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
status = QueryForCapabilities();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Do not set m_CapsQueried to TRUE yet because we will do that once we
|
|
|
|
|
// know the entire stack has been built we will do the query again.
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (Settings->IdleCaps) {
|
|
|
|
|
case IdleUsbSelectiveSuspend:
|
|
|
|
|
case IdleCanWakeFromS0:
|
|
|
|
|
s0Capable = TRUE;
|
|
|
|
|
|
|
|
|
|
if (Settings->DxState == PowerDeviceMaximum) {
|
|
|
|
|
dxState = PowerPolicyGetDeviceDeepestDeviceWakeState(PowerSystemWorking);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Some bus drivers
|
|
|
|
|
|
|
|
|
|
// incorrectly report DeviceWake=D0 to
|
|
|
|
|
// indicate that it does not support wake instead of specifying
|
|
|
|
|
// PowerDeviceUnspecified and KMDF ends up requesting
|
|
|
|
|
// a D0 irp when going to Dx. The check prevents this bug.
|
|
|
|
|
//
|
|
|
|
|
if (dxState < PowerDeviceD1 ||
|
|
|
|
|
dxState > PowerDeviceD3 ||
|
|
|
|
|
(dxState > PowerDeviceD2 && Settings->IdleCaps == IdleUsbSelectiveSuspend)
|
|
|
|
|
) {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"DeviceWake power state reported in device capabilities "
|
|
|
|
|
"%!DEVICE_POWER_STATE! indicates that device can not signal"
|
|
|
|
|
" a wake event, %!STATUS!",
|
|
|
|
|
dxState, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DEVICE_POWER_STATE dxDeepest;
|
|
|
|
|
|
|
|
|
|
dxState = Settings->DxState;
|
|
|
|
|
dxDeepest = PowerPolicyGetDeviceDeepestDeviceWakeState(PowerSystemWorking);
|
|
|
|
|
|
|
|
|
|
if (dxState > dxDeepest) {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"DxState specified by driver %!DEVICE_POWER_STATE! cannot "
|
|
|
|
|
"be lighter than lightest available device wake state"
|
|
|
|
|
" %!DEVICE_POWER_STATE!, %!STATUS!", dxState,
|
|
|
|
|
dxDeepest, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Can only perform wait wake from D2 on a USB device
|
|
|
|
|
//
|
|
|
|
|
if (dxState > PowerDeviceD2 &&
|
|
|
|
|
Settings->IdleCaps == IdleUsbSelectiveSuspend) {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"DxState specified by driver %!DEVICE_POWER_STATE! cannot "
|
|
|
|
|
"be lighter than PowerDeviceD2 for USB selective suspend "
|
|
|
|
|
"%!STATUS!",
|
|
|
|
|
dxState, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Settings->IdleCaps == IdleUsbSelectiveSuspend) {
|
|
|
|
|
status = m_PowerPolicyMachine.InitUsbSS();
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Failed to initialize USB selective suspend %!STATUS!",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IdleCannotWakeFromS0:
|
|
|
|
|
s0Capable = FALSE;
|
|
|
|
|
|
|
|
|
|
if (Settings->DxState == PowerDeviceMaximum) {
|
|
|
|
|
dxState = PowerDeviceD3;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dxState = Settings->DxState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(FALSE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Settings->IdleTimeout == IdleTimeoutDefaultValue) {
|
|
|
|
|
idleTimeout = FxPowerPolicyDefaultTimeout;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
idleTimeout = Settings->IdleTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Settings->UserControlOfIdleSettings == IdleAllowUserControl) {
|
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// status = UpdateWmiInstanceForS0Idle(AddInstance);
|
|
|
|
|
// if (!NT_SUCCESS(status)) {
|
|
|
|
|
// return status;
|
|
|
|
|
// } __REACTOS__
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
|
|
|
|
if (Settings->Enabled == WdfUseDefault) {
|
|
|
|
|
//
|
|
|
|
|
// Read the registry entry for idle enabled if it's the first time.
|
|
|
|
|
//
|
|
|
|
|
if (firstTime && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
DECLARE_CONST_UNICODE_STRING(valueName, WDF_S0_IDLE_ENABLED_VALUE_NAME);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Read registry. If registry value is not found, the value of
|
|
|
|
|
// "enabled" remains unchanged
|
|
|
|
|
//
|
|
|
|
|
ReadRegistryS0Idle(&valueName, &enabled);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// Use the saved value for idle enabled.
|
|
|
|
|
//
|
|
|
|
|
enabled = m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overridable = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (Settings->UserControlOfIdleSettings == IdleDoNotAllowUserControl) {
|
|
|
|
|
//
|
|
|
|
|
// No user control
|
|
|
|
|
//
|
|
|
|
|
overridable = FALSE;
|
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// (void) UpdateWmiInstanceForS0Idle(RemoveInstance); __REACTOS__
|
2020-09-24 20:51:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// !!!! DO NOT INTRODUCE FAILURES BEYOND THIS POINT !!!!
|
|
|
|
|
//
|
|
|
|
|
// We should not introduce any failures that are not due to driver errors
|
|
|
|
|
// beyond this point. This is because we are going to commit the driver's
|
|
|
|
|
// S0-idle settings now and any failure in the midst of that could leave us
|
|
|
|
|
// in a bad state. Therefore, all failable code where the failure is beyond
|
|
|
|
|
// the driver's control should be placed above this point.
|
|
|
|
|
//
|
|
|
|
|
// For example, a driver may want wake-from-S0 support, but the device may
|
|
|
|
|
// not support it. We already checked for that failure above, before we
|
|
|
|
|
// started committing any of the driver's S0-idle settings.
|
|
|
|
|
//
|
|
|
|
|
// Any failures below this point should only be due to driver errors, i.e.
|
|
|
|
|
// the driver incorrectly calling the AssignS0IdleSettings DDI.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if (firstTime) {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Set = TRUE;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable = overridable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// IdleTimeoutType is available only on > 1.9
|
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
|
#ifndef __REACTOS__
|
2020-09-24 20:51:15 +00:00
|
|
|
|
if (Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_9)) {
|
|
|
|
|
if (firstTime) {
|
|
|
|
|
if ((SystemManagedIdleTimeout == Settings->IdleTimeoutType) ||
|
|
|
|
|
(SystemManagedIdleTimeoutWithHint ==
|
|
|
|
|
Settings->IdleTimeoutType)) {
|
|
|
|
|
//
|
|
|
|
|
// This is the first time S0-idle policy is being specified and
|
|
|
|
|
// the caller has asked for the idle timeout to be determined
|
|
|
|
|
// by the power manager.
|
|
|
|
|
//
|
|
|
|
|
status = m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
|
|
|
m_TimeoutMgmt.UseSystemManagedIdleTimeout(
|
|
|
|
|
GetDriverGlobals()
|
|
|
|
|
);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//
|
|
|
|
|
// This is not the first time S0-idle policy is being specified.
|
|
|
|
|
// Verify that the caller is not trying to change their mind about
|
|
|
|
|
// whether the idle timeout is determined by the power manager.
|
|
|
|
|
//
|
|
|
|
|
BOOLEAN currentlyUsingSystemManagedIdleTimeout;
|
|
|
|
|
BOOLEAN callerWantsSystemManagedIdleTimeout;
|
|
|
|
|
|
|
|
|
|
currentlyUsingSystemManagedIdleTimeout =
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.m_TimeoutMgmt.
|
|
|
|
|
UsingSystemManagedIdleTimeout();
|
|
|
|
|
callerWantsSystemManagedIdleTimeout =
|
|
|
|
|
((SystemManagedIdleTimeout == Settings->IdleTimeoutType) ||
|
|
|
|
|
(SystemManagedIdleTimeoutWithHint == Settings->IdleTimeoutType));
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// UMDF currently does not implement
|
|
|
|
|
// IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable. So
|
|
|
|
|
// that method must be called only as part of the second check in
|
|
|
|
|
// the "if" statement below. Since UMDF currently does not support
|
|
|
|
|
// system managed idle timeout, the first check will always evaluate
|
|
|
|
|
// to 'FALSE', so the second check never gets executed for UMDF.
|
|
|
|
|
//
|
|
|
|
|
if ((callerWantsSystemManagedIdleTimeout !=
|
|
|
|
|
currentlyUsingSystemManagedIdleTimeout)
|
|
|
|
|
&&
|
|
|
|
|
(IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable())
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"A previous call to assign S0-idle policy specified that "
|
|
|
|
|
"the idle timeout %s be determined by the power manager. "
|
|
|
|
|
"This decision cannot be changed. %!STATUS!",
|
|
|
|
|
currentlyUsingSystemManagedIdleTimeout ?
|
|
|
|
|
"should" :
|
|
|
|
|
"should not",
|
|
|
|
|
status);
|
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-16 03:30:51 +00:00
|
|
|
|
#endif
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
|
|
|
|
if (Settings->IdleCaps == IdleCannotWakeFromS0) {
|
|
|
|
|
//
|
|
|
|
|
// PowerUpIdleDeviceOnSystemWake field added after v1.7.
|
|
|
|
|
// By default KMDF uses an optimization where the device is not powered
|
|
|
|
|
// up when resuming from Sx if it is idle. The field
|
|
|
|
|
// PowerUpIdleDeviceOnSystemWake is used to turn off this optimization and allow
|
|
|
|
|
// device to power up when resuming from Sx. Note that this optimization
|
|
|
|
|
// is applicable only for IdleCannotWakeFromS0. In other cases the
|
|
|
|
|
// device is always powered up in order to arm for wake.
|
|
|
|
|
//
|
|
|
|
|
powerUpOnSystemWake =
|
|
|
|
|
(Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_7)) ?
|
|
|
|
|
Settings->PowerUpIdleDeviceOnSystemWake :
|
|
|
|
|
WdfUseDefault;
|
|
|
|
|
|
|
|
|
|
switch(powerUpOnSystemWake) {
|
|
|
|
|
case WdfTrue:
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.PowerUpIdleDeviceOnSystemWake = TRUE;
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Driver turned off S0Idle optimization. Device will be "
|
|
|
|
|
"powered up on resume from Sx even when it is idle");
|
|
|
|
|
break;
|
|
|
|
|
case WdfFalse:
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.PowerUpIdleDeviceOnSystemWake = FALSE;
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Driver turned on S0Idle optimization. Device will remain "
|
|
|
|
|
"powered off if idle when resuming from Sx");
|
|
|
|
|
break;
|
|
|
|
|
case WdfUseDefault:
|
|
|
|
|
DO_NOTHING();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (FALSE ==
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.UsbSSCapabilityKnown)
|
|
|
|
|
{
|
|
|
|
|
if (Settings->IdleCaps == IdleUsbSelectiveSuspend) {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.UsbSSCapable = TRUE;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
m_IdleSettings.UsbSSCapabilityKnown = TRUE;
|
|
|
|
|
|
|
|
|
|
} else if (Settings->IdleCaps == IdleCanWakeFromS0) {
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
m_IdleSettings.UsbSSCapabilityKnown = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Wake FromS0Capable is set every time because we want to allow the driver
|
|
|
|
|
// to swap between idle wake capable and idle not wake capable. This should
|
|
|
|
|
// be allowed so that a scenario similar to the following can be implemented:
|
|
|
|
|
//
|
|
|
|
|
// a) when the device has an outstanding open, the device should arm itself
|
|
|
|
|
// for wake when idle
|
|
|
|
|
//
|
|
|
|
|
// b) when the device does not have an outstanding open, the device should
|
|
|
|
|
// be off and not armed.
|
|
|
|
|
//
|
|
|
|
|
// The only way to be off is to assign S0 wake settings, so the
|
|
|
|
|
// WakeFromS0Capable field must change on each DDI call. This is not a
|
|
|
|
|
// problem for the power policy state machine because it evaluates
|
|
|
|
|
// WakeFromS0Capable state before enabling of idle. If we are not
|
|
|
|
|
// WakeFromS0Capable and USB SS capable (ie a have a torn/unsynchronized
|
|
|
|
|
// state) in m_IdleSettings, we will recover from it when processing
|
|
|
|
|
// PwrPolS0IdlePolicyChanged in the state machine (b/c this event causes
|
|
|
|
|
// both fields to be reevaluated).
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.WakeFromS0Capable = s0Capable;
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.DxState = dxState;
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
m_IdleSettings.m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
|
|
|
//
|
|
|
|
|
// With system managed idle timeout, we don't want to apply an idle
|
|
|
|
|
// timeout of our own on top of that. Effectively, our idle timeout is
|
|
|
|
|
// 0.
|
|
|
|
|
// But we apply a negligibly small timeout value as this allows us to
|
|
|
|
|
// keep the same logic in the idle state machine, regardless of whether
|
|
|
|
|
// we're using system-managed idle timeout or driver-managed idle
|
|
|
|
|
// timeout.
|
|
|
|
|
//
|
|
|
|
|
if (firstTime) {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
m_PowerIdleMachine.m_PowerTimeout.QuadPart =
|
|
|
|
|
negliblySmallIdleTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SystemManagedIdleTimeoutWithHint == Settings->IdleTimeoutType) {
|
|
|
|
|
//
|
|
|
|
|
// We save the idle timeout hint, but we don't provide the hint to
|
|
|
|
|
// the power framework immediately. This is because currently we may
|
|
|
|
|
// or may not be registered with the power framework. Note that
|
|
|
|
|
// WdfDeviceAssignS0IdleSettings might get called even when we are
|
|
|
|
|
// not registered with the power framework.
|
|
|
|
|
//
|
|
|
|
|
// Therefore, we provide the hint to the power framework only when
|
|
|
|
|
// we get to the WdfDevStatePwrPolStartingDecideS0Wake state. This
|
|
|
|
|
// state is a good choice for providing the hint because:
|
|
|
|
|
// 1. We know we would be registered with the power framework when
|
|
|
|
|
// we are in this state.
|
|
|
|
|
// 2. Any change in S0-idle settings causes us to go through this
|
|
|
|
|
// state.
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_PoxInterface.m_NextIdleTimeoutHint =
|
|
|
|
|
idleTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.m_PowerTimeout.QuadPart
|
|
|
|
|
= WDF_REL_TIMEOUT_IN_MS(idleTimeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the driver is 1.11 or later, update the bus drivers with the client's
|
|
|
|
|
// choice on the topic of D3hot or D3cold.
|
|
|
|
|
//
|
|
|
|
|
if ((Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_9)) &&
|
|
|
|
|
(Settings->ExcludeD3Cold != WdfUseDefault)) {
|
|
|
|
|
MxDeviceObject deviceObject;
|
|
|
|
|
BOOLEAN enableD3Cold;
|
|
|
|
|
|
|
|
|
|
deviceObject.SetObject(m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
switch (Settings->ExcludeD3Cold) {
|
|
|
|
|
case WdfFalse:
|
|
|
|
|
enableD3Cold = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Invalid tri-state value for ExcludeD3Cold %d",
|
|
|
|
|
Settings->ExcludeD3Cold);
|
|
|
|
|
__fallthrough;
|
|
|
|
|
case WdfTrue:
|
|
|
|
|
enableD3Cold = FALSE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetD3ColdSupport(GetDriverGlobals(),
|
|
|
|
|
&deviceObject,
|
|
|
|
|
&m_D3ColdInterface,
|
|
|
|
|
enableD3Cold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PowerPolicySetS0IdleState(enabled);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PowerPolicySetSxWakeSettings(
|
|
|
|
|
__in PWDF_DEVICE_POWER_POLICY_WAKE_SETTINGS Settings,
|
|
|
|
|
__in BOOLEAN ArmForWakeIfChildrenAreArmedForWake,
|
|
|
|
|
__in BOOLEAN IndicateChildWakeOnParentWake
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Updates the Sx wake settings for the device. No event is posted to the
|
|
|
|
|
state machine because this setting is statically checked when the machine
|
|
|
|
|
is entering an Sx state (unlike S0 idle which can be checked at any time).
|
|
|
|
|
|
|
|
|
|
The first this function is called, the ability to allow the user to control
|
|
|
|
|
this setting is set.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Settings - the new settings to apply
|
|
|
|
|
|
|
|
|
|
ArmForWakeIfChildrenAreArmedForWake - Inidicates whether the device
|
|
|
|
|
should arm for wake when one or more children are armed for wake
|
|
|
|
|
|
|
|
|
|
IndicateChildWakeOnParentWake - Indicates whether the device should
|
|
|
|
|
propagate the wake status to its children
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
DEVICE_POWER_STATE dxState;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
BOOLEAN overridable, firstTime, enabled;
|
|
|
|
|
|
|
|
|
|
dxState = PowerDeviceD3;
|
|
|
|
|
overridable = FALSE;
|
|
|
|
|
firstTime = TRUE;
|
|
|
|
|
|
|
|
|
|
if (Settings->Enabled == WdfTrue) {
|
|
|
|
|
enabled = TRUE;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (Settings->Enabled == WdfUseDefault) {
|
|
|
|
|
enabled = TRUE;
|
|
|
|
|
|
|
|
|
|
if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
DECLARE_CONST_UNICODE_STRING(valueName, WDF_SX_WAKE_DEFAULT_VALUE_NAME);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Read registry. If registry value is not found, the value of "enabled"
|
|
|
|
|
// remains unchanged
|
|
|
|
|
//
|
|
|
|
|
ReadRegistrySxWake(&valueName, &enabled);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
|
"If registry value WdfDefaultWakeFromSleepState was present, "
|
|
|
|
|
"it was not read because DDI WdfDeviceAssignSxWakeSettings "
|
|
|
|
|
"was not called at PASSIVE_LEVEL");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
enabled = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Set) {
|
|
|
|
|
firstTime = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_CapsQueried == FALSE && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
status = QueryForCapabilities();
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Do not set m_CapsQueried to TRUE yet because we will do that once we
|
|
|
|
|
// know the entire stack has been built we will do the query again.
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Settings->DxState == PowerDeviceMaximum) {
|
|
|
|
|
dxState = PowerPolicyGetDeviceDeepestDeviceWakeState((SYSTEM_POWER_STATE)m_SystemWake);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Some bus drivers
|
|
|
|
|
|
|
|
|
|
// incorrectly report DeviceWake=D0 to
|
|
|
|
|
// indicate that it does not support wake instead of specifying
|
|
|
|
|
// PowerDeviceUnspecified and KMDF ends up requesting
|
|
|
|
|
// a D0 irp when going to Dx. The check prevents this bug.
|
|
|
|
|
//
|
|
|
|
|
if (dxState < PowerDeviceD1 || dxState > PowerDeviceD3) {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"DeviceWake power state reported in device capabilities "
|
|
|
|
|
"%!DEVICE_POWER_STATE! indicates that device can not signal a "
|
|
|
|
|
"wake event, %!STATUS!",
|
|
|
|
|
dxState, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DEVICE_POWER_STATE dxDeepest;
|
|
|
|
|
|
|
|
|
|
dxState = Settings->DxState;
|
|
|
|
|
dxDeepest = PowerPolicyGetDeviceDeepestDeviceWakeState((SYSTEM_POWER_STATE)m_SystemWake);
|
|
|
|
|
|
|
|
|
|
if (dxState > dxDeepest) {
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"DxState specified by driver %!DEVICE_POWER_STATE! cannot be"
|
|
|
|
|
" lighter than lightest available device wake state "
|
|
|
|
|
"%!DEVICE_POWER_STATE!, %!STATUS!", dxState,
|
|
|
|
|
dxDeepest, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Settings->UserControlOfWakeSettings == WakeAllowUserControl) {
|
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// status = UpdateWmiInstanceForSxWake(AddInstance); __REACTOS__
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// if (!NT_SUCCESS(status)) {
|
|
|
|
|
// return status;
|
|
|
|
|
// }
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
|
|
|
|
if (Settings->Enabled == WdfUseDefault) {
|
|
|
|
|
//
|
|
|
|
|
// Read the registry entry for wake enabled if it's the first time.
|
|
|
|
|
//
|
|
|
|
|
if (firstTime && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
DECLARE_CONST_UNICODE_STRING(valueName, WDF_SX_WAKE_ENABLED_VALUE_NAME);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Read registry. If registry value is not found, the value of
|
|
|
|
|
// "enabled" remains unchanged
|
|
|
|
|
//
|
|
|
|
|
ReadRegistrySxWake(&valueName, &enabled);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// Use the saved value for wake enabled.
|
|
|
|
|
//
|
|
|
|
|
enabled = m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overridable = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (Settings->UserControlOfWakeSettings == WakeDoNotAllowUserControl) {
|
|
|
|
|
//
|
|
|
|
|
// No user control, just set to enabled
|
|
|
|
|
//
|
|
|
|
|
overridable = FALSE;
|
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// (void) UpdateWmiInstanceForSxWake(RemoveInstance); __REACTOS__
|
2020-09-24 20:51:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (firstTime) {
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Set = TRUE;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable = overridable;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If ArmForWakeIfChildrenAreArmedForWake setting is set to FALSE,
|
|
|
|
|
// then we use the legacy framework behavior which did not depend
|
|
|
|
|
// on the child device being capable of arming for wake or not.
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.ArmForWakeIfChildrenAreArmedForWake =
|
|
|
|
|
ArmForWakeIfChildrenAreArmedForWake;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If IndicateChildWakeOnParentWake setting is set to FALSE, then
|
|
|
|
|
// we use the legacy framework behavior wherein the wake status
|
|
|
|
|
// is not propagated from the parent device to the child device.
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.IndicateChildWakeOnParentWake =
|
|
|
|
|
IndicateChildWakeOnParentWake;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.DxState = dxState;
|
|
|
|
|
|
|
|
|
|
PowerPolicySetSxWakeState(enabled);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_S0IdleQueryInstance(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG /* OutBufferSize */,
|
|
|
|
|
__out PVOID OutBuffer,
|
|
|
|
|
__out PULONG BufferUsed
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
*((BOOLEAN*) OutBuffer) =
|
|
|
|
|
(Device->m_PkgPnp)->m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
|
|
|
|
|
*BufferUsed = sizeof(BOOLEAN);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_S0IdleSetInstance(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG /* InBufferSize */,
|
|
|
|
|
__in PVOID InBuffer
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FxWmiIrpHandler makes sure the buffer is at least one byte big, so we
|
|
|
|
|
// don't check the buffer size.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
value = *(PBOOLEAN) InBuffer;
|
|
|
|
|
|
|
|
|
|
(Device->m_PkgPnp)->PowerPolicySetS0IdleState(value);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_S0IdleSetItem(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG DataItemId,
|
|
|
|
|
__in ULONG InBufferSize,
|
|
|
|
|
__in PVOID InBuffer
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN value;
|
|
|
|
|
|
|
|
|
|
if (DataItemId != 0) {
|
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InBufferSize < sizeof(BOOLEAN)) {
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = *(BOOLEAN*) InBuffer;
|
|
|
|
|
(Device->m_PkgPnp)->PowerPolicySetS0IdleState(value);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_SxWakeQueryInstance(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG /* OutBufferSize */,
|
|
|
|
|
__out PVOID OutBuffer,
|
|
|
|
|
__out PULONG BufferUsed
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
*((BOOLEAN*) OutBuffer) =
|
|
|
|
|
(Device->m_PkgPnp)->m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
|
|
|
|
|
*BufferUsed = sizeof(BOOLEAN);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_SxWakeSetInstance(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG /* InBufferSize */,
|
|
|
|
|
__in PVOID InBuffer
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN value;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FxWmiIrpHandler makes sure that the buffer is at least one byte big, so
|
|
|
|
|
// we don't check the buffer size
|
|
|
|
|
//
|
|
|
|
|
value = *(PBOOLEAN) InBuffer;
|
|
|
|
|
|
|
|
|
|
(Device->m_PkgPnp)->PowerPolicySetSxWakeState(value);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_SxWakeSetItem(
|
|
|
|
|
__in CfxDevice* Device,
|
|
|
|
|
__in FxWmiInstanceInternal* /* Instance */,
|
|
|
|
|
__in ULONG DataItemId,
|
|
|
|
|
__in ULONG InBufferSize,
|
|
|
|
|
__in PVOID InBuffer
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN value;
|
|
|
|
|
|
|
|
|
|
if (DataItemId != 0) {
|
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InBufferSize < sizeof(BOOLEAN)) {
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = *(BOOLEAN*) InBuffer;
|
|
|
|
|
(Device->m_PkgPnp)->PowerPolicySetSxWakeState(value);
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PowerPolicyHandleSystemQueryPower(
|
|
|
|
|
__in SYSTEM_POWER_STATE QueryState
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Framework Philosophy Discussion:
|
|
|
|
|
|
|
|
|
|
WDM sends IRP_MN_QUERY_POWER for system power states (where system power
|
|
|
|
|
states are S0-working, S1-light-sleep, S2-deeper-sleep, S3-deepest-sleep,
|
|
|
|
|
S4-hibernation, S5-soft-off.) The idea is that, if a driver can't support
|
|
|
|
|
a particular state, it fails the query. The problem is that this idea is
|
|
|
|
|
horribly broken.
|
|
|
|
|
|
|
|
|
|
The first problem is that WDM doesn't always send these IRPs. In some
|
|
|
|
|
situations, (very low battery, system getting critically hot) WDM will
|
|
|
|
|
attempt to preserve user data by sleeping or hibernating, rather than just
|
|
|
|
|
crashing. This is good, but it makes a driver writer's life very difficult,
|
|
|
|
|
since it means that you have to deal with being told to go to a particular
|
|
|
|
|
state even if you think that your device won't be able to deal well with
|
|
|
|
|
that state.
|
|
|
|
|
|
|
|
|
|
The second problem is that, by the time the system is going to sleep, the
|
|
|
|
|
user probably isn't still looking at the screen. This is especially true
|
|
|
|
|
for laptop computers, as the system is very likely sleeping because the
|
|
|
|
|
user closed the clamshell lid. So any attempt to ask the user how to
|
|
|
|
|
resolve a situation where a driver doesn't want to go to a low power state
|
|
|
|
|
is futile. Furthermore, even when the screen is still available, users
|
|
|
|
|
dislike it when they push the sleep button or the power button on their
|
|
|
|
|
machines and the machines don't do what they were told to do.
|
|
|
|
|
|
|
|
|
|
The third problem is related to the second. While there may be completely
|
|
|
|
|
legitimate reasons for the driver to want to delay or even to veto a
|
|
|
|
|
transition into a sleep state, (an example of a valid scenario would be one
|
|
|
|
|
in which the driver was involved in burning a CD, an operation which can't
|
|
|
|
|
be interrupted,) there isn't any good way for a driver to interact with a
|
|
|
|
|
user anyhow. (Which desktop is the right one to send messages to? What
|
|
|
|
|
should the UI for problem resolution look like? How does a driver put up
|
|
|
|
|
UI anyhow?)
|
|
|
|
|
|
|
|
|
|
All the driver really knows is that it will or won't be able to maintain
|
|
|
|
|
device state, and it will or won't be able to get enough power to arm
|
|
|
|
|
any wake events that it might want to deliver (like PME#.)
|
|
|
|
|
|
|
|
|
|
Consequently, the designers of the PnP/Power model in the Framework have
|
|
|
|
|
decided that all QueryPower-Sx IRPs will be completed successfully
|
|
|
|
|
except if the device cannot maintain enough power to trigger its wake
|
|
|
|
|
signal *AND* if the system supports lighter sleep states than the
|
|
|
|
|
one that is currently being queried. (If it does, then the kernel's power
|
|
|
|
|
manager will turn right around and query for those, next.)
|
|
|
|
|
|
|
|
|
|
This story usually brings up a few objections:
|
|
|
|
|
|
|
|
|
|
1) My device is important! When it's operating, I don't want
|
|
|
|
|
the machine to just fall asleep. I need to fail QueryPower-Sx to
|
|
|
|
|
prevent that.
|
|
|
|
|
|
|
|
|
|
This objection is an unfortunate consequence of the existing DDK. There
|
|
|
|
|
is a perfectly good API that allows a driver to say that the machine
|
|
|
|
|
shouldn't just fall asleep. (See PoSetSystemState.) If a user presses
|
|
|
|
|
a button telling the machine to go to sleep, then the driver has a
|
|
|
|
|
responsibility to do that.
|
|
|
|
|
|
|
|
|
|
2) There are certain operations that just can't be interrupted!
|
|
|
|
|
|
|
|
|
|
While that's true, those operations started somewhere, probably in user-
|
|
|
|
|
mode. Those same user-mode components would be much, much better suited
|
|
|
|
|
toward negotiating with the user or with other components to figure out
|
|
|
|
|
what to do when the uninterruptable must be interrupted. User-mode
|
|
|
|
|
components get notification that the system is going to sleep and they
|
|
|
|
|
can delay or veto the transition. Get over the idea that your driver
|
|
|
|
|
needs to be involved, too.
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
Determines if for the passed in System state, if we can wake the machine
|
|
|
|
|
from it. If the query state is the machine's minimum system state, then
|
|
|
|
|
we always succeed it because we want the machine to go to at least some
|
|
|
|
|
sleeping state. We always succeed hibernate and system off requests as well.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
QueryState - The proposed system state
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
NT_SUCCESS if the queried state should be allowed, !NT_SUCCESS otherwise
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
if (QueryState >= PowerSystemHibernate ||
|
|
|
|
|
PowerPolicyCanWakeFromSystemState(QueryState)) {
|
|
|
|
|
//
|
|
|
|
|
// If the query is for the machine's minimum S state or we going into
|
|
|
|
|
// hibernate or off, always succeed it.
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// On Windows Vista and above, its OK to return a failure status code
|
|
|
|
|
// if the system is going into an S state at which the device cannot
|
|
|
|
|
// wake the system.
|
|
|
|
|
//
|
|
|
|
|
ASSERT(FxLibraryGlobals.OsVersionInfo.dwMajorVersion >= 6);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The S state the machine is going into is one where we can't
|
|
|
|
|
// wake it up because our D state is too low for this S state.
|
|
|
|
|
// Since this isn't the minimum S state the machine is capable
|
|
|
|
|
// of, reject the current query.
|
|
|
|
|
//
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
|
"failing system query power because the device cannot wake the "
|
|
|
|
|
"machine from S%d",
|
|
|
|
|
QueryState - 1);
|
|
|
|
|
|
|
|
|
|
status = STATUS_POWER_STATE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::PowerPolicySetS0IdleState(
|
|
|
|
|
__in BOOLEAN State
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled = State ? TRUE : FALSE;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty = TRUE;
|
|
|
|
|
PowerPolicyProcessEvent(PwrPolS0IdlePolicyChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::PowerPolicySetSxWakeState(
|
|
|
|
|
__in BOOLEAN State
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Sets the wake from Sx state
|
|
|
|
|
|
|
|
|
|
No need to post an event to the power policy state machine because we
|
|
|
|
|
will not change any active due to a change in this setting. We only
|
|
|
|
|
evaluate this state when going into Sx and once in this state we will not
|
|
|
|
|
change our behavior until the next Sx, which will then evaluate this state.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
State - New state
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled = State ? TRUE : FALSE;
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty = TRUE;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Since we are not posting an event to the power policy state machine, try
|
|
|
|
|
// to write out the value now, otherwise it will be written when we
|
|
|
|
|
// transition
|
|
|
|
|
//
|
|
|
|
|
if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
LONGLONG timeout;
|
|
|
|
|
|
|
|
|
|
timeout = 0;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the lock is already acquired on this thread, this will fail, which
|
|
|
|
|
// is OK.
|
|
|
|
|
//
|
|
|
|
|
status = m_PowerPolicyMachine.m_StateMachineLock.AcquireLock(
|
|
|
|
|
GetDriverGlobals(),
|
|
|
|
|
&timeout
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (FxWaitLockInternal::IsLockAcquired(status)) {
|
|
|
|
|
SaveState(TRUE);
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_StateMachineLock.ReleaseLock(
|
|
|
|
|
GetDriverGlobals()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetDeviceFailed(
|
|
|
|
|
__in WDF_DEVICE_FAILED_ACTION FailedAction
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Marks the device as a victim of catastrophic failure, either in software
|
|
|
|
|
or in hardware.
|
|
|
|
|
|
|
|
|
|
If AttemptToRestart is TRUE, then we should try to get the stack re-built
|
|
|
|
|
after it has been torn down. This would typically be the case the failure
|
|
|
|
|
was in the software, and possibly not be the case if the failure was in
|
|
|
|
|
the hardware.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
FailedAction - action to take once the stack has been removed
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
MdDeviceObject pdo;
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
|
|
|
|
|
if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(2, 15) == FALSE &&
|
|
|
|
|
FailedAction == WdfDeviceFailedAttemptRestart) {
|
|
|
|
|
|
|
|
|
|
FailedAction = WdfDeviceFailedNoRestart;
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGDEVICE,
|
|
|
|
|
"WdfDeviceFailedAttemptRestart is only available for UMDF 2.15 "
|
|
|
|
|
"and later drivers. Reverting to WdfDeviceFailedNoRestart.");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
m_FailedAction = (BYTE) FailedAction;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This will cause the PnP manager to tear down this stack, even if
|
|
|
|
|
// the PDO can't be surprise-removed.
|
|
|
|
|
//
|
|
|
|
|
m_Failed = TRUE;
|
|
|
|
|
|
|
|
|
|
if (FailedAction == WdfDeviceFailedAttemptRestart) {
|
|
|
|
|
//
|
|
|
|
|
// Attempt to get the PDO surprise-removed.
|
|
|
|
|
//
|
|
|
|
|
status = AskParentToRemoveAndReenumerate();
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
|
|
|
|
|
//
|
|
|
|
|
// In between creating a PDO WDFDEVICE and it starting, if this DDI is called,
|
|
|
|
|
// we will not have a valid PDO. Make sure it is valid before we proceed.
|
|
|
|
|
//
|
|
|
|
|
pdo = m_Device->GetSafePhysicalDevice();
|
|
|
|
|
|
|
|
|
|
if (pdo != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Now tell the PnP manager to re-query us for our state.
|
|
|
|
|
//
|
|
|
|
|
MxDeviceObject physicalDeviceObject(pdo);
|
|
|
|
|
|
|
|
|
|
physicalDeviceObject.InvalidateDeviceState(
|
|
|
|
|
m_Device->GetDeviceObject()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
#else // USER_MODE
|
|
|
|
|
m_Device->GetMxDeviceObject()->InvalidateDeviceState(
|
|
|
|
|
m_Device->GetDeviceObject());
|
|
|
|
|
UNREFERENCED_PARAMETER(pdo);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::_PnpDeviceUsageNotification(
|
|
|
|
|
__inout FxPkgPnp* This,
|
|
|
|
|
__inout FxIrp *Irp
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
return This->PnpDeviceUsageNotification(Irp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PnpDeviceUsageNotification(
|
|
|
|
|
__inout FxIrp* Irp
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
FxRelatedDeviceList* pList;
|
|
|
|
|
FxRelatedDevice *pDependent;
|
|
|
|
|
FxAutoIrp relatedIrp(NULL), parentIrp(NULL);
|
|
|
|
|
MxDeviceObject topOfParentStack;
|
|
|
|
|
DEVICE_USAGE_NOTIFICATION_TYPE type;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
MxDeviceObject pAttached;
|
|
|
|
|
MdIrp pNewIrp;
|
|
|
|
|
CCHAR maxStack;
|
|
|
|
|
BOOLEAN inPath, supported;
|
|
|
|
|
ULONG oldFlags;
|
|
|
|
|
MxAutoWorkItem workItem;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Entering DeviceUsageNotification handler");
|
|
|
|
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
type = Irp->GetParameterUsageNotificationType();
|
|
|
|
|
inPath = Irp->GetParameterUsageNotificationInPath();
|
|
|
|
|
supported = FALSE;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"type %x, in path %x, can support paging %x, dump file %x, "
|
|
|
|
|
"hiber file %x, boot file %x",
|
|
|
|
|
type, inPath,
|
|
|
|
|
IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFilePaging)),
|
|
|
|
|
IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileDump)),
|
|
|
|
|
IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileHibernation)),
|
|
|
|
|
IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileBoot)));
|
|
|
|
|
|
|
|
|
|
|
2020-10-16 03:30:51 +00:00
|
|
|
|
if (type >= static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFilePaging)
|
|
|
|
|
&& type < static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileMax)) {
|
2020-09-24 20:51:15 +00:00
|
|
|
|
if (inPath) {
|
|
|
|
|
if (m_Device->IsFilter()) {
|
|
|
|
|
//
|
|
|
|
|
// Filters always support usage notifications
|
|
|
|
|
//
|
|
|
|
|
supported = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
supported = IsUsageSupported(type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// We always handle notifications where we are out of the path
|
|
|
|
|
//
|
|
|
|
|
supported = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (supported == FALSE) {
|
|
|
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Usage type %x not supported, %!STATUS!", type, status);
|
|
|
|
|
|
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Usage notification IRP gets forwarded to parent stack or to
|
|
|
|
|
// dependent stack. Since in such cases (with deep device tree) the stack
|
|
|
|
|
// may run out quickly, ensure there is enough stack, otherwise use a
|
|
|
|
|
// workitem.
|
|
|
|
|
//
|
|
|
|
|
if (Mx::MxHasEnoughRemainingThreadStack() == FALSE &&
|
|
|
|
|
(m_Device->IsPdo() ||
|
|
|
|
|
m_UsageDependentDeviceList != NULL)) {
|
|
|
|
|
|
|
|
|
|
status = workItem.Allocate(m_Device->GetDeviceObject());
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p !devobj %p could not allocate workitem "
|
|
|
|
|
"to send usage notification type %d, inpath %d, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject(),
|
|
|
|
|
type, inPath, status);
|
|
|
|
|
|
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Usage notification is supported. Set the flags on the device object
|
|
|
|
|
// before processing this any further and save the current flags on the
|
|
|
|
|
// device object.
|
|
|
|
|
//
|
|
|
|
|
oldFlags = SetUsageNotificationFlags(type, inPath);
|
|
|
|
|
|
|
|
|
|
if (m_Device->IsPdo()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
topOfParentStack.SetObject(
|
|
|
|
|
m_Device->m_ParentDevice->GetAttachedDeviceReference());
|
|
|
|
|
|
|
|
|
|
pNewIrp = FxIrp::AllocateIrp(topOfParentStack.GetStackSize());
|
|
|
|
|
if (pNewIrp != NULL) {
|
|
|
|
|
parentIrp.SetIrp(pNewIrp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// parentIrp now owns the irp
|
|
|
|
|
//
|
|
|
|
|
pNewIrp = NULL;
|
|
|
|
|
|
|
|
|
|
status = SendDeviceUsageNotification(&topOfParentStack,
|
|
|
|
|
&parentIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
FALSE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p could not allocate PIRP for parent !devobj %p to "
|
|
|
|
|
"send usage notification type %d, inpath %d, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), topOfParentStack.GetObject(),
|
|
|
|
|
type, inPath, status);
|
|
|
|
|
}
|
|
|
|
|
topOfParentStack.DereferenceObject();
|
|
|
|
|
topOfParentStack.SetObject(NULL);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Exit %!STATUS!", status);
|
|
|
|
|
|
|
|
|
|
RevertUsageNotificationFlags(type, inPath, oldFlags);
|
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxStack = 0;
|
|
|
|
|
pDependent = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the driver supports the given special file, lets notify dependent
|
|
|
|
|
// stacks.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// LockForEnum will lock out new changes to the list until we unlock the list.
|
|
|
|
|
// We remain at passive level once we are locked.
|
|
|
|
|
//
|
|
|
|
|
if (m_UsageDependentDeviceList != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// We capture the m_UsageDependentDeviceList pointer value so that we
|
|
|
|
|
// always use the same pointer value and that we have matched actions
|
|
|
|
|
// (lock for enum / unlock from enum). What we are trying to avoid is
|
|
|
|
|
// this:
|
|
|
|
|
// 1) we do not lock for enum because m_UsageDependentDeviceList == NULL
|
|
|
|
|
// 2) in the middle of this function, m_UsageDependentDeviceList is
|
|
|
|
|
// assigned a pointer value
|
|
|
|
|
// 3) we try to unlock from enum later (or iterate, thinking the enum
|
|
|
|
|
// lock is held) by checking m_UsageDependentDeviceList for NULL, and
|
|
|
|
|
// now that is != NULL, use it.
|
|
|
|
|
//
|
|
|
|
|
// By capturing the pointer now, we either have a list or not and we don't
|
|
|
|
|
// hit situations 2 or 3. So, the rule is every subseqeunt time we need
|
|
|
|
|
// to check if there is valid m_UsageDependentDeviceList pointer, we
|
|
|
|
|
// use pList, but when we use the list, we can use m_UsageDependentDeviceList
|
|
|
|
|
// directly.
|
|
|
|
|
//
|
|
|
|
|
pList = m_UsageDependentDeviceList;
|
|
|
|
|
|
|
|
|
|
m_UsageDependentDeviceList->LockForEnum(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
|
|
|
|
|
|
|
|
|
|
MxDeviceObject deviceObject(pDependent->GetDevice());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pAttached.SetObject(deviceObject.GetAttachedDeviceReference());
|
|
|
|
|
|
|
|
|
|
if (pAttached.GetStackSize() > maxStack) {
|
|
|
|
|
maxStack = pAttached.GetStackSize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pAttached.DereferenceObject();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pList = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxStack > 0) {
|
|
|
|
|
//
|
|
|
|
|
// If we have a maxStack size, we have a list as well
|
|
|
|
|
//
|
|
|
|
|
ASSERT(m_UsageDependentDeviceList != NULL);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Allocate one irp for all the stacks so that we don't have an
|
|
|
|
|
// allocation later. This way, once we have the irp, we can send the
|
|
|
|
|
// usage notification to all stacks reliably, as well as the reverting
|
|
|
|
|
// of the notification if we encounter failure.
|
|
|
|
|
//
|
|
|
|
|
pNewIrp = FxIrp::AllocateIrp(maxStack);
|
|
|
|
|
if (pNewIrp == NULL) {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFDEVICE %p could not allocate IRP to send usage notifications"
|
|
|
|
|
" to related stacks, type %d, inpath %d, status %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), type, inPath, status);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
MxDeviceObject dependentDevice;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// relatedIrp will free the irp when it goes out of scope
|
|
|
|
|
//
|
|
|
|
|
relatedIrp.SetIrp(pNewIrp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Walk our collection of dependent device stacks, and notify
|
|
|
|
|
// each stack of the device notification. If any fail, notify
|
|
|
|
|
// the stacks who already were told of the reverted behavior.
|
|
|
|
|
//
|
|
|
|
|
pDependent = NULL;
|
|
|
|
|
while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
|
|
|
|
|
dependentDevice.SetObject(pDependent->GetDevice());
|
|
|
|
|
status = SendDeviceUsageNotification(&dependentDevice,
|
|
|
|
|
&relatedIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
FxRelatedDevice* pDependent2;
|
|
|
|
|
|
|
|
|
|
pDependent2 = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// A device failed the device usage notification request.
|
|
|
|
|
// Notify the stacks that didn't fail, so they can unwind
|
|
|
|
|
// the operation.
|
|
|
|
|
//
|
|
|
|
|
while ((pDependent2 = m_UsageDependentDeviceList->GetNextEntry(pDependent2)) != NULL &&
|
|
|
|
|
pDependent2 != pDependent) {
|
|
|
|
|
dependentDevice.SetObject(pDependent2->GetDevice());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We're already in a failure path. We can't do anything
|
|
|
|
|
// about yet another failure. So we ignore the return
|
|
|
|
|
// value.
|
|
|
|
|
//
|
|
|
|
|
(void) SendDeviceUsageNotification(&dependentDevice,
|
|
|
|
|
&relatedIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now break out of our outter loop.
|
|
|
|
|
//
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If we are successful to this point, then send the IRP down the
|
|
|
|
|
// stack.
|
|
|
|
|
//
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
BOOLEAN referenceSucceeded, sendDown;
|
|
|
|
|
|
|
|
|
|
referenceSucceeded = FALSE;
|
|
|
|
|
sendDown = TRUE;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Make sure the stack is in D0 before sending down the request. This
|
|
|
|
|
// will at least guarantee that all devices below this one are in D0
|
|
|
|
|
// when the make the transition from power pageable to non or vice versa.
|
|
|
|
|
//
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
status = PowerReference(TRUE);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
referenceSucceeded = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
sendDown = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sendDown) {
|
|
|
|
|
//
|
|
|
|
|
// If we supported the usage, set the status to success, otherwise we
|
|
|
|
|
// keep the status in the irp as it arrived to this device
|
|
|
|
|
//
|
|
|
|
|
if (supported) {
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
}
|
|
|
|
|
status = SendIrpSynchronously(Irp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Transitioning from a thread which was power pagable to non power
|
|
|
|
|
// pagable. We now need a power thread for the stack, ask for it.
|
|
|
|
|
// Note that there is no need for power thread in case of "boot"
|
|
|
|
|
// notification since boot notification doesn't require clearing device's
|
|
|
|
|
// DO_POWER_PAGABLE flag (power thread is required when handling power
|
|
|
|
|
// irp at dispatch level which can happen if the DO_POWER_PAGABLE flag
|
|
|
|
|
// is cleared).
|
|
|
|
|
//
|
|
|
|
|
// NOTE: Once we have a power thread, we never go back to using work
|
|
|
|
|
// items even though the stack may revert to power pagable.
|
|
|
|
|
// This is an acceptable tradeoff between resource usage and
|
|
|
|
|
// WDF complexity.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
if (NT_SUCCESS(status) &&
|
|
|
|
|
inPath &&
|
|
|
|
|
(HasPowerThread() == FALSE) &&
|
2020-10-16 03:30:51 +00:00
|
|
|
|
type != static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileBoot)
|
2020-09-24 20:51:15 +00:00
|
|
|
|
) {
|
|
|
|
|
status = QueryForPowerThread();
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// Keep status the same through out so we can set it back in
|
|
|
|
|
// the irp when we are done.
|
|
|
|
|
//
|
|
|
|
|
if (m_Device->IsPdo()) {
|
|
|
|
|
//
|
|
|
|
|
// need to revert our parent's stack
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
topOfParentStack.SetObject(
|
|
|
|
|
m_Device->m_ParentDevice->GetAttachedDeviceReference());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Ignore the status because we can't do anything on failure
|
|
|
|
|
//
|
|
|
|
|
(void) SendDeviceUsageNotification(&topOfParentStack,
|
|
|
|
|
&parentIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
TRUE);
|
|
|
|
|
|
|
|
|
|
topOfParentStack.DereferenceObject();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// Notify the stack below us
|
|
|
|
|
//
|
|
|
|
|
Irp->CopyCurrentIrpStackLocationToNext();
|
|
|
|
|
Irp->SetParameterUsageNotificationInPath(FALSE);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Required for pnp irps
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_NOT_SUPPORTED);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Ignore the status because we can't do anything on failure
|
|
|
|
|
//
|
|
|
|
|
(void) Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now check whether the lower devices succeeded or failed. If they
|
|
|
|
|
// failed, back out our changes and propogate the failure.
|
|
|
|
|
//
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// Revert the flags set on the device object.
|
|
|
|
|
//
|
|
|
|
|
RevertUsageNotificationFlags(type, inPath, oldFlags);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Notify dependent stacks of the failure.
|
|
|
|
|
//
|
|
|
|
|
pDependent = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// See pList initiatilazation as to why we compare pList for != NULL
|
|
|
|
|
// and not m_UsageDependentDeviceList.
|
|
|
|
|
//
|
|
|
|
|
if (pList != NULL) {
|
|
|
|
|
MxDeviceObject dependentDevice;
|
|
|
|
|
|
|
|
|
|
while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
|
|
|
|
|
dependentDevice.SetObject(pDependent->GetDevice());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We're already in a failure path. We can't do anything
|
|
|
|
|
// about yet another failure. So we ignore the return value.
|
|
|
|
|
//
|
|
|
|
|
(void) SendDeviceUsageNotification(&dependentDevice,
|
|
|
|
|
&relatedIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
TRUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// By this point, we have propagated the notification to dependent devices
|
|
|
|
|
// and lower stack, and if anyone failed during that time, we also
|
|
|
|
|
// propagated failure to dependent stacks and lower stack.
|
|
|
|
|
// If status is success at this point, invoke the driver's callback.
|
|
|
|
|
//
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// Invoke callback. Note that only one of the callbacks
|
|
|
|
|
// DeviceUsageNotification or DeviceUsgeNotificationEx will get
|
|
|
|
|
// invoked since only one of the callbacks at a time is supported.
|
|
|
|
|
// We ensured that during registration of the callback.
|
|
|
|
|
// Note that Ex callback will return success if driver did not
|
|
|
|
|
// supply any callback.
|
|
|
|
|
//
|
|
|
|
|
m_DeviceUsageNotification.Invoke(m_Device->GetHandle(),
|
|
|
|
|
_UsageToSpecialType(type),
|
|
|
|
|
inPath);
|
|
|
|
|
|
|
|
|
|
status = m_DeviceUsageNotificationEx.Invoke(
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
_UsageToSpecialType(type),
|
|
|
|
|
inPath
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// Driver's callback returned failure. We need to propagate
|
|
|
|
|
// failure to lower stack and dependent stacks.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Keep status the same through out so we can set it back in
|
|
|
|
|
// the irp when we are done.
|
|
|
|
|
//
|
|
|
|
|
if (m_Device->IsPdo()) {
|
|
|
|
|
//
|
|
|
|
|
// need to revert our parent's stack
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
topOfParentStack.SetObject(
|
|
|
|
|
m_Device->m_ParentDevice->GetAttachedDeviceReference());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Ignore the status because we can't do anything on failure
|
|
|
|
|
//
|
|
|
|
|
(void) SendDeviceUsageNotification(&topOfParentStack,
|
|
|
|
|
&parentIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
TRUE);
|
|
|
|
|
|
|
|
|
|
topOfParentStack.DereferenceObject();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// Notify the stack below us
|
|
|
|
|
//
|
|
|
|
|
Irp->CopyCurrentIrpStackLocationToNext();
|
|
|
|
|
Irp->SetParameterUsageNotificationInPath(FALSE);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Required for pnp irps
|
|
|
|
|
//
|
|
|
|
|
Irp->SetStatus(STATUS_NOT_SUPPORTED);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Ignore the status because we can't do anything on failure
|
|
|
|
|
//
|
|
|
|
|
(void) Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(status);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Revert the flags set on the device object.
|
|
|
|
|
//
|
|
|
|
|
RevertUsageNotificationFlags(type, inPath, oldFlags);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Notify dependent stacks of the failure.
|
|
|
|
|
//
|
|
|
|
|
pDependent = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// See pList initiatilazation as to why we compare pList for != NULL
|
|
|
|
|
// and not m_UsageDependentDeviceList.
|
|
|
|
|
//
|
|
|
|
|
if (pList != NULL) {
|
|
|
|
|
MxDeviceObject dependentDevice;
|
|
|
|
|
|
|
|
|
|
while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
|
|
|
|
|
dependentDevice.SetObject(pDependent->GetDevice());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We're already in a failure path. We can't do anything
|
|
|
|
|
// about yet another failure. So we ignore the return value.
|
|
|
|
|
//
|
|
|
|
|
(void) SendDeviceUsageNotification(&dependentDevice,
|
|
|
|
|
&relatedIrp,
|
|
|
|
|
&workItem,
|
|
|
|
|
Irp,
|
|
|
|
|
TRUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
|
|
|
|
|
CommitUsageNotification(type, oldFlags);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If we are in the dump file path, we cannot idle out because we
|
|
|
|
|
// can experience a crash dump at any time.
|
|
|
|
|
//
|
|
|
|
|
if (IsPowerPolicyOwner() && type == DeviceUsageTypeDumpFile) {
|
|
|
|
|
//
|
|
|
|
|
// Add a reference everytime we are notified of being in the
|
|
|
|
|
// path, no need to match the first inPath notification and the
|
|
|
|
|
// last !inPath notification.
|
|
|
|
|
//
|
|
|
|
|
if (inPath) {
|
|
|
|
|
NTSTATUS refStatus;
|
|
|
|
|
|
|
|
|
|
ASSERT(GetUsageCount(type) > 0);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Since our previous synchronous power reference succeeded,
|
|
|
|
|
// an addtional reference while we are powered up should
|
|
|
|
|
// never fail.
|
|
|
|
|
//
|
|
|
|
|
refStatus = PowerReference(FALSE);
|
|
|
|
|
#if DBG
|
|
|
|
|
ASSERT(NT_SUCCESS(refStatus));
|
|
|
|
|
#else
|
|
|
|
|
UNREFERENCED_PARAMETER(refStatus);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ASSERT(GetUsageCount(type) >= 0);
|
|
|
|
|
PowerDereference();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We no longer need to be in D0 if we don't have to be.
|
|
|
|
|
//
|
|
|
|
|
if (referenceSucceeded) {
|
|
|
|
|
ASSERT(IsPowerPolicyOwner());
|
|
|
|
|
PowerDereference();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// See pList initiatilazation as to why we compare pList for != NULL
|
|
|
|
|
// and not m_UsageDependentDeviceList.
|
|
|
|
|
//
|
|
|
|
|
if (pList != NULL) {
|
|
|
|
|
m_UsageDependentDeviceList->UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Exit %!STATUS!", status);
|
|
|
|
|
|
|
|
|
|
return CompletePnpRequest(Irp, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
|
FxPkgPnp::SetUsageNotificationFlags(
|
|
|
|
|
__in DEVICE_USAGE_NOTIFICATION_TYPE Type,
|
|
|
|
|
__in BOOLEAN InPath
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine sets the usage notification flags on the device object (for
|
|
|
|
|
non-boot usages) and updates the special file usage count .
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Type - the special file type - paging, hibernate, dump or boot file.
|
|
|
|
|
|
|
|
|
|
InPath - indicates whether the system is creating or removing the special
|
|
|
|
|
file on the device.
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
Returns the old flags on the device object.
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
ULONG oldFlags, newFlags;
|
|
|
|
|
|
|
|
|
|
oldFlags = m_Device->GetDeviceObjectFlags();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Before: type %d, in path %d, special count %d, flags 0x%x, "
|
|
|
|
|
"device %p (WDFDEVICE %p), is pageable capable %d",
|
|
|
|
|
Type, InPath, GetUsageCount(Type), oldFlags,
|
|
|
|
|
m_Device->GetDeviceObject(), m_Device->GetHandle(),
|
|
|
|
|
m_Device->IsPowerPageableCapable());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Adjust our "special file count".
|
|
|
|
|
//
|
|
|
|
|
AdjustUsageCount(Type, InPath);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Boot notification doesn't require updating device flags.
|
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
|
if (Type == static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileBoot)) {
|
2020-09-24 20:51:15 +00:00
|
|
|
|
return oldFlags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_Device->IsFilter()) {
|
|
|
|
|
//
|
|
|
|
|
// Clear the previous flags and reset them to the attached
|
|
|
|
|
// device's flags
|
|
|
|
|
//
|
|
|
|
|
newFlags = oldFlags & ~(DO_POWER_PAGABLE | DO_POWER_INRUSH);
|
|
|
|
|
newFlags |= m_Device->GetAttachedDeviceObjectFlags() &
|
|
|
|
|
(DO_POWER_PAGABLE | DO_POWER_INRUSH);
|
|
|
|
|
m_Device->SetDeviceObjectFlags(newFlags);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (InPath) {
|
|
|
|
|
m_Device->SetDeviceObjectFlags(
|
|
|
|
|
m_Device->GetDeviceObjectFlags() & ~DO_POWER_PAGABLE
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (m_Device->IsPowerPageableCapable() && IsInSpecialUse() == FALSE) {
|
|
|
|
|
m_Device->SetDeviceObjectFlags(
|
|
|
|
|
m_Device->GetDeviceObjectFlags() | DO_POWER_PAGABLE
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return oldFlags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RevertUsageNotificationFlags(
|
|
|
|
|
__in DEVICE_USAGE_NOTIFICATION_TYPE Type,
|
|
|
|
|
__in BOOLEAN InPath,
|
|
|
|
|
__in ULONG OldFlags
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine reverts the usage notification flags to the old flags on
|
|
|
|
|
the device object and updates the special file usage count.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Type - the special file type - paging, hibernate or dump file.
|
|
|
|
|
|
|
|
|
|
InPath - indicates whether the system is creating or removing the special
|
|
|
|
|
file on the device.
|
|
|
|
|
|
|
|
|
|
OldFlags - the previous flags on the device object.
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Re-adjust our "special file count".
|
|
|
|
|
//
|
|
|
|
|
InPath = !InPath;
|
|
|
|
|
AdjustUsageCount(Type, InPath);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Restore the flags on the device object.
|
|
|
|
|
//
|
|
|
|
|
m_Device->SetDeviceObjectFlags(OldFlags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::CommitUsageNotification(
|
|
|
|
|
__in DEVICE_USAGE_NOTIFICATION_TYPE Type,
|
|
|
|
|
__in ULONG OldFlags
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine commits the usage notification flags on the device object
|
|
|
|
|
and invokes the usage notification callbacks. If the current flags on
|
|
|
|
|
the device object indicates that there was a transition from power-pagable
|
|
|
|
|
to non power-pagable, or vice-versa, then an event is posted to the power
|
|
|
|
|
state machine to notify it of the change. After this routine is called
|
|
|
|
|
the PNP manager will no longer be able to disable the device.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Type - the special file type - paging, hibernation or crash dump file.
|
|
|
|
|
|
|
|
|
|
InPath - indicates whether the system is creating or removing the special
|
|
|
|
|
file on the device.
|
|
|
|
|
|
|
|
|
|
OldFlags - the previous flags on the device object.
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
|
|
|
|
|
ULONG newFlags;
|
|
|
|
|
|
|
|
|
|
newFlags = m_Device->GetDeviceObjectFlags();
|
|
|
|
|
|
|
|
|
|
if ((OldFlags & DO_POWER_PAGABLE) == DO_POWER_PAGABLE &&
|
|
|
|
|
(newFlags & DO_POWER_PAGABLE) == 0) {
|
|
|
|
|
//
|
|
|
|
|
// We transitioned from a power pageable to a non power pageable
|
|
|
|
|
// device. Move the power state machine to the appropriate
|
|
|
|
|
// state.
|
|
|
|
|
//
|
|
|
|
|
PowerProcessEvent(PowerMarkNonpageable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((OldFlags & DO_POWER_PAGABLE) == 0 &&
|
|
|
|
|
(newFlags & DO_POWER_PAGABLE) == DO_POWER_PAGABLE) {
|
|
|
|
|
//
|
|
|
|
|
// We transitioned from a non power pageable to a power pageable
|
|
|
|
|
// device. Move the power state machine to the appropriate
|
|
|
|
|
// state.
|
|
|
|
|
//
|
|
|
|
|
PowerProcessEvent(PowerMarkPageable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Notify PNP that it should no longer be able
|
|
|
|
|
// to disable this device.
|
|
|
|
|
//
|
|
|
|
|
MxDeviceObject physicalDeviceObject(
|
|
|
|
|
m_Device->GetPhysicalDevice()
|
|
|
|
|
);
|
|
|
|
|
physicalDeviceObject.InvalidateDeviceState(
|
|
|
|
|
m_Device->GetDeviceObject()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"After: special count %d, flags 0x%x, device %p (WDFDEVICE %p)",
|
|
|
|
|
GetUsageCount(Type), newFlags,
|
|
|
|
|
m_Device->GetDeviceObject(), m_Device->GetHandle());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::AddUsageDevice(
|
|
|
|
|
__in MdDeviceObject DependentDevice
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
FxRelatedDevice* pRelated;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
if (m_UsageDependentDeviceList == NULL) {
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
if (m_UsageDependentDeviceList == NULL) {
|
|
|
|
|
m_UsageDependentDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
|
|
|
|
|
|
|
|
|
|
if (m_UsageDependentDeviceList != NULL) {
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Could not allocate usage device list for WDFDEVICE %p, "
|
|
|
|
|
"%!STATUS!", m_Device->GetHandle(), status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// another thread allocated the list already
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pRelated = new(GetDriverGlobals())
|
|
|
|
|
FxRelatedDevice(DependentDevice, GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
if (pRelated == NULL) {
|
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_UsageDependentDeviceList->Add(GetDriverGlobals(), pRelated);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
pRelated->DeleteFromFailedCreate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RemoveUsageDevice(
|
|
|
|
|
__in MdDeviceObject DependentDevice
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (m_UsageDependentDeviceList != NULL) {
|
|
|
|
|
m_UsageDependentDeviceList->Remove(GetDriverGlobals(), DependentDevice);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::AddRemovalDevice(
|
|
|
|
|
__in MdDeviceObject DependentDevice
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
FxRelatedDevice* pRelated;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
if (m_RemovalDeviceList == NULL) {
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
if (m_RemovalDeviceList == NULL) {
|
|
|
|
|
m_RemovalDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
|
|
|
|
|
|
|
|
|
|
if (m_RemovalDeviceList != NULL) {
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Could not allocate removal device list for WDFDEVICE %p, "
|
|
|
|
|
"%!STATUS!", m_Device->GetHandle(), status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// another thread allocated the list already
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pRelated = new(GetDriverGlobals())
|
|
|
|
|
FxRelatedDevice(DependentDevice, GetDriverGlobals());
|
|
|
|
|
if (pRelated == NULL) {
|
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = m_RemovalDeviceList->Add(GetDriverGlobals(), pRelated);
|
|
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// RemovalRelations are queried automatically by PnP when the device is
|
|
|
|
|
// going to be query removed. No need to tell pnp that the list changed
|
|
|
|
|
// until it needs to query for it.
|
|
|
|
|
//
|
|
|
|
|
DO_NOTHING();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pRelated->DeleteFromFailedCreate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RemoveRemovalDevice(
|
|
|
|
|
__in MdDeviceObject DependentDevice
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (m_RemovalDeviceList != NULL) {
|
|
|
|
|
m_RemovalDeviceList->Remove(GetDriverGlobals(), DependentDevice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// RemovalRelations are queried automatically by PnP when the device is
|
|
|
|
|
// going to be query removed. No need to tell pnp that the list changed
|
|
|
|
|
// until it needs to query for it.
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::ClearRemovalDevicesList(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
FxRelatedDevice* pEntry;
|
|
|
|
|
|
|
|
|
|
if (m_RemovalDeviceList == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_RemovalDeviceList->LockForEnum(GetDriverGlobals());
|
|
|
|
|
while ((pEntry = m_RemovalDeviceList->GetNextEntry(NULL)) != NULL) {
|
|
|
|
|
m_RemovalDeviceList->Remove(GetDriverGlobals(), pEntry->GetDevice());
|
|
|
|
|
}
|
|
|
|
|
m_RemovalDeviceList->UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// RemovalRelations are queried automatically by PnP when the device is
|
|
|
|
|
// going to be query removed. No need to tell pnp that the list changed
|
|
|
|
|
// until it needs to query for it.
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetInternalFailure(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Sets the failure field and then optionally invalidates the device state.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
InvalidateState - If TRUE, the state is invalidated
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
m_InternalFailure = TRUE;
|
|
|
|
|
|
|
|
|
|
MxDeviceObject physicalDeviceObject(
|
|
|
|
|
m_Device->GetPhysicalDevice()
|
|
|
|
|
);
|
|
|
|
|
physicalDeviceObject.InvalidateDeviceState(
|
|
|
|
|
m_Device->GetDeviceObject()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetPendingPnpIrp(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__in BOOLEAN MarkIrpPending
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (m_PendingPnPIrp != NULL ) {
|
|
|
|
|
FxIrp pendingIrp(m_PendingPnPIrp);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// A state changing pnp irp is already pended. If we don't bugcheck
|
|
|
|
|
// the pended pnp irp will be overwritten with new pnp irp and the old
|
|
|
|
|
// one may never get completed, which may have drastic implications (
|
|
|
|
|
// unresponsive system, power manager not sending Sx Irp etc.)
|
|
|
|
|
//
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"A new state changing pnp irp %!pnpmn! IRP %p arrived while another "
|
|
|
|
|
"pnp irp %!pnpmn! IRP %p is still pending WDFDEVICE %p\n",
|
|
|
|
|
Irp->GetMinorFunction(), Irp->GetIrp(),
|
|
|
|
|
pendingIrp.GetMinorFunction(),pendingIrp.GetIrp(),
|
|
|
|
|
m_Device->GetHandle());
|
|
|
|
|
|
|
|
|
|
FxVerifierBugCheck(GetDriverGlobals(), // globals
|
|
|
|
|
WDF_PNP_FATAL_ERROR, // specific type
|
|
|
|
|
(ULONG_PTR)m_Device->GetHandle(), //parm 2
|
|
|
|
|
(ULONG_PTR)Irp->GetIrp()); // parm 3
|
|
|
|
|
|
|
|
|
|
/* NOTREACHED */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (MarkIrpPending) {
|
|
|
|
|
Irp->MarkIrpPending();
|
|
|
|
|
}
|
|
|
|
|
m_PendingPnPIrp = Irp->GetIrp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::AllocateEnumInfo(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
if (m_EnumInfo == NULL) {
|
|
|
|
|
m_EnumInfo = new (GetDriverGlobals()) FxEnumerationInfo(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
|
|
|
status = m_EnumInfo->Initialize();
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
delete m_EnumInfo;
|
|
|
|
|
m_EnumInfo = NULL;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
|
|
|
|
|
TRACINGPNP,
|
|
|
|
|
"Could not initialize enum info for "
|
|
|
|
|
"WDFDEVICE %p, %!STATUS!",
|
|
|
|
|
m_Device->GetHandle(), status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"Could not allocate enum info for WDFDEVICE %p, "
|
|
|
|
|
"%!STATUS!", m_Device->GetHandle(), status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// another thread allocated the list already
|
|
|
|
|
//
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::AddChildList(
|
|
|
|
|
__in FxChildList* List
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Adding FxChildList %p, WDFCHILDLIST %p", List,
|
|
|
|
|
List->GetHandle());
|
|
|
|
|
|
|
|
|
|
m_EnumInfo->m_ChildListList.Add(GetDriverGlobals(),
|
|
|
|
|
&List->m_TransactionLink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RemoveChildList(
|
|
|
|
|
__in FxChildList* List
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Removing FxChildList %p, WDFCHILDLIST %p", List,
|
|
|
|
|
List->GetHandle());
|
|
|
|
|
|
|
|
|
|
m_EnumInfo->m_ChildListList.Remove(GetDriverGlobals(), &List->m_TransactionLink);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::ChildListNotifyRemove(
|
|
|
|
|
__inout PLONG PendingCount
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
FxTransactionedEntry* ple;
|
|
|
|
|
|
|
|
|
|
ple = NULL;
|
|
|
|
|
|
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
|
|
|
m_EnumInfo->m_ChildListList.LockForEnum(GetDriverGlobals());
|
|
|
|
|
while ((ple = m_EnumInfo->m_ChildListList.GetNextEntry(ple)) != NULL) {
|
|
|
|
|
FxChildList::_FromEntry(ple)->NotifyDeviceRemove(PendingCount);
|
|
|
|
|
}
|
|
|
|
|
m_EnumInfo->m_ChildListList.UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::AddQueryInterface(
|
|
|
|
|
__in FxQueryInterface* QI,
|
|
|
|
|
__in BOOLEAN Lock
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Add a query interface structure to the list of interfaces supported
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
QI - the interface to add
|
|
|
|
|
|
|
|
|
|
Lock - indication of the list lock should be acquired or not
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
SINGLE_LIST_ENTRY **ppPrev, *pCur;
|
|
|
|
|
|
|
|
|
|
if (Lock) {
|
|
|
|
|
m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSERT(QI->m_Entry.Next == NULL);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Iterate until we find the end of the list and then append the new
|
|
|
|
|
// structure. ppPrev is the pointer to the Next pointer value. When we
|
|
|
|
|
// get to the end, ppPrev will be equal to the Next field which points to NULL.
|
|
|
|
|
//
|
|
|
|
|
ppPrev = &m_QueryInterfaceHead.Next;
|
|
|
|
|
pCur = m_QueryInterfaceHead.Next;
|
|
|
|
|
|
|
|
|
|
while (pCur != NULL) {
|
|
|
|
|
ppPrev = &pCur->Next;
|
|
|
|
|
pCur = pCur->Next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*ppPrev = &QI->m_Entry;
|
|
|
|
|
|
|
|
|
|
if (Lock) {
|
|
|
|
|
m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__drv_maxIRQL(DISPATCH_LEVEL)
|
|
|
|
|
__drv_minIRQL(DISPATCH_LEVEL)
|
|
|
|
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
|
|
|
|
__drv_sameIRQL
|
|
|
|
|
VOID
|
|
|
|
|
FxWatchdog::_WatchdogDpc(
|
|
|
|
|
__in PKDPC Dpc,
|
|
|
|
|
__in_opt PVOID Context,
|
|
|
|
|
__in_opt PVOID SystemArgument1,
|
|
|
|
|
__in_opt PVOID SystemArgument2
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine's job is to crash the machine, attempting to get some data
|
|
|
|
|
into the crashdump file (or minidump) about why the machine stopped
|
|
|
|
|
responding during an attempt to put the machine to sleep.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
This - the instance of FxPkgPnp
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
this routine never returns
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
WDF_POWER_ROUTINE_TIMED_OUT_DATA data;
|
|
|
|
|
FxWatchdog* pThis;
|
|
|
|
|
CfxDevice* pDevice;
|
|
|
|
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
|
|
|
|
|
|
pThis = (FxWatchdog*) Context;
|
|
|
|
|
pDevice = pThis->m_PkgPnp->GetDevice();
|
|
|
|
|
|
|
|
|
|
DoTraceLevelMessage(pDevice->GetDriverGlobals(),
|
|
|
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"The driver failed to return from a callback routine "
|
|
|
|
|
"in a reasonable period of time. This prevented the "
|
|
|
|
|
"machine from going to sleep or from hibernating. The "
|
|
|
|
|
"machine crashed because that was the best way to get "
|
|
|
|
|
"data about the cause of the crash into a minidump file.");
|
|
|
|
|
|
|
|
|
|
data.PowerState = pDevice->GetDevicePowerState();
|
|
|
|
|
data.PowerPolicyState = pDevice->GetDevicePowerPolicyState();
|
|
|
|
|
data.DeviceObject = reinterpret_cast<PDEVICE_OBJECT>(pDevice->GetDeviceObject());
|
|
|
|
|
data.Device = pDevice->GetHandle();
|
|
|
|
|
data.TimedOutThread = reinterpret_cast<PKTHREAD>(pThis->m_CallingThread);
|
|
|
|
|
|
|
|
|
|
FxVerifierBugCheck(pDevice->GetDriverGlobals(),
|
|
|
|
|
WDF_POWER_ROUTINE_TIMED_OUT,
|
|
|
|
|
(ULONG_PTR) &data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::CreatePowerThread(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Creates a power thread for the device node. This thread is share among all
|
|
|
|
|
the devices in the stack through the POWER_THREAD_INTERFACE structure and
|
|
|
|
|
PnP query interface.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
NTSTATUS
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxSystemThread *pThread, *pOld;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
status = FxSystemThread::_CreateAndInit(
|
|
|
|
|
&pThread,
|
|
|
|
|
GetDriverGlobals(),
|
|
|
|
|
m_Device->GetHandle(),
|
|
|
|
|
m_Device->GetDeviceObject());
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Simple locking logic in case N requests are conncurrent. (The requests
|
|
|
|
|
// never should be concurrent, but in the case that there are 2 usage
|
|
|
|
|
// notifications coming in from two different sources, it could
|
|
|
|
|
// theoritically happen.)
|
|
|
|
|
//
|
|
|
|
|
pOld = (FxSystemThread*) InterlockedCompareExchangePointer(
|
|
|
|
|
(PVOID*) &m_PowerThread, pThread, NULL);
|
|
|
|
|
|
|
|
|
|
if (pOld != NULL) {
|
|
|
|
|
//
|
|
|
|
|
// Someone also set the thread pointer value at the same time, free
|
|
|
|
|
// our new one here.
|
|
|
|
|
//
|
|
|
|
|
pThread->ExitThread();
|
|
|
|
|
pThread->DeleteObject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_HasPowerThread = TRUE;
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::ReleasePowerThread(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
If this device is the owner of the power thread, it kills the thread.
|
|
|
|
|
Otherwise, if this device has acquired the thread from a lower device,
|
|
|
|
|
release the reference now.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
BOOLEAN hadThread;
|
|
|
|
|
|
|
|
|
|
hadThread = m_HasPowerThread;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Set to FALSE before cleaning up the reference or thread itself in case
|
|
|
|
|
// there is some other context trying to enqueue. The only way that could
|
|
|
|
|
// be happening is if the power policy owner is not WDF and sends power irps
|
|
|
|
|
// after query remove or surprise remove.
|
|
|
|
|
//
|
|
|
|
|
m_HasPowerThread = FALSE;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Check for ownership
|
|
|
|
|
//
|
|
|
|
|
if (m_PowerThread != NULL) {
|
|
|
|
|
|
|
|
|
|
FxCREvent event;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Event on stack is used, which is fine since this code is invoked
|
|
|
|
|
// only in KM. Verify this assumption.
|
|
|
|
|
//
|
|
|
|
|
// If this code is ever needed for UM, m_PowerThreadEvent should be
|
|
|
|
|
// pre-initialized (simlar to the way m_RemoveEventUm is used)
|
|
|
|
|
//
|
|
|
|
|
WDF_VERIFY_KM_ONLY_CODE();
|
|
|
|
|
|
|
|
|
|
ASSERT(m_PowerThreadEvent == NULL);
|
|
|
|
|
m_PowerThreadEvent = event.GetSelfPointer();
|
|
|
|
|
|
|
|
|
|
if (InterlockedDecrement(&m_PowerThreadInterfaceReferenceCount) > 0) {
|
|
|
|
|
//
|
|
|
|
|
// Wait for all references to go away before exitting the thread.
|
|
|
|
|
// A reference will be taken for every device in the stack above this
|
|
|
|
|
// one which queried for the interface.
|
|
|
|
|
//
|
|
|
|
|
event.EnterCRAndWaitAndLeave();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerThreadEvent = NULL;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Wait for the thread to exit and then delete it. Since we have
|
|
|
|
|
// turned off the power policy state machine, we can safely do this here.
|
|
|
|
|
// Any upper level clients will have also turned off their power policy
|
|
|
|
|
// state machines.
|
|
|
|
|
//
|
|
|
|
|
m_PowerThread->ExitThread();
|
|
|
|
|
m_PowerThread->DeleteObject();
|
|
|
|
|
|
|
|
|
|
m_PowerThread = NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (hadThread) {
|
|
|
|
|
//
|
|
|
|
|
// Release our reference
|
|
|
|
|
//
|
|
|
|
|
m_PowerThreadInterface.Interface.InterfaceDereference(
|
|
|
|
|
m_PowerThreadInterface.Interface.Context
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::_PowerThreadInterfaceReference(
|
|
|
|
|
__inout PVOID Context
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Increments the ref count on the thread interface.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Context - FxPkgPnp*
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG count;
|
|
|
|
|
|
|
|
|
|
count = InterlockedIncrement(
|
|
|
|
|
&((FxPkgPnp*) Context)->m_PowerThreadInterfaceReferenceCount
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
|
ASSERT(count >= 2);
|
|
|
|
|
#else
|
|
|
|
|
UNREFERENCED_PARAMETER(count);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::_PowerThreadInterfaceDereference(
|
|
|
|
|
__inout PVOID Context
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Interface deref for the thread interface. If this is the last reference
|
|
|
|
|
released, an event is set so that the thread which waiting for the last ref
|
|
|
|
|
to go away can unblock.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Context - FxPkgPnp*
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
FxPkgPnp* pThis;
|
|
|
|
|
|
|
|
|
|
pThis = (FxPkgPnp*) Context;
|
|
|
|
|
|
|
|
|
|
if (InterlockedDecrement(&pThis->m_PowerThreadInterfaceReferenceCount) == 0) {
|
|
|
|
|
pThis->m_PowerThreadEvent->Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PnpPowerReferenceSelf(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Take a power reference during a query pnp transition.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
//
|
|
|
|
|
// We want to synchronously wait to move into D0
|
|
|
|
|
//
|
|
|
|
|
return PowerReference(TRUE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::PnpPowerReferenceDuringQueryPnp(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Take a power reference during a query pnp transition.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
//
|
|
|
|
|
// We want to synchronously wait to move into D0
|
|
|
|
|
//
|
|
|
|
|
return m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
m_PowerIdleMachine.PowerReferenceWithFlags(
|
|
|
|
|
FxPowerReferenceSendPnpPowerUpEvent
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::PnpPowerDereferenceSelf(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Release the power reference taken during a query pnp transition
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
|
|
|
PowerDereference();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::CompletePowerRequest(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__in NTSTATUS Status
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
MdIrp irp;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Once we call CompleteRequest, 2 things happen
|
|
|
|
|
// 1) this object may go away
|
|
|
|
|
// 2) Irp->m_Irp will be cleared
|
|
|
|
|
//
|
|
|
|
|
// As such, we capture the underlying WDM objects so that we can use them
|
|
|
|
|
// to release the remove lock and use the PIRP *value* as a tag to release
|
|
|
|
|
// the remlock.
|
|
|
|
|
//
|
|
|
|
|
irp = Irp->GetIrp();
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(Status);
|
|
|
|
|
Irp->StartNextPowerIrp();
|
|
|
|
|
Irp->CompleteRequest(IO_NO_INCREMENT);
|
|
|
|
|
|
|
|
|
|
Mx::MxReleaseRemoveLock(m_Device->GetRemoveLock(),
|
|
|
|
|
irp);
|
|
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
|
|
|
FxPkgPnp::GetPnpStateInternal(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Returns the pnp device state encoded into a ULONG. This state is the state
|
|
|
|
|
that is reported to PNp via IRP_MN_QUERY_PNP_DEVICE_STATE after it has been
|
|
|
|
|
decoded into the bits pnp expects
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
the current state bits
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG state;
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// State is shared with the caps bits. Use a lock to guard against
|
|
|
|
|
// corruption of the value between these 2 values
|
|
|
|
|
//
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
state = m_PnpStateAndCaps.Value & FxPnpStateMask;
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
|
|
|
FxPkgPnp::GetPnpCapsInternal(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Returns the pnp device capabilities encoded into a LONG. This state is used
|
|
|
|
|
in reporting device capabilities via IRP_MN_QUERY_CAPABILITIES and filling
|
|
|
|
|
in the PDEVICE_CAPABILITIES structure.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
the current pnp cap bits
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG caps;
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
caps = m_PnpStateAndCaps.Value & FxPnpCapMask;
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
|
|
|
|
|
return caps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetPnpCaps(
|
|
|
|
|
__in PWDF_DEVICE_PNP_CAPABILITIES PnpCapabilities
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Encode the driver provided pnp capabilities into our internal capabilities
|
|
|
|
|
bit field and store the result.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
PnpCapabilities - capabilities as reported by the driver writer
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG pnpCaps;
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
pnpCaps = 0;
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, LockSupported);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, EjectSupported);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, Removable);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, DockDevice);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, UniqueID);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, SilentInstall);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, SurpriseRemovalOK);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, HardwareDisabled);
|
|
|
|
|
pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, NoDisplayInUI);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Since the caller of IRP_MN_QUERY_CAPABILITIES sets these 2 values to -1,
|
|
|
|
|
// we can reuse the -1 as the default/no override value since the associated
|
|
|
|
|
// PDEVICE_CAPAPBILITIES structure will have been predisposed to these values
|
|
|
|
|
//
|
|
|
|
|
if (PnpCapabilities->Address != (ULONG) -1) {
|
|
|
|
|
m_PnpCapsAddress = PnpCapabilities->Address;
|
|
|
|
|
}
|
|
|
|
|
if (PnpCapabilities->UINumber != (ULONG) -1) {
|
|
|
|
|
m_PnpCapsUINumber = PnpCapabilities->UINumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Use the FxPnpStateMask to keep the state mask while applying the new
|
|
|
|
|
// pnp capabilities.
|
|
|
|
|
//
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
m_PnpStateAndCaps.Value = (m_PnpStateAndCaps.Value & FxPnpStateMask) | pnpCaps;
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::GetPnpState(
|
|
|
|
|
__out PWDF_DEVICE_STATE State
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Decodes our internal pnp state bitfield into the external WDF_DEVICE_STATE
|
|
|
|
|
structure
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
State - the structure to decode into
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG state;
|
|
|
|
|
|
|
|
|
|
state = GetPnpStateInternal();
|
|
|
|
|
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, Disabled);
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, DontDisplayInUI);
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, Failed);
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, NotDisableable);
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, Removed);
|
|
|
|
|
SET_TRI_STATE_FROM_STATE_BITS(state, State, ResourcesChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetPnpState(
|
|
|
|
|
__in PWDF_DEVICE_STATE State
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Encodes the driver writer provided state into our internal bit field.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
State - the states to encode
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
LONG pnpState;
|
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
|
|
pnpState = 0x0;
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Disabled);
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, DontDisplayInUI);
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Failed);
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, NotDisableable);
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Removed);
|
|
|
|
|
pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, ResourcesChanged);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Mask off FxPnpCapMask to keep the capabilities part of the bitfield
|
|
|
|
|
// the same while change the pnp state.
|
|
|
|
|
//
|
|
|
|
|
Lock(&irql);
|
|
|
|
|
m_PnpStateAndCaps.Value = (m_PnpStateAndCaps.Value & FxPnpCapMask) | pnpState;
|
|
|
|
|
Unlock(irql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::_SetPowerCapState(
|
|
|
|
|
__in ULONG Index,
|
|
|
|
|
__in DEVICE_POWER_STATE State,
|
|
|
|
|
__out PULONG Result
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Encodes the given device power state (State) into Result at the given Index.
|
|
|
|
|
States are encoded in nibbles (4 bit chunks), starting at the bottom of the
|
|
|
|
|
result and moving upward
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Index - zero based index into the number of nibbles to encode the value
|
|
|
|
|
|
|
|
|
|
State - State to encode
|
|
|
|
|
|
|
|
|
|
Result - pointer to where the encoding will take place
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// We store off state in 4 bits, starting at the lowest byte
|
|
|
|
|
//
|
|
|
|
|
ASSERT(Index < 8);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Erase the old value
|
|
|
|
|
//
|
|
|
|
|
*Result &= ~(0xF << (Index * 4));
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Write in the new one
|
|
|
|
|
//
|
|
|
|
|
*Result |= (0xF & State) << (Index * 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEVICE_POWER_STATE
|
|
|
|
|
FxPkgPnp::_GetPowerCapState(
|
|
|
|
|
__in ULONG Index,
|
|
|
|
|
__in ULONG State
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Decodes our internal device state encoding and returns a normalized device
|
|
|
|
|
power state for the given index.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
Index - nibble (4 bit chunk) index into the State
|
|
|
|
|
|
|
|
|
|
State - value which has the device states encoded into it
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
device power state for the given Index
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
ASSERT(Index < 8);
|
|
|
|
|
// isolate the value and normalize it
|
|
|
|
|
return (DEVICE_POWER_STATE) ((State & (0xF << (Index * 4))) >> (Index * 4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SetPowerCaps(
|
|
|
|
|
__in PWDF_DEVICE_POWER_CAPABILITIES PowerCapabilities
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Encodes the driver provided power capabilities into the object. The device
|
|
|
|
|
power states are encoded into one ULONG while the other power caps are
|
|
|
|
|
encoded into their own distinct fields.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
PowerCapabilities - the power caps reported by the driver writer
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
ULONG states, i;
|
|
|
|
|
USHORT powerCaps;
|
|
|
|
|
|
|
|
|
|
states = 0x0;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Build up the device power state encoding into a temp var so that if we are
|
|
|
|
|
// retrieving the encoding in another thread, we don't get a partial view
|
|
|
|
|
//
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(PowerCapabilities->DeviceState); i++) {
|
|
|
|
|
_SetPowerCapState(i, PowerCapabilities->DeviceState[i], &states);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.States = states;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Same idea. Build up the caps locally first so that when we assign them
|
|
|
|
|
// into the object, it is assigned as a whole.
|
|
|
|
|
//
|
|
|
|
|
powerCaps = 0x0;
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, DeviceD1);
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, DeviceD2);
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD0);
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD1);
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD2);
|
|
|
|
|
powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD3);
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.Caps = powerCaps;
|
|
|
|
|
|
|
|
|
|
if (PowerCapabilities->DeviceWake != PowerDeviceMaximum) {
|
|
|
|
|
m_PowerCaps.DeviceWake = (BYTE) PowerCapabilities->DeviceWake;
|
|
|
|
|
}
|
|
|
|
|
if (PowerCapabilities->SystemWake != PowerSystemMaximum) {
|
|
|
|
|
m_PowerCaps.SystemWake = (BYTE) PowerCapabilities->SystemWake;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PowerCaps.D1Latency = PowerCapabilities->D1Latency;
|
|
|
|
|
m_PowerCaps.D2Latency = PowerCapabilities->D2Latency;
|
|
|
|
|
m_PowerCaps.D3Latency = PowerCapabilities->D3Latency;
|
|
|
|
|
|
|
|
|
|
if (PowerCapabilities->IdealDxStateForSx != PowerDeviceMaximum) {
|
|
|
|
|
//
|
|
|
|
|
// Caller has already validated that IdealDxStateForSx is only set if
|
|
|
|
|
// they device is the power policy owner.
|
|
|
|
|
//
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdealDxStateForSx = (BYTE)
|
|
|
|
|
PowerCapabilities->IdealDxStateForSx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::CompletePnpRequest(
|
|
|
|
|
__inout FxIrp* Irp,
|
|
|
|
|
__in NTSTATUS Status
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
MdIrp pIrp = Irp->GetIrp();
|
|
|
|
|
|
|
|
|
|
Irp->SetStatus(Status);
|
|
|
|
|
Irp->CompleteRequest(IO_NO_INCREMENT);
|
|
|
|
|
|
|
|
|
|
Mx::MxReleaseRemoveLock(m_Device->GetRemoveLock(),
|
|
|
|
|
pIrp);
|
|
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
|
FxPkgPnp::PowerPolicyIsWakeEnabled(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (IsPowerPolicyOwner() && PowerPolicyGetCurrentWakeReason() != 0x0) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
|
FxPkgPnp::PowerPolicyGetCurrentWakeReason(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine determines the reasons for whether wake should be enabled or
|
|
|
|
|
not. Wake could be enabled because it is explicitly enabled for the device
|
|
|
|
|
in the wake policy settings or, because the device opted to depend on its
|
|
|
|
|
children being armed for wake.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
Returns a combination of FxPowerPolicySxWakeChildrenArmedFlag, to indicate
|
|
|
|
|
that wake can be enabled because of more than one children being armed for
|
|
|
|
|
wake, and FxPowerPolicySxWakeDeviceEnabledFlag, to indicate that wake can
|
|
|
|
|
be enabled because the device was explicitly enabled in the wake policy
|
|
|
|
|
settings.
|
|
|
|
|
|
|
|
|
|
Returns Zero to indicate that wake is currently disabled for the device.
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
ULONG wakeReason;
|
|
|
|
|
|
|
|
|
|
wakeReason = 0x0;
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.ArmForWakeIfChildrenAreArmedForWake &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_ChildrenArmedCount > 0) {
|
|
|
|
|
//
|
|
|
|
|
// Wake settings depends on children and one or more children are
|
|
|
|
|
// armed for wake.
|
|
|
|
|
//
|
|
|
|
|
wakeReason |= FxPowerPolicySxWakeChildrenArmedFlag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled) {
|
|
|
|
|
//
|
|
|
|
|
// Wake settings is explicitly enabled.
|
|
|
|
|
//
|
|
|
|
|
wakeReason |= FxPowerPolicySxWakeDeviceEnabledFlag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wakeReason;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SaveState(
|
|
|
|
|
__in BOOLEAN UseCanSaveState
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
Saves any permanent state of the device out to the registry
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
UNICODE_STRING name;
|
|
|
|
|
FxAutoRegKey hKey;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
ULONG value;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// We only have settings to save if we are the power policy owner
|
|
|
|
|
//
|
|
|
|
|
if (IsPowerPolicyOwner() == FALSE) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (UseCanSaveState &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_CanSaveState == FALSE) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
|
|
|
"Not saving wake settings for WDFDEVICE %p due to system power "
|
|
|
|
|
"transition", m_Device->GetHandle());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Check to see if there is anything to write out
|
|
|
|
|
//
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty == FALSE &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty == FALSE) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// No need to write out if user control is not enabled
|
|
|
|
|
//
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable == FALSE &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable == FALSE) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the device is in paging path we should not be touching registry during
|
|
|
|
|
// power up because it may incur page fault which won't be satisfied if the
|
|
|
|
|
// device is still not powered up, blocking power Irp. User control state
|
|
|
|
|
// change will not get written if any at this time but will be flushed out
|
|
|
|
|
// to registry during device disable/remove in the remove path.
|
|
|
|
|
//
|
|
|
|
|
if (IsUsageSupported(DeviceUsageTypePaging) && IsDevicePowerUpIrpPending()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
|
|
|
|
|
|
|
|
|
|
status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_WRITE);
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty) {
|
|
|
|
|
RtlInitUnicodeString(&name, WDF_S0_IDLE_ENABLED_VALUE_NAME);
|
|
|
|
|
value = m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
|
|
|
|
|
|
|
|
|
|
WriteStateToRegistry(hKey.m_Key, &name, value);
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable &&
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty) {
|
|
|
|
|
RtlInitUnicodeString(&name, WDF_SX_WAKE_ENABLED_VALUE_NAME);
|
|
|
|
|
value = m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
|
|
|
|
|
|
|
|
|
|
WriteStateToRegistry(hKey.m_Key, &name, value);
|
|
|
|
|
|
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::AddInterruptObject(
|
|
|
|
|
__in FxInterrupt* Interrupt
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine adds a WDFINTERRUPT object onto the list of interrupts
|
|
|
|
|
which are attached to this device. This list is used in response to
|
|
|
|
|
several IRPs.
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
|
|
|
|
|
It shouldn't be necessary to lock this list, since the driver will add or
|
|
|
|
|
remove interrupt objects only in callbacks that are effectively serialized
|
|
|
|
|
by the PnP manager.
|
|
|
|
|
|
|
|
|
|
Further note:
|
|
|
|
|
|
|
|
|
|
This list must remain sorted by order of interrupt object creation. E.g.
|
|
|
|
|
the first interrupt object created must be first in this list.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Interrupt - a Framework interrupt object
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
m_InterruptObjectCount++;
|
|
|
|
|
InsertTailList(&m_InterruptListHead, &Interrupt->m_PnpList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::RemoveInterruptObject(
|
|
|
|
|
__in FxInterrupt* Interrupt
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine removes a WDFINTERRUPT object onto the list of interrupts
|
|
|
|
|
which are attached to this device. This list is used in response to
|
|
|
|
|
several IRPs.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Interrupt - a Framework interrupt object
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
m_InterruptObjectCount--;
|
|
|
|
|
RemoveEntryList(&Interrupt->m_PnpList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::NotifyResourceobjectsToReleaseResources(
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine traverses all resource objects and tells them that their
|
|
|
|
|
resources are no longer valid.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
none
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxInterrupt* interruptInstance;
|
|
|
|
|
PLIST_ENTRY intListEntry;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Revoke each of the interrupts.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
intListEntry = m_InterruptListHead.Flink;
|
|
|
|
|
|
|
|
|
|
while (intListEntry != &m_InterruptListHead) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Disconnect interrupts and then tell them that they no longer
|
|
|
|
|
// own their resources.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
interruptInstance = CONTAINING_RECORD(intListEntry, FxInterrupt, m_PnpList);
|
|
|
|
|
interruptInstance->RevokeResources();
|
|
|
|
|
intListEntry = intListEntry->Flink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Now revoke each of the DMA enablers (only system-mode enablers
|
|
|
|
|
// will be affected by this)
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if (m_DmaEnablerList != NULL) {
|
|
|
|
|
FxTransactionedEntry* listEntry;
|
|
|
|
|
|
|
|
|
|
m_DmaEnablerList->LockForEnum(GetDriverGlobals());
|
|
|
|
|
|
|
|
|
|
for (listEntry = m_DmaEnablerList->GetNextEntry(NULL);
|
|
|
|
|
listEntry != NULL;
|
|
|
|
|
listEntry = m_DmaEnablerList->GetNextEntry(listEntry)) {
|
|
|
|
|
RevokeDmaEnablerResources(
|
|
|
|
|
(FxDmaEnabler *) listEntry->GetTransactionedObject()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_DmaEnablerList->UnlockFromEnum(GetDriverGlobals());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::NotifyResourceObjectsD0(
|
|
|
|
|
__in ULONG NotifyFlags
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine traverses all resource objects and tells them that the device
|
|
|
|
|
is entering D0. If an error is encountered, the walking of the list is
|
|
|
|
|
halted.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
none
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxInterrupt* pInterrupt;
|
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
|
|
for (ple = m_InterruptListHead.Flink;
|
|
|
|
|
ple != &m_InterruptListHead;
|
|
|
|
|
ple = ple->Flink) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Connect the interrupts
|
|
|
|
|
//
|
|
|
|
|
pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|
|
|
|
|
|
|
|
|
status = pInterrupt->Connect(NotifyFlags);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFINTERRUPT %p failed to connect, %!STATUS!",
|
|
|
|
|
pInterrupt->GetHandle(), status);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::NotifyResourceObjectsDx(
|
|
|
|
|
__in ULONG NotifyFlags
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine traverses all resource objects and tells them that the device
|
|
|
|
|
is leaving D0. If there is an error, the remaining resources in the list
|
|
|
|
|
are still notified of exitting D0.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
NotifyFlags - combination of values from the enum NotifyResourcesFlags
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxInterrupt* pInterrupt;
|
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
|
NTSTATUS status, finalStatus;
|
|
|
|
|
|
|
|
|
|
finalStatus = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Disconect in the reverse order in which we connected the interrupts
|
|
|
|
|
//
|
|
|
|
|
for (ple = m_InterruptListHead.Blink;
|
|
|
|
|
ple != &m_InterruptListHead;
|
|
|
|
|
ple = ple->Blink) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Disconnect interrupts and then tell them that they no longer
|
|
|
|
|
// own their resources.
|
|
|
|
|
//
|
|
|
|
|
pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|
|
|
|
|
|
|
|
|
status = pInterrupt->Disconnect(NotifyFlags);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
//
|
|
|
|
|
// When we encounter an error we still disconnect the remaining
|
|
|
|
|
// interrupts b/c we would just do it later during device tear down
|
|
|
|
|
// (might as well do it now).
|
|
|
|
|
//
|
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
"WDFINTERRUPT %p failed to disconnect, %!STATUS!",
|
|
|
|
|
pInterrupt->GetHandle(), status);
|
|
|
|
|
//
|
|
|
|
|
// Overwriting a previous finalStatus is OK, we'll just report the
|
|
|
|
|
// last one
|
|
|
|
|
//
|
|
|
|
|
finalStatus = status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return finalStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::SendEventToAllWakeInterrupts(
|
|
|
|
|
__in FxWakeInterruptEvents WakeInterruptEvent
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine traverses all interrupt objects and queues the
|
|
|
|
|
given event into those interrupts that are marked as wake
|
|
|
|
|
capable and have a state machine
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
WakeInterruptEvent - Event to be queued in the wake interrupt
|
|
|
|
|
state machines
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
FxInterrupt* pInterrupt;
|
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
|
|
|
|
|
|
if (m_WakeInterruptCount == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Initialize the pending count to the wake interrupt count
|
|
|
|
|
// If the event needs an acknowledgement, this variable will
|
|
|
|
|
// be decremented as the interrupt machines ack.
|
|
|
|
|
//
|
|
|
|
|
m_WakeInterruptPendingAckCount = m_WakeInterruptCount;
|
|
|
|
|
|
|
|
|
|
for (ple = m_InterruptListHead.Flink;
|
|
|
|
|
ple != &m_InterruptListHead;
|
|
|
|
|
ple = ple->Flink) {
|
|
|
|
|
|
|
|
|
|
pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|
|
|
|
|
|
|
|
|
if (pInterrupt->IsWakeCapable()) {
|
|
|
|
|
pInterrupt->ProcessWakeInterruptEvent(WakeInterruptEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FxPkgPnp::AckPendingWakeInterruptOperation(
|
|
|
|
|
__in BOOLEAN ProcessPowerEventOnDifferentThread
|
|
|
|
|
)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
This routine is invoked by the interrupt wake machine to acknowledge
|
|
|
|
|
back an event that was queued into it. When the last interrupt machine
|
|
|
|
|
acknowledges, an event is queued back into the Pnp/power state machine
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
ProcessPowerEventOnDifferentThread - Once all wake interrupts for the device
|
|
|
|
|
have acknowledged the operation, if this is TRUE, the power state
|
|
|
|
|
machine will process the PowerWakeInterruptCompleteTransition event on a
|
|
|
|
|
seperate thread.
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
if (InterlockedDecrement((LONG *)&m_WakeInterruptPendingAckCount) == 0) {
|
|
|
|
|
PowerProcessEvent(
|
|
|
|
|
PowerWakeInterruptCompleteTransition,
|
|
|
|
|
ProcessPowerEventOnDifferentThread
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
|
FxPkgPnp::AssignPowerFrameworkSettings(
|
|
|
|
|
__in PWDF_POWER_FRAMEWORK_SETTINGS PowerFrameworkSettings
|
|
|
|
|
)
|
|
|
|
|
{
|
2020-10-16 03:30:51 +00:00
|
|
|
|
// NTSTATUS status;
|
|
|
|
|
// PPO_FX_COMPONENT_IDLE_STATE idleStates = NULL;
|
|
|
|
|
// ULONG idleStatesSize = 0;
|
|
|
|
|
// PPO_FX_COMPONENT component = NULL;
|
|
|
|
|
// ULONG componentSize = 0;
|
|
|
|
|
// PPOX_SETTINGS poxSettings = NULL;
|
|
|
|
|
// ULONG poxSettingsSize = 0;
|
|
|
|
|
// BYTE * buffer = NULL;
|
|
|
|
|
|
|
|
|
|
// if (FALSE==(IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable())) {
|
|
|
|
|
// //
|
|
|
|
|
// // If system-managed idle timeout is not available on this OS, then
|
|
|
|
|
// // there is nothing to do.
|
|
|
|
|
// //
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
|
|
|
// "WDFDEVICE %p !devobj %p Power framework is not supported on the "
|
|
|
|
|
// "current OS. Therefore, the power framework settings will not take "
|
|
|
|
|
// "effect.",
|
|
|
|
|
// m_Device->GetHandle(),
|
|
|
|
|
// m_Device->GetDeviceObject()
|
|
|
|
|
// );
|
|
|
|
|
// return STATUS_SUCCESS;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if (NULL != PowerFrameworkSettings->Component) {
|
|
|
|
|
// //
|
|
|
|
|
// // Caller should ensure that IdleStateCount is not zero
|
|
|
|
|
// //
|
|
|
|
|
// ASSERT(0 != PowerFrameworkSettings->Component->IdleStateCount);
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Compute buffer size needed for storing F-states
|
|
|
|
|
// //
|
|
|
|
|
// status = RtlULongMult(
|
|
|
|
|
// PowerFrameworkSettings->Component->IdleStateCount,
|
|
|
|
|
// sizeof(*(PowerFrameworkSettings->Component->IdleStates)),
|
|
|
|
|
// &idleStatesSize
|
|
|
|
|
// );
|
|
|
|
|
// if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
// "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
|
|
|
|
|
// "required to store F-states. RtlULongMult failed with "
|
|
|
|
|
// "%!STATUS!",
|
|
|
|
|
// m_Device->GetHandle(),
|
|
|
|
|
// m_Device->GetDeviceObject(),
|
|
|
|
|
// status
|
|
|
|
|
// );
|
|
|
|
|
// goto exit;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Compute buffer size needed for storing component information
|
|
|
|
|
// // (including F-states)
|
|
|
|
|
// //
|
|
|
|
|
// status = RtlULongAdd(idleStatesSize,
|
|
|
|
|
// sizeof(*component),
|
|
|
|
|
// &componentSize);
|
|
|
|
|
// if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
// "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
|
|
|
|
|
// "required to store driver's component information. RtlULongAdd "
|
|
|
|
|
// "failed with %!STATUS!",
|
|
|
|
|
// m_Device->GetHandle(),
|
|
|
|
|
// m_Device->GetDeviceObject(),
|
|
|
|
|
// status
|
|
|
|
|
// );
|
|
|
|
|
// goto exit;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Compute total buffer size needed for power framework settings
|
|
|
|
|
// //
|
|
|
|
|
// status = RtlULongAdd(componentSize,
|
|
|
|
|
// sizeof(*poxSettings),
|
|
|
|
|
// &poxSettingsSize);
|
|
|
|
|
// if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
// "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
|
|
|
|
|
// "required to store driver's power framework settings. RtlULongAdd "
|
|
|
|
|
// "failed with %!STATUS!",
|
|
|
|
|
// m_Device->GetHandle(),
|
|
|
|
|
// m_Device->GetDeviceObject(),
|
|
|
|
|
// status
|
|
|
|
|
// );
|
|
|
|
|
// goto exit;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Allocate memory to copy the settings
|
|
|
|
|
// //
|
|
|
|
|
// buffer = (BYTE *) MxMemory::MxAllocatePoolWithTag(NonPagedPool,
|
|
|
|
|
// poxSettingsSize,
|
|
|
|
|
// GetDriverGlobals()->Tag);
|
|
|
|
|
// if (NULL == buffer) {
|
|
|
|
|
// status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
// DoTraceLevelMessage(
|
|
|
|
|
// GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
|
|
|
// "WDFDEVICE %p !devobj %p Unable to allocate buffer required to "
|
|
|
|
|
// "store F-states. %!STATUS!",
|
|
|
|
|
// m_Device->GetHandle(),
|
|
|
|
|
// m_Device->GetDeviceObject(),
|
|
|
|
|
// status
|
|
|
|
|
// );
|
|
|
|
|
// goto exit;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Set our pointers to point to appropriate locations in the buffer.
|
|
|
|
|
// //
|
|
|
|
|
// // NOTES:
|
|
|
|
|
// // - The array of F-states comes first because it has ULONGLONG members
|
|
|
|
|
// // because of which it has the biggest alignment requirement.
|
|
|
|
|
// // - The logic below works even if the client driver did not specify any
|
|
|
|
|
// // component information. In that case idleStatesSize and componentSize
|
|
|
|
|
// // are both 0 and 'poxSettings' points to the beginning of the allocated
|
|
|
|
|
// // buffer
|
|
|
|
|
// //
|
|
|
|
|
// idleStates = (PPO_FX_COMPONENT_IDLE_STATE) buffer;
|
|
|
|
|
// component = (PPO_FX_COMPONENT) (buffer + idleStatesSize);
|
|
|
|
|
// poxSettings = (PPOX_SETTINGS) (buffer + componentSize);
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Copy the relevant parts of the settings buffer
|
|
|
|
|
// //
|
|
|
|
|
// poxSettings->EvtDeviceWdmPostPoFxRegisterDevice =
|
|
|
|
|
// PowerFrameworkSettings->EvtDeviceWdmPostPoFxRegisterDevice;
|
|
|
|
|
// poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice =
|
|
|
|
|
// PowerFrameworkSettings->EvtDeviceWdmPrePoFxUnregisterDevice;
|
|
|
|
|
// poxSettings->Component = PowerFrameworkSettings->Component;
|
|
|
|
|
// poxSettings->ComponentActiveConditionCallback =
|
|
|
|
|
// PowerFrameworkSettings->ComponentActiveConditionCallback;
|
|
|
|
|
// poxSettings->ComponentIdleConditionCallback =
|
|
|
|
|
// PowerFrameworkSettings->ComponentIdleConditionCallback;
|
|
|
|
|
// poxSettings->ComponentIdleStateCallback =
|
|
|
|
|
// PowerFrameworkSettings->ComponentIdleStateCallback;
|
|
|
|
|
// poxSettings->PowerControlCallback =
|
|
|
|
|
// PowerFrameworkSettings->PowerControlCallback;
|
|
|
|
|
// poxSettings->PoFxDeviceContext = PowerFrameworkSettings->PoFxDeviceContext;
|
|
|
|
|
|
|
|
|
|
// if (NULL != PowerFrameworkSettings->Component) {
|
|
|
|
|
// //
|
|
|
|
|
// // Copy the component information
|
|
|
|
|
// //
|
|
|
|
|
// poxSettings->Component = component;
|
|
|
|
|
// RtlCopyMemory(poxSettings->Component,
|
|
|
|
|
// PowerFrameworkSettings->Component,
|
|
|
|
|
// sizeof(*component));
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Caller should ensure that IdleStates is not NULL
|
|
|
|
|
// //
|
|
|
|
|
// ASSERT(NULL != PowerFrameworkSettings->Component->IdleStates);
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Copy the F-states
|
|
|
|
|
// //
|
|
|
|
|
// poxSettings->Component->IdleStates = idleStates;
|
|
|
|
|
// RtlCopyMemory(poxSettings->Component->IdleStates,
|
|
|
|
|
// PowerFrameworkSettings->Component->IdleStates,
|
|
|
|
|
// idleStatesSize);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
// // Commit these settings
|
|
|
|
|
// //
|
|
|
|
|
// status = m_PowerPolicyMachine.m_Owner->
|
|
|
|
|
// m_IdleSettings.m_TimeoutMgmt.CommitPowerFrameworkSettings(
|
|
|
|
|
// GetDriverGlobals(),
|
|
|
|
|
// poxSettings
|
|
|
|
|
// );
|
|
|
|
|
// if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
|
// goto exit;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
// exit:
|
|
|
|
|
// if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
|
// if (NULL != buffer) {
|
|
|
|
|
// MxMemory::MxFreePool(buffer);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// return status;
|
|
|
|
|
ROSWDFNOTIMPLEMENTED;
|
|
|
|
|
return STATUS_SUCCESS;
|
2020-09-24 20:51:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEVICE_POWER_STATE
|
|
|
|
|
FxPkgPnp::PowerPolicyGetDeviceDeepestDeviceWakeState(
|
|
|
|
|
__in SYSTEM_POWER_STATE SystemState
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DEVICE_POWER_STATE dxState;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Earlier versions of WDF (pre-1.11) did not take into account SystemState
|
|
|
|
|
// in figuring out the deepest wake state. So for compatibility we restrict
|
|
|
|
|
// this to drivers compiled for 1.11 or newer. See comments in the
|
|
|
|
|
// FxPkgPnp::QueryForCapabilities function for more information on
|
|
|
|
|
// compatibility risk.
|
|
|
|
|
//
|
|
|
|
|
if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
|
|
|
|
|
if ((SystemState < PowerSystemWorking) ||
|
|
|
|
|
(SystemState > PowerSystemHibernate)) {
|
|
|
|
|
dxState = PowerDeviceD0;
|
|
|
|
|
} else {
|
|
|
|
|
dxState = MapWakeDepthToDstate(
|
|
|
|
|
(DEVICE_WAKE_DEPTH)m_DeviceWake[SystemState - PowerSystemWorking]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//
|
|
|
|
|
// For pre-1.11 drivers, all of m_DeviceWake array is populated with
|
|
|
|
|
// the same DeviceWake value obtained from device capabilities so we
|
|
|
|
|
// just use the first one (index 0).
|
|
|
|
|
//
|
|
|
|
|
dxState = MapWakeDepthToDstate((DEVICE_WAKE_DEPTH)m_DeviceWake[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Per WDM docs DeviceWake and SystemWake must be non-zero to support
|
|
|
|
|
// wake. We warn about the violation. Ideally, a verifier check would have
|
|
|
|
|
// been better but we want to avoid that because some drivers may
|
|
|
|
|
// intentionally call this DDI and do not treat the DDI failure as fatal (
|
|
|
|
|
// because they are aware that DDI may succeed in some cases), and a verifier
|
|
|
|
|
// breakpoint would force them to avoid the verifier failure by not enabling
|
|
|
|
|
// verifier.
|
|
|
|
|
//
|
|
|
|
|
if (dxState == PowerDeviceUnspecified ||
|
|
|
|
|
m_SystemWake == PowerSystemUnspecified) {
|
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
|
"SystemWake %!SYSTEM_POWER_STATE! and DeviceWake "
|
|
|
|
|
"%!DEVICE_POWER_STATE! power state reported in device "
|
|
|
|
|
"capabilities do not support wake. Both the SystemWake and "
|
|
|
|
|
"DeviceWake members should be nonzero to support wake-up on "
|
|
|
|
|
"this system.", m_SystemWake, dxState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dxState;
|
|
|
|
|
}
|
|
|
|
|
|