--- /dev/null
+/*++
+
+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])));
+}
+\f
+/*++
+
+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);
+ }
+ }
+}
+\f
+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);
+ }
+ }
+}
+\f
+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;
+}
+