mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 18:24:02 +00:00
4794 lines
130 KiB
C++
4794 lines
130 KiB
C++
![]() |
/*++
|
|||
|
|
|||
|
Copyright (c) Microsoft. All rights reserved.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
PnpStateMachine.cpp
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the PnP state machine for the driver framework.
|
|||
|
This code was split out from FxPkgPnp.cpp.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Both kernel and user mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pnppriv.hpp"
|
|||
|
#include <wdmguid.h>
|
|||
|
|
|||
|
#include<ntstrsafe.h>
|
|||
|
|
|||
|
extern "C" {
|
|||
|
#if defined(EVENT_TRACING)
|
|||
|
#include "PnpStateMachine.tmh"
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The PnP State Machine
|
|||
|
//
|
|||
|
// This state machine responds to several PnP events:
|
|||
|
//
|
|||
|
// AddDevice -- Always targets the same state
|
|||
|
// IRP_MN_START_DEVICE
|
|||
|
// IRP_MN_START_DEVICE Complete -- Handled on the way up the stack
|
|||
|
// IRP_MN_QUERY_REMOVE_DEVICE
|
|||
|
// IRP_MN_QUERY_STOP_DEVICE
|
|||
|
// IRP_MN_CANCEL_REMOVE_DEVICE
|
|||
|
// IRP_MN_CANCEL_STOP_DEVICE
|
|||
|
// IRP_MN_STOP_DEVICE
|
|||
|
// IRP_MN_REMOVE_DEVICE
|
|||
|
// IRP_MN_SURPRISE_REMOVE_DEVICE -- Always targets the same state
|
|||
|
// IRP_MN_EJECT
|
|||
|
//
|
|||
|
// Each state has an entry for each of these events, listing the
|
|||
|
// target state for each of them.
|
|||
|
//
|
|||
|
|
|||
|
#if FX_STATE_MACHINE_VERIFY
|
|||
|
#define VALIDATE_PNP_STATE(_CurrentState, _NewState) \
|
|||
|
ValidatePnpStateEntryFunctionReturnValue((_CurrentState), (_NewState))
|
|||
|
#else
|
|||
|
#define VALIDATE_PNP_STATE(_CurrentState, _NewState) (0)
|
|||
|
#endif //FX_STATE_MACHINE_VERIFY
|
|||
|
|
|||
|
// @@SMVERIFY_SPLIT_BEGIN
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventQueryRemove, WdfDevStatePnpInitQueryRemove DEBUGGED_EVENT },
|
|||
|
{ PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventParentRemoved, WdfDevStatePnpRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpInitSurpriseRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventEject, WdfDevStatePnpEjectHardware DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitStartingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventStartDeviceFailed, WdfDevStatePnpInit DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpHardwareAvailableOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStartFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueryStopPendingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventCancelStop, WdfDevStatePnpQueryCanceled DEBUGGED_EVENT },
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpQueriedSurpriseRemove TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRemovedPdoWaitOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpPdoRestart DEBUGGED_EVENT },
|
|||
|
{ PnpEventRemove, WdfDevStatePnpCheckForDevicePresence DEBUGGED_EVENT },
|
|||
|
{ PnpEventParentRemoved, WdfDevStatePnpPdoRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpRemovedPdoSurpriseRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStartFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventQueryStop, WdfDevStatePnpQueryStopStaticCheck DEBUGGED_EVENT },
|
|||
|
{ PnpEventCancelStop, WdfDevStatePnpStartedCancelStop DEBUGGED_EVENT },
|
|||
|
{ PnpEventCancelRemove, WdfDevStatePnpStartedCancelRemove DEBUGGED_EVENT },
|
|||
|
{ PnpEventRemove, WdfDevStatePnpStartedRemoving DEBUGGED_EVENT },
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemoveIoStarted DEBUGGED_EVENT },
|
|||
|
{ PnpEventPowerUpFailed, WdfDevStatePnpFailedIoStarting DEBUGGED_EVENT },
|
|||
|
{ PnpEventPowerDownFailed, WdfDevStatePnpFailedPowerDown DEBUGGED_EVENT },
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpRestart DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueryRemovePendingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventCancelRemove, WdfDevStatePnpQueryCanceled DEBUGGED_EVENT },
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpQueriedSurpriseRemove TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueriedRemovingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitQueryRemoveOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventCancelRemove, WdfDevStatePnpInitQueryRemoveCanceled DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStoppedOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemove DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStoppedWaitForStartCompletionOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventStartDeviceFailed, WdfDevStatePnpFailed TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedStoppingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedStoppingFailedOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpEjectFailedOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemove TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedRemovingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedPowerDownOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedIoStartingOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedWaitForRemoveOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventSurpriseRemove, WdfDevStatePnpFailedSurpriseRemoved DEBUGGED_EVENT },
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpFailedStarted TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStopFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartReleaseHardware[] =
|
|||
|
{
|
|||
|
{ PnpEventStartDeviceFailed, WdfDevStatePnpFailed TRAP_ON_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartHardwareAvailableOtherStates[] =
|
|||
|
{
|
|||
|
{ PnpEventPwrPolStartFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
};
|
|||
|
|
|||
|
const PNP_STATE_TABLE FxPkgPnp::m_WdfPnpStates[] = {
|
|||
|
// State function
|
|||
|
// First transition event & state
|
|||
|
// Other transition events & states
|
|||
|
// state info
|
|||
|
|
|||
|
// WdfDevStatePnpObjectCreated
|
|||
|
{ NULL,
|
|||
|
{ PnpEventAddDevice, WdfDevStatePnpInit DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpCheckForDevicePresence
|
|||
|
{ FxPkgPnp::PnpEventCheckForDevicePresence,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpEjectFailed
|
|||
|
{ NULL,
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpPdoRestart DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpEjectFailedOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpEjectHardware
|
|||
|
{ FxPkgPnp::PnpEventEjectHardware,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpEjectedWaitingForRemove
|
|||
|
{ NULL,
|
|||
|
{ PnpEventRemove, WdfDevStatePnpPdoRemoved DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
PnpEventSurpriseRemove }, // can receive this if parent is surprise
|
|||
|
// removed while the ejected pdo is waiting
|
|||
|
// for remove.
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpInit
|
|||
|
{ NULL,
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpInitStarting DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpInitOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventStartDevice },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpInitStarting
|
|||
|
{ FxPkgPnp::PnpEventInitStarting,
|
|||
|
{ PnpEventStartDeviceComplete, WdfDevStatePnpHardwareAvailable DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpInitStartingOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpInitSurpriseRemoved
|
|||
|
{ FxPkgPnp::PnpEventInitSurpriseRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpHardwareAvailable
|
|||
|
{ FxPkgPnp::PnpEventHardwareAvailable,
|
|||
|
{ PnpEventPwrPolStarted, WdfDevStatePnpEnableInterfaces DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpHardwareAvailableOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerUpFailed
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpEnableInterfaces
|
|||
|
{ FxPkgPnp::PnpEventEnableInterfaces,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpHardwareAvailablePowerPolicyFailed
|
|||
|
{ FxPkgPnp::PnpEventHardwareAvailablePowerPolicyFailed,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryRemoveAskDriver
|
|||
|
{ FxPkgPnp::PnpEventQueryRemoveAskDriver,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryRemovePending
|
|||
|
{ FxPkgPnp::PnpEventQueryRemovePending,
|
|||
|
{ PnpEventRemove, WdfDevStatePnpQueriedRemoving DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpQueryRemovePendingOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryRemoveStaticCheck
|
|||
|
{ FxPkgPnp::PnpEventQueryRemoveStaticCheck,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueriedRemoving,
|
|||
|
{ FxPkgPnp::PnpEventQueriedRemoving,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpQueriedRemovingOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerDownFailed | // We ignore these power failed events because
|
|||
|
PnpEventPowerUpFailed // they will be translated into failed power
|
|||
|
// policy events.
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryStopAskDriver
|
|||
|
{ FxPkgPnp::PnpEventQueryStopAskDriver,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryStopPending
|
|||
|
{ FxPkgPnp::PnpEventQueryStopPending,
|
|||
|
{ PnpEventStop, WdfDevStatePnpStartedStopping DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpQueryStopPendingOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryStopStaticCheck
|
|||
|
{ FxPkgPnp::PnpEventQueryStopStaticCheck,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryCanceled,
|
|||
|
{ FxPkgPnp::PnpEventQueryCanceled,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemoved
|
|||
|
{ FxPkgPnp::PnpEventRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpPdoRemoved
|
|||
|
{ FxPkgPnp::PnpEventPdoRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemovedPdoWait
|
|||
|
{ FxPkgPnp::PnpEventRemovedPdoWait,
|
|||
|
{ PnpEventEject, WdfDevStatePnpEjectHardware DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpRemovedPdoWaitOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventCancelRemove | // Amazingly enough, you can get a cancel q.r.
|
|||
|
// on a PDO without seeing the query remove if
|
|||
|
// the stack is partially built
|
|||
|
PnpEventQueryRemove | // Can get a query remove from the removed state
|
|||
|
// when installing a PDO that is disabled
|
|||
|
PnpEventPowerDownFailed // We may get this for a PDO if implicit power
|
|||
|
// down callbacks were failed. The failed power
|
|||
|
// policy stop event took care of rundown.
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemovedPdoSurpriseRemoved
|
|||
|
{ FxPkgPnp::PnpEventRemovedPdoSurpriseRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemovingDisableInterfaces
|
|||
|
{ FxPkgPnp::PnpEventRemovingDisableInterfaces,
|
|||
|
{ PnpEventPwrPolRemoved, WdfDevStatePnpRemoved DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRestarting
|
|||
|
{ FxPkgPnp::PnpEventRestarting,
|
|||
|
{ PnpEventPwrPolStarted, WdfDevStatePnpStarted DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpRestartingOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerUpFailed
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStarted
|
|||
|
{ FxPkgPnp::PnpEventStarted,
|
|||
|
{ PnpEventQueryRemove, WdfDevStatePnpQueryRemoveStaticCheck DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpStartedOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStartedCancelStop
|
|||
|
{ FxPkgPnp::PnpEventStartedCancelStop,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStartedCancelRemove
|
|||
|
{ FxPkgPnp::PnpEventStartedCancelRemove,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStartedRemoving
|
|||
|
{ FxPkgPnp::PnpEventStartedRemoving,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpStartedRemovingOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerUpFailed | // device was idled out and in Dx when we got removed
|
|||
|
// and this event is due to the power up that occured
|
|||
|
// to move it into D0 so it could be disarmed
|
|||
|
PnpEventPowerDownFailed
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStartingFromStopped
|
|||
|
{ FxPkgPnp::PnpEventStartingFromStopped,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStopped
|
|||
|
{ FxPkgPnp::PnpEventStopped,
|
|||
|
{ PnpEventStartDevice, WdfDevStatePnpStoppedWaitForStartCompletion DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpStoppedOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStoppedWaitForStartCompletion
|
|||
|
{ FxPkgPnp::PnpEventStoppedWaitForStartCompletion,
|
|||
|
{ PnpEventStartDeviceComplete, WdfDevStatePnpStartingFromStopped DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpStoppedWaitForStartCompletionOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpStartedStopping
|
|||
|
{ FxPkgPnp::PnpEventStartedStopping,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpStopped DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpStartedStoppingOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerUpFailed | // device was idled out and in Dx when we got stopped
|
|||
|
// and this event is due to the power up that occured
|
|||
|
// to move it into D0 so it could be disarmed
|
|||
|
PnpEventPowerDownFailed
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// The function is named PnpEventSurpriseRemoved with a 'd' because
|
|||
|
// PnpEventSurpriseRemove (no 'd') is an event name
|
|||
|
|
|||
|
// WdfDevStatePnpSurpriseRemove
|
|||
|
{ FxPkgPnp::PnpEventSurpriseRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpInitQueryRemove
|
|||
|
{ FxPkgPnp::PnpEventInitQueryRemove,
|
|||
|
{ PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpInitQueryRemoveOtherStates,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpInitQueryRemoveCanceled
|
|||
|
{ FxPkgPnp::PnpEventInitQueryRemoveCanceled,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFdoRemoved
|
|||
|
{ FxPkgPnp::PnpEventFdoRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemovedWaitForChildren
|
|||
|
{ NULL,
|
|||
|
{ PnpEventChildrenRemovalComplete, WdfDevStatePnpRemovedChildrenRemoved DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerDownFailed // device power down even from processing remove
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueriedSurpriseRemove
|
|||
|
{ FxPkgPnp::PnpEventQueriedSurpriseRemove,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpSurpriseRemoveIoStarted
|
|||
|
{ FxPkgPnp::PnpEventSurpriseRemoveIoStarted,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedPowerDown
|
|||
|
{ FxPkgPnp::PnpEventFailedPowerDown,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpFailedPowerDownOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerDownFailed ,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedIoStarting
|
|||
|
{ FxPkgPnp::PnpEventFailedIoStarting,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpFailedIoStartingOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerDownFailed |
|
|||
|
|
|||
|
PnpEventPowerUpFailed // if the device idled out and then failed
|
|||
|
// d0 entry, the power up failed can be passed
|
|||
|
// up by the IoInvalidateDeviceRelations and
|
|||
|
// subsequence surprise remove event.
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedOwnHardware
|
|||
|
{ FxPkgPnp::PnpEventFailedOwnHardware,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailed
|
|||
|
{ FxPkgPnp::PnpEventFailed,
|
|||
|
{ PnpEventPwrPolRemoved, WdfDevStatePnpFailedPowerPolicyRemoved DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0,
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedSurpriseRemoved
|
|||
|
{ FxPkgPnp::PnpEventFailedSurpriseRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0, },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedStarted
|
|||
|
{ FxPkgPnp::PnpEventFailedStarted,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0, },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedWaitForRemove,
|
|||
|
{ NULL,
|
|||
|
{ PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpFailedWaitForRemoveOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerUpFailed | // initial power up failed, power policy start
|
|||
|
// failed event moved the state machine to the
|
|||
|
// failed state first
|
|||
|
PnpEventPowerDownFailed | // implicitD3 power down failed
|
|||
|
PnpEventQueryRemove | // start succeeded, but we still get a query in
|
|||
|
// the removed case
|
|||
|
PnpEventCancelStop | // power down failure while processing query stop
|
|||
|
// and q.s. irp completed with error
|
|||
|
PnpEventCancelRemove // power down failure while processing query remove
|
|||
|
// and q.r. irp completed with error
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedInit
|
|||
|
{ FxPkgPnp::PnpEventFailedInit,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpPdoInitFailed
|
|||
|
{ FxPkgPnp::PnpEventPdoInitFailed,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRestart
|
|||
|
{ FxPkgPnp::PnpEventRestart,
|
|||
|
{ PnpEventPwrPolStopped, WdfDevStatePnpRestartReleaseHardware DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpRestartOtherStates,
|
|||
|
{ FALSE,
|
|||
|
PnpEventPowerUpFailed | // when stopping power policy, device was in
|
|||
|
// Dx and bringing it to D0 succeeded or failed
|
|||
|
PnpEventPowerDownFailed // same as power up
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRestartReleaseHardware
|
|||
|
{ FxPkgPnp::PnpEventRestartReleaseHardware,
|
|||
|
{ PnpEventStartDeviceComplete, WdfDevStatePnpRestartHardwareAvailable DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpRestartReleaseHardware,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerDownFailed // the previous pwr policy stop
|
|||
|
// in WdfDevStaePnpRestart will
|
|||
|
// cause these events to show up here
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRestartHardwareAvailable
|
|||
|
{ FxPkgPnp::PnpEventRestartHardwareAvailable,
|
|||
|
{ PnpEventPwrPolStarted, WdfDevStatePnpStarted DEBUGGED_EVENT },
|
|||
|
FxPkgPnp::m_PnpRestartHardwareAvailableOtherStates,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerUpFailed
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpPdoRestart
|
|||
|
{ FxPkgPnp::PnpEventPdoRestart,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFinal
|
|||
|
{ FxPkgPnp::PnpEventFinal,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
PnpEventPowerDownFailed, // on the final implicit power down, a
|
|||
|
// callback returned !NT_SUCCESS
|
|||
|
},
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpRemovedChildrenRemoved
|
|||
|
{ FxPkgPnp::PnpEventRemovedChildrenRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ TRUE,
|
|||
|
0 } ,
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryRemoveEnsureDeviceAwake
|
|||
|
{ FxPkgPnp::PnpEventQueryRemoveEnsureDeviceAwake,
|
|||
|
{ PnpEventDeviceInD0, WdfDevStatePnpQueryRemovePending DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpQueryStopEnsureDeviceAwake
|
|||
|
{ FxPkgPnp::PnpEventQueryStopEnsureDeviceAwake,
|
|||
|
{ PnpEventDeviceInD0, WdfDevStatePnpQueryStopPending DEBUGGED_EVENT },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 },
|
|||
|
},
|
|||
|
|
|||
|
// WdfDevStatePnpFailedPowerPolicyRemoved
|
|||
|
{ FxPkgPnp::PnpEventFailedPowerPolicyRemoved,
|
|||
|
{ PnpEventNull, WdfDevStatePnpNull },
|
|||
|
NULL,
|
|||
|
{ FALSE,
|
|||
|
0 } ,
|
|||
|
},
|
|||
|
};
|
|||
|
|
|||
|
// @@SMVERIFY_SPLIT_END
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpCheckAssumptions(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This routine is never actually called by running code, it just has
|
|||
|
WDFCASSERTs who upon failure, would not allow this file to be compiled.
|
|||
|
|
|||
|
DO NOT REMOVE THIS FUNCTION just because it is not callec by any running
|
|||
|
code.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WDFCASSERT(sizeof(FxPnpStateInfo) == sizeof(ULONG));
|
|||
|
|
|||
|
WDFCASSERT((sizeof(m_WdfPnpStates)/sizeof(m_WdfPnpStates[0]))
|
|||
|
==
|
|||
|
(WdfDevStatePnpNull - WdfDevStatePnpObjectCreated));
|
|||
|
|
|||
|
// we assume these are the same length when we update the history index
|
|||
|
WDFCASSERT((sizeof(m_PnpMachine.m_Queue)/sizeof(m_PnpMachine.m_Queue[0]))
|
|||
|
==
|
|||
|
(sizeof(m_PnpMachine.m_States.History)/
|
|||
|
sizeof(m_PnpMachine.m_States.History[0])));
|
|||
|
}
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
The locking model for the PnP state machine requires that events be enqueued
|
|||
|
possibly at DISPATCH_LEVEL. It also requires that the PnP state machine be
|
|||
|
runnable at PASSIVE_LEVEL. Consequently, we have two locks, one DISPATCH_LEVEL
|
|||
|
lock that guards the event queue and one PASSIVE_LEVEL lock that guards the
|
|||
|
state machine itself.
|
|||
|
|
|||
|
Algorithm:
|
|||
|
|
|||
|
1) Acquire the PnP queue lock.
|
|||
|
2) Enqueue the request. Requests are put at the end of the queue, except if
|
|||
|
they are PowerUp, or PowerDown, in which case they are put at the head of
|
|||
|
the queue.
|
|||
|
3) Drop the PnP queue lock.
|
|||
|
4) If the thread is running at PASSIVE_LEVEL, skip to step 6.
|
|||
|
5) Queue a work item onto any work queue.
|
|||
|
6) Attempt to acquire the state machine lock, with a near-zero-length timeout.
|
|||
|
7) If successful, skip to step 10.
|
|||
|
8) Queue a work item onto any work queue.
|
|||
|
9) Acquire the state machine lock.
|
|||
|
10) Acquire the PnP queue lock.
|
|||
|
11) Attempt to dequeue an event.
|
|||
|
12) Drop the PnP queue lock.
|
|||
|
13) If there was no event to dequeue, drop the state machine lock and exit.
|
|||
|
14) Execute the state handler. This may involve taking one of the other state
|
|||
|
machine queue locks, briefly, to deliver an event.
|
|||
|
15) Go to Step 10.
|
|||
|
|
|||
|
Implementing this algorithm requires three functions.
|
|||
|
|
|||
|
PnpProcessEvent -- Implements steps 1-8.
|
|||
|
_PnpProcessEventInner -- Implements step 9.
|
|||
|
PnpProcessEventInner -- Implements steps 10-15.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpProcessEvent(
|
|||
|
__in FxPnpEvent Event,
|
|||
|
__in BOOLEAN ProcessOnDifferentThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements steps 1-8 of the algorithm described above.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Event - Current PnP event
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Take the lock, raising to DISPATCH_LEVEL.
|
|||
|
//
|
|||
|
m_PnpMachine.Lock(&oldIrql);
|
|||
|
|
|||
|
if (m_PnpMachine.IsFull()) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"WDFDEVICE 0x%p !devobj 0x%p current pnp state %!WDF_DEVICE_PNP_STATE! "
|
|||
|
"dropping event %!FxPnpEvent! because of a full queue",
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Device->GetDeviceObject(),
|
|||
|
m_Device->GetDevicePnpState(),
|
|||
|
Event);
|
|||
|
|
|||
|
//
|
|||
|
// The queue is full. Bail.
|
|||
|
//
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
|
|||
|
ASSERT(!"The PnP queue is full. This shouldn't be able to happen.");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (m_PnpMachine.IsClosedLocked()) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"WDFDEVICE 0x%p !devobj 0x%p current pnp state %!WDF_DEVICE_PNP_STATE! "
|
|||
|
"dropping event %!FxPnpEvent! because of a closed queue",
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Device->GetDeviceObject(),
|
|||
|
m_Device->GetDevicePnpState(),
|
|||
|
Event);
|
|||
|
|
|||
|
//
|
|||
|
// The queue is closed. Bail
|
|||
|
//
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enqueue the event. Whether the event goes on the front
|
|||
|
// or the end of the queue depends on which event it is.
|
|||
|
//
|
|||
|
if (Event & PnpPriorityEventsMask) {
|
|||
|
//
|
|||
|
// Stick it on the front of the queue, making it the next
|
|||
|
// event that will be processed.
|
|||
|
//
|
|||
|
m_PnpMachine.m_Queue[m_PnpMachine.InsertAtHead()] = Event;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Stick it on the end of the queue.
|
|||
|
//
|
|||
|
m_PnpMachine.m_Queue[m_PnpMachine.InsertAtTail()] = Event;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Drop the lock.
|
|||
|
//
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Now, if we are running at PASSIVE_LEVEL, attempt to run the state
|
|||
|
// machine on this thread. If we can't do that, then queue a work item.
|
|||
|
//
|
|||
|
|
|||
|
if (FALSE == ShouldProcessPnpEventOnDifferentThread(
|
|||
|
oldIrql,
|
|||
|
ProcessOnDifferentThread
|
|||
|
)) {
|
|||
|
|
|||
|
LONGLONG timeout = 0;
|
|||
|
|
|||
|
status = m_PnpMachine.m_StateMachineLock.AcquireLock(GetDriverGlobals(),
|
|||
|
&timeout);
|
|||
|
|
|||
|
if (FxWaitLockInternal::IsLockAcquired(status)) {
|
|||
|
FxPostProcessInfo info;
|
|||
|
|
|||
|
//
|
|||
|
// We now hold the state machine lock. So call the function that
|
|||
|
// dispatches the next state.
|
|||
|
//
|
|||
|
PnpProcessEventInner(&info);
|
|||
|
|
|||
|
m_PnpMachine.m_StateMachineLock.ReleaseLock(GetDriverGlobals());
|
|||
|
|
|||
|
info.Evaluate(this);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For one reason or another, we couldn't run the state machine on this
|
|||
|
// thread. So queue a work item to do it. If m_PnPWorkItemEnqueuing
|
|||
|
// is non-zero, that means that the work item is already being enqueued
|
|||
|
// on another thread. This is significant, since it means that we can't do
|
|||
|
// anything with the work item on this thread, but it's okay, since the
|
|||
|
// work item will pick up our work and do it.
|
|||
|
//
|
|||
|
m_PnpMachine.QueueToThread();
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::_PnpProcessEventInner(
|
|||
|
__inout FxPkgPnp* This,
|
|||
|
__inout FxPostProcessInfo* Info,
|
|||
|
__in PVOID WorkerContext
|
|||
|
)
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(WorkerContext);
|
|||
|
|
|||
|
//
|
|||
|
// Take the state machine lock.
|
|||
|
//
|
|||
|
This->m_PnpMachine.m_StateMachineLock.AcquireLock(
|
|||
|
This->GetDriverGlobals()
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Call the function that will actually run the state machine.
|
|||
|
//
|
|||
|
This->PnpProcessEventInner(Info);
|
|||
|
|
|||
|
//
|
|||
|
// We are being called from the work item and m_WorkItemRunning is > 0, so
|
|||
|
// we cannot be deleted yet.
|
|||
|
//
|
|||
|
ASSERT(Info->SomethingToDo() == FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Now release the lock
|
|||
|
//
|
|||
|
This->m_PnpMachine.m_StateMachineLock.ReleaseLock(
|
|||
|
This->GetDriverGlobals()
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpProcessEventInner(
|
|||
|
__inout FxPostProcessInfo* Info
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This routine runs the state machine. It implements steps 10-15 of the
|
|||
|
algorithm described above.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WDF_DEVICE_PNP_STATE newState;
|
|||
|
CPPNP_STATE_TABLE entry;
|
|||
|
FxPnpEvent event;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Process as many events as we can.
|
|||
|
//
|
|||
|
for ( ; ; ) {
|
|||
|
entry = GetPnpTableEntry(m_Device->GetDevicePnpState());
|
|||
|
|
|||
|
//
|
|||
|
// Get an event from the queue.
|
|||
|
//
|
|||
|
m_PnpMachine.Lock(&oldIrql);
|
|||
|
|
|||
|
if (m_PnpMachine.IsEmpty()) {
|
|||
|
m_PnpMachine.GetFinishedState(Info);
|
|||
|
|
|||
|
if (m_PnpMachine.m_FireAndForget) {
|
|||
|
m_PnpMachine.m_FireAndForget = FALSE;
|
|||
|
Info->m_FireAndForgetIrp = ClearPendingPnpIrp();
|
|||
|
|
|||
|
ASSERT(Info->m_FireAndForgetIrp != NULL);
|
|||
|
}
|
|||
|
|
|||
|
Info->m_SetRemovedEvent = m_SetDeviceRemoveProcessed;
|
|||
|
m_SetDeviceRemoveProcessed = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// The queue is empty.
|
|||
|
//
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
event = m_PnpMachine.m_Queue[m_PnpMachine.GetHead()];
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we need to determine whether we can process this
|
|||
|
// event.
|
|||
|
//
|
|||
|
if (event & PnpPriorityEventsMask) {
|
|||
|
//
|
|||
|
// These are always possible to handle.
|
|||
|
//
|
|||
|
DO_NOTHING();
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Check to see if this state can handle new events.
|
|||
|
//
|
|||
|
if (entry->StateInfo.Bits.QueueOpen == FALSE) {
|
|||
|
//
|
|||
|
// This state can't handle new events.
|
|||
|
//
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
m_PnpMachine.IncrementHead();
|
|||
|
m_PnpMachine.Unlock(oldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Find the entry in the PnP state table that corresponds
|
|||
|
// to this event.
|
|||
|
//
|
|||
|
newState = WdfDevStatePnpNull;
|
|||
|
|
|||
|
if (entry->FirstTargetState.PnpEvent == event) {
|
|||
|
newState = entry->FirstTargetState.TargetState;
|
|||
|
|
|||
|
DO_EVENT_TRAP(&entry->FirstTargetState);
|
|||
|
}
|
|||
|
else if (entry->OtherTargetStates != NULL) {
|
|||
|
ULONG i = 0;
|
|||
|
|
|||
|
for (i = 0;
|
|||
|
entry->OtherTargetStates[i].PnpEvent != PnpEventNull;
|
|||
|
i++) {
|
|||
|
if (entry->OtherTargetStates[i].PnpEvent == event) {
|
|||
|
newState = entry->OtherTargetStates[i].TargetState;
|
|||
|
DO_EVENT_TRAP(&entry->OtherTargetStates[i]);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (newState == WdfDevStatePnpNull) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|||
|
"WDFDEVICE 0x%p !devobj 0x%p current pnp state "
|
|||
|
"%!WDF_DEVICE_PNP_STATE! dropping event %!FxPnpEvent!",
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Device->GetDeviceObject(),
|
|||
|
m_Device->GetDevicePnpState(),
|
|||
|
event);
|
|||
|
|
|||
|
if ((entry->StateInfo.Bits.KnownDroppedEvents & event) == 0) {
|
|||
|
COVERAGE_TRAP();
|
|||
|
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|||
|
"WDFDEVICE 0x%p !devobj %p current state "
|
|||
|
"%!WDF_DEVICE_PNP_STATE!, policy event %!FxPnpEvent! is not"
|
|||
|
" a known dropped event, known dropped events are "
|
|||
|
"%!FxPnpEvent!",
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Device->GetDeviceObject(),
|
|||
|
m_Device->GetDevicePnpState(),
|
|||
|
event,
|
|||
|
entry->StateInfo.Bits.KnownDroppedEvents);
|
|||
|
|
|||
|
// DIAG: add diag code here
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This state doesn't respond to the Event. Make sure we do not
|
|||
|
// drop an event which pends a pnp irp on the floor though.
|
|||
|
//
|
|||
|
if (event & PnpEventPending) {
|
|||
|
//
|
|||
|
// In the case of a previous power up/down failure, the following
|
|||
|
// can happen
|
|||
|
// 1 invalidate device relations
|
|||
|
// 2 failure event is posted to pnp state machine
|
|||
|
// 3 process power failure event first, but while processing,
|
|||
|
// query device state is completed, and failed /removed is reported
|
|||
|
// 4 surprise remove comes, the irp is queued, the event is queued
|
|||
|
// 5 processing of power failure event continues, gets to
|
|||
|
// Failed and completes the s.r irp
|
|||
|
// 6 the surprise remove event is processed (the current value
|
|||
|
// of the local var event), but since we are already in the
|
|||
|
// Failed state, we ignore this event and end up
|
|||
|
// here.
|
|||
|
//
|
|||
|
// This means that if we are processing surprise remove, we cannot
|
|||
|
// 100% expect that an irp has been pended.
|
|||
|
//
|
|||
|
PnpFinishProcessingIrp(
|
|||
|
(event == PnpEventSurpriseRemove) ? FALSE : TRUE);
|
|||
|
}
|
|||
|
else {
|
|||
|
DO_NOTHING();
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Now enter the new state.
|
|||
|
//
|
|||
|
PnpEnterNewState(newState);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpEnterNewState(
|
|||
|
__in WDF_DEVICE_PNP_STATE State
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function looks up the handler for a state and
|
|||
|
then calls it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Event - Current PnP event
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
CPPNP_STATE_TABLE entry;
|
|||
|
WDF_DEVICE_PNP_STATE currentState, newState;
|
|||
|
WDF_DEVICE_PNP_NOTIFICATION_DATA data;
|
|||
|
|
|||
|
currentState = m_Device->GetDevicePnpState();
|
|||
|
newState = State;
|
|||
|
|
|||
|
while (newState != WdfDevStatePnpNull) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNPPOWERSTATES,
|
|||
|
"WDFDEVICE 0x%p !devobj 0x%p entering PnP State "
|
|||
|
"%!WDF_DEVICE_PNP_STATE! from %!WDF_DEVICE_PNP_STATE!",
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Device->GetDeviceObject(),
|
|||
|
newState,
|
|||
|
currentState);
|
|||
|
|
|||
|
if (m_PnpStateCallbacks != NULL) {
|
|||
|
//
|
|||
|
// Callback for leaving the old state
|
|||
|
//
|
|||
|
RtlZeroMemory(&data, sizeof(data));
|
|||
|
|
|||
|
data.Type = StateNotificationLeaveState;
|
|||
|
data.Data.LeaveState.CurrentState = currentState;
|
|||
|
data.Data.LeaveState.NewState = newState;
|
|||
|
|
|||
|
m_PnpStateCallbacks->Invoke(currentState,
|
|||
|
StateNotificationLeaveState,
|
|||
|
m_Device->GetHandle(),
|
|||
|
&data);
|
|||
|
}
|
|||
|
|
|||
|
m_PnpMachine.m_States.History[m_PnpMachine.IncrementHistoryIndex()] =
|
|||
|
(USHORT) newState;
|
|||
|
|
|||
|
if (m_PnpStateCallbacks != NULL) {
|
|||
|
//
|
|||
|
// Callback for entering the new state
|
|||
|
//
|
|||
|
RtlZeroMemory(&data, sizeof(data));
|
|||
|
|
|||
|
data.Type = StateNotificationEnterState;
|
|||
|
data.Data.EnterState.CurrentState = currentState;
|
|||
|
data.Data.EnterState.NewState = newState;
|
|||
|
|
|||
|
m_PnpStateCallbacks->Invoke(newState,
|
|||
|
StateNotificationEnterState,
|
|||
|
m_Device->GetHandle(),
|
|||
|
&data);
|
|||
|
}
|
|||
|
|
|||
|
m_Device->SetDevicePnpState(newState);
|
|||
|
currentState = newState;
|
|||
|
|
|||
|
entry = GetPnpTableEntry(currentState);
|
|||
|
|
|||
|
//
|
|||
|
// Call the state handler if one is present and record our new state
|
|||
|
//
|
|||
|
if (entry->StateFunc != NULL) {
|
|||
|
newState = entry->StateFunc(this);
|
|||
|
|
|||
|
//
|
|||
|
// Validate the return value if FX_STATE_MACHINE_VERIFY is enabled
|
|||
|
//
|
|||
|
VALIDATE_PNP_STATE(currentState, newState);
|
|||
|
}
|
|||
|
else {
|
|||
|
newState = WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
if (m_PnpStateCallbacks != NULL) {
|
|||
|
//
|
|||
|
// Callback for post processing the new state
|
|||
|
//
|
|||
|
RtlZeroMemory(&data, sizeof(data));
|
|||
|
|
|||
|
data.Type = StateNotificationPostProcessState;
|
|||
|
data.Data.PostProcessState.CurrentState = currentState;
|
|||
|
|
|||
|
m_PnpStateCallbacks->Invoke(currentState,
|
|||
|
StateNotificationPostProcessState,
|
|||
|
m_Device->GetHandle(),
|
|||
|
&data);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventCheckForDevicePresence(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Check For Device
|
|||
|
Presence state. This is a state that is specific
|
|||
|
to PDOs, so this function should be overloaded by
|
|||
|
the PDO class and never called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return This->PnpEventCheckForDevicePresenceOverload();
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventEjectHardware(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Eject Hardware state.
|
|||
|
This is a state that is specific to PDOs, so this
|
|||
|
function should be overloaded by the PDO class
|
|||
|
and never called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return This->PnpEventEjectHardwareOverload();
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventInitStarting(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device is recieving a start for the first time. The start is on the way
|
|||
|
down the stack.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
|
|||
|
//
|
|||
|
// Start was sent asynchronously down the stack, the irp's completion
|
|||
|
// routine will move the state machine to the new state.
|
|||
|
//
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpHardwareAvailable;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventInitSurpriseRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This transition should only occur for a PDO.
|
|||
|
|
|||
|
The device was initialized, but then it's parent bus was surprise removed.
|
|||
|
Complete the surprise remove and wait for the remove.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpInit
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->PnpFinishProcessingIrp(TRUE);
|
|||
|
|
|||
|
return WdfDevStatePnpInit;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventHardwareAvailable(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Hardware Available state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN matched;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
matched = FALSE;
|
|||
|
|
|||
|
This->QueryForReenumerationInterface();
|
|||
|
|
|||
|
status = This->CreatePowerThreadIfNeeded();
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
status = This->PnpPrepareHardware(&matched);
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
if (matched == FALSE) {
|
|||
|
//
|
|||
|
// NOTE: consider going to WdfDevStatePnpFailed instead of yet
|
|||
|
// another failed state out of start device handling.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// We can handle remove out of the init state, revert back to that
|
|||
|
// state.
|
|||
|
//
|
|||
|
return WdfDevStatePnpFailedInit;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// EvtDevicePrepareHardware is what failed, goto a state where we
|
|||
|
// undo that call.
|
|||
|
//
|
|||
|
return WdfDevStatePnpFailedOwnHardware;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We only query for the capabilities for the power policy owner because
|
|||
|
// we use the capabilities to determine the right Dx state when we want to
|
|||
|
// wake from S0 or Sx. Since only the power policy owner can enable wake
|
|||
|
// behavior, only the owner needs to query for the information.
|
|||
|
//
|
|||
|
// ALSO, if we are a filter, there are issues in stacks wrt pnp reentrancy.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
if (This->IsPowerPolicyOwner()) {
|
|||
|
//
|
|||
|
// Query the stack for capabilities before telling the stack hw is
|
|||
|
// available
|
|||
|
//
|
|||
|
status = This->QueryForCapabilities();
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"could not query caps for stack, %!STATUS!", status);
|
|||
|
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
return WdfDevStatePnpFailedOwnHardware;
|
|||
|
}
|
|||
|
|
|||
|
This->m_CapsQueried = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
This->PnpPowerPolicyStart();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventEnableInterfaces(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device has powered up and fully started, now enable the device
|
|||
|
interfaces and WMI. We wait until the last possible moment because on
|
|||
|
win2k WMI registration is not synchronized with the completion of start
|
|||
|
device, so if we register WMI early in start device processing, we could get
|
|||
|
wmi requests while we are initializing, which is a race condition we want
|
|||
|
to eliminate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
status = This->PnpEnableInterfacesAndRegisterWmi();
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Upon failure, PnpEnableInterfacesAndRegisterWmi already marked the
|
|||
|
// irp as failed and recorded an internal error.
|
|||
|
//
|
|||
|
// FailedPowerDown will gracefully tear down the stack and bring it out
|
|||
|
// of D0.
|
|||
|
//
|
|||
|
return WdfDevStatePnpFailedPowerDown;
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventHardwareAvailablePowerPolicyFailed(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Our previous state called PowerPolicyStart or PowerPolicyStopRemove and the
|
|||
|
power state machine could not perform the requested action. We still have
|
|||
|
a start irp pending, so set its status and then proceed down the start failure
|
|||
|
path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machien
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpFailedOwnHardware
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->SetPendingPnpIrpStatus(STATUS_DEVICE_POWER_FAILURE);
|
|||
|
|
|||
|
return WdfDevStatePnpFailedOwnHardware;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryRemoveAskDriver(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Query Remove Ask Driver
|
|||
|
state. It's job is to invoke EvtDeviceQueryRemove and then complete the
|
|||
|
QueryRemove IRP if needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WDF_DEVICE_PNP_STATE state;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// First, call the driver. If it succeeds, look at whether
|
|||
|
// it managed to stop its stuff.
|
|||
|
//
|
|||
|
status = This->m_DeviceQueryRemove.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// The driver has stopped all of its self managed io. Proceed to
|
|||
|
// stop everything before passing the request down the stack.
|
|||
|
//
|
|||
|
state = WdfDevStatePnpQueryRemoveEnsureDeviceAwake;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The callback didn't manage to stop. Go back to the Started state
|
|||
|
// where we will complete the pended pnp irp
|
|||
|
//
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceQueryRemove failed, %!STATUS!", status);
|
|||
|
|
|||
|
if (status == STATUS_NOT_SUPPORTED) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceQueryRemove returned an invalid status "
|
|||
|
"STATUS_NOT_SUPPORTED");
|
|||
|
|
|||
|
if (This->GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|||
|
FxVerifierDbgBreakPoint(This->GetDriverGlobals());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
state = WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryRemoveEnsureDeviceAwake(
|
|||
|
__inout FxPkgPnp *This
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
This function brings the device to a working state if it is idle. If the
|
|||
|
device is already in working state, it ensures that it does not idle out.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
WDF_DEVICE_PNP_STATE state;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the device is powered on before we send the query remove
|
|||
|
// on its way. If we do this after we send the query remove, we could race
|
|||
|
// with the remove which removes the reference and we want the device
|
|||
|
// powered on when processing remove.
|
|||
|
//
|
|||
|
status = This->PnpPowerReferenceDuringQueryPnp();
|
|||
|
if (STATUS_PENDING == status) {
|
|||
|
//
|
|||
|
// Device is transitioning to D0. The Pnp state machine will wait in
|
|||
|
// the current state until the transition is complete
|
|||
|
//
|
|||
|
state = WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
else if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Already in D0
|
|||
|
//
|
|||
|
state = WdfDevStatePnpQueryRemovePending;
|
|||
|
}
|
|||
|
else {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"StopIdle on WDFDEVICE %p failed, %!STATUS!, failing query remove",
|
|||
|
This->m_Device->GetHandle(), status);
|
|||
|
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
//
|
|||
|
// The Started state will complete the irp when it sees the failure
|
|||
|
// status set on the irp.
|
|||
|
//
|
|||
|
state = WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryRemovePending(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device has fully stopped. Let go of the query remove irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine for this device
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
FxIrp irp;
|
|||
|
|
|||
|
irp.SetIrp(This->ClearPendingPnpIrp());
|
|||
|
(void) This->FireAndForgetIrp(&irp);
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryRemoveStaticCheck(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Query Remove Static Check
|
|||
|
state. It's job is to determine whether this device,
|
|||
|
in general, can stop. If it can, then we proceed on
|
|||
|
to Query Remove Ask Driver. If not, then we go to
|
|||
|
back to Started.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN completeQuery;
|
|||
|
|
|||
|
completeQuery = TRUE;
|
|||
|
|
|||
|
if (This->m_DeviceStopCount != 0) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"Failing QueryRemoveDevice because the driver "
|
|||
|
"has indicated that it cannot be stopped, count %d",
|
|||
|
This->m_DeviceStopCount);
|
|||
|
|
|||
|
status = STATUS_INVALID_DEVICE_STATE;
|
|||
|
}
|
|||
|
else if (This->IsInSpecialUse()) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"Failing QueryRemoveDevice due to open special file counts "
|
|||
|
"(paging %d, hiber %d, dump %d, boot %d)",
|
|||
|
This->GetUsageCount(WdfSpecialFilePaging),
|
|||
|
This->GetUsageCount(WdfSpecialFileHibernation),
|
|||
|
This->GetUsageCount(WdfSpecialFileDump),
|
|||
|
This->GetUsageCount(WdfSpecialFileBoot));
|
|||
|
|
|||
|
status = STATUS_DEVICE_NOT_READY;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Go on to next state in the "remove" progression.
|
|||
|
//
|
|||
|
completeQuery = FALSE;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (completeQuery) {
|
|||
|
//
|
|||
|
// Store the status which Started will complete
|
|||
|
//
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
//
|
|||
|
// Revert to started
|
|||
|
//
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Wait for the other state machines to stop
|
|||
|
//
|
|||
|
return WdfDevStatePnpQueryRemoveAskDriver;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueriedRemoving(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device was query removed and is now in the removed state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// It is important to stop power policy before releasing the reference.
|
|||
|
// If the reference was released first, we could get into a situation where
|
|||
|
// we immediately go idle and then we must send a D0 irp when in the remove.
|
|||
|
// If there are devices on top of this device and we send a D0 irp during
|
|||
|
// remove processing, the upper devices will be sent an irp after getting a
|
|||
|
// pnp remove (and either crash or fail the power irp upon receiving it).
|
|||
|
//
|
|||
|
This->PnpPowerPolicyStop();
|
|||
|
This->PnpPowerDereferenceSelf();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryStopAskDriver(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Query Stop Ask Driver
|
|||
|
state. It's job is to invoke the EvtDeviceQueryStop
|
|||
|
callback and then complete the QueryStop IRP if needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WDF_DEVICE_PNP_STATE state;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// First, call the driver. If it succeeds, look at whether
|
|||
|
// it managed to stop its stuff.
|
|||
|
//
|
|||
|
status = This->m_DeviceQueryStop.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Tell the other state machines to stop
|
|||
|
//
|
|||
|
state = WdfDevStatePnpQueryStopEnsureDeviceAwake;
|
|||
|
}
|
|||
|
else {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceQueryStop failed, %!STATUS!", status);
|
|||
|
|
|||
|
if (status == STATUS_NOT_SUPPORTED) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceQueryStop returned an invalid status "
|
|||
|
"STATUS_NOT_SUPPORTED");
|
|||
|
|
|||
|
if (This->GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|||
|
FxVerifierDbgBreakPoint(This->GetDriverGlobals());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The callback didn't manage to stop. Go back to the Started state.
|
|||
|
//
|
|||
|
state = WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryStopEnsureDeviceAwake(
|
|||
|
__inout FxPkgPnp *This
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
This function brings the device to a working state if it is idle. If the
|
|||
|
device is already in working state, it ensures that it does not idle out.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
WDF_DEVICE_PNP_STATE state;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the device is powered on before we send the query stop
|
|||
|
// on its way. If we do this after we send the query stop, we could race
|
|||
|
// with the stop and we want the device powered on when processing stop.
|
|||
|
//
|
|||
|
status = This->PnpPowerReferenceDuringQueryPnp();
|
|||
|
if (STATUS_PENDING == status) {
|
|||
|
//
|
|||
|
// Device is transitioning to D0. The Pnp state machine will wait in
|
|||
|
// the current state until the transition is complete
|
|||
|
//
|
|||
|
state = WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
else if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Already in D0
|
|||
|
//
|
|||
|
state = WdfDevStatePnpQueryStopPending;
|
|||
|
}
|
|||
|
else {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"StopIdle on WDFDEVICE %p failed, %!STATUS!, failing query stop",
|
|||
|
This->m_Device->GetHandle(), status);
|
|||
|
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
//
|
|||
|
// The Started state will complete the irp when it sees the failure
|
|||
|
// status set on the irp.
|
|||
|
//
|
|||
|
state = WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryStopPending(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Everything in the device has stopped due to the stop device irp. Complete
|
|||
|
it now
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
FxIrp irp;
|
|||
|
|
|||
|
irp.SetIrp(This->ClearPendingPnpIrp());
|
|||
|
(void) This->FireAndForgetIrp(&irp);
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryStopStaticCheck(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Query Stop Static Check state. It's job is to
|
|||
|
determine whether this device, in general, can stop. If it can, then we
|
|||
|
proceed on to Query Stop Ask Driver. Otherwise we will return to the
|
|||
|
Started state. If the driver has set that the state machine should ignore
|
|||
|
query stop/remove, the query will succeed, otherwise it will fail
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN completeQuery = TRUE;
|
|||
|
|
|||
|
if (This->m_DeviceStopCount != 0) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"Failing QueryStopDevice because the driver "
|
|||
|
"has indicated that it cannot be stopped, count %d",
|
|||
|
This->m_DeviceStopCount);
|
|||
|
|
|||
|
status = STATUS_INVALID_DEVICE_STATE;
|
|||
|
}
|
|||
|
else if (This->IsInSpecialUse()) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"Failing QueryStopDevice due to open special file counts (paging %d,"
|
|||
|
" hiber %d, dump %d, boot %d)",
|
|||
|
This->GetUsageCount(WdfSpecialFilePaging),
|
|||
|
This->GetUsageCount(WdfSpecialFileHibernation),
|
|||
|
This->GetUsageCount(WdfSpecialFileDump),
|
|||
|
This->GetUsageCount(WdfSpecialFileBoot));
|
|||
|
|
|||
|
status = STATUS_DEVICE_NOT_READY;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Go on to next state in the "stop" progression.
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
completeQuery = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (completeQuery) {
|
|||
|
//
|
|||
|
// Set the state which started will complete the request with
|
|||
|
//
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
//
|
|||
|
// Revert to started
|
|||
|
//
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Go ask power what the self managed io state is
|
|||
|
//
|
|||
|
return WdfDevStatePnpQueryStopAskDriver;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpEventRemovedCommonCode(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Removed state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Purge non power managed queues now
|
|||
|
//
|
|||
|
m_Device->m_PkgIo->StopProcessingForPower(
|
|||
|
FxIoStopProcessingForPowerPurgeNonManaged
|
|||
|
);
|
|||
|
|
|||
|
if (m_SelfManagedIoMachine != NULL) {
|
|||
|
m_SelfManagedIoMachine->Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup WMI *after* EvtDeviceSelfManagedIoCleanup b/c we want to cleanup
|
|||
|
// after a well known and documented time. The WMI docs state that you can
|
|||
|
// register providers and instances all the way through
|
|||
|
// EvtDeviceSelfManagedIoCleanup, so we mark WMI as cleaned up after that
|
|||
|
// call.
|
|||
|
//
|
|||
|
m_Device->WmiPkgCleanup();
|
|||
|
|
|||
|
//
|
|||
|
// Mark the device as removed.
|
|||
|
//
|
|||
|
m_PnpStateAndCaps.Value &= ~FxPnpStateRemovedMask;
|
|||
|
m_PnpStateAndCaps.Value |= FxPnpStateRemovedTrue;
|
|||
|
|
|||
|
//
|
|||
|
// Now call the driver and tell it to cleanup all its software state.
|
|||
|
//
|
|||
|
// We do the dispose early here before deleting the object
|
|||
|
// since the PNP remove event is the main trigger for
|
|||
|
// the Dispose chain in the framework.
|
|||
|
//
|
|||
|
// (Almost everything in the driver is rooted on the device object)
|
|||
|
//
|
|||
|
|
|||
|
m_Device->EarlyDispose();
|
|||
|
|
|||
|
//
|
|||
|
// All the children are in the disposed state, destroy them all. m_Device
|
|||
|
// is not destroyed in this call.
|
|||
|
//
|
|||
|
|
|||
|
m_Device->DestroyChildren();
|
|||
|
|
|||
|
//
|
|||
|
// Wait for all children to drain out and cleanup.
|
|||
|
//
|
|||
|
|
|||
|
m_Device->m_DisposeList->WaitForEmpty();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueryCanceled(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device was in the queried state (remove or stop) and the query was
|
|||
|
canceled. Remove the power reference taken and return to the started state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpStarted
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->PnpPowerDereferenceSelf();
|
|||
|
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Removed state. It tears down any remaining
|
|||
|
children and the moves into a role (FDO/PDO) specific state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Remove any child PDOs which may still be lingering around. We do
|
|||
|
// the cleanup here so that we do it only once for the PDO which is being
|
|||
|
// removed (but may stick around) b/c it was not reported as missing.
|
|||
|
//
|
|||
|
//
|
|||
|
// Iterate over all of the reported children
|
|||
|
//
|
|||
|
This->ChildListNotifyRemove(&This->m_PendingChildCount);
|
|||
|
|
|||
|
//
|
|||
|
// Decrement our bias from when the device was (re)started. If all of the
|
|||
|
// children removed themselves synchronously, we just move to the cleanup
|
|||
|
// state, otherwise wait for all the children to fully process the remove
|
|||
|
// before remove the parent so that ordering of removal between parent and
|
|||
|
// child is guaranteed (where the order is that all children are cleaned
|
|||
|
// up before the parent is cleaned up).
|
|||
|
//
|
|||
|
if (InterlockedDecrement(&This->m_PendingChildCount) > 0) {
|
|||
|
return WdfDevStatePnpRemovedWaitForChildren;
|
|||
|
}
|
|||
|
else {
|
|||
|
return WdfDevStatePnpRemovedChildrenRemoved;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventPdoRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the PDO Removed state. This function is called
|
|||
|
when the PDO is actually reported missing to the OS or the FDO is removed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return This->PnpEventPdoRemovedOverload();
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRemovedPdoWait(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Indicates to the remove path that remove processing is done and we will
|
|||
|
wait for additional PNP events to arrive at this state machine
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - Instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (This->m_DeviceRemoveProcessed != NULL) {
|
|||
|
This->m_SetDeviceRemoveProcessed = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRemovedPdoSurpriseRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
PDO has been removed (could have been disabled in user mode) and is now
|
|||
|
surprise removed by the underlying bus.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpRemovedPdoWait
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Invoke EvtDeviceSurpriseRemove
|
|||
|
//
|
|||
|
This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
//
|
|||
|
// Call the overloaded surprise remove handler since
|
|||
|
// PnpEventSurpriseRemovePendingOverload will not get called
|
|||
|
//
|
|||
|
This->PnpEventSurpriseRemovePendingOverload();
|
|||
|
|
|||
|
//
|
|||
|
// The surprise remove irp was pended, complete it now
|
|||
|
//
|
|||
|
This->PnpFinishProcessingIrp();
|
|||
|
|
|||
|
return WdfDevStatePnpRemovedPdoWait;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpCleanupForRemove(
|
|||
|
__in BOOLEAN GracefulRemove
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This is a common worker function between surprise remove and the graceful
|
|||
|
remove path to do common cleanup. This involves deregistering from WMI,
|
|||
|
device interfaces, symbolic links and stopping power managed i/o.
|
|||
|
|
|||
|
Arguments:
|
|||
|
GracefulRemove - if TRUE, we are in the graceful remove path, otherwise we
|
|||
|
are in the surprise remove path.
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Disable WMI.
|
|||
|
//
|
|||
|
m_Device->WmiPkgDeregister();
|
|||
|
|
|||
|
//
|
|||
|
// Disable any device interfaces.
|
|||
|
//
|
|||
|
PnpDisableInterfaces();
|
|||
|
|
|||
|
DeleteSymbolicLinkOverload(GracefulRemove);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Flush/purge top-edge queues
|
|||
|
m_Device->m_PkgIo->StopProcessingForPower(
|
|||
|
FxIoStopProcessingForPowerPurgeManaged
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Invoke EvtDeviceSelfManagedIoFlush
|
|||
|
//
|
|||
|
if (m_SelfManagedIoMachine != NULL) {
|
|||
|
m_SelfManagedIoMachine->Flush();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Tell all the resource objects that they no longer own anything.
|
|||
|
//
|
|||
|
NotifyResourceobjectsToReleaseResources();
|
|||
|
|
|||
|
//
|
|||
|
// Flush persistent state to permanent storage. We do this in the failed
|
|||
|
// state for the surprise removed case. By storing the state when we are
|
|||
|
// surprise removed, if the stack is reenumerated, it will pick up the saved
|
|||
|
// state that was just committed. If the state was saved during remove
|
|||
|
// device it can be too late because the new instance of the same device
|
|||
|
// could already be up and running and not pick up the saved state.
|
|||
|
//
|
|||
|
// It is important to save the state before completing the (potentially)
|
|||
|
// pended pnp irp. Completing the pnp irp will allow a new instance of the
|
|||
|
// device to be enumerated and we want to save state before that happens.
|
|||
|
//
|
|||
|
SaveState(FALSE);
|
|||
|
|
|||
|
if (m_SharedPower.m_WaitWakeOwner) {
|
|||
|
//
|
|||
|
// Don't care about the return code, just blindly try to complete the
|
|||
|
// wake request. The function can handle the case where there is no
|
|||
|
// irp to complete.
|
|||
|
//
|
|||
|
(void) PowerIndicateWaitWakeStatus(STATUS_NO_SUCH_DEVICE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRemovingDisableInterfaces(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Removing Disable
|
|||
|
Interfaces state. It disables any device interfaces
|
|||
|
and then tells the power policy state machine to prepare for device removal.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Surprise remove path releases hardware first then disables the interfaces,
|
|||
|
// so do the same in the graceful remove path.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Call the driver and tell it to unmap resources.
|
|||
|
//
|
|||
|
status = This->PnpReleaseHardware();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// The driver failed to unmap resources. Presumably this means that
|
|||
|
// there are now some leaked PTEs. Just log the failure.
|
|||
|
//
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceReleaseHardware %p failed, %!STATUS!",
|
|||
|
This->m_Device->GetHandle(), status);
|
|||
|
}
|
|||
|
|
|||
|
This->PnpCleanupForRemove(TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Tell the power policy state machine to prepare for device removal
|
|||
|
//
|
|||
|
This->PnpPowerPolicyRemove();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStarted(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Completes the pending request or sends it on its way.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine for the device
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
This->m_AchievedStart = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Log Telemetry event for the FDO
|
|||
|
//
|
|||
|
if (This->m_Device->IsPdo() == FALSE) {
|
|||
|
This->m_Device->FxLogDeviceStartTelemetryEvent();
|
|||
|
}
|
|||
|
|
|||
|
This->PnpFinishProcessingIrp();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStartedCancelStop(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Cancel stop received from the started state. Just return to the started
|
|||
|
state where we will handle the pended irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - Instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpStarted
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(This);
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStartedCancelRemove(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Cancel remove received from the started state. Just return to the started
|
|||
|
state where we will handle the pended irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - Instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpStarted
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(This);
|
|||
|
|
|||
|
return WdfDevStatePnpStarted;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStartedRemoving(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Remove directly from started. Power down the other state machines.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->PnpPowerPolicyStop();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRestarting(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Cancelling Stop state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Send an event to the Power Policy State Machine
|
|||
|
// telling it to "Start."
|
|||
|
//
|
|||
|
This->PnpPowerPolicyStart();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStartingFromStopped(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Restarting From Stopped state. It's job
|
|||
|
is to map new resources and then proceed to the Restarting state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN matched;
|
|||
|
|
|||
|
status = This->PnpPrepareHardware(&matched);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// We can handle remove out of the init state, revert back to that state
|
|||
|
//
|
|||
|
if (matched == FALSE) {
|
|||
|
//
|
|||
|
// Wait for the remove irp to come in
|
|||
|
//
|
|||
|
COVERAGE_TRAP();
|
|||
|
return WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// EvtDevicePrepareHardware is what failed, goto a state where we
|
|||
|
// undo that call.
|
|||
|
//
|
|||
|
COVERAGE_TRAP();
|
|||
|
return WdfDevStatePnpFailedOwnHardware;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpRestarting;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStopped(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This function implements the Stopped state. It's job is to invoke
|
|||
|
EvtDeviceReleaseHardware.
|
|||
|
|
|||
|
Arguments:
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WDF_DEVICE_PNP_STATE state;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
status = This->PnpReleaseHardware();
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// Tell all the resource objects that they no longer own anything.
|
|||
|
//
|
|||
|
This->NotifyResourceobjectsToReleaseResources();
|
|||
|
|
|||
|
state = WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
else {
|
|||
|
DoTraceLevelMessage(This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceReleaseHardware failed - %!STATUS!",
|
|||
|
status);
|
|||
|
COVERAGE_TRAP();
|
|||
|
|
|||
|
This->SetInternalFailure();
|
|||
|
state = WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the irp on its merry way. This irp (stop device) cannot be failed
|
|||
|
// and must always be sent down the stack.
|
|||
|
//
|
|||
|
This->PnpFinishProcessingIrp();
|
|||
|
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStoppedWaitForStartCompletion(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The start irp is coming down from the stopped state. Send it down the stack
|
|||
|
and transition to the new state if needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
|
|||
|
//
|
|||
|
// The start irp's completion routine will move the state machine into
|
|||
|
// the new state.
|
|||
|
//
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpStartingFromStopped;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventStartedStopping(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Received a stop irp. Stop the power policy machine and then wait for it to
|
|||
|
complete.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// It is important to stop power policy before releasing the reference.
|
|||
|
// If the reference was released first, we could get into a situation where
|
|||
|
// we immediately go idle and then we must send a D0 irp when in the remove.
|
|||
|
// If there are devices on top of this device and we send a D0 irp during
|
|||
|
// remove processing, the upper devices will be sent an irp after getting a
|
|||
|
// pnp remove (and either crash or fail the power irp upon receiving it).
|
|||
|
//
|
|||
|
This->PnpPowerPolicyStop();
|
|||
|
This->PnpPowerDereferenceSelf();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventSurpriseRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
We got IRP_MN_SURPRISE_REMOVE_DEVICE while the system was pretty far down
|
|||
|
the removal path, or never started. Call EvtDeviceSurpriseRemoval, call the
|
|||
|
surprise remove virtual and drop into the Failed path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Invoke EvtDeviceSurpriseRemove
|
|||
|
//
|
|||
|
This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
//
|
|||
|
// Notify the virtual override of the surprise remove.
|
|||
|
//
|
|||
|
This->PnpEventSurpriseRemovePendingOverload();
|
|||
|
|
|||
|
return WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventInitQueryRemove(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Query remove from the init state. Complete the pended request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of th state machine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
FxIrp irp(This->ClearPendingPnpIrp());
|
|||
|
|
|||
|
irp.SetStatus(STATUS_SUCCESS);
|
|||
|
This->FireAndForgetIrp(&irp);
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventInitQueryRemoveCanceled(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Handle a query remove canceled from the init state. Complete the pended
|
|||
|
request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpInit
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
FxIrp irp(This->ClearPendingPnpIrp());
|
|||
|
|
|||
|
This->FireAndForgetIrp(&irp);
|
|||
|
|
|||
|
return WdfDevStatePnpInit;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFdoRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
FDO is being removed, hand off to the derived pnp package
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return This->PnpEventFdoRemovedOverload();
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventQueriedSurpriseRemove(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device was in a queried (either stop or cancel) state and was surprise
|
|||
|
removed from it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state, WdfDevStatePnpSurpriseRemoveIoStarted
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
COVERAGE_TRAP();
|
|||
|
|
|||
|
This->PnpPowerDereferenceSelf();
|
|||
|
|
|||
|
return WdfDevStatePnpSurpriseRemoveIoStarted;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventSurpriseRemoveIoStarted(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
We got IRP_MN_SURPRISE_REMOVE_DEVICE while the system was more or less
|
|||
|
running. Start down the Surprise Remove/Device Failed path. This state
|
|||
|
calls EvtDeviceSurpriseRemoval, calls the virtual surprise remove overload,
|
|||
|
and then drops into the Failed path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Invoke EvtDeviceSurpriseRemove
|
|||
|
//
|
|||
|
|
|||
|
This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
//
|
|||
|
// Notify the virtual override of the surprise remove.
|
|||
|
//
|
|||
|
This->PnpEventSurpriseRemovePendingOverload();
|
|||
|
|
|||
|
return WdfDevStatePnpFailedIoStarting;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedPowerDown(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device was in the started state and failed power down or could not
|
|||
|
enable its interfaces. Gracefully power down the device and tear down
|
|||
|
the stack.
|
|||
|
|
|||
|
The difference between this routine and PnpEventFailedIoStarting is that
|
|||
|
FailedIoStarting sends a surprise remove to the power state machine. After
|
|||
|
surprise remove has been sent to the power state machine, it will not attempt
|
|||
|
to put the device into Dx because it assumes the device is no longer present.
|
|||
|
In this error case, we still want the device to be powered down, so we send
|
|||
|
a normal stop remove.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Normal stop so that the power state machine will go through the power off
|
|||
|
// path and not skip directly to off like it would if we sent it a surprise
|
|||
|
// remove notification.
|
|||
|
//
|
|||
|
This->PnpPowerPolicyStop();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedIoStarting(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device failed (or was yanked out of the machine) while it was more
|
|||
|
or less running. Tell the driver to stop self-managed I/O and drop into
|
|||
|
the next state on the failure path.
|
|||
|
|
|||
|
Arguments: j
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->PnpPowerPolicySurpriseRemove();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedOwnHardware(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device failed (or was yanked out of the machine) while it owned access
|
|||
|
to the hardware. Tell the driver to release resources and drop into
|
|||
|
the next state on the failure path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Invoke EvtDeviceReleaseHardware
|
|||
|
//
|
|||
|
(void) This->PnpReleaseHardware();
|
|||
|
|
|||
|
return WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailed(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device failed (or was yanked out of the machine). Disable interfaces,
|
|||
|
flush queues and tell the driver to clean up random stuff.
|
|||
|
Also ask the power policy state machine to prepare for device removal.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
This->PnpCleanupForRemove(FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Tell the power policy state machine to prepare for device removal
|
|||
|
//
|
|||
|
This->PnpPowerPolicyRemove();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedPowerPolicyRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The power policy state machine has prepared for device removal. Invalidate
|
|||
|
the device state and wait for IRP_MN_REMOVE_DEVICE.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpFailedWaitForRemove
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Finish processing any pended PnP IRP. Since we can reach this state from
|
|||
|
// states where a pnp irp was *not* pended, we do not require a pnp irp to
|
|||
|
// have been pended when trying to complete it.
|
|||
|
//
|
|||
|
This->PnpFinishProcessingIrp(FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Request reenumeration if the client driver asked for it or if there was
|
|||
|
// an internal failure *and* if the client driver didn't specify failure...
|
|||
|
// AND if we have not yet exceeded our restart count within a period of time.
|
|||
|
//
|
|||
|
if ((This->m_FailedAction == WdfDeviceFailedAttemptRestart ||
|
|||
|
(This->m_FailedAction == WdfDeviceFailedUndefined && This->m_InternalFailure))
|
|||
|
&&
|
|||
|
This->PnpCheckAndIncrementRestartCount()) {
|
|||
|
//
|
|||
|
// No need to invalidate state because we are in a state waiting for
|
|||
|
// a remove device anyways so failure is imminent.
|
|||
|
//
|
|||
|
This->AskParentToRemoveAndReenumerate();
|
|||
|
}
|
|||
|
|
|||
|
if (This->m_FailedAction != WdfDeviceFailedUndefined || This->m_InternalFailure) {
|
|||
|
//
|
|||
|
// If the failure occurred in this device, then tear down the stack if
|
|||
|
// we are in a state in which pnp thinks we are started. If we are
|
|||
|
// already in a stopped state, this invalidation will do no harm.
|
|||
|
//
|
|||
|
MxDeviceObject physicalDeviceObject(
|
|||
|
This->m_Device->GetPhysicalDevice()
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We need to pass FDO as a parameter as UMDF currently doesn't have
|
|||
|
// PDOs and instead needs FDO to invalidate device state.
|
|||
|
//
|
|||
|
physicalDeviceObject.InvalidateDeviceState(
|
|||
|
This->m_Device->GetDeviceObject() //FDO
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpFailedWaitForRemove;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedSurpriseRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The device has failed and then received a surprise remove. This can easily
|
|||
|
happen on return from low power as following:
|
|||
|
|
|||
|
1 device attempts to enter D0, D0Entry fails
|
|||
|
2 pnp state machine proceeds down failure path and stops at FailedWaitForRemove
|
|||
|
3 bus driver finds device missing, reports it as such and a s.r. irp
|
|||
|
arrives
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpFailedWaitForRemove
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Invoke EvtDeviceSurpriseRemove
|
|||
|
//
|
|||
|
This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
|
|||
|
|
|||
|
//
|
|||
|
// Call the overloaded surprise remove handler
|
|||
|
//
|
|||
|
This->PnpEventSurpriseRemovePendingOverload();
|
|||
|
|
|||
|
//
|
|||
|
// The surprise remove irp was pended, complete it now. The irp need not
|
|||
|
// be present. If we failed before the surprise irp was sent and the irp
|
|||
|
// arrived in the middle of processing the failure, we could have completed
|
|||
|
// the s.r. irp in FailedWaitForRemove, which is OK.
|
|||
|
//
|
|||
|
This->PnpFinishProcessingIrp(FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Return back to the failed state where will wait for remove
|
|||
|
//
|
|||
|
return WdfDevStatePnpFailedWaitForRemove;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedStarted(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Device failed (probably somewhere in the start path) and got another start
|
|||
|
request. Fail the start and return to the state where we will wait for a
|
|||
|
remove irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpFailedWaitForRemove
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Complete the pended start irp with error
|
|||
|
//
|
|||
|
This->SetPendingPnpIrpStatus(STATUS_INVALID_DEVICE_STATE);
|
|||
|
This->PnpFinishProcessingIrp();
|
|||
|
|
|||
|
return WdfDevStatePnpFailedWaitForRemove;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFailedInit(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Processing of the start irp's resources failed. Complete the start irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Release the power thread that we may have previously acquired in
|
|||
|
// HardwareAvailable.
|
|||
|
//
|
|||
|
This->ReleasePowerThread();
|
|||
|
|
|||
|
//
|
|||
|
// Deref the reenumeration interface
|
|||
|
//
|
|||
|
This->ReleaseReenumerationInterface();
|
|||
|
|
|||
|
This->PnpFinishProcessingIrp();
|
|||
|
|
|||
|
return WdfDevStatePnpInit;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventPdoInitFailed(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The driver failed EvtDeviceSoftwareInit. Run cleanup and die.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new device pnp state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
COVERAGE_TRAP();
|
|||
|
|
|||
|
|
|||
|
This->m_Device->EarlyDispose();
|
|||
|
|
|||
|
//
|
|||
|
// All the children are in the disposed state, destroy them all. m_Device
|
|||
|
// is not destroyed in this call.
|
|||
|
//
|
|||
|
|
|||
|
This->m_Device->DestroyChildren();
|
|||
|
|
|||
|
return WdfDevStatePnpFinal;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRestart(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
A start to start transition has occurred. Go through the normal stop path
|
|||
|
first and then restart things.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStateNull or new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Stop the power policy machine so that we simulate stopping first
|
|||
|
//
|
|||
|
This->PnpPowerPolicyStop();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRestartReleaseHardware(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Release the hardware resources and send the start irp down the stack
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
status = This->PnpReleaseHardware();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceReleaseHardware failed with %!STATUS!", status);
|
|||
|
|
|||
|
COVERAGE_TRAP();
|
|||
|
|
|||
|
This->SetInternalFailure();
|
|||
|
This->SetPendingPnpIrpStatus(status);
|
|||
|
|
|||
|
return WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
|
|||
|
if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
|
|||
|
//
|
|||
|
// The start irp's completion routine will move the state machine into
|
|||
|
// the new state.
|
|||
|
//
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start happened synchronously. Transition to the new state now.
|
|||
|
//
|
|||
|
return WdfDevStatePnpRestartHardwareAvailable;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRestartHardwareAvailable(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Prepare the hardware and restart the power and power policy state machines
|
|||
|
after a successful start -> start transition where the start irp is coming
|
|||
|
up the stack.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new machine state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN matched;
|
|||
|
|
|||
|
status = This->PnpPrepareHardware(&matched);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
if (matched == FALSE) {
|
|||
|
//
|
|||
|
// Wait for the remove irp to come in
|
|||
|
//
|
|||
|
COVERAGE_TRAP();
|
|||
|
return WdfDevStatePnpFailed;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// EvtDevicePrepareHardware is what failed, goto a state where we
|
|||
|
// undo that call.
|
|||
|
//
|
|||
|
COVERAGE_TRAP();
|
|||
|
return WdfDevStatePnpFailedOwnHardware;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
This->PnpPowerPolicyStart();
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventPdoRestart(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The PDO was in the removed state has received another start irp. Reset state
|
|||
|
and then move into the start sequence.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpHardwareAvailable
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Since this is a PDO and it is being restarted, it could have had an internal
|
|||
|
// failure during its previous start. Since we use m_InternalFailure to set
|
|||
|
// PNP_DEVICE_FAILED when handling IRP_MN_QUERY_PNP_DEVICE_STATE, it should
|
|||
|
// be set to FALSE so we don't immediately fail the device after the start
|
|||
|
// has been succeeded.
|
|||
|
//
|
|||
|
This->m_InternalFailure = FALSE;
|
|||
|
This->m_Failed = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// The PDO is being restarted and could have previous had a power thread
|
|||
|
// running. If so, the reference count goes to zero when removed from a
|
|||
|
// started state. Reset back to 1.
|
|||
|
//
|
|||
|
This->m_PowerThreadInterfaceReferenceCount = 1;
|
|||
|
|
|||
|
//
|
|||
|
// The count is decremented on the initial started->removed transition (and
|
|||
|
// not subsequent removed -> removed transitiosn). On removed -> restarted,
|
|||
|
// we need to set the count back to a bias of 1 so when we process the remove
|
|||
|
// again we can know if there are any pending child (of this PDO) removals
|
|||
|
// that we must wait for.
|
|||
|
//
|
|||
|
This->m_PendingChildCount = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Reset WMI state
|
|||
|
//
|
|||
|
This->m_Device->m_PkgWmi->ResetStateForPdoRestart();
|
|||
|
|
|||
|
|
|||
|
This->m_Device->m_PkgIo->ResetStateForRestart();
|
|||
|
|
|||
|
if (This->IsPowerPolicyOwner()) {
|
|||
|
This->m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.Reset();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set STATUS_SUCCESS in the irp so that the stack will start smoothly after
|
|||
|
// we have powered up.
|
|||
|
//
|
|||
|
This->SetPendingPnpIrpStatus(STATUS_SUCCESS);
|
|||
|
|
|||
|
//
|
|||
|
// This flag is set on the wake-enabled device powering down path,
|
|||
|
// but if the device power down failed it may not have been cleared.
|
|||
|
//
|
|||
|
This->m_WakeInterruptsKeepConnected = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// This flag is cleared so we can reacquire the start time and state
|
|||
|
//
|
|||
|
This->m_AchievedStart = FALSE;
|
|||
|
|
|||
|
return WdfDevStatePnpHardwareAvailable;
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventRemovedChildrenRemoved(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
All of this device's previously enumerated children have been removed. Move
|
|||
|
the state machine into its next state based on the device's role.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
new state
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return This->PnpGetPostRemoveState();
|
|||
|
}
|
|||
|
|
|||
|
WDF_DEVICE_PNP_STATE
|
|||
|
FxPkgPnp::PnpEventFinal(
|
|||
|
__inout FxPkgPnp* This
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
The final resting and dead state for the FxDevice that has been removed. We
|
|||
|
release our final reference here and destroy the object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
This - This instance of the state machine
|
|||
|
|
|||
|
Return Value:
|
|||
|
WdfDevStatePnpNull
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// We may not have a pnp irp at this stage (esp for PDO which are in the
|
|||
|
// removed state and whose parent is being removed) so we use the function
|
|||
|
// pointer as the unique tag.
|
|||
|
//
|
|||
|
// IoReleaseRemoveLockAndWait requires an outstanding reference to release,
|
|||
|
// so acquire it before calling it if we are in the case where the PDO is
|
|||
|
// being removed with no outstanding PNP remove irp b/c the parent is being
|
|||
|
// removed.
|
|||
|
//
|
|||
|
if (This->m_DeviceRemoveProcessed == NULL) {
|
|||
|
status = Mx::MxAcquireRemoveLock(
|
|||
|
This->m_Device->GetRemoveLock(),
|
|||
|
&FxPkgPnp::PnpEventFinal);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(status));
|
|||
|
UNREFERENCED_PARAMETER(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate to the parent device that we are removed now (vs the destructor
|
|||
|
// of the object where we would never reach because in the case of the PDO
|
|||
|
// being removed b/c the parent is going away, the parent has a reference
|
|||
|
// on the PDO).
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (This->m_Device->m_ParentWaitingOnChild) {
|
|||
|
(This->m_Device->m_ParentDevice->m_PkgPnp)->ChildRemoved();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (This->m_DeviceRemoveProcessed == NULL) {
|
|||
|
//
|
|||
|
// We can get into this state w/out an event to set when a PDO (this
|
|||
|
// device) is in the removed state and then the parent is removed.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// After this is called, any irp dispatched to FxDevice::DispatchWithLock
|
|||
|
// will fail with STATUS_INVALID_DEVICE_REQUEST.
|
|||
|
//
|
|||
|
Mx::MxReleaseRemoveLockAndWait(
|
|||
|
This->m_Device->GetRemoveLock(),
|
|||
|
&FxPkgPnp::PnpEventFinal);
|
|||
|
|
|||
|
//
|
|||
|
// Delete the object when we exit the state machine. Dispose was run
|
|||
|
// early in a previous state.
|
|||
|
//
|
|||
|
This->m_PnpMachine.SetDelayedDeletion();
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The thread which received the pnp remove irp will delete the device
|
|||
|
//
|
|||
|
This->m_SetDeviceRemoveProcessed = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return WdfDevStatePnpNull;
|
|||
|
}
|
|||
|
|
|||
|
_Must_inspect_result_
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::PnpEnableInterfacesAndRegisterWmi(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Enables all of the device interfaces and then registers wmi.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
NT_SUCCESS if all goes well, !NT_SUCCESS otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSINGLE_LIST_ENTRY ple;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Enable any device interfaces.
|
|||
|
//
|
|||
|
m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
|
|||
|
|
|||
|
m_DeviceInterfacesCanBeEnabled = TRUE;
|
|||
|
|
|||
|
for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
|
|||
|
FxDeviceInterface *pDeviceInterface;
|
|||
|
|
|||
|
pDeviceInterface = FxDeviceInterface::_FromEntry(ple);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
|
|||
|
//
|
|||
|
// By this time, the device interface, no matter what the WDFDEVICE role
|
|||
|
// will have been registered.
|
|||
|
//
|
|||
|
ASSERT(pDeviceInterface->m_SymbolicLinkName.Buffer != NULL);
|
|||
|
#endif
|
|||
|
pDeviceInterface->SetState(TRUE);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
status = m_Device->WmiPkgRegister();
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
__drv_when(!NT_SUCCESS(return), __drv_arg(ResourcesMatched, _Must_inspect_result_))
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::PnpPrepareHardware(
|
|||
|
__inout PBOOLEAN ResourcesMatched
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Matches the PNP resources with the WDFINTERRUPT objects registered and then
|
|||
|
calls EvtDevicePrepareHardware. All start paths call this function
|
|||
|
|
|||
|
Arguments:
|
|||
|
ResourcesMatched - indicates to the caller what stage failed if !NT_SUCCESS
|
|||
|
is returned
|
|||
|
|
|||
|
Return Value:
|
|||
|
NT_SUCCESS if all goes well, !NT_SUCCESS if failure occurrs
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
*ResourcesMatched = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// FxPnpStateRemoved:
|
|||
|
// Mark the device a not removed. This is just so that anybody sending
|
|||
|
// a PnP IRP_MN_QUERY_DEVICE_STATE gets a reasonable answer.
|
|||
|
//
|
|||
|
// FxPnpStateFailed, FxPnpStateResourcesChanged:
|
|||
|
// Both of these values can be set to true and cause another start to
|
|||
|
// be sent down the stack. Reset these values back to false. If there is
|
|||
|
// a need to set these values, the driver can set them in
|
|||
|
// EvtDevicePrepareHardware.
|
|||
|
//
|
|||
|
m_PnpStateAndCaps.Value &= ~(FxPnpStateRemovedMask |
|
|||
|
FxPnpStateFailedMask |
|
|||
|
FxPnpStateResourcesChangedMask);
|
|||
|
m_PnpStateAndCaps.Value |= (FxPnpStateRemovedUseDefault |
|
|||
|
FxPnpStateFailedUseDefault |
|
|||
|
FxPnpStateResourcesChangedUseDefault);
|
|||
|
|
|||
|
//
|
|||
|
// This will parse the resources and setup all the WDFINTERRUPT handles
|
|||
|
//
|
|||
|
status = PnpMatchResources();
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
*ResourcesMatched = FALSE;
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
|
|||
|
//
|
|||
|
// Build register resource table
|
|||
|
//
|
|||
|
status = m_Resources->BuildRegisterResourceTable();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build Port resource table
|
|||
|
//
|
|||
|
status = m_Resources->BuildPortResourceTable();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We keep track if the device has any connection resources,
|
|||
|
// in which case we allow unrestricted access to interrupts
|
|||
|
// regardless of the UmdfDirectHardwareAccess directive.
|
|||
|
//
|
|||
|
status = m_Resources->CheckForConnectionResources();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
*ResourcesMatched = TRUE;
|
|||
|
|
|||
|
m_Device->SetCallbackFlags(
|
|||
|
FXDEVICE_CALLBACK_IN_PREPARE_HARDWARE
|
|||
|
);
|
|||
|
|
|||
|
status = m_DevicePrepareHardware.Invoke(m_Device->GetHandle(),
|
|||
|
m_ResourcesRaw->GetHandle(),
|
|||
|
m_Resources->GetHandle());
|
|||
|
|
|||
|
m_Device->ClearCallbackFlags(
|
|||
|
FXDEVICE_CALLBACK_IN_PREPARE_HARDWARE
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDevicePrepareHardware failed %!STATUS!", status);
|
|||
|
|
|||
|
if (status == STATUS_NOT_SUPPORTED) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDevicePrepareHardware returned an invalid status "
|
|||
|
"STATUS_NOT_SUPPORTED");
|
|||
|
|
|||
|
if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|||
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now that we have assigned the resources to all the interrupts, figure out
|
|||
|
// the highest synch irql for each interrupt set which shares a spinlock.
|
|||
|
//
|
|||
|
PnpAssignInterruptsSyncIrql();
|
|||
|
|
|||
|
//
|
|||
|
// Do mode-specific work. For KMDF, there is nothing additional to do.
|
|||
|
//
|
|||
|
status = PnpPrepareHardwareInternal();
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"PrepareHardware failed %!STATUS!", status);
|
|||
|
|
|||
|
SetInternalFailure();
|
|||
|
SetPendingPnpIrpStatus(status);
|
|||
|
}
|
|||
|
|
|||
|
exit:
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
_Must_inspect_result_
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::PnpReleaseHardware(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Invokes the driver's release hardware callback if present.
|
|||
|
Releases any interrupt resources allocated during the prepare hardware callback.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
Driver's release hardware callback return status or
|
|||
|
STATUS_SUCCESS if callback is not present.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
FxInterrupt* interrupt;
|
|||
|
PLIST_ENTRY le;
|
|||
|
|
|||
|
//
|
|||
|
// Invoke the device's release hardware callback.
|
|||
|
//
|
|||
|
status = m_DeviceReleaseHardware.Invoke(
|
|||
|
m_Device->GetHandle(),
|
|||
|
m_Resources->GetHandle());
|
|||
|
|
|||
|
if (status == STATUS_NOT_SUPPORTED) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"EvtDeviceReleaseHardware returned an invalid status "
|
|||
|
"STATUS_NOT_SUPPORTED");
|
|||
|
|
|||
|
if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
|||
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if (FX_CORE_MODE == FX_CORE_USER_MODE)
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// make sure driver has unmapped its resources
|
|||
|
//
|
|||
|
m_Resources->ValidateResourceUnmap();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// delete the register and port resource tables
|
|||
|
//
|
|||
|
m_Resources->DeleteRegisterResourceTable();
|
|||
|
m_Resources->DeletePortResourceTable();
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Delete all interrupt objects that were created in the prepare hardware
|
|||
|
// callback that the driver did not explicitly delete (in reverse order).
|
|||
|
//
|
|||
|
le = m_InterruptListHead.Blink;
|
|||
|
|
|||
|
while(le != &m_InterruptListHead) {
|
|||
|
|
|||
|
// Find the interrupt object pointer.
|
|||
|
interrupt = CONTAINING_RECORD(le, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
// Advance the entry before it becomes invalid.
|
|||
|
le = le->Blink;
|
|||
|
|
|||
|
// Let the interrupt know that 'release hardware' was called.
|
|||
|
interrupt->OnPostReleaseHardware();
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpPowerPolicyStart(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Informs the power policy state machine that it should start.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PowerPolicyProcessEvent(PwrPolStart);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpPowerPolicyStop(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Informs the power and power policy state machines that they should stop.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PowerPolicyProcessEvent(PwrPolStop);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpPowerPolicySurpriseRemove(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Informs the policy state machines that it should stop due to the hardware
|
|||
|
being surprise removed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PowerPolicyProcessEvent(PwrPolSurpriseRemove);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpPowerPolicyRemove(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Informs the policy state machine that it should prepare for device removal.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PowerPolicyProcessEvent(PwrPolRemove);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpFinishProcessingIrp(
|
|||
|
__in BOOLEAN IrpMustBePresent
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Finishes handling a pended pnp irp
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
FxIrp irp;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(IrpMustBePresent);
|
|||
|
ASSERT(IrpMustBePresent == FALSE || IsPresentPendingPnpIrp());
|
|||
|
|
|||
|
//
|
|||
|
// Start device is the only request we handle on the way back up the stack.
|
|||
|
// Also, if we fail any pnp irps that we are allowed to fail, we just
|
|||
|
// complete them.
|
|||
|
//
|
|||
|
if (IsPresentPendingPnpIrp()) {
|
|||
|
|
|||
|
irp.SetIrp(GetPendingPnpIrp());
|
|||
|
if (irp.GetMinorFunction() == IRP_MN_START_DEVICE
|
|||
|
||
|
|||
|
!NT_SUCCESS(irp.GetStatus())) {
|
|||
|
|
|||
|
irp.SetIrp(ClearPendingPnpIrp());
|
|||
|
CompletePnpRequest(&irp, irp.GetStatus());
|
|||
|
}
|
|||
|
else {
|
|||
|
m_PnpMachine.m_FireAndForget = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpDisableInterfaces(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Disables all of the registerd interfaces on the device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSINGLE_LIST_ENTRY ple;
|
|||
|
|
|||
|
m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
|
|||
|
|
|||
|
m_DeviceInterfacesCanBeEnabled = FALSE;
|
|||
|
|
|||
|
for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
|
|||
|
|
|||
|
FxDeviceInterface *pDeviceInterface;
|
|||
|
pDeviceInterface = FxDeviceInterface::_FromEntry(ple);
|
|||
|
pDeviceInterface->SetState(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpEventSurpriseRemovePendingOverload(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Mark all of the children as missing because the parent has just been
|
|||
|
// removed. Note that this will happen after all of the children have
|
|||
|
// already received the surprise remove event. This is OK because the
|
|||
|
// reported status is inspected during the remove device event which will
|
|||
|
// happen after the parent finishes processing the surprise event.
|
|||
|
//
|
|||
|
if (m_EnumInfo != NULL) {
|
|||
|
m_EnumInfo->m_ChildListList.LockForEnum(GetDriverGlobals());
|
|||
|
FxTransactionedEntry* ple;
|
|||
|
|
|||
|
ple = NULL;
|
|||
|
while ((ple = m_EnumInfo->m_ChildListList.GetNextEntry(ple)) != NULL) {
|
|||
|
FxChildList::_FromEntry(ple)->NotifyDeviceSurpriseRemove();
|
|||
|
}
|
|||
|
|
|||
|
m_EnumInfo->m_ChildListList.UnlockFromEnum(GetDriverGlobals());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_Must_inspect_result_
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::PnpMatchResources(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method is called in response to a PnP StartDevice IRP
|
|||
|
coming up the stack. It:
|
|||
|
|
|||
|
- Captures the device's resources
|
|||
|
- Calls out to interested resource objects
|
|||
|
- Sends an event to the PnP state machine
|
|||
|
|
|||
|
Arguemnts:
|
|||
|
|
|||
|
Irp - a pointer to the FxIrp
|
|||
|
|
|||
|
Returns:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_RESOURCE_LIST pResourcesRaw;
|
|||
|
PCM_RESOURCE_LIST pResourcesTranslated;
|
|||
|
FxResourceCm* resCmRaw;
|
|||
|
FxResourceCm* resCmTrans;
|
|||
|
FxInterrupt* interrupt;
|
|||
|
PLIST_ENTRY ple;
|
|||
|
NTSTATUS status;
|
|||
|
FxCollectionEntry *curRaw, *curTrans, *endTrans;
|
|||
|
ULONG messageCount;
|
|||
|
FxIrp irp;
|
|||
|
|
|||
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|||
|
"Entering PnpMatchResources");
|
|||
|
|
|||
|
//
|
|||
|
// We must clear these flags before calling into the event handler because
|
|||
|
// it might set these states back (which is OK). If we don't clear these
|
|||
|
// states and the start succeeds, we would endlessly report that our
|
|||
|
// resources have changed and be restarted over and over.
|
|||
|
//
|
|||
|
m_PnpStateAndCaps.Value &= ~(FxPnpStateFailedMask |
|
|||
|
FxPnpStateResourcesChangedMask);
|
|||
|
m_PnpStateAndCaps.Value |= (FxPnpStateFailedUseDefault |
|
|||
|
FxPnpStateResourcesChangedUseDefault);
|
|||
|
|
|||
|
irp.SetIrp(m_PendingPnPIrp);
|
|||
|
pResourcesRaw = irp.GetParameterAllocatedResources();
|
|||
|
pResourcesTranslated = irp.GetParameterAllocatedResourcesTranslated();
|
|||
|
|
|||
|
status = m_ResourcesRaw->BuildFromWdmList(pResourcesRaw, FxResourceNoAccess);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"Could not allocate raw resource list for WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
m_Device->GetHandle(), status);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
status = m_Resources->BuildFromWdmList(pResourcesTranslated, FxResourceNoAccess);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"Could not allocate translated resource list for WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
m_Device->GetHandle(), status);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// reset the stored information in all interrupts in the rebalance case
|
|||
|
//
|
|||
|
for (ple = m_InterruptListHead.Flink;
|
|||
|
ple != &m_InterruptListHead;
|
|||
|
ple = ple->Flink) {
|
|||
|
interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|||
|
interrupt->Reset();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now iterate across the resources, looking for ones that correspond
|
|||
|
// to objects that we are managing. Tell those objects about the resources
|
|||
|
// that were assigned.
|
|||
|
//
|
|||
|
ple = &m_InterruptListHead;
|
|||
|
|
|||
|
endTrans = m_Resources->End();
|
|||
|
|
|||
|
for (curTrans = m_Resources->Start(), curRaw = m_ResourcesRaw->Start();
|
|||
|
curTrans != endTrans;
|
|||
|
curTrans = curTrans->Next(), curRaw = curRaw->Next()) {
|
|||
|
|
|||
|
ASSERT(curTrans->m_Object->GetType() == FX_TYPE_RESOURCE_CM);
|
|||
|
ASSERT(curRaw->m_Object->GetType() == FX_TYPE_RESOURCE_CM);
|
|||
|
|
|||
|
resCmRaw = (FxResourceCm*) curRaw->m_Object;
|
|||
|
|
|||
|
if (resCmRaw->m_Descriptor.Type == CmResourceTypeInterrupt) {
|
|||
|
//
|
|||
|
// We're looking at an interrupt resource.
|
|||
|
//
|
|||
|
if (ple->Flink == &m_InterruptListHead) {
|
|||
|
//
|
|||
|
// Oops, there are no more interrupt objects.
|
|||
|
//
|
|||
|
DoTraceLevelMessage(
|
|||
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|||
|
"Not enough interrupt objects created by WDFDEVICE 0x%p",
|
|||
|
m_Device->GetHandle());
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
resCmTrans = (FxResourceCm*) curTrans->m_Object;
|
|||
|
ASSERT(resCmTrans->m_Descriptor.Type == CmResourceTypeInterrupt);
|
|||
|
|
|||
|
messageCount = resCmRaw->m_Descriptor.u.MessageInterrupt.Raw.MessageCount;
|
|||
|
|
|||
|
if (FxInterrupt::_IsMessageInterrupt(resCmTrans->m_Descriptor.Flags)
|
|||
|
&&
|
|||
|
(messageCount > 1)) {
|
|||
|
ULONG i;
|
|||
|
//
|
|||
|
// Multi-message MSI 2.2 needs to be handled differently
|
|||
|
//
|
|||
|
for (i = 0, ple = ple->Flink;
|
|||
|
i < messageCount && ple != &m_InterruptListHead;
|
|||
|
i++, ple = ple->Flink) {
|
|||
|
|
|||
|
//
|
|||
|
// Get the next interrupt object.
|
|||
|
//
|
|||
|
interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
//
|
|||
|
// Tell the interrupt object what its resources are.
|
|||
|
//
|
|||
|
interrupt->AssignResources(&resCmRaw->m_Descriptor,
|
|||
|
&resCmTrans->m_Descriptor);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// This is either MSI2.2 with 1 message, MSI-X or Line based.
|
|||
|
//
|
|||
|
ple = ple->Flink;
|
|||
|
interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
//
|
|||
|
// Tell the interrupt object what its resources are.
|
|||
|
//
|
|||
|
interrupt->AssignResources(&resCmRaw->m_Descriptor,
|
|||
|
&resCmTrans->m_Descriptor);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if FX_IS_KERNEL_MODE
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If there are any pended I/Os that were sent to the target
|
|||
|
// that were pended in the transition to stop, then this will
|
|||
|
// resend them.
|
|||
|
//
|
|||
|
// ISSUE: This has the potential of I/O completing
|
|||
|
// before the driver's start callback has been called...but,
|
|||
|
// this is the same as the PDO pending a sent irp and completing
|
|||
|
// it when the PDO is restarted before the FDO has a change to
|
|||
|
// process the start irp which was still pended below.
|
|||
|
//
|
|||
|
if (m_Device->IsFilter()) {
|
|||
|
//
|
|||
|
// If this is a filter device, then copy the FILE_REMOVABLE_MEDIA
|
|||
|
// characteristic from the lower device.
|
|||
|
//
|
|||
|
if (m_Device->GetAttachedDevice()->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|||
|
ULONG characteristics;
|
|||
|
|
|||
|
characteristics =
|
|||
|
m_Device->GetDeviceObject()->Characteristics | FILE_REMOVABLE_MEDIA;
|
|||
|
|
|||
|
m_Device->GetDeviceObject()->Characteristics = characteristics;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
m_Device->SetFilterIoType();
|
|||
|
}
|
|||
|
|
|||
|
#endif // FX_IS_KERNEL_MODE
|
|||
|
|
|||
|
Done:
|
|||
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|||
|
"Exiting PnpMatchResources %!STATUS!", status);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FxPkgPnp::PnpAssignInterruptsSyncIrql(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Figure out the highest synch irql for each interrupt set which shares a
|
|||
|
spinlock.
|
|||
|
|
|||
|
foreach(interrupt assigned to this instance)
|
|||
|
determine the max sync irql in the set
|
|||
|
set all the associated interrupts to the sync irql
|
|||
|
set the sync irql on the first interrupt in the set
|
|||
|
|
|||
|
Arguments:
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY ple;
|
|||
|
FxInterrupt* pInterrupt;
|
|||
|
|
|||
|
for (ple = m_InterruptListHead.Flink;
|
|||
|
ple != &m_InterruptListHead;
|
|||
|
ple = ple->Flink) {
|
|||
|
|
|||
|
KIRQL syncIrql;
|
|||
|
|
|||
|
pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
syncIrql = pInterrupt->GetResourceIrql();
|
|||
|
|
|||
|
if (syncIrql == PASSIVE_LEVEL) {
|
|||
|
//
|
|||
|
// The irql associated with the resources assigned is passive,
|
|||
|
// this can happen in the following scenarios:
|
|||
|
//
|
|||
|
// (1) no resources were assigned. Skip this interrupt b/c it has
|
|||
|
// no associated resources. Note: setting the SynchronizeIrql
|
|||
|
// to PASSIVE_LEVEL is a no-op.
|
|||
|
//
|
|||
|
// (2) this interrupt is handled at passive-level.
|
|||
|
// Set SynchronizeIrql to passive-level and continue.
|
|||
|
//
|
|||
|
pInterrupt->SetSyncIrql(PASSIVE_LEVEL);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (pInterrupt->IsSharedSpinLock() == FALSE) {
|
|||
|
//
|
|||
|
// If the interrupt spinlock is not shared, it's sync irql is the
|
|||
|
// irql assigned to it in the resources.
|
|||
|
//
|
|||
|
pInterrupt->SetSyncIrql(syncIrql);
|
|||
|
}
|
|||
|
else if (pInterrupt->IsSyncIrqlSet() == FALSE) {
|
|||
|
FxInterrupt* pFwdInterrupt;
|
|||
|
PLIST_ENTRY pleFwd;
|
|||
|
|
|||
|
//
|
|||
|
// Find all of the other interrupts which share the lock and compute
|
|||
|
// the max sync irql.
|
|||
|
//
|
|||
|
for (pleFwd = ple->Flink;
|
|||
|
pleFwd != &m_InterruptListHead;
|
|||
|
pleFwd = pleFwd->Flink) {
|
|||
|
|
|||
|
pFwdInterrupt = CONTAINING_RECORD(pleFwd, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
//
|
|||
|
// If the 2 do not share the same lock, they are not in the same
|
|||
|
// set.
|
|||
|
//
|
|||
|
if (pFwdInterrupt->SharesLock(pInterrupt) == FALSE) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (pFwdInterrupt->GetResourceIrql() > syncIrql) {
|
|||
|
syncIrql = pFwdInterrupt->GetResourceIrql();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now that we found the max sync irql, set it for all interrupts in
|
|||
|
// the set which share the lock
|
|||
|
//
|
|||
|
for (pleFwd = ple->Flink;
|
|||
|
pleFwd != &m_InterruptListHead;
|
|||
|
pleFwd = pleFwd->Flink) {
|
|||
|
|
|||
|
pFwdInterrupt = CONTAINING_RECORD(pleFwd, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
//
|
|||
|
// If the 2 do not share the same lock, they are not in the same
|
|||
|
// set.
|
|||
|
//
|
|||
|
if (pFwdInterrupt->SharesLock(pInterrupt) == FALSE) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
pFwdInterrupt->SetSyncIrql(syncIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the sync irql for the first interrupt in the set. We have set
|
|||
|
// the sync irql for all other interrupts in the set.
|
|||
|
//
|
|||
|
pInterrupt->SetSyncIrql(syncIrql);
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// If IsSyncIrqlSet is TRUE, we already covered this interrupt in a
|
|||
|
// previous pass of this loop when we computed the max sync irql for
|
|||
|
// an interrupt set.
|
|||
|
//
|
|||
|
ASSERT(pInterrupt->GetSyncIrql() > PASSIVE_LEVEL);
|
|||
|
DO_NOTHING();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_Must_inspect_result_
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::ValidateCmResource(
|
|||
|
__inout PCM_PARTIAL_RESOURCE_DESCRIPTOR* CmResourceRaw,
|
|||
|
__inout PCM_PARTIAL_RESOURCE_DESCRIPTOR* CmResource
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Makes sure the specified resource is valid.
|
|||
|
|
|||
|
Arguments:
|
|||
|
CmResourceRaw - the raw resource to validate.
|
|||
|
CmResource - the translated resources to validate.
|
|||
|
|
|||
|
Return Value:
|
|||
|
STATUS_SUCCESS if resource is valid or
|
|||
|
NTSTATUS error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
FxCollectionEntry* cur;
|
|||
|
FxCollectionEntry* curRaw;
|
|||
|
FxResourceCm* res;
|
|||
|
FxResourceCm* resRaw;
|
|||
|
PFX_DRIVER_GLOBALS fxDriverGlobals;
|
|||
|
|
|||
|
ASSERT(m_ResourcesRaw != NULL);
|
|||
|
ASSERT(m_Resources != NULL);
|
|||
|
|
|||
|
res = NULL;
|
|||
|
resRaw = NULL;
|
|||
|
|
|||
|
fxDriverGlobals = GetDriverGlobals();
|
|||
|
|
|||
|
//
|
|||
|
// Find the resource in our list.
|
|||
|
//
|
|||
|
for (cur = m_Resources->Start(), curRaw = m_ResourcesRaw->Start();
|
|||
|
cur != m_Resources->End();
|
|||
|
cur = cur->Next(), curRaw = curRaw->Next()) {
|
|||
|
|
|||
|
res = (FxResourceCm*) cur->m_Object;
|
|||
|
resRaw = (FxResourceCm*) curRaw->m_Object;
|
|||
|
|
|||
|
if (&res->m_DescriptorClone == *CmResource) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Error out if not found.
|
|||
|
//
|
|||
|
if (cur == m_Resources->End()) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
|
|||
|
"WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
*CmResource, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Error out if the associated raw resource is not the same.
|
|||
|
//
|
|||
|
if (&resRaw->m_DescriptorClone != *CmResourceRaw) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
|
|||
|
"WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
*CmResourceRaw, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure driver didn't change any of the PnP settings.
|
|||
|
//
|
|||
|
if (sizeof(res->m_Descriptor) !=
|
|||
|
RtlCompareMemory(&res->m_DescriptorClone,
|
|||
|
&res->m_Descriptor,
|
|||
|
sizeof(res->m_Descriptor))) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
|
|||
|
"driver cannot change the assigned PnP resources, WDFDEVICE 0x%p, "
|
|||
|
"%!STATUS!",
|
|||
|
*CmResource, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
if (sizeof(resRaw->m_Descriptor) !=
|
|||
|
RtlCompareMemory(&resRaw->m_DescriptorClone,
|
|||
|
&resRaw->m_Descriptor,
|
|||
|
sizeof(resRaw->m_Descriptor))) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
|
|||
|
"driver cannot change the assigned PnP resources, WDFDEVICE 0x%p, "
|
|||
|
"%!STATUS!",
|
|||
|
*CmResourceRaw, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return the real descriptor.
|
|||
|
//
|
|||
|
ASSERT(res != NULL && resRaw != NULL);
|
|||
|
*CmResource = &res->m_Descriptor;
|
|||
|
*CmResourceRaw = &resRaw->m_Descriptor;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
Done:
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
_Must_inspect_result_
|
|||
|
NTSTATUS
|
|||
|
FxPkgPnp::ValidateInterruptResourceCm(
|
|||
|
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResourceRaw,
|
|||
|
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResource,
|
|||
|
__in PWDF_INTERRUPT_CONFIG Configuration
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
Makes sure the specified resource is valid for an interrupt resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
CmIntResourceRaw - the raw interrupt resource to validate.
|
|||
|
CmIntResource - the translated interrupt resource to validate.
|
|||
|
|
|||
|
Return Value:
|
|||
|
STATUS_SUCCESS if resource is valid or
|
|||
|
NTSTATUS error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PLIST_ENTRY le;
|
|||
|
FxInterrupt* interrupt;
|
|||
|
ULONG messageCount;
|
|||
|
PFX_DRIVER_GLOBALS fxDriverGlobals;
|
|||
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmIntResourceRaw;
|
|||
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmIntResource;
|
|||
|
|
|||
|
cmIntResourceRaw = CmIntResourceRaw;
|
|||
|
cmIntResource = CmIntResource;
|
|||
|
fxDriverGlobals = GetDriverGlobals();
|
|||
|
|
|||
|
//
|
|||
|
// Get the real descriptor not the copy.
|
|||
|
//
|
|||
|
status = ValidateCmResource(&cmIntResourceRaw, &cmIntResource);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure this is an interrupt resource.
|
|||
|
//
|
|||
|
if (cmIntResourceRaw->Type != CmResourceTypeInterrupt) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not an "
|
|||
|
"interrupt resource, WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
CmIntResourceRaw, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
if (cmIntResource->Type != CmResourceTypeInterrupt) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not an "
|
|||
|
"interrupt resource, WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
CmIntResource, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure resource was not claimed by another interrupt.
|
|||
|
// Multi-message MSI 2.2 interrupts: allowed to reuse claimed resources.
|
|||
|
// Driver must create them sequentially.
|
|||
|
// Line-based interrupts: allowed to reuse claimed resources.
|
|||
|
// Driver can create them out-of-order.
|
|||
|
// Other MSI: not allowed to reuse claimed resources.
|
|||
|
//
|
|||
|
messageCount = 0;
|
|||
|
|
|||
|
for (le = m_InterruptListHead.Flink;
|
|||
|
le != &m_InterruptListHead;
|
|||
|
le = le->Flink) {
|
|||
|
|
|||
|
interrupt = CONTAINING_RECORD(le, FxInterrupt, m_PnpList);
|
|||
|
|
|||
|
if (cmIntResource != interrupt->GetResources()) {
|
|||
|
//
|
|||
|
// Multi-message MSI 2.2 interrupts must be sequential.
|
|||
|
//
|
|||
|
if (messageCount != 0) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"Multi-message MSI 2.2 interrupts must be created "
|
|||
|
"sequentially, WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (interrupt->IsWakeCapable() &&
|
|||
|
Configuration->PassiveHandling) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
|
|||
|
"to create a wakable interrupt 0x%p, WDFDEVICE 0x%p and "
|
|||
|
"any functional interrupt being shared with wakable interrupt "
|
|||
|
"can not use passive level handling",
|
|||
|
CmIntResource, interrupt->GetHandle(),
|
|||
|
m_Device->GetHandle());
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
if (interrupt->IsPassiveHandling() &&
|
|||
|
Configuration->CanWakeDevice) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
|
|||
|
"to create a passive level interrupt 0x%p, WDFDEVICE 0x%p and "
|
|||
|
"is now being used to create a wakable interrupt. A functional "
|
|||
|
"passive level interrupt can not be shared with wakable interrupt",
|
|||
|
CmIntResource, interrupt->GetHandle(),
|
|||
|
m_Device->GetHandle());
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for multi-message MSI 2.2 interrupts. These are allowed
|
|||
|
// to use the same resource.
|
|||
|
// We allow line based interrupts to reuse claimed resources.
|
|||
|
//
|
|||
|
if (FxInterrupt::_IsMessageInterrupt(cmIntResource->Flags) == FALSE) {
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|||
|
"The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
|
|||
|
"to create interrupt 0x%p, WDFDEVICE 0x%p",
|
|||
|
CmIntResource, interrupt->GetHandle(),
|
|||
|
m_Device->GetHandle());
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Only allow the correct # of messages.
|
|||
|
//
|
|||
|
messageCount++;
|
|||
|
if (messageCount >
|
|||
|
cmIntResourceRaw->u.MessageInterrupt.Raw.MessageCount) {
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
DoTraceLevelMessage(
|
|||
|
fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
|||
|
"All the MSI 2.2 interrupts for "
|
|||
|
"PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p are already created, "
|
|||
|
"WDFDEVICE 0x%p, %!STATUS!",
|
|||
|
CmIntResource, m_Device->GetHandle(), status);
|
|||
|
FxVerifierDbgBreakPoint(fxDriverGlobals);
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
Done:
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
#define RESTART_START_ACHIEVED_NAME L"StartAchieved"
|
|||
|
#define RESTART_START_TIME_NAME L"StartTime"
|
|||
|
#define RESTART_COUNT_NAME L"Count"
|
|||
|
|
|||
|
const PWCHAR FxPkgPnp::m_RestartStartAchievedName = RESTART_START_ACHIEVED_NAME;
|
|||
|
const PWCHAR FxPkgPnp::m_RestartStartTimeName = RESTART_START_TIME_NAME;
|
|||
|
const PWCHAR FxPkgPnp::m_RestartCountName = RESTART_COUNT_NAME;
|
|||
|
|
|||
|
const ULONG FxPkgPnp::m_RestartTimePeriodMaximum = 60;
|
|||
|
const ULONG FxPkgPnp::m_RestartCountMaximum = 5;
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FxPkgPnp::PnpIncrementRestartCountLogic(
|
|||
|
_In_ HANDLE RestartKey,
|
|||
|
_In_ BOOLEAN CreatedNewKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
This routine determines if this device should ask the bus driver to
|
|||
|
reenumerate the device. This is determined by how many times the entire
|
|||
|
stack has asked for a restart within a given period. This is stack wide
|
|||
|
because the settings are stored in a key in the device node itself (which all
|
|||
|
devices share).
|
|||
|
|
|||
|
The period and number of times a restart are attempted are defined as constants
|
|||
|
(m_RestartTimePeriodMaximum, m_RestartCountMaximum)in this class. They are
|
|||
|
current defined as a period of 60 seconds and a restart max count of 5.
|
|||
|
|
|||
|
The settings are stored in a volatile key so that they do not persist across
|
|||
|
machine reboots. Persisting across reboots makes no sense if we restrict the
|
|||
|
number of restarts w/in a period.
|
|||
|
|
|||
|
The rules are as follows
|
|||
|
1) if the key does not exist, treat this as the beginning of the period
|
|||
|
and ask for a reenumeration
|
|||
|
2) if the key exists
|
|||
|
a) if the beginning of the period and the restart count cannot be read
|
|||
|
do not ask for a reenumeration
|
|||
|
b) if the beginning of the period is after the current time, either the
|
|||
|
current tick count has wrapped or the key has somehow survived a
|
|||
|
reboot. Either way, treat this as a reset of the period and ask
|
|||
|
for a reenumeration
|
|||
|
c) if the current time is after the period start time and within the
|
|||
|
restart period, increment the restart count. if the count is <=
|
|||
|
the max restart count, ask for a reenumeration. If it exceeds the
|
|||
|
max, do not ask for a reenumeration.
|
|||
|
d) if the current time is after the period stat time and exceeds the
|
|||
|
maximum period, and if the device as reached the started state,
|
|||
|
reset the period, count, and started state, then ask for a
|
|||
|
reenumeration.
|
|||
|
|
|||
|
Considerations:
|
|||
|
There is a reenumeration loop that a device can get caught in. If a device
|
|||
|
takes more than m_RestartTimePeriodMaximum to fail m_RestartCountMaximum
|
|||
|
times then the device will be caught in this loop. If it is failing on the
|
|||
|
way to PnpEventStarted then the device will likely cause a 9F bugcheck.
|
|||
|
This is because they hold a power lock while in this loop. If the device
|
|||
|
fails after PnpEventStarted then pnp can progress and the device can loop
|
|||
|
here indefinitely. We have shipped with this behavior for several releases,
|
|||
|
so we are hesitant to completely change this behavior. The concern is that
|
|||
|
a device out there relies on this behavior.
|
|||
|
|
|||
|
Arguments:
|
|||
|
RestartKey - opened handle to the Restart registry key
|
|||
|
CreatedNewKey - TRUE if the Restart key was created just now
|
|||
|
|
|||
|
Return Value:
|
|||
|
TRUE if a restart should be requested.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
ULONG count;
|
|||
|
LARGE_INTEGER currentTickCount, startTickCount;
|
|||
|
BOOLEAN started, writeTick, writeCount, writeStarted;
|
|||
|
|
|||
|
DECLARE_CONST_UNICODE_STRING(valueNameStartTime, RESTART_START_TIME_NAME);
|
|||
|
DECLARE_CONST_UNICODE_STRING(valueNameCount, RESTART_COUNT_NAME);
|
|||
|
DECLARE_CONST_UNICODE_STRING(valueNameStartAchieved, RESTART_START_ACHIEVED_NAME);
|
|||
|
|
|||
|
count = 0;
|
|||
|
started = FALSE;
|
|||
|
writeTick = FALSE;
|
|||
|
writeCount = FALSE;
|
|||
|
writeStarted = FALSE;
|
|||
|
|
|||
|
Mx::MxQueryTickCount(¤tTickCount);
|
|||
|
|
|||
|
started = m_AchievedStart;
|
|||
|
if (started) {
|
|||
|
//
|
|||
|
// Save the fact the driver started without failing
|
|||
|
//
|
|||
|
writeStarted = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the key was created right now, there is nothing to check, just write out
|
|||
|
// the data.
|
|||
|
//
|
|||
|
if (CreatedNewKey) {
|
|||
|
writeTick = TRUE;
|
|||
|
writeCount = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// First restart
|
|||
|
//
|
|||
|
count = 1;
|
|||
|
}
|
|||
|
else {
|
|||
|
ULONG length, type;
|
|||
|
|
|||
|
//
|
|||
|
// First try to get the start time of when we first attempted a restart
|
|||
|
//
|
|||
|
status = FxRegKey::_QueryValue(GetDriverGlobals(),
|
|||
|
RestartKey,
|
|||
|
&valueNameStartTime,
|
|||
|
sizeof(startTickCount.QuadPart),
|
|||
|
&startTickCount.QuadPart,
|
|||
|
&length,
|
|||
|
&type);
|
|||
|
|
|||
|
if (NT_SUCCESS(status) &&
|
|||
|
length == sizeof(startTickCount.QuadPart) && type == REG_BINARY) {
|
|||
|
|
|||
|
//
|
|||
|
// Now try to get the last restart count
|
|||
|
//
|
|||
|
status = FxRegKey::_QueryULong(RestartKey,
|
|||
|
&valueNameCount,
|
|||
|
&count);
|
|||
|
|
|||
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|||
|
//
|
|||
|
// We read the start time, but not the count. Assume there was
|
|||
|
// at least one previous restart.
|
|||
|
//
|
|||
|
count = 1;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
if (currentTickCount.QuadPart < startTickCount.QuadPart) {
|
|||
|
//
|
|||
|
// Somehow the key survived a reboot or the clock overflowed
|
|||
|
// and the current time is less then the last time we started
|
|||
|
// timing restarts. Either way, just treat this as the first
|
|||
|
// time we are restarting.
|
|||
|
//
|
|||
|
writeTick = TRUE;
|
|||
|
writeCount = TRUE;
|
|||
|
count = 1;
|
|||
|
}
|
|||
|
else {
|
|||
|
LONGLONG delta;
|
|||
|
|
|||
|
//
|
|||
|
// Compute the difference in time in 100 ns units
|
|||
|
//
|
|||
|
delta = (currentTickCount.QuadPart - startTickCount.QuadPart) *
|
|||
|
Mx::MxQueryTimeIncrement();
|
|||
|
|
|||
|
if (delta <= WDF_ABS_TIMEOUT_IN_SEC(m_RestartTimePeriodMaximum)) {
|
|||
|
//
|
|||
|
// We are within the time limit, see if we are within the
|
|||
|
// count limit
|
|||
|
count++;
|
|||
|
|
|||
|
//
|
|||
|
// The count starts at one, so include the maximum in the
|
|||
|
// compare.
|
|||
|
//
|
|||
|
if (count <= m_RestartCountMaximum) {
|
|||
|
writeCount = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Exceeded the restart count, do not attempt to restart
|
|||
|
// the device.
|
|||
|
//
|
|||
|
status = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
if (started == FALSE) {
|
|||
|
ULONG length, type, value;
|
|||
|
status = FxRegKey::_QueryValue(GetDriverGlobals(),
|
|||
|
RestartKey,
|
|||
|
&valueNameStartAchieved,
|
|||
|
sizeof(value),
|
|||
|
&value,
|
|||
|
&length,
|
|||
|
&type);
|
|||
|
if (!NT_SUCCESS(status) || length != sizeof(value) ||
|
|||
|
type != REG_DWORD) {
|
|||
|
value = 0;
|
|||
|
}
|
|||
|
started = value != 0;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (started) {
|
|||
|
//
|
|||
|
// Exceeded the time limit. This is treated as a reset of
|
|||
|
// the time limit, so we will try to restart and reset the
|
|||
|
// start time and restart count.
|
|||
|
//
|
|||
|
writeTick = TRUE;
|
|||
|
writeCount = TRUE;
|
|||
|
count = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Erase the fact the driver once started and
|
|||
|
// make it do it again to get another 5 attempts to
|
|||
|
// restart.
|
|||
|
//
|
|||
|
writeStarted = TRUE;
|
|||
|
started = FALSE;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Device never started
|
|||
|
//
|
|||
|
status = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (writeTick) {
|
|||
|
//
|
|||
|
// Write out the time and the count
|
|||
|
//
|
|||
|
NTSTATUS status2;
|
|||
|
status2 = FxRegKey::_SetValue(RestartKey,
|
|||
|
(PUNICODE_STRING)&valueNameStartTime,
|
|||
|
REG_BINARY,
|
|||
|
¤tTickCount.QuadPart,
|
|||
|
sizeof(currentTickCount.QuadPart));
|
|||
|
//
|
|||
|
// Don't let status report success if it was an error prior to _SetValue
|
|||
|
//
|
|||
|
if(NT_SUCCESS(status)) {
|
|||
|
status = status2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status) && writeCount) {
|
|||
|
status = FxRegKey::_SetValue(RestartKey,
|
|||
|
(PUNICODE_STRING)&valueNameCount,
|
|||
|
REG_DWORD,
|
|||
|
&count,
|
|||
|
sizeof(count));
|
|||
|
}
|
|||
|
|
|||
|
if (writeStarted) {
|
|||
|
NTSTATUS status2;
|
|||
|
DWORD value = started;
|
|||
|
status2 = FxRegKey::_SetValue(RestartKey,
|
|||
|
(PUNICODE_STRING)&valueNameStartAchieved,
|
|||
|
REG_DWORD,
|
|||
|
&value,
|
|||
|
sizeof(value));
|
|||
|
//
|
|||
|
// Don't let status report success if it was an error prior to _SetValue
|
|||
|
//
|
|||
|
if(NT_SUCCESS(status)) {
|
|||
|
status = status2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NT_SUCCESS(status) ? TRUE : FALSE;
|
|||
|
}
|
|||
|
|