1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FdoPower.cpp
8 
9 Abstract:
10 
11     This module implements the pnp/power package for the driver
12     framework.  This, specifically, is the power code.
13 
14 Author:
15 
16 
17 
18 
19 Environment:
20 
21     Both kernel and user mode
22 
23 Revision History:
24 
25 
26 
27 
28 --*/
29 
30 #include "pnppriv.hpp"
31 
32 
33 
34 
35 #if defined(EVENT_TRACING)
36 extern "C" {
37 #include "FdoPower.tmh"
38 }
39 #endif
40 
41 _Must_inspect_result_
42 NTSTATUS
43 FxPkgFdo::_PowerPassDown(
44     __inout FxPkgPnp* This,
45     __in FxIrp *Irp
46     )
47 
48 /*++
49 
50 Routine Description:
51 
52     This method is invoked when a Power Irp we don't handle comes into the
53     driver.
54 
55 Arguemnts:
56 
57     This - the package
58 
59     Irp - a pointer to the FxIrp
60 
61 Returns:
62 
63     NTSTATUS
64 
65 --*/
66 {
67     FxPkgFdo* pThis;
68     NTSTATUS status;
69     MdIrp pIrp;
70 
71     pIrp = Irp->GetIrp();
72     pThis = (FxPkgFdo*) This;
73 
74     //
75     // FDOs don't handle this IRP, so simply pass it down.
76     //
77     Irp->StartNextPowerIrp();
78     Irp->CopyCurrentIrpStackLocationToNext();
79 
80     status = Irp->PoCallDriver(pThis->m_Device->GetAttachedDevice());
81 
82     Mx::MxReleaseRemoveLock(pThis->m_Device->GetRemoveLock(),
83                             pIrp);
84 
85     return status;
86 }
87 
88 _Must_inspect_result_
89 NTSTATUS
90 FxPkgFdo::_DispatchSetPower(
91     __inout FxPkgPnp* This,
92     __in FxIrp *Irp
93     )
94 /*++
95 
96 Routine Description:
97 
98     This method is invoked when a SetPower IRP enters the driver.
99 
100 Arguemnts:
101 
102     Device - a pointer to the FxDevice
103 
104     Irp - a pointer to the FxIrp
105 
106 Returns:
107 
108     NTSTATUS
109 
110 --*/
111 
112 {
113     if (Irp->GetParameterPowerType() == SystemPowerState) {
114         return ((FxPkgFdo*) This)->DispatchSystemSetPower(Irp);
115     }
116     else {
117         return ((FxPkgFdo*) This)->DispatchDeviceSetPower(Irp);
118     }
119 }
120 
121 _Must_inspect_result_
122 NTSTATUS
123 FxPkgFdo::_DispatchQueryPower(
124     __inout FxPkgPnp* This,
125     __in FxIrp *Irp
126     )
127 
128 /*++
129 
130 Routine Description:
131 
132     This method is invoked when a QueryPower IRP enters the driver.
133 
134 Arguemnts:
135 
136     This - The package
137 
138     Irp - a pointer to the FxIrp
139 
140 Returns:
141 
142     NTSTATUS
143 
144 --*/
145 
146 {
147     if (Irp->GetParameterPowerType() == SystemPowerState) {
148         return ((FxPkgFdo*) This)->DispatchSystemQueryPower(Irp);
149     }
150     else {
151         return ((FxPkgFdo*) This)->DispatchDeviceQueryPower(Irp);
152     }
153 }
154 
155 _Must_inspect_result_
156 NTSTATUS
157 FxPkgFdo::_SystemPowerS0Completion(
158     __in MdDeviceObject DeviceObject,
159     __in MdIrp OriginalIrp,
160     __in PVOID Context
161     )
162 {
163     FxPkgPnp* pPkgPnp;
164     KIRQL irql;
165     FxIrp irp(OriginalIrp);
166 
167     pPkgPnp = (FxPkgPnp*) Context;
168 
169     //
170     // Ideally we would like to complete the S0 irp before we start
171     // processing the event in the state machine so that the D0 irp
172     // comes after the S0 is moving up the stack...
173     //
174     // ... BUT ...
175     //
176     // ... by allowing the S0 irp to go up the stack first, we must then
177     // handle pnp requests from the current power policy state (because
178     // the S0 irp could be the last S irp in the system and when completed,
179     // the pnp lock is released).  So, we process the event first so
180     // that we can move into a state where we can handle pnp events in
181     // the power policy state machine.
182     //
183     // We mitigate the situation a little bit by forcing the processing of the
184     // event to occur on the power policy thread rather then in the current
185     // context.
186     //
187     Mx::MxRaiseIrql(DISPATCH_LEVEL, &irql);
188     pPkgPnp->PowerPolicyProcessEvent(PwrPolS0);
189     Mx::MxLowerIrql(irql);
190 
191     irp.StartNextPowerIrp();
192 
193     //
194     // Let the irp continue on its way
195     //
196     if (irp.PendingReturned()) {
197         irp.MarkIrpPending();
198     }
199 
200     Mx::MxReleaseRemoveLock((&FxDevice::_GetFxWdmExtension(DeviceObject)->IoRemoveLock),
201                             OriginalIrp);
202 
203     return irp.GetStatus();
204 }
205 
206 _Must_inspect_result_
207 NTSTATUS
208 FxPkgFdo::_SystemPowerSxCompletion(
209     __in MdDeviceObject DeviceObject,
210     __in MdIrp OriginalIrp,
211     __in PVOID Context
212     )
213 {
214     FxPkgFdo *pThis;
215     FxIrp irp(OriginalIrp);
216 
217     UNREFERENCED_PARAMETER(DeviceObject);
218 
219     pThis = (FxPkgFdo*) Context;
220 
221     ASSERT(pThis->IsPowerPolicyOwner());
222     ASSERT(OriginalIrp == pThis->GetPendingSystemPowerIrp());
223 
224     pThis->PowerPolicyProcessEvent(PwrPolSx);
225 
226     //
227     // Power policy will complete the system irp
228     //
229     return STATUS_MORE_PROCESSING_REQUIRED;
230 }
231 
232 _Must_inspect_result_
233 NTSTATUS
234 FxPkgFdo::DispatchSystemSetPower(
235     __in FxIrp *Irp
236     )
237 {
238     NTSTATUS status;
239     MxDeviceObject deviceObject(m_Device->GetDeviceObject());
240 
241     m_SystemPowerState = (BYTE) Irp->GetParameterPowerStateSystemState();
242     deviceObject.SetPowerState(SystemPowerState,
243                                Irp->GetParameterPowerState());
244 
245     if (IsPowerPolicyOwner()) {
246         //
247         // If we are going to S0, we just notify the power policy state machine
248         // and then let the request go (per the fast resume spec).  Otherwise,
249         // send the request down and on the way up, send the Dx request.
250         //
251         if (m_SystemPowerState == PowerSystemWorking) {
252             //
253             // Post the event into the state machine when the irp is going up
254             // the stack.  See the comment in _SystemPowerS0Completion for more
255             // detail as to why.
256             //
257             Irp->CopyCurrentIrpStackLocationToNext();
258             Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
259                                         _SystemPowerS0Completion,
260                                         this);
261 
262             return Irp->PoCallDriver(m_Device->GetAttachedDevice());
263         }
264         else {
265             //
266             // Stash away the irp for the power policy state machine.  We will
267             // post the event to the power policy state machine when the S irp
268             // completes back to this driver.
269             //
270             SetPendingSystemPowerIrp(Irp);
271 
272             Irp->CopyCurrentIrpStackLocationToNext();
273             Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
274                                         _SystemPowerSxCompletion,
275                                         this);
276 
277             Irp->PoCallDriver(m_Device->GetAttachedDevice());
278 
279             status = STATUS_PENDING;
280         }
281     }
282     else {
283         //
284         // We don't do anything with S irps if we are not the power policy
285         // owner.
286         //
287         // This will release the remove lock as well.
288         //
289         status = _PowerPassDown(this, Irp);
290     }
291 
292     return status;
293 }
294 
295 _Must_inspect_result_
296 NTSTATUS
297 FxPkgFdo::DispatchDeviceSetPower(
298     __in FxIrp *Irp
299     )
300 
301 {
302     NTSTATUS status;
303 
304     if (IsPowerPolicyOwner()) {
305         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp == FALSE &&
306             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp == FALSE) {
307             //
308             // A power irp arrived, but we did not request it.  log and bugcheck
309             //
310             DoTraceLevelMessage(
311                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
312                 "Received set device power irp 0x%p on WDFDEVICE 0x%p !devobj 0x%p, "
313                 "but the irp was not requested by the device (the power policy owner)",
314                 Irp->GetIrp(), m_Device->GetHandle(),
315                 m_Device->GetDeviceObject());
316 
317             FxVerifierBugCheck(GetDriverGlobals(),  // globals
318                    WDF_POWER_MULTIPLE_PPO, // specific type
319                    (ULONG_PTR)m_Device->GetDeviceObject(), //parm 2
320                    (ULONG_PTR)Irp->GetIrp());  // parm 3
321 
322             /* NOTREACHED */
323         }
324 
325         //
326         // We are no longer requesting a power irp because we received the one
327         // we requested.
328         //
329         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp) {
330             m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp = FALSE;
331         } else {
332             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp = FALSE;
333         }
334     }
335 
336     //
337     // Determine if we are raising or lowering the device power state.
338     //
339     if (Irp->GetParameterPowerStateDeviceState() == PowerDeviceD0) {
340         status = RaiseDevicePower(Irp);
341     }
342     else {
343         status = LowerDevicePower(Irp);
344     }
345 
346     return status;
347 }
348 
349 _Must_inspect_result_
350 NTSTATUS
351 FxPkgFdo::RaiseDevicePower(
352     __in FxIrp *Irp
353     )
354 {
355     Irp->MarkIrpPending();
356     Irp->CopyCurrentIrpStackLocationToNext();
357     Irp->SetCompletionRoutineEx(m_Device->GetDeviceObject(),
358                                 RaiseDevicePowerCompletion,
359                                 this);
360 
361     Irp->PoCallDriver(m_Device->GetAttachedDevice());
362 
363     return STATUS_PENDING;
364 }
365 
366 _Must_inspect_result_
367 NTSTATUS
368 FxPkgFdo::RaiseDevicePowerCompletion(
369     __in MdDeviceObject DeviceObject,
370     __in MdIrp OriginalIrp,
371     __in PVOID Context
372     )
373 {
374     FxPkgFdo* pThis;
375     FxIrp irp(OriginalIrp);
376 
377     UNREFERENCED_PARAMETER(DeviceObject);
378 
379     pThis = (FxPkgFdo*) Context;
380 
381     //
382     // We can safely cache away the device power irp in our fdo package
383     // storage because we know we can only get one device power irp at
384     // a time.
385     //
386     pThis->SetPendingDevicePowerIrp(&irp);
387 
388     //
389     // Kick off the power state machine.
390     //
391     pThis->PowerProcessEvent(PowerD0);
392 
393     return STATUS_MORE_PROCESSING_REQUIRED;
394 }
395 
396 _Must_inspect_result_
397 NTSTATUS
398 FxPkgFdo::LowerDevicePower(
399     __in FxIrp *Irp
400     )
401 {
402     SetPendingDevicePowerIrp(Irp);
403 
404     //
405     // Kick off the power state machine.
406     //
407     PowerProcessEvent(PowerDx);
408 
409     return STATUS_PENDING;
410 }
411 
412 _Must_inspect_result_
413 NTSTATUS
414 FxPkgFdo::DispatchSystemQueryPower(
415     __in FxIrp *Irp
416     )
417 {
418     if (PowerPolicyIsWakeEnabled()) {
419         NTSTATUS status;
420 
421         status = PowerPolicyHandleSystemQueryPower(
422             Irp->GetParameterPowerStateSystemState()
423             );
424 
425         Irp->SetStatus(status);
426 
427         if (!NT_SUCCESS(status)) {
428             return CompletePowerRequest(Irp, status);
429         }
430     }
431 
432     //
433     // Passing down the irp because one of the following
434     // a) We don't care b/c we don't control the power policy
435     // b) we are not enabled for arming for wake from Sx
436     // c) we can wake from the queried S state
437     //
438     return _PowerPassDown(this, Irp);
439 }
440 
441 _Must_inspect_result_
442 NTSTATUS
443 FxPkgFdo::DispatchDeviceQueryPower(
444     __in FxIrp *Irp
445     )
446 {
447     //
448     // Either the framework is the power policy owner and we wouldn't be sending
449     // a device query power or we are a subordinate will do what the power
450     // policy owner wants 100% of the time.
451     //
452     Irp->SetStatus(STATUS_SUCCESS);
453 
454     //
455     // This will release the remove lock
456     //
457     return _PowerPassDown(this, Irp);
458 }
459 
460 VOID
461 FxPkgFdo::PowerReleasePendingDeviceIrp(
462     __in BOOLEAN IrpMustBePresent
463     )
464 {
465     MdIrp pIrp;
466 
467     pIrp = ClearPendingDevicePowerIrp();
468 
469     UNREFERENCED_PARAMETER(IrpMustBePresent);
470     ASSERT(IrpMustBePresent == FALSE || pIrp != NULL);
471 
472     if (pIrp != NULL) {
473         FxIrp irp(pIrp);
474 
475         if (irp.GetParameterPowerStateDeviceState() == PowerDeviceD0) {
476             //
477             // We catch D0 irps on the way up, so complete it
478             //
479             CompletePowerRequest(&irp, STATUS_SUCCESS);
480         }
481         else {
482             irp.SetStatus(STATUS_SUCCESS);
483 
484             //
485             // We catch Dx irps on the way down, so send it on its way
486             //
487             // This will also release the remove lock
488             //
489             (void) _PowerPassDown(this, &irp);
490         }
491     }
492 }
493 
494 WDF_DEVICE_POWER_STATE
495 FxPkgFdo::PowerCheckDeviceTypeOverload(
496     VOID
497     )
498 /*++
499 
500 Routine Description:
501     This function implements the Check Type state.  This is FDO code,
502     so the answer is reductionistly simple.
503 
504 Arguments:
505     none
506 
507 Return Value:
508 
509     new power state
510 
511 --*/
512 {
513     return WdfDevStatePowerWaking;
514 }
515 
516 WDF_DEVICE_POWER_STATE
517 FxPkgFdo::PowerCheckDeviceTypeNPOverload(
518     VOID
519     )
520 /*++
521 
522 Routine Description:
523     This function implements the Check Type state.  This is FDO code,
524     so the answer is reductionistly simple.
525 
526 Arguments:
527     none
528 
529 Return Value:
530 
531     new power state
532 
533 --*/
534 {
535     return WdfDevStatePowerWakingNP;
536 }
537 
538 _Must_inspect_result_
539 NTSTATUS
540 FxPkgFdo::PowerCheckParentOverload(
541     __out BOOLEAN* ParentOn
542     )
543 /*++
544 
545 Routine Description:
546     This function implements the CheckParent state.  Its
547     job is to determine which state we should go to next based on whether
548     the parent is in D0.  But since this is the FDO code, we can't know
549     that.  So just assume that the PDO will guarantee it.
550 
551 Arguments:
552     none
553 
554 Return Value:
555 
556     new power state
557 
558 --*/
559 {
560     ASSERT(!"This state shouldn't be reachable for an FDO.");
561     *ParentOn = TRUE;
562     return STATUS_SUCCESS;
563 }
564 
565 VOID
566 FxPkgFdo::PowerParentPowerDereference(
567     VOID
568     )
569 /*++
570 
571 Routine Description:
572     This virtual function is a nop for an FDO.  PDOs implement this function
573 
574 Arguments:
575     None
576 
577 Return Value:
578     None
579 
580   --*/
581 {
582     DO_NOTHING();
583 }
584