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