1 /*++ 2 3 Copyright (c) Microsoft. All rights reserved. 4 5 Module Name: 6 7 PowerPolicyStateMachineUM.cpp 8 9 Abstract: 10 11 12 Environment: 13 14 User mode only 15 16 Revision History: 17 18 --*/ 19 20 #include "../pnppriv.hpp" 21 22 #include "FxUsbIdleInfo.hpp" 23 24 extern "C" { 25 #if defined(EVENT_TRACING) 26 #include "PowerPolicyStateMachineUM.tmh" 27 #endif 28 } 29 30 VOID 31 FxPkgPnp::PowerPolicyUpdateSystemWakeSource( 32 __in FxIrp* Irp 33 ) 34 /*++ 35 36 Routine Description: 37 Gets source of wake if OS supports this. 38 39 Arguments: 40 Irp 41 42 Return Value: 43 None 44 45 --*/ 46 { 47 PFX_DRIVER_GLOBALS pFxDriverGlobals; 48 49 UNREFERENCED_PARAMETER(Irp); 50 51 if (m_Device->IsPdo()) { 52 53 pFxDriverGlobals = GetDriverGlobals(); 54 55 DoTraceLevelMessage( 56 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 57 "For PDOs, FxPkgPnp::PowerPolicyUpdateSystemWakeSource should NOT " 58 "be a no-op!"); 59 FxVerifierDbgBreakPoint(pFxDriverGlobals); 60 } 61 } 62 63 BOOLEAN 64 FxPkgPnp::ShouldProcessPowerPolicyEventOnDifferentThread( 65 __in KIRQL CurrentIrql, 66 __in BOOLEAN CallerSpecifiedProcessingOnDifferentThread 67 ) 68 /*++ 69 Routine Description: 70 71 This function returns whether the power policy state machine should process 72 the current event on the same thread or on a different one. 73 74 This function has been added to work around a bug in the state machines. 75 The idle state machine always calls PowerPolicyProcessEvent with the idle 76 state machine lock held. Some events sent by the idle state machine can 77 cause the power policy state machine to invoke 78 FxPowerIdleMachine::QueryReturnToIdle(). 79 FxPowerIdleMachine::QueryReturnToIdle() will try to acquire the idle state 80 machine lock, which is already being held, so it will result in a recursive 81 acquire of the idle state machine lock. 82 83 The above bug only affects UMDF, but not KMDF. In KMDF, the idle state 84 machine lock is a spinlock. When PowerPolicyProcessEvent is called, it is 85 called with the spinlock held and hence at dispatch level. Note that if 86 called at a non-passive IRQL, PowerPolicyProcessEvent will always queue a 87 work item to process the event at passive IRQL later. Queuing a work item 88 forces processing to happen on a different thread and hence we don't 89 attempt to recursively acquire the spinlock. On the other hand, with UMDF 90 we are always at passive IRQL and hence we process the event on the same 91 thread and run into the recursive acquire problem. 92 93 94 95 96 97 98 99 100 Arguments: 101 102 CurrentIrql - The current IRQL 103 104 CallerSpecifiedProcessingOnDifferentThread - Whether or not caller of 105 PowerPolicyProcessEvent specified that the event be processed on a 106 different thread. 107 108 Returns: 109 TRUE if the power policy state machine should process the event on a 110 different thread. 111 112 FALSE if the power policy state machine should process the event on the 113 same thread 114 115 --*/ 116 { 117 // 118 // For UMDF, we ignore the IRQL and just do what the caller of 119 // PowerPolicyProcessEvent wants. 120 // 121 UNREFERENCED_PARAMETER(CurrentIrql); 122 123 return CallerSpecifiedProcessingOnDifferentThread; 124 } 125 126 _Must_inspect_result_ 127 NTSTATUS 128 FxUsbIdleInfo::Initialize( 129 VOID 130 ) 131 { 132 HRESULT hr; 133 NTSTATUS status; 134 FxDevice* device; 135 IWudfDeviceStack *devStack; 136 137 device = ((FxPkgPnp*)m_CallbackInfo.IdleContext)->GetDevice(); 138 devStack = device->GetDeviceStack(); 139 140 hr = devStack->InitializeUsbSS(); 141 if (S_OK == hr) { 142 status = STATUS_SUCCESS; 143 } 144 else { 145 PUMDF_VERSION_DATA driverVersion = devStack->GetMinDriverVersion(); 146 BOOL preserveCompat = 147 devStack->ShouldPreserveIrpCompletionStatusCompatibility(); 148 149 status = CHostFxUtil::NtStatusFromHr(hr, 150 driverVersion->MajorNumber, 151 driverVersion->MinorNumber, 152 preserveCompat); 153 } 154 155 return status; 156 } 157 158 VOID 159 FxPkgPnp::PowerPolicySubmitUsbIdleNotification( 160 VOID 161 ) 162 { 163 // 164 // This will be set to TRUE if USBSS completion event gets dropped. 165 // 166 m_PowerPolicyMachine.m_Owner->m_UsbIdle->m_EventDropped = FALSE; 167 168 m_Device->GetDeviceStack()->SubmitUsbIdleNotification( 169 &(m_PowerPolicyMachine.m_Owner->m_UsbIdle->m_CallbackInfo), 170 _PowerPolicyUsbSelectiveSuspendCompletionRoutine, 171 this); 172 } 173 174 VOID 175 FxPkgPnp::PowerPolicyCancelUsbSS( 176 VOID 177 ) 178 { 179 m_Device->GetDeviceStack()->CancelUsbSS(); 180 } 181 182 __drv_maxIRQL(PASSIVE_LEVEL) 183 VOID 184 FxUsbIdleInfo::_UsbIdleCallback( 185 __in PVOID Context 186 ) 187 { 188 FxPkgPnp* pPkgPnp; 189 190 pPkgPnp = (FxPkgPnp*) Context; 191 192 DoTraceLevelMessage( 193 pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 194 "Entering USB Selective Suspend Idle callback"); 195 196 pPkgPnp->PowerPolicyProcessEvent(PwrPolUsbSelectiveSuspendCallback); 197 } 198 199 200 VOID 201 FxPowerPolicyMachine::UsbSSCallbackProcessingComplete( 202 VOID 203 ) 204 { 205 FxDevice* device = m_PkgPnp->GetDevice(); 206 207 DoTraceLevelMessage( 208 m_PkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 209 "USB Selective Suspend Idle callback processing is complete"); 210 211 device->GetDeviceStack()->SignalUsbSSCallbackProcessingComplete(); 212 } 213 214