1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     PdoPower.cpp
8 
9 Abstract:
10 
11     This module implements the Pnp package for Pdo devices.
12 
13 Author:
14 
15 
16 
17 
18 Environment:
19 
20     Both kernel and user mode
21 
22 Revision History:
23 
24 
25 
26 --*/
27 
28 #include "pnppriv.hpp"
29 
30 // Tracing support
31 #if defined(EVENT_TRACING)
32 extern "C" {
33 #include "PdoPower.tmh"
34 }
35 #endif
36 
37 _Must_inspect_result_
38 NTSTATUS
_DispatchPowerSequence(__inout FxPkgPnp * This,__in FxIrp * Irp)39 FxPkgPdo::_DispatchPowerSequence(
40     __inout FxPkgPnp* This,
41     __in FxIrp *Irp
42     )
43 /*++
44 
45 Routine Description:
46     report the power sequence for the child
47 
48 Arguments:
49     This - the package
50 
51     Irp - the request
52 
53 Return Value:
54     STATUS_NOT_SUPPORTED
55 
56   --*/
57 {
58     return ((FxPkgPdo*) This)->CompletePowerRequest(Irp, STATUS_NOT_SUPPORTED);
59 }
60 
61 _Must_inspect_result_
62 NTSTATUS
_DispatchSetPower(__inout FxPkgPnp * This,__in FxIrp * Irp)63 FxPkgPdo::_DispatchSetPower(
64     __inout FxPkgPnp* This,
65     __in FxIrp *Irp
66     )
67 /*++
68 
69 Routine Description:
70 
71     This method is invoked when a SetPower IRP enters the driver.
72 
73 Arguemnts:
74 
75     Device - a pointer to the FxDevice
76 
77     Irp - a pointer to the FxIrp
78 
79 Returns:
80 
81     NTSTATUS
82 
83 --*/
84 {
85     if (Irp->GetParameterPowerType() == SystemPowerState) {
86         return ((FxPkgPdo*) This)->DispatchSystemSetPower(Irp);
87     }
88     else {
89         return ((FxPkgPdo*) This)->DispatchDeviceSetPower(Irp);
90     }
91 }
92 
93 _Must_inspect_result_
94 NTSTATUS
DispatchSystemSetPower(__in FxIrp * Irp)95 FxPkgPdo::DispatchSystemSetPower(
96     __in FxIrp *Irp
97     )
98 {
99     KIRQL irql;
100     MxDeviceObject deviceObject(m_Device->GetDeviceObject());
101 
102     m_SystemPowerState = (BYTE) Irp->GetParameterPowerStateSystemState();
103     deviceObject.SetPowerState(SystemPowerState,
104                                Irp->GetParameterPowerState());
105 
106     if (IsPowerPolicyOwner()) {
107         if (m_SystemPowerState == PowerSystemWorking) {
108             //
109             // Ideally we would like to complete the S0 irp before we start
110             // processing the event in the state machine so that the D0 irp
111             // comes after the S0 is moving up the stack...
112             //
113             // ... BUT ...
114             //
115             // ... by allowing the S0 irp to go up the stack first, we must then
116             // handle pnp requests from the current power policy state (because
117             // the S0 irp could be the last S irp in the system and when completed,
118             // the pnp lock is released).  So, we process the event first so
119             // that we can move into a state where we can handle pnp events in
120             // the power policy state machine.
121             //
122             // We mitigate the situation a little bit by forcing the processing of the
123             // event to occur on the power policy thread rather then in the current
124             // context.
125             //
126             Mx::MxRaiseIrql(DISPATCH_LEVEL, &irql);
127             PowerPolicyProcessEvent(PwrPolS0);
128             Mx::MxLowerIrql(irql);
129 
130             return CompletePowerRequest(Irp, STATUS_SUCCESS);
131         }
132         else {
133             //
134             // Power policy state machine will complete the request later
135             //
136             SetPendingSystemPowerIrp(Irp);
137             PowerPolicyProcessEvent(PwrPolSx);
138             return STATUS_PENDING;
139         }
140     }
141     else {
142         //
143         // Since we are not the power policy owner, we just complete all S irps
144         //
145         return CompletePowerRequest(Irp, STATUS_SUCCESS);
146     }
147 }
148 
149 _Must_inspect_result_
150 NTSTATUS
DispatchDeviceSetPower(__in FxIrp * Irp)151 FxPkgPdo::DispatchDeviceSetPower(
152     __in FxIrp *Irp
153     )
154 {
155     if (IsPowerPolicyOwner()) {
156         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp == FALSE &&
157             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp == FALSE) {
158             //
159             // A power irp arrived, but we did not request it.  ASSERT and log
160             // an error.
161             //
162             DoTraceLevelMessage(
163                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
164                 "Received set device power irp 0x%p on WDFDEVICE 0x%p !devobj 0x%p, "
165                 "but the irp was not requested by the device (the power policy owner)",
166                 Irp->GetIrp(),
167                 m_Device->GetHandle(),
168                 m_Device->GetDeviceObject());
169 
170             ASSERTMSG("Received set device power irp but the irp was not "
171                 "requested by the device (the power policy owner)\n",
172                 FALSE);
173         }
174 
175         //
176         // We are no longer requesting a power irp because we received the one
177         // we requested.
178         //
179         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp) {
180             m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp = FALSE;
181         }
182         else {
183             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp = FALSE;
184         }
185     }
186 
187     SetPendingDevicePowerIrp(Irp);
188 
189     if (Irp->GetParameterPowerStateDeviceState() == PowerDeviceD0) {
190         PowerProcessEvent(PowerD0);
191     }
192     else {
193         PowerProcessEvent(PowerDx);
194     }
195 
196     return STATUS_PENDING;
197 }
198 
199 _Must_inspect_result_
200 NTSTATUS
_DispatchQueryPower(__inout FxPkgPnp * This,__in FxIrp * Irp)201 FxPkgPdo::_DispatchQueryPower(
202     __inout FxPkgPnp* This,
203     __in FxIrp *Irp
204     )
205 /*++
206 
207 Routine Description:
208     Dispatches query power for system and device requests
209 
210 Arguments:
211     This - the package
212 
213     Irp - the query power request
214 
215 Return Value:
216     NTSTATUS
217 
218   --*/
219 {
220     FxPkgPdo* pThis;
221     NTSTATUS status;
222 
223     pThis = ((FxPkgPdo*) This);
224 
225     if (Irp->GetParameterPowerType() == SystemPowerState
226         &&
227         This->PowerPolicyIsWakeEnabled()) {
228 
229         status = pThis->PowerPolicyHandleSystemQueryPower(
230             Irp->GetParameterPowerStateSystemState()
231             );
232     }
233     else {
234         status = STATUS_SUCCESS;
235     }
236 
237     return pThis->CompletePowerRequest(Irp, status);
238 }
239 
240 VOID
PowerReleasePendingDeviceIrp(__in BOOLEAN IrpMustBePresent)241 FxPkgPdo::PowerReleasePendingDeviceIrp(
242     __in BOOLEAN IrpMustBePresent
243     )
244 {
245     MdIrp pIrp;
246 
247     pIrp = ClearPendingDevicePowerIrp();
248 
249     UNREFERENCED_PARAMETER(IrpMustBePresent);
250     ASSERT(IrpMustBePresent == FALSE || pIrp != NULL);
251 
252     if (pIrp != NULL) {
253         FxIrp irp(pIrp);
254 
255         CompletePowerRequest(&irp, STATUS_SUCCESS);
256     }
257 }
258 
259 _Must_inspect_result_
260 NTSTATUS
PowerCheckParentOverload(__in BOOLEAN * ParentOn)261 FxPkgPdo::PowerCheckParentOverload(
262     __in BOOLEAN* ParentOn
263     )
264 /*++
265 
266 Routine Description:
267     This function implements the CheckParent state.  Its
268     job is to determine which state we should go to next based on whether
269     the parent is in D0.
270 
271 Arguments:
272     none
273 
274 Return Value:
275 
276     VOID
277 
278 --*/
279 {
280 
281 
282 
283 
284     return (m_Device->m_ParentDevice->m_PkgPnp)->
285         PowerPolicyCanChildPowerUp(ParentOn);
286 }
287 
288 WDF_DEVICE_POWER_STATE
PowerCheckDeviceTypeOverload(VOID)289 FxPkgPdo::PowerCheckDeviceTypeOverload(
290     VOID
291     )
292 /*++
293 
294 Routine Description:
295     This function implements the Check Type state.  This is a PDO.
296 
297 Arguments:
298     none
299 
300 Return Value:
301 
302     new power state
303 
304 --*/
305 {
306     return WdfDevStatePowerCheckParentState;
307 }
308 
309 WDF_DEVICE_POWER_STATE
PowerCheckDeviceTypeNPOverload(VOID)310 FxPkgPdo::PowerCheckDeviceTypeNPOverload(
311     VOID
312     )
313 /*++
314 
315 Routine Description:
316     This function implements the Check Type state.  This is a PDO.
317 
318 Arguments:
319     none
320 
321 Return Value:
322 
323     new power state
324 
325 --*/
326 {
327     return WdfDevStatePowerCheckParentStateNP;
328 }
329 
330 _Must_inspect_result_
331 NTSTATUS
PowerEnableWakeAtBusOverload(VOID)332 FxPkgPdo::PowerEnableWakeAtBusOverload(
333     VOID
334     )
335 /*++
336 
337 Routine Description:
338     Arms the device at the bus level for wake.  This arming is generic since
339     the bus driver can only configure the device generically.  The power policy
340     owner has already armed the device for wake in a device specific fashion
341     when it processed the wake irp (EvtDeviceArmDeviceForWakeFromS0/x if the ppo
342     is a WDF driver).
343 
344 Arguments:
345     None
346 
347 Return Value:
348     NTSTATUS, !NT_SUCCESS if the arm failed
349 
350   --*/
351 {
352     NTSTATUS status;
353 
354     //
355     // The EnableWakeAtBus callback should not be called twice in a row without
356     // an intermediate call to the DisableWakeAtBus callback.
357     //
358     ASSERT(m_EnableWakeAtBusInvoked == FALSE);
359 
360     status = m_DeviceEnableWakeAtBus.Invoke(
361         m_Device->GetHandle(),
362         (SYSTEM_POWER_STATE) m_SystemPowerState
363         );
364 
365     if (NT_SUCCESS(status)) {
366         m_EnableWakeAtBusInvoked = TRUE;
367         PowerNotifyParentChildWakeArmed();
368     }
369 
370     return status;
371 }
372 
373 VOID
PowerDisableWakeAtBusOverload(VOID)374 FxPkgPdo::PowerDisableWakeAtBusOverload(
375     VOID
376     )
377 /*++
378 
379 Routine Description:
380     Disarms the device at the bus level for wake.  This disarming is generic
381     since the bus driver can only configure the device generically.  The power
382     policy owner may have already disarmed the device for wake in a device
383     specific fashion.  For a WDF ppo EvtDeviceDisarmDeviceForWakeFromS0/x is
384     called after the bus has disarmed.
385 
386 Arguments:
387     None
388 
389 Return Value:
390     None
391 
392   --*/
393 {
394     if (m_EnableWakeAtBusInvoked) {
395         m_EnableWakeAtBusInvoked = FALSE;
396         PowerNotifyParentChildWakeDisarmed();
397 
398         m_DeviceDisableWakeAtBus.Invoke(m_Device->GetHandle());
399     }
400 }
401 
402 VOID
PowerParentPowerDereference(VOID)403 FxPkgPdo::PowerParentPowerDereference(
404     VOID
405     )
406 /*++
407 
408 Routine Description:
409     Releases the child power reference on the parent device.  This allows the
410     parent to enter into an idle capable state.  This power reference does not
411     prevent the parent from moving into Dx when the system power state changes.
412 
413 Arguments:
414     None
415 
416 Return Value:
417     None
418 
419   --*/
420 {
421 
422     m_Device->m_ParentDevice->m_PkgPnp->PowerPolicyChildPoweredDown();
423 }
424 
425 VOID
PowerNotifyParentChildWakeArmed(VOID)426 FxPkgPdo::PowerNotifyParentChildWakeArmed(
427     VOID
428     )
429 /*++
430 
431 Routine Description:
432     Notifies the parent device that the child is armed for wake.  This will
433     cause the parent to increment its count of children armed for wake.
434 
435 Arguments:
436     None
437 
438 Return Value:
439     None
440 
441   --*/
442 {
443     FxPowerPolicyOwnerSettings* settings;
444 
445     ASSERT(m_Device->m_ParentDevice != NULL);
446 
447 
448 
449 
450 
451     settings = m_Device->m_ParentDevice->m_PkgPnp->
452         m_PowerPolicyMachine.m_Owner;
453     if (settings != NULL) {
454         settings->IncrementChildrenArmedForWakeCount();
455     }
456 }
457 
458 VOID
PowerNotifyParentChildWakeDisarmed(VOID)459 FxPkgPdo::PowerNotifyParentChildWakeDisarmed(
460     VOID
461     )
462 /*++
463 
464 Routine Description:
465     Notifies the parent device that the child is not armed for wake.  This will
466     cause the parent to decrement its count of children armed for wake.
467 
468 Arguments:
469     None
470 
471 Return Value:
472     None
473 
474   --*/
475 {
476     FxPowerPolicyOwnerSettings* settings;
477 
478     ASSERT(m_Device->m_ParentDevice != NULL);
479 
480 
481 
482 
483 
484     settings = m_Device->m_ParentDevice->m_PkgPnp->
485         m_PowerPolicyMachine.m_Owner;
486     if (settings != NULL) {
487         settings->DecrementChildrenArmedForWakeCount();
488     }
489 }
490