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 STDCALL
158 FxPkgFdo::_SystemPowerS0Completion(
159     __in MdDeviceObject DeviceObject,
160     __in MdIrp OriginalIrp,
161     __in PVOID Context
162     )
163 {
164     FxPkgPnp* pPkgPnp;
165     KIRQL irql;
166     FxIrp irp(OriginalIrp);
167 
168     pPkgPnp = (FxPkgPnp*) Context;
169 
170     //
171     // Ideally we would like to complete the S0 irp before we start
172     // processing the event in the state machine so that the D0 irp
173     // comes after the S0 is moving up the stack...
174     //
175     // ... BUT ...
176     //
177     // ... by allowing the S0 irp to go up the stack first, we must then
178     // handle pnp requests from the current power policy state (because
179     // the S0 irp could be the last S irp in the system and when completed,
180     // the pnp lock is released).  So, we process the event first so
181     // that we can move into a state where we can handle pnp events in
182     // the power policy state machine.
183     //
184     // We mitigate the situation a little bit by forcing the processing of the
185     // event to occur on the power policy thread rather then in the current
186     // context.
187     //
188     Mx::MxRaiseIrql(DISPATCH_LEVEL, &irql);
189     pPkgPnp->PowerPolicyProcessEvent(PwrPolS0);
190     Mx::MxLowerIrql(irql);
191 
192     irp.StartNextPowerIrp();
193 
194     //
195     // Let the irp continue on its way
196     //
197     if (irp.PendingReturned()) {
198         irp.MarkIrpPending();
199     }
200 
201     Mx::MxReleaseRemoveLock((&FxDevice::_GetFxWdmExtension(DeviceObject)->IoRemoveLock),
202                             OriginalIrp);
203 
204     return irp.GetStatus();
205 }
206 
207 _Must_inspect_result_
208 NTSTATUS
209 STDCALL
210 FxPkgFdo::_SystemPowerSxCompletion(
211     __in MdDeviceObject DeviceObject,
212     __in MdIrp OriginalIrp,
213     __in PVOID Context
214     )
215 {
216     FxPkgFdo *pThis;
217     FxIrp irp(OriginalIrp);
218 
219     UNREFERENCED_PARAMETER(DeviceObject);
220 
221     pThis = (FxPkgFdo*) Context;
222 
223     ASSERT(pThis->IsPowerPolicyOwner());
224     ASSERT(OriginalIrp == pThis->GetPendingSystemPowerIrp());
225 
226     pThis->PowerPolicyProcessEvent(PwrPolSx);
227 
228     //
229     // Power policy will complete the system irp
230     //
231     return STATUS_MORE_PROCESSING_REQUIRED;
232 }
233 
234 _Must_inspect_result_
235 NTSTATUS
236 FxPkgFdo::DispatchSystemSetPower(
237     __in FxIrp *Irp
238     )
239 {
240     NTSTATUS status;
241     MxDeviceObject deviceObject(m_Device->GetDeviceObject());
242 
243     m_SystemPowerState = (BYTE) Irp->GetParameterPowerStateSystemState();
244     deviceObject.SetPowerState(SystemPowerState,
245                                Irp->GetParameterPowerState());
246 
247     if (IsPowerPolicyOwner()) {
248         //
249         // If we are going to S0, we just notify the power policy state machine
250         // and then let the request go (per the fast resume spec).  Otherwise,
251         // send the request down and on the way up, send the Dx request.
252         //
253         if (m_SystemPowerState == PowerSystemWorking) {
254             //
255             // Post the event into the state machine when the irp is going up
256             // the stack.  See the comment in _SystemPowerS0Completion for more
257             // detail as to why.
258             //
259             Irp->CopyCurrentIrpStackLocationToNext();
260             Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
261                                         _SystemPowerS0Completion,
262                                         this);
263 
264             return Irp->PoCallDriver(m_Device->GetAttachedDevice());
265         }
266         else {
267             //
268             // Stash away the irp for the power policy state machine.  We will
269             // post the event to the power policy state machine when the S irp
270             // completes back to this driver.
271             //
272             SetPendingSystemPowerIrp(Irp);
273 
274             Irp->CopyCurrentIrpStackLocationToNext();
275             Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
276                                         _SystemPowerSxCompletion,
277                                         this);
278 
279             Irp->PoCallDriver(m_Device->GetAttachedDevice());
280 
281             status = STATUS_PENDING;
282         }
283     }
284     else {
285         //
286         // We don't do anything with S irps if we are not the power policy
287         // owner.
288         //
289         // This will release the remove lock as well.
290         //
291         status = _PowerPassDown(this, Irp);
292     }
293 
294     return status;
295 }
296 
297 _Must_inspect_result_
298 NTSTATUS
299 FxPkgFdo::DispatchDeviceSetPower(
300     __in FxIrp *Irp
301     )
302 
303 {
304     NTSTATUS status;
305 
306     if (IsPowerPolicyOwner()) {
307         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp == FALSE &&
308             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp == FALSE) {
309             //
310             // A power irp arrived, but we did not request it.  log and bugcheck
311             //
312             DoTraceLevelMessage(
313                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
314                 "Received set device power irp 0x%p on WDFDEVICE 0x%p !devobj 0x%p, "
315                 "but the irp was not requested by the device (the power policy owner)",
316                 Irp->GetIrp(), m_Device->GetHandle(),
317                 m_Device->GetDeviceObject());
318 
319             FxVerifierBugCheck(GetDriverGlobals(),  // globals
320                    WDF_POWER_MULTIPLE_PPO, // specific type
321                    (ULONG_PTR)m_Device->GetDeviceObject(), //parm 2
322                    (ULONG_PTR)Irp->GetIrp());  // parm 3
323 
324             /* NOTREACHED */
325         }
326 
327         //
328         // We are no longer requesting a power irp because we received the one
329         // we requested.
330         //
331         if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp) {
332             m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp = FALSE;
333         } else {
334             m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp = FALSE;
335         }
336     }
337 
338     //
339     // Determine if we are raising or lowering the device power state.
340     //
341     if (Irp->GetParameterPowerStateDeviceState() == PowerDeviceD0) {
342         status = RaiseDevicePower(Irp);
343     }
344     else {
345         status = LowerDevicePower(Irp);
346     }
347 
348     return status;
349 }
350 
351 _Must_inspect_result_
352 NTSTATUS
353 FxPkgFdo::RaiseDevicePower(
354     __in FxIrp *Irp
355     )
356 {
357     Irp->MarkIrpPending();
358     Irp->CopyCurrentIrpStackLocationToNext();
359     Irp->SetCompletionRoutineEx(m_Device->GetDeviceObject(),
360                                 RaiseDevicePowerCompletion,
361                                 this);
362 
363     Irp->PoCallDriver(m_Device->GetAttachedDevice());
364 
365     return STATUS_PENDING;
366 }
367 
368 _Must_inspect_result_
369 NTSTATUS
370 STDCALL
371 FxPkgFdo::RaiseDevicePowerCompletion(
372     __in MdDeviceObject DeviceObject,
373     __in MdIrp OriginalIrp,
374     __in PVOID Context
375     )
376 {
377     FxPkgFdo* pThis;
378     FxIrp irp(OriginalIrp);
379 
380     UNREFERENCED_PARAMETER(DeviceObject);
381 
382     pThis = (FxPkgFdo*) Context;
383 
384     //
385     // We can safely cache away the device power irp in our fdo package
386     // storage because we know we can only get one device power irp at
387     // a time.
388     //
389     pThis->SetPendingDevicePowerIrp(&irp);
390 
391     //
392     // Kick off the power state machine.
393     //
394     pThis->PowerProcessEvent(PowerD0);
395 
396     return STATUS_MORE_PROCESSING_REQUIRED;
397 }
398 
399 _Must_inspect_result_
400 NTSTATUS
401 FxPkgFdo::LowerDevicePower(
402     __in FxIrp *Irp
403     )
404 {
405     SetPendingDevicePowerIrp(Irp);
406 
407     //
408     // Kick off the power state machine.
409     //
410     PowerProcessEvent(PowerDx);
411 
412     return STATUS_PENDING;
413 }
414 
415 _Must_inspect_result_
416 NTSTATUS
417 FxPkgFdo::DispatchSystemQueryPower(
418     __in FxIrp *Irp
419     )
420 {
421     if (PowerPolicyIsWakeEnabled()) {
422         NTSTATUS status;
423 
424         status = PowerPolicyHandleSystemQueryPower(
425             Irp->GetParameterPowerStateSystemState()
426             );
427 
428         Irp->SetStatus(status);
429 
430         if (!NT_SUCCESS(status)) {
431             return CompletePowerRequest(Irp, status);
432         }
433     }
434 
435     //
436     // Passing down the irp because one of the following
437     // a) We don't care b/c we don't control the power policy
438     // b) we are not enabled for arming for wake from Sx
439     // c) we can wake from the queried S state
440     //
441     return _PowerPassDown(this, Irp);
442 }
443 
444 _Must_inspect_result_
445 NTSTATUS
446 FxPkgFdo::DispatchDeviceQueryPower(
447     __in FxIrp *Irp
448     )
449 {
450     //
451     // Either the framework is the power policy owner and we wouldn't be sending
452     // a device query power or we are a subordinate will do what the power
453     // policy owner wants 100% of the time.
454     //
455     Irp->SetStatus(STATUS_SUCCESS);
456 
457     //
458     // This will release the remove lock
459     //
460     return _PowerPassDown(this, Irp);
461 }
462 
463 VOID
464 FxPkgFdo::PowerReleasePendingDeviceIrp(
465     __in BOOLEAN IrpMustBePresent
466     )
467 {
468     MdIrp pIrp;
469 
470     pIrp = ClearPendingDevicePowerIrp();
471 
472     UNREFERENCED_PARAMETER(IrpMustBePresent);
473     ASSERT(IrpMustBePresent == FALSE || pIrp != NULL);
474 
475     if (pIrp != NULL) {
476         FxIrp irp(pIrp);
477 
478         if (irp.GetParameterPowerStateDeviceState() == PowerDeviceD0) {
479             //
480             // We catch D0 irps on the way up, so complete it
481             //
482             CompletePowerRequest(&irp, STATUS_SUCCESS);
483         }
484         else {
485             irp.SetStatus(STATUS_SUCCESS);
486 
487             //
488             // We catch Dx irps on the way down, so send it on its way
489             //
490             // This will also release the remove lock
491             //
492             (void) _PowerPassDown(this, &irp);
493         }
494     }
495 }
496 
497 WDF_DEVICE_POWER_STATE
498 FxPkgFdo::PowerCheckDeviceTypeOverload(
499     VOID
500     )
501 /*++
502 
503 Routine Description:
504     This function implements the Check Type state.  This is FDO code,
505     so the answer is reductionistly simple.
506 
507 Arguments:
508     none
509 
510 Return Value:
511 
512     new power state
513 
514 --*/
515 {
516     return WdfDevStatePowerWaking;
517 }
518 
519 WDF_DEVICE_POWER_STATE
520 FxPkgFdo::PowerCheckDeviceTypeNPOverload(
521     VOID
522     )
523 /*++
524 
525 Routine Description:
526     This function implements the Check Type state.  This is FDO code,
527     so the answer is reductionistly simple.
528 
529 Arguments:
530     none
531 
532 Return Value:
533 
534     new power state
535 
536 --*/
537 {
538     return WdfDevStatePowerWakingNP;
539 }
540 
541 _Must_inspect_result_
542 NTSTATUS
543 FxPkgFdo::PowerCheckParentOverload(
544     __out BOOLEAN* ParentOn
545     )
546 /*++
547 
548 Routine Description:
549     This function implements the CheckParent state.  Its
550     job is to determine which state we should go to next based on whether
551     the parent is in D0.  But since this is the FDO code, we can't know
552     that.  So just assume that the PDO will guarantee it.
553 
554 Arguments:
555     none
556 
557 Return Value:
558 
559     new power state
560 
561 --*/
562 {
563     ASSERT(!"This state shouldn't be reachable for an FDO.");
564     *ParentOn = TRUE;
565     return STATUS_SUCCESS;
566 }
567 
568 VOID
569 FxPkgFdo::PowerParentPowerDereference(
570     VOID
571     )
572 /*++
573 
574 Routine Description:
575     This virtual function is a nop for an FDO.  PDOs implement this function
576 
577 Arguments:
578     None
579 
580 Return Value:
581     None
582 
583   --*/
584 {
585     DO_NOTHING();
586 }
587