1 /*++
2 
3 Copyright (c) Microsoft. All rights reserved.
4 
5 Module Name:
6 
7     PowerIdleStateMachine.cpp
8 
9 Abstract:
10 
11     This module implements the Power Policy idle state machine for the driver
12     framework.
13 
14 Author:
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 extern "C" {
31 #if defined(EVENT_TRACING)
32 #include "PowerIdleStateMachine.tmh"
33 #endif
34 }
35 
36 const FxPowerIdleTargetState FxPowerIdleMachine::m_StoppedStates[] =
37 {
38     { PowerIdleEventStart, FxIdleStarted DEBUGGED_EVENT },
39 };
40 
41 const FxPowerIdleTargetState FxPowerIdleMachine::m_StartedStates[] =
42 {
43     { PowerIdleEventPowerUpComplete, FxIdleStartedPowerUp DEBUGGED_EVENT },
44     { PowerIdleEventPowerUpFailed, FxIdleStartedPowerFailed DEBUGGED_EVENT },
45     { PowerIdleEventStop, FxIdleStopped DEBUGGED_EVENT },
46 };
47 
48 const FxPowerIdleTargetState FxPowerIdleMachine::m_DisabledStates[] =
49 {
50     { PowerIdleEventEnabled, FxIdleCheckIoCount DEBUGGED_EVENT },
51     { PowerIdleEventDisabled, FxIdleDisabled DEBUGGED_EVENT },
52     { PowerIdleEventPowerDown, FxIdleGoingToDx DEBUGGED_EVENT },
53     { PowerIdleEventPowerDownFailed, FxIdlePowerFailed DEBUGGED_EVENT },
54     { PowerIdleEventPowerUpFailed, FxIdlePowerFailed DEBUGGED_EVENT },
55     { PowerIdleEventStop, FxIdleStopped DEBUGGED_EVENT },
56 };
57 
58 const FxPowerIdleTargetState FxPowerIdleMachine::m_BusyStates[] =
59 {
60     { PowerIdleEventIoDecrement, FxIdleDecrementIo DEBUGGED_EVENT },
61     { PowerIdleEventDisabled, FxIdleDisabled DEBUGGED_EVENT },
62     { PowerIdleEventPowerDown, FxIdleGoingToDx TRAP_ON_EVENT },
63     { PowerIdleEventPowerDownFailed, FxIdlePowerFailed TRAP_ON_EVENT },
64 };
65 
66 const FxPowerIdleTargetState FxPowerIdleMachine::m_TimerRunningStates[] =
67 {
68     { PowerIdleEventDisabled, FxIdleDisabling DEBUGGED_EVENT },
69     { PowerIdleEventIoIncrement, FxIdleCancelTimer DEBUGGED_EVENT },
70     { PowerIdleEventEnabled, FxIdleCancelTimer DEBUGGED_EVENT },
71     { PowerIdleEventTimerExpired, FxIdleTimingOut DEBUGGED_EVENT },
72 };
73 
74 const FxPowerIdleTargetState FxPowerIdleMachine::m_TimedOutStates[] =
75 {
76     { PowerIdleEventPowerDown, FxIdleTimedOutPowerDown DEBUGGED_EVENT },
77     { PowerIdleEventPowerDownFailed, FxIdleTimedOutPowerDownFailed DEBUGGED_EVENT },
78     { PowerIdleEventDisabled, FxIdleTimedOutDisabled DEBUGGED_EVENT },
79     { PowerIdleEventEnabled, FxIdleTimedOutEnabled DEBUGGED_EVENT },
80     { PowerIdleEventIoIncrement, FxIdleTimedOutIoIncrement DEBUGGED_EVENT },
81 };
82 
83 const FxPowerIdleTargetState FxPowerIdleMachine::m_InDxStates[] =
84 {
85     { PowerIdleEventPowerUpComplete, FxIdlePowerUp DEBUGGED_EVENT },
86     { PowerIdleEventPowerUpFailed, FxIdleInDxPowerUpFailure DEBUGGED_EVENT },
87     { PowerIdleEventPowerDownFailed, FxIdleInDxPowerUpFailure TRAP_ON_EVENT },
88     { PowerIdleEventStop, FxIdleInDxStopped DEBUGGED_EVENT },
89     { PowerIdleEventDisabled, FxIdleInDxDisabled DEBUGGED_EVENT },
90     { PowerIdleEventIoIncrement, FxIdleInDxIoIncrement DEBUGGED_EVENT },
91     { PowerIdleEventIoDecrement, FxIdleInDx DEBUGGED_EVENT },
92     { PowerIdleEventPowerDown, FxIdleInDx DEBUGGED_EVENT },
93     { PowerIdleEventEnabled, FxIdleInDxEnabled DEBUGGED_EVENT },
94 };
95 
96 const FxPowerIdleTargetState FxPowerIdleMachine::m_WaitForTimeoutStates[] =
97 {
98     { PowerIdleEventTimerExpired, FxIdleTimerExpired DEBUGGED_EVENT },
99     { PowerIdleEventPowerDownFailed, FxIdlePowerFailedWaitForTimeout TRAP_ON_EVENT },
100     { PowerIdleEventDisabled, FxIdleDisablingWaitForTimeout DEBUGGED_EVENT },
101 };
102 
103 const FxPowerIdleTargetState FxPowerIdleMachine::m_DisablingWaitForTimeoutStates[] =
104 {
105     { PowerIdleEventTimerExpired, FxIdleDisablingTimerExpired DEBUGGED_EVENT },
106 };
107 
108 const FxPowerIdleTargetState FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates[] =
109 {
110     { PowerIdleEventTimerExpired, FxIdlePowerFailed TRAP_ON_EVENT },
111 };
112 
113 const FxIdleStateTable FxPowerIdleMachine::m_StateTable[] =
114 {
115     // FxIdleStopped
116     {   FxPowerIdleMachine::Stopped,
117         FxPowerIdleMachine::m_StoppedStates,
118         ARRAY_SIZE(FxPowerIdleMachine::m_StoppedStates),
119     },
120 
121     // FxIdleStarted
122     {   FxPowerIdleMachine::Started,
123         FxPowerIdleMachine::m_StartedStates,
124         ARRAY_SIZE(FxPowerIdleMachine::m_StartedStates),
125     },
126 
127     // FxIdleStartedPowerUp
128     {   FxPowerIdleMachine::StartedPowerUp,
129         NULL,
130         0,
131     },
132 
133     // FxIdleStartedPowerFailed
134     {   FxPowerIdleMachine::StartedPowerFailed,
135         NULL,
136         0,
137     },
138 
139     // FxIdleDisabled
140     {   FxPowerIdleMachine::Disabled,
141         FxPowerIdleMachine::m_DisabledStates,
142         ARRAY_SIZE(FxPowerIdleMachine::m_DisabledStates),
143     },
144 
145     // FxIdleCheckIoCount
146     {   FxPowerIdleMachine::CheckIoCount,
147         NULL,
148         0,
149     },
150 
151     // FxIdleBusy
152     {   NULL,
153         FxPowerIdleMachine::m_BusyStates,
154         ARRAY_SIZE(FxPowerIdleMachine::m_BusyStates),
155     },
156 
157     // FxIdleDecrementIo
158     {   FxPowerIdleMachine::DecrementIo,
159         NULL,
160         0,
161     },
162 
163     // FxIdleStartTimer
164     {   FxPowerIdleMachine::StartTimer,
165         NULL,
166         0,
167     },
168 
169     // FxIdleTimerRunning
170     {   NULL,
171         FxPowerIdleMachine::m_TimerRunningStates,
172         ARRAY_SIZE(FxPowerIdleMachine::m_TimerRunningStates)
173     },
174 
175     // FxIdleTimingOut
176     {   FxPowerIdleMachine::TimingOut,
177         NULL,
178         0,
179     },
180 
181     // FxIdleTimedOut
182     {   NULL,
183         FxPowerIdleMachine::m_TimedOutStates,
184         ARRAY_SIZE(FxPowerIdleMachine::m_TimedOutStates),
185     },
186 
187     // FxIdleTimedOutIoIncrement
188     {   FxPowerIdleMachine::TimedOutIoIncrement,
189         NULL,
190         0,
191     },
192 
193     // FxIdleTimedOutPowerDown
194     {   FxPowerIdleMachine::TimedOutPowerDown,
195         NULL,
196         0,
197     },
198 
199     // FxIdleTimedOutPowerDownFailed
200     {   FxPowerIdleMachine::TimedOutPowerDownFailed,
201         NULL,
202         0,
203     },
204 
205     // FxIdleGoingToDx,
206     {   FxPowerIdleMachine::GoingToDx,
207         NULL,
208         0,
209     },
210 
211     // FxIdleInDx,
212     {   FxPowerIdleMachine::InDx,
213         FxPowerIdleMachine::m_InDxStates,
214         ARRAY_SIZE(FxPowerIdleMachine::m_InDxStates),
215     },
216 
217     // FxIdleInDxIoIncrement
218     {   FxPowerIdleMachine::InDxIoIncrement,
219         NULL,
220         0,
221     },
222 
223     // FxIdleInDxPowerUpFailure
224     {   FxPowerIdleMachine::InDxPowerUpFailure,
225         NULL,
226         0,
227     },
228 
229     // FxIdleInDxStopped
230     {   FxPowerIdleMachine::InDxStopped,
231         NULL,
232         0,
233     },
234 
235     // FxIdleInDxDisabled
236     {   FxPowerIdleMachine::InDxDisabled,
237         NULL,
238         0,
239     },
240 
241     // FxIdleInDxEnabled
242     {   FxPowerIdleMachine::InDxEnabled,
243         NULL,
244         0,
245     },
246 
247     // FxIdlePowerUp
248     {   FxPowerIdleMachine::PowerUp,
249         NULL,
250         0,
251     },
252 
253     // FxIdlePowerUpComplete
254     {   FxPowerIdleMachine::PowerUpComplete,
255         NULL,
256         0,
257     },
258 
259     // FxIdleTimedOutDisabled
260     {   FxPowerIdleMachine::TimedOutDisabled,
261         NULL,
262         0,
263     },
264 
265     // FxIdleTimedOutEnabled
266     {   FxPowerIdleMachine::TimedOutEnabled,
267         NULL,
268         0,
269     },
270 
271     // FxIdleCancelTimer
272     {   FxPowerIdleMachine::CancelTimer,
273         NULL,
274         0,
275     },
276 
277     // FxIdleWaitForTimeout
278     {   NULL,
279         FxPowerIdleMachine::m_WaitForTimeoutStates,
280         ARRAY_SIZE(FxPowerIdleMachine::m_WaitForTimeoutStates),
281     },
282 
283     // FxIdleTimerExpired
284     {   FxPowerIdleMachine::TimerExpired,
285         NULL,
286         0,
287     },
288 
289     // FxIdleDisabling
290     {   FxPowerIdleMachine::Disabling,
291         NULL,
292         0,
293     },
294 
295     // FxIdleDisablingWaitForTimeout
296     {   NULL,
297         FxPowerIdleMachine::m_DisablingWaitForTimeoutStates,
298         ARRAY_SIZE(FxPowerIdleMachine::m_DisablingWaitForTimeoutStates),
299     },
300 
301     // FxIdleDisablingTimerExpired
302     {   FxPowerIdleMachine::DisablingTimerExpired,
303         NULL,
304         0,
305     },
306 
307     // FxIdlePowerFailedWaitForTimeout
308     {   NULL,
309         FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates,
310         ARRAY_SIZE(FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates),
311     },
312 
313     // FxIdlePowerFailed
314     {   FxPowerIdleMachine::PowerFailed,
315         NULL,
316         0,
317     },
318 };
319 
320 __inline
321 FxPkgPnp*
GetPnpPkg(__inout FxPowerIdleMachine * This)322 GetPnpPkg(
323     __inout FxPowerIdleMachine* This
324     )
325 {
326     return CONTAINING_RECORD(This,
327                              FxPowerPolicyOwnerSettings,
328                              m_PowerIdleMachine)->m_PkgPnp;
329 }
330 
331 
FxPowerIdleMachine(VOID)332 FxPowerIdleMachine::FxPowerIdleMachine(
333     VOID
334     )
335 /*++
336 
337 Routine Description:
338     Constructs the power idle state machine
339 
340 Arguments:
341     None
342 
343 Return Value:
344     None
345 
346   --*/
347 {
348     //
349     // m_Lock and m_PowerTimeoutTimer are now being initialized in Init method
350     // since they may fail for UM.
351     //
352 
353     m_PowerTimeout.QuadPart = 0;
354     m_CurrentIdleState = FxIdleStopped;
355 
356     m_EventHistoryIndex = 0;
357     m_StateHistoryIndex = 0;
358 
359     RtlZeroMemory(&m_EventHistory[0], sizeof(m_EventHistory));
360     RtlZeroMemory(&m_StateHistory[0], sizeof(m_StateHistory));
361 
362     m_TagTracker = NULL;
363 }
364 
~FxPowerIdleMachine(VOID)365 FxPowerIdleMachine::~FxPowerIdleMachine(
366     VOID
367     )
368 {
369     if (m_TagTracker != NULL) {
370         delete m_TagTracker;
371         m_TagTracker = NULL;
372     }
373 }
374 
375 _Must_inspect_result_
376 NTSTATUS
Init(VOID)377 FxPowerIdleMachine::Init(
378     VOID
379     )
380 {
381     NTSTATUS status;
382 
383     //
384     // For KM, event initialize always succeeds. For UM, it might fail.
385     //
386     status = m_D0NotificationEvent.Initialize(NotificationEvent, TRUE);
387     if (!NT_SUCCESS(status)) {
388         return status;
389     }
390 
391     //
392     // For KM, timer initialize always succeeds. For UM, it might fail.
393     //
394     status = m_PowerTimeoutTimer.Initialize(this, _PowerTimeoutDpcRoutine, 0);
395     if (!NT_SUCCESS(status)) {
396         return status;
397     }
398 
399     Reset();
400 
401     return STATUS_SUCCESS;
402 }
403 
404 VOID
CheckAssumptions(VOID)405 FxPowerIdleMachine::CheckAssumptions(
406     VOID
407     )
408 /*++
409 
410 Routine Description:
411     This routine is never actually called by running code, it just has
412     WDFCASSERTs who upon failure, would not allow this file to be compiled.
413 
414     DO NOT REMOVE THIS FUNCTION just because it is not called by any running
415     code.
416 
417 Arguments:
418     None
419 
420 Return Value:
421     None
422 
423   --*/
424 {
425     WDFCASSERT((sizeof(m_StateTable)/sizeof(m_StateTable[0]))
426                ==
427                (FxIdleMax - FxIdleStopped));
428 }
429 
430 FxPowerIdleStates
Stopped(__inout FxPowerIdleMachine * This)431 FxPowerIdleMachine::Stopped(
432     __inout FxPowerIdleMachine* This
433     )
434 /*++
435 
436 Routine Description:
437     State machine has entered the stopped state, clear the started flag
438 
439 Arguments:
440     This - instance of the state machine
441 
442 Return Value:
443     FxIdleMax
444 
445   --*/
446 {
447     This->m_Flags &= ~FxPowerIdleIsStarted;
448 
449     return FxIdleMax;
450 }
451 
452 FxPowerIdleStates
Started(__inout FxPowerIdleMachine * This)453 FxPowerIdleMachine::Started(
454     __inout FxPowerIdleMachine* This
455     )
456 /*++
457 
458 Routine Description:
459     State machine has entered the started state, set the started flag
460 
461 Arguments:
462     This - instance of the state machine
463 
464 Return Value:
465     FxIdleMax
466 
467   --*/
468 {
469     This->m_Flags |= FxPowerIdleIsStarted;
470 
471     //
472     // We are in the started state, but we are not powered up.
473     //
474     This->m_D0NotificationEvent.Clear();
475 
476     return FxIdleMax;
477 }
478 
479 FxPowerIdleStates
StartedPowerUp(__inout FxPowerIdleMachine * This)480 FxPowerIdleMachine::StartedPowerUp(
481     __inout FxPowerIdleMachine* This
482     )
483 /*++
484 
485 Routine Description:
486     We were in the started and powered off state.  We are powered up,
487     so set the event now so that we can wake up any waiters.
488 
489 Arguments:
490     This - instance of the state machine
491 
492 Return Value:
493     FxIdleDisabled
494 
495   --*/
496 {
497     //
498     // Moving from the started state to the powered on state
499     //
500     This->SendD0Notification();
501 
502     return FxIdleDisabled;
503 }
504 
505 FxPowerIdleStates
StartedPowerFailed(__inout FxPowerIdleMachine * This)506 FxPowerIdleMachine::StartedPowerFailed(
507     __inout FxPowerIdleMachine* This
508     )
509 /*++
510 
511 Routine Description:
512     The state machine was started, but the initial power up failed.  Mark the
513     failure.
514 
515 Arguments:
516     This - instance of the state machine
517 
518 Return Value:
519     FxIdleStarted
520 
521   --*/
522 {
523     //
524     // Failed to initially power up
525     //
526     This->m_Flags |= FxPowerIdlePowerFailed;
527 
528     //
529     // We assume in the started state that the event is set
530     //
531     ASSERT(This->m_D0NotificationEvent.ReadState() == 0);
532 
533     return FxIdleStarted;
534 }
535 
536 FxPowerIdleStates
Disabled(__inout FxPowerIdleMachine * This)537 FxPowerIdleMachine::Disabled(
538     __inout FxPowerIdleMachine* This
539     )
540 /*++
541 
542 Routine Description:
543     State machine has entered the disabled state, unblock all waiters
544 
545 Arguments:
546     This - instance of the state machine
547 
548 Return Value:
549     FxIdleMax
550 
551   --*/
552 
553 {
554     This->m_Flags &= ~FxPowerIdleTimerEnabled;
555 
556     return FxIdleMax;
557 }
558 
559 FxPowerIdleStates
CheckIoCount(__inout FxPowerIdleMachine * This)560 FxPowerIdleMachine::CheckIoCount(
561     __inout FxPowerIdleMachine* This
562     )
563 /*++
564 
565 Routine Description:
566     Checks the IO count and transitions the appropriate state.   This is the
567     first state we are in after being disabled or after transitioning from Dx to
568     D0.
569 
570 Arguments:
571     This - instance of the state machine
572 
573 Return Value:
574     new state machine state
575 
576   --*/
577 {
578     This->m_Flags |= FxPowerIdleTimerEnabled;
579 
580     if (This->m_IoCount == 0) {
581         return FxIdleStartTimer;
582     }
583     else {
584         return FxIdleBusy;
585     }
586 }
587 
588 FxPowerIdleStates
DecrementIo(__inout FxPowerIdleMachine * This)589 FxPowerIdleMachine::DecrementIo(
590     __inout FxPowerIdleMachine* This
591     )
592 /*++
593 
594 Routine Description:
595     Checks the IO count and returns a new state
596 
597 Arguments:
598     This - instance of the state machine
599 
600 Return Value:
601     new state machine state
602 
603   --*/
604 {
605     if (This->m_IoCount == 0) {
606         return FxIdleStartTimer;
607     }
608     else {
609         return FxIdleBusy;
610     }
611 }
612 
613 FxPowerIdleStates
StartTimer(__inout FxPowerIdleMachine * This)614 FxPowerIdleMachine::StartTimer(
615     __inout FxPowerIdleMachine* This
616     )
617 /*++
618 
619 Routine Description:
620     The io count is now at zero.  Start the idle timer so that when it expires,
621     the device will move into Dx.
622 
623 Arguments:
624     This - instance of the state machine
625 
626 Return Value:
627     FxIdleMax
628 
629   --*/
630 {
631     ASSERT((This->m_Flags & FxPowerIdleTimerEnabled) && This->m_IoCount == 0);
632 
633     This->m_Flags |= FxPowerIdleTimerStarted;
634     This->m_PowerTimeoutTimer.Start(This->m_PowerTimeout,
635                                     m_IdleTimerTolerableDelayMS);
636 
637     return FxIdleTimerRunning;
638 }
639 
640 
641 FxPowerIdleStates
TimingOut(__inout FxPowerIdleMachine * This)642 FxPowerIdleMachine::TimingOut(
643     __inout FxPowerIdleMachine* This
644     )
645 /*++
646 
647 Routine Description:
648     The idle timer has expired. Indicate to the power policy state machine
649     that it should power down.
650 
651 Arguments:
652     This - instance of the state machine
653 
654 Return Value:
655     FxIdleTimedOut
656 
657   --*/
658 {
659 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
660     GetPnpPkg(This)->PowerPolicyProcessEvent(PwrPolPowerTimeoutExpired);
661 #else
662     GetPnpPkg(This)->PowerPolicyProcessEvent(
663         PwrPolPowerTimeoutExpired,
664         TRUE // ProcessEventOnDifferentThread
665         );
666 #endif
667 
668     //
669     // Timer is no longer running.  Used when we disable the state machine and
670     // need to know the timer's running state.
671     //
672     //
673     This->m_Flags &= ~FxPowerIdleTimerStarted;
674 
675     //
676     // While the device is still powered up, we are no longer in D0 in terms of
677     // PowerReference returning immmediately if TRUE is specified to that call.
678     //
679     This->m_D0NotificationEvent.Clear();
680 
681     return FxIdleTimedOut;
682 }
683 
684 FxPowerIdleStates
TimedOutIoIncrement(__inout FxPowerIdleMachine * This)685 FxPowerIdleMachine::TimedOutIoIncrement(
686     __inout FxPowerIdleMachine* This
687     )
688 /*++
689 
690 Routine Description:
691     A power reference occurred after we notified the power policy machine of
692     a power timeout, but before we timed out. Send an io present event to the
693     power policy machine so that it can move into the D0 state/not timed out
694     state again.
695 
696 Arguments:
697     This - instance of the state machine
698 
699 Return Value:
700     FxIdleTimedOut
701 
702   --*/
703 {
704     FxPkgPnp* pPkgPnp;
705 
706     pPkgPnp = GetPnpPkg(This);
707 
708     if (This->m_Flags & FxPowerIdleIoPresentSent) {
709         DoTraceLevelMessage(
710             pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
711             "WDFDEVICE %p idle (in D0) not sending io present event (already sent)",
712             pPkgPnp->GetDevice()->GetHandle());
713     }
714     else {
715 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
716         pPkgPnp->PowerPolicyProcessEvent(PwrPolIoPresent);
717 #else
718         pPkgPnp->PowerPolicyProcessEvent(
719             PwrPolIoPresent,
720             TRUE // ProcessEventOnDifferentThread
721             );
722 #endif
723 
724         This->m_Flags |= FxPowerIdleIoPresentSent;
725     }
726 
727     return FxIdleTimedOut;
728 }
729 
730 FxPowerIdleStates
TimedOutPowerDown(__inout FxPowerIdleMachine * This)731 FxPowerIdleMachine::TimedOutPowerDown(
732     __inout FxPowerIdleMachine* This
733     )
734 /*++
735 
736 Routine Description:
737     The idle timer fired and we are now powering down.  Clear the flag that
738     limits our sending of the io present event to one time while in the timed
739     out state.
740 
741 Arguments:
742     This - instance of the state machine
743 
744 Return Value:
745     FxIdleGoingToDx
746 
747   --*/
748 {
749     //
750     // We can send the io present event again
751     //
752     This->m_Flags &= ~FxPowerIdleIoPresentSent;
753 
754     return FxIdleGoingToDx;
755 }
756 
757 FxPowerIdleStates
TimedOutPowerDownFailed(__inout FxPowerIdleMachine * This)758 FxPowerIdleMachine::TimedOutPowerDownFailed(
759     __inout FxPowerIdleMachine* This
760     )
761 /*++
762 
763 Routine Description:
764     The idle timer fired and we could no power down.  Clear the flag that
765     limits our sending of the io present event to one time while in the timed
766     out state.
767 
768 Arguments:
769     This - instance of the state machine
770 
771 Return Value:
772     FxIdlePowerFailed
773 
774   --*/
775 {
776     //
777     // We can send the io present event again
778     //
779     This->m_Flags &= ~FxPowerIdleIoPresentSent;
780 
781     return FxIdlePowerFailed;
782 }
783 
784 FxPowerIdleStates
GoingToDx(__inout FxPowerIdleMachine * This)785 FxPowerIdleMachine::GoingToDx(
786     __inout FxPowerIdleMachine* This
787     )
788 /*++
789 
790 Routine Description:
791     The device is going into Dx.  It could be going into Dx because the idle
792     timer expired or because the machine is moving into Sx, the reason doesn't
793     matter though.  Clear the in D0 event.
794 
795 Arguments:
796     This - instance of the state machine
797 
798 Return Value:
799     FxIdleInDx
800 
801   --*/
802 {
803     This->m_D0NotificationEvent.Clear();
804     return FxIdleInDx;
805 }
806 
807 FxPowerIdleStates
InDx(__inout FxPowerIdleMachine * This)808 FxPowerIdleMachine::InDx(
809     __inout FxPowerIdleMachine* This
810     )
811 /*++
812 
813 Routine Description:
814     The device has moved into Dx.   If there is no pending io, mark the device
815     as idle.  We can be in Dx with pending io if IoIncrement was called after
816     the device moved into Dx.
817 
818 Arguments:
819     This - instance of the state machine
820 
821 Return Value:
822     FxIdleMax
823 
824   --*/
825 {
826     This->m_Flags |= FxPowerIdleInDx;
827 
828     return FxIdleMax;
829 }
830 
831 FxPowerIdleStates
InDxIoIncrement(__inout FxPowerIdleMachine * This)832 FxPowerIdleMachine::InDxIoIncrement(
833     __inout FxPowerIdleMachine* This
834     )
835 /*++
836 
837 Routine Description:
838     In Dx and the io count went up.  Send the event to the power policy state
839     machine indicating new io is present.
840 
841 Arguments:
842     This - instance of the state machine
843 
844 Return Value:
845     FxIdleInDx
846 
847   --*/
848 {
849     FxPkgPnp* pPkgPnp;
850 
851     pPkgPnp = GetPnpPkg(This);
852 
853     if (This->m_Flags & FxPowerIdleIoPresentSent) {
854         DoTraceLevelMessage(
855             pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
856             "WDFDEVICE %p idle (in Dx) not sending io present event (already sent)",
857             pPkgPnp->GetDevice()->GetHandle());
858     }
859     else {
860 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
861         pPkgPnp->PowerPolicyProcessEvent(PwrPolIoPresent);
862 #else
863         pPkgPnp->PowerPolicyProcessEvent(
864             PwrPolIoPresent,
865             TRUE // ProcessEventOnDifferentThread
866             );
867 #endif
868         This->m_Flags |= FxPowerIdleIoPresentSent;
869     }
870 
871     return FxIdleInDx;
872 }
873 
874 FxPowerIdleStates
InDxPowerUpFailure(__inout FxPowerIdleMachine * This)875 FxPowerIdleMachine::InDxPowerUpFailure(
876     __inout FxPowerIdleMachine* This
877     )
878 /*++
879 
880 Routine Description:
881     Device is in Dx and there was a failure in the power up path.  The device
882     is no longer idle, even though it is stil in Dx.
883 
884 Arguments:
885     This - instance of the state machine
886 
887 Return Value:
888     FxIdlePowerFailed
889 
890   --*/
891 {
892     //
893     // FxPowerIdleInDx - We are no longer in Dx
894     // FxPowerIdleIoPresentSent = We can send the io present event again
895     //
896     This->m_Flags &= ~(FxPowerIdleInDx | FxPowerIdleIoPresentSent);
897 
898     return FxIdlePowerFailed;
899 }
900 
901 FxPowerIdleStates
InDxStopped(__inout FxPowerIdleMachine * This)902 FxPowerIdleMachine::InDxStopped(
903     __inout FxPowerIdleMachine* This
904     )
905 /*++
906 
907 Routine Description:
908     The state machine was stopped while in the Dx state.  When the machine is in
909     the stopped state, the notification event is in the signaled state.
910 
911 Arguments:
912     This - instance of the state machine
913 
914 Return Value:
915     FxIdleStopped
916 
917   --*/
918 {
919     //
920     // When the machine is in the stopped state, the notification event is in
921     // the signaled state.
922     //
923     This->SendD0Notification();
924 
925     //
926     // We are not longer idle since we are not in the Dx state anymore
927     //
928     This->m_Flags &= ~FxPowerIdleInDx;
929 
930     //
931     // We are no longer enabled since we are stopped from the Dx state
932     //
933     This->m_Flags &= ~FxPowerIdleTimerEnabled;
934 
935     //
936     // We can send the io present event again
937     //
938     This->m_Flags &= ~FxPowerIdleIoPresentSent;
939 
940     return FxIdleStopped;
941 }
942 
943 FxPowerIdleStates
InDxDisabled(__inout FxPowerIdleMachine * This)944 FxPowerIdleMachine::InDxDisabled(
945     __inout FxPowerIdleMachine* This
946     )
947 /*++
948 
949 Routine Description:
950     The device is in Dx and the state machine is being disabled (most likely due
951     to a surprise remove).
952 
953 Arguments:
954     This - instance of the state machine
955 
956 Return Value:
957     FxIdleDisabled
958 
959   --*/
960 {
961     //
962     // Idle timer is being disabled while in Dx.  Remain in Dx.
963     //
964     This->m_Flags &= ~FxPowerIdleTimerEnabled;
965 
966     return FxIdleInDx;
967 }
968 
969 FxPowerIdleStates
InDxEnabled(__inout FxPowerIdleMachine * This)970 FxPowerIdleMachine::InDxEnabled(
971     __inout FxPowerIdleMachine* This
972     )
973 /*++
974 
975 Routine Description:
976     The device is in Dx and the state machine is being enabled (most like due
977     to trying to remain in Dx after Sx->S0.
978 
979 Arguments:
980     This - instance of the state machine
981 
982 Return Value:
983     FxIdleInDx
984 
985   --*/
986 {
987     //
988     // Idle timer is being enabled while in Dx.  Remain in Dx.
989     //
990     This->m_Flags |= FxPowerIdleTimerEnabled;
991 
992     return FxIdleInDx;
993 }
994 
995 FxPowerIdleStates
PowerUp(__inout FxPowerIdleMachine * This)996 FxPowerIdleMachine::PowerUp(
997     __inout FxPowerIdleMachine* This
998     )
999 /*++
1000 
1001 Routine Description:
1002     The device powered up enough to where we can let waiters go and start pounding
1003     on their hardware.
1004 
1005 Arguments:
1006     This - instance of the state machine
1007 
1008 Return Value:
1009     FxIdlePowerUpComplete
1010 
1011   --*/
1012 {
1013     //
1014     // FxPowerIdleInDx - we are not longer idle since we are not in the Dx state
1015     //                   anymore
1016     // FxPowerIdleIoPresentSent - We can send the io present event again
1017     //
1018     This->m_Flags &= ~(FxPowerIdleInDx | FxPowerIdleIoPresentSent);
1019 
1020     This->SendD0Notification();
1021 
1022     return FxIdlePowerUpComplete;
1023 }
1024 
1025 FxPowerIdleStates
PowerUpComplete(__inout FxPowerIdleMachine * This)1026 FxPowerIdleMachine::PowerUpComplete(
1027     __inout FxPowerIdleMachine* This
1028     )
1029 /*++
1030 
1031 Routine Description:
1032     The device is moving into D0, determine which D0 state to move into
1033     based on the enabled state and io count.
1034 
1035 Arguments:
1036     This - instance of the state machine
1037 
1038 Return Value:
1039     new state
1040 
1041   --*/
1042 {
1043     if (This->m_Flags & FxPowerIdleTimerEnabled) {
1044         if (This->m_Flags & FxPowerIdleTimerStarted) {
1045             COVERAGE_TRAP();
1046             return FxIdleTimerRunning;
1047         }
1048         else {
1049             return FxIdleCheckIoCount;
1050         }
1051     }
1052     else {
1053         //
1054         // Not enabled, better not have a timer running
1055         //
1056         ASSERT((This->m_Flags & FxPowerIdleTimerStarted) == 0);
1057 
1058         return FxIdleDisabled;
1059     }
1060 }
1061 
1062 FxPowerIdleStates
TimedOutDisabled(__inout FxPowerIdleMachine * This)1063 FxPowerIdleMachine::TimedOutDisabled(
1064     __inout FxPowerIdleMachine* This
1065     )
1066 /*++
1067 
1068 Routine Description:
1069     The power idle state machine is moving into the disabled state.  Set the
1070     D0 event.
1071 
1072 Arguments:
1073     This - instance of the state machine
1074 
1075 Return Value:
1076     FxIdleDisabled
1077 
1078   --*/
1079 {
1080     //
1081     // Notify any waiters that we are now in D0
1082     //
1083     This->SendD0Notification();
1084 
1085     //
1086     // We can send the io present event again
1087     //
1088     This->m_Flags &= ~FxPowerIdleIoPresentSent;
1089 
1090     return FxIdleDisabled;
1091 }
1092 
1093 FxPowerIdleStates
TimedOutEnabled(__inout FxPowerIdleMachine * This)1094 FxPowerIdleMachine::TimedOutEnabled(
1095     __inout FxPowerIdleMachine* This
1096     )
1097 {
1098     //
1099     // Notify any waiters that we are now in D0
1100     //
1101     This->SendD0Notification();
1102 
1103     //
1104     // We can send the io present event again
1105     //
1106     This->m_Flags &= ~FxPowerIdleIoPresentSent;
1107 
1108     return FxIdleCheckIoCount;
1109 }
1110 
1111 FxPowerIdleStates
CancelTimer(__inout FxPowerIdleMachine * This)1112 FxPowerIdleMachine::CancelTimer(
1113     __inout FxPowerIdleMachine* This
1114     )
1115 /*++
1116 
1117 Routine Description:
1118     The timer is running and we need to cancel it because of an io increment or
1119     the state machine being disabled.
1120 
1121 Arguments:
1122     This - instance of the state machine
1123 
1124 Return Value:
1125     new state
1126 
1127   --*/
1128 {
1129     if (This->CancelIdleTimer()) {
1130         return FxIdleCheckIoCount;
1131     }
1132     else {
1133         return FxIdleWaitForTimeout;
1134     }
1135 }
1136 
1137 FxPowerIdleStates
TimerExpired(__inout FxPowerIdleMachine * This)1138 FxPowerIdleMachine::TimerExpired(
1139     __inout FxPowerIdleMachine* This
1140     )
1141 /*++
1142 
1143 Routine Description:
1144     The timer was not canceled because it was running.  The timer has now
1145     fired, so we can move forward.
1146 
1147 Arguments:
1148     This - instance of the state machine
1149 
1150 Return Value:
1151     FxIdleCheckIoCount
1152 
1153   --*/
1154 {
1155     This->m_Flags &= ~FxPowerIdleTimerStarted;
1156 
1157     return FxIdleCheckIoCount;
1158 }
1159 
1160 FxPowerIdleStates
Disabling(__inout FxPowerIdleMachine * This)1161 FxPowerIdleMachine::Disabling(
1162     __inout FxPowerIdleMachine* This
1163     )
1164 /*++
1165 
1166 Routine Description:
1167     Timer is running and the state machine is being disabled.  Cancel the idle
1168     timer.
1169 
1170 Arguments:
1171     This - instance of the state machine
1172 
1173 Return Value:
1174     new state
1175 
1176   --*/
1177 {
1178     if (This->CancelIdleTimer()) {
1179         return FxIdleDisabled;
1180     }
1181     else {
1182         return FxIdleDisablingWaitForTimeout;
1183     }
1184 }
1185 
1186 FxPowerIdleStates
DisablingTimerExpired(__inout FxPowerIdleMachine * This)1187 FxPowerIdleMachine::DisablingTimerExpired(
1188     __inout FxPowerIdleMachine* This
1189     )
1190 /*++
1191 
1192 Routine Description:
1193     When disabling the state machine, the timer could not be canceled.  The
1194     timer has now expired and the state machine can move forward.
1195 
1196 Arguments:
1197     This - instance of the state machine
1198 
1199 Return Value:
1200     FxIdleDisabled
1201 
1202   --*/
1203 {
1204     This->m_Flags &= ~FxPowerIdleTimerStarted;
1205 
1206 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
1207     GetPnpPkg(This)->PowerPolicyProcessEvent(PwrPolPowerTimeoutExpired);
1208 #else
1209     GetPnpPkg(This)->PowerPolicyProcessEvent(
1210         PwrPolPowerTimeoutExpired,
1211         TRUE // ProcessEventOnDifferentThread
1212         );
1213 #endif
1214 
1215     return FxIdleDisabled;
1216 }
1217 
1218 FxPowerIdleStates
PowerFailed(__inout FxPowerIdleMachine * This)1219 FxPowerIdleMachine::PowerFailed(
1220     __inout FxPowerIdleMachine* This
1221     )
1222 /*++
1223 
1224 Routine Description:
1225     A power operation (up or down) failed.  Mark the machine as failed so that
1226     PowerReference will fail properly.
1227 
1228 Arguments:
1229     This - instance of the state machine
1230 
1231 Return Value:
1232     FxIdleDisabled
1233 
1234   --*/
1235 {
1236     //
1237     // By this time, the timer should be stopped
1238     //
1239     ASSERT((This->m_Flags & FxPowerIdleTimerStarted) == 0);
1240 
1241     This->m_Flags |= FxPowerIdlePowerFailed;
1242 
1243     //
1244     // We are no longer enabled to time out since we are in a failed state
1245     //
1246     This->m_Flags &= ~FxPowerIdleTimerEnabled;
1247 
1248     //
1249     // Wake up any waiters and indicate failure to them.
1250     //
1251     This->SendD0Notification();
1252 
1253     return FxIdleDisabled;
1254 }
1255 
1256 __drv_maxIRQL(DISPATCH_LEVEL)
__drv_minIRQL(DISPATCH_LEVEL)1257 __drv_minIRQL(DISPATCH_LEVEL)
1258 __drv_requiresIRQL(DISPATCH_LEVEL)
1259 __drv_sameIRQL
1260 VOID
1261 FxPowerIdleMachine::_PowerTimeoutDpcRoutine(
1262     __in     PKDPC Dpc,
1263     __in_opt PVOID Context,
1264     __in_opt PVOID SystemArgument1,
1265     __in_opt PVOID SystemArgument2
1266     )
1267 /*++
1268 
1269 Routine Description:
1270     Timer DPC which posts the timeout expired event to the power policy state
1271     machine
1272 
1273 Arguments:
1274     Dpc - DPC
1275     Context, SysArg1, SysArg2 - Unused
1276 
1277 Return Value:
1278     None
1279 
1280   --*/
1281 {
1282     FxPowerIdleMachine* pThis;
1283 
1284     UNREFERENCED_PARAMETER(Dpc);
1285     UNREFERENCED_PARAMETER(SystemArgument1);
1286     UNREFERENCED_PARAMETER(SystemArgument2);
1287 
1288     pThis = (FxPowerIdleMachine*) Context;
1289 
1290     pThis->m_Lock.AcquireAtDpcLevel();
1291     pThis->ProcessEventLocked(PowerIdleEventTimerExpired);
1292 
1293 #if FX_IS_KERNEL_MODE
1294     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1295     PFN_WDF_DRIVER_DEVICE_ADD pDriverDeviceAdd;
1296 
1297     pFxDriverGlobals = GetPnpPkg(pThis)->GetDriverGlobals();
1298 
1299     //
1300     // We need to provide XPerf with a symbol of the client to figure out
1301     // which component this idle timer is for. Since AddDevice is always there
1302     // we use that to pass the symbol along.
1303     //
1304     pDriverDeviceAdd = pFxDriverGlobals->Driver->GetDriverDeviceAddMethod();
1305 
1306     FxPerfTraceDpc(&pDriverDeviceAdd);
1307 #endif
1308 
1309     pThis->m_Lock.ReleaseFromDpcLevel();
1310 }
1311 
1312 VOID
Reset(VOID)1313 FxPowerIdleMachine::Reset(
1314     VOID
1315     )
1316 /*++
1317 
1318 Routine Description:
1319     Reset the state machine to a known state on a PDO restart.
1320 
1321 Arguments:
1322     None
1323 
1324 Return Value:
1325     None
1326 
1327   --*/
1328 {
1329     FxPkgPnp* pPkgPnp;
1330     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1331 
1332     ASSERT(m_CurrentIdleState == FxIdleStopped);
1333 
1334     m_IoCount = 0;
1335     m_Flags = 0x0;
1336 
1337     pPkgPnp = GetPnpPkg(this);
1338     pFxDriverGlobals = pPkgPnp->GetDriverGlobals();
1339 
1340     if (pFxDriverGlobals->DebugExtension != NULL &&
1341         pFxDriverGlobals->DebugExtension->TrackPower != FxTrackPowerNone) {
1342         //
1343         // Ignore potential failure, power ref tracking is not an essential feature.
1344         //
1345         (void)FxTagTracker::CreateAndInitialize(&m_TagTracker,
1346                                                 pFxDriverGlobals,
1347                                                 FxTagTrackerTypePower,
1348                                                 pFxDriverGlobals->DebugExtension->TrackPower == FxTrackPowerRefsAndStack,
1349                                                 pPkgPnp->GetDevice());
1350     }
1351 
1352     SendD0Notification();
1353 }
1354 
1355 VOID
EnableTimer(VOID)1356 FxPowerIdleMachine::EnableTimer(
1357     VOID
1358     )
1359 /*++
1360 
1361 Routine Description:
1362     Public function that the power policy state machine uses to put this state
1363     machine in to an enabled state and potentially start the idle timer.
1364 
1365 Arguments:
1366     None
1367 
1368 Return Value:
1369     None
1370 
1371   --*/
1372 {
1373     KIRQL irql;
1374 
1375     m_Lock.Acquire(&irql);
1376     ProcessEventLocked(PowerIdleEventEnabled);
1377     m_Lock.Release(irql);
1378 }
1379 
1380 BOOLEAN
DisableTimer(VOID)1381 FxPowerIdleMachine::DisableTimer(
1382     VOID
1383     )
1384 /*++
1385 
1386 Routine Description:
1387     Public function which the power policy state machine uses to put this state
1388     machine into a disabled state.  If necessary, the state machine will attempt
1389     to cancel the idle timer.
1390 
1391 Arguments:
1392     None
1393 
1394 Return Value:
1395     TRUE if the idle timer was cancelled and the caller may proceed directly to
1396     its new state
1397 
1398     FALSE if the idle timer was not cancelled and the caller must wait for the
1399     io timeout event to be posted before proceeding.
1400 
1401   --*/
1402 {
1403     KIRQL irql;
1404     BOOLEAN disabledImmediately;
1405 
1406     m_Lock.Acquire(&irql);
1407 
1408     ProcessEventLocked(PowerIdleEventDisabled);
1409 
1410     //
1411     // If FxPowerIdleTimerStarted is still set after disabling the state machine,
1412     // then we could not cancel the timer and we must wait for the timer expired
1413     // event to be posted to this state machine.  This state machine will then
1414     // post a PwrPolIoPresent event to power policy.
1415     //
1416     if (m_Flags & FxPowerIdleTimerStarted) {
1417         disabledImmediately = FALSE;
1418     }
1419     else {
1420         disabledImmediately = TRUE;
1421     }
1422 
1423     m_Lock.Release(irql);
1424 
1425     return disabledImmediately;
1426 }
1427 
1428 VOID
Start(VOID)1429 FxPowerIdleMachine::Start(
1430     VOID
1431     )
1432 /*++
1433 
1434 Routine Description:
1435     Public function that the power policy state machine uses to put this state
1436     machine into a started state so that the caller can call PowerReference
1437     successfully.
1438 
1439 Arguments:
1440     None
1441 
1442 Return Value:
1443     None
1444 
1445   --*/
1446 {
1447     KIRQL irql;
1448     m_Lock.Acquire(&irql);
1449     ProcessEventLocked(PowerIdleEventStart);
1450     m_Lock.Release(irql);
1451 }
1452 
1453 VOID
Stop(VOID)1454 FxPowerIdleMachine::Stop(
1455     VOID
1456     )
1457 /*++
1458 
1459 Routine Description:
1460     Public function which the power policy state machine uses to put this state
1461     machine into a state where PowerReference will no longer work.
1462 
1463 Arguments:
1464     None
1465 
1466 Return Value:
1467     None
1468 
1469   --*/
1470 {
1471     KIRQL irql;
1472 
1473     m_Lock.Acquire(&irql);
1474     ProcessEventLocked(PowerIdleEventStop);
1475     m_Lock.Release(irql);
1476 }
1477 
1478 _Must_inspect_result_
1479 NTSTATUS
PowerReferenceWorker(__in BOOLEAN WaitForD0,__in FxPowerReferenceFlags Flags,__in_opt PVOID Tag,__in_opt LONG Line,__in_opt PSTR File)1480 FxPowerIdleMachine::PowerReferenceWorker(
1481     __in BOOLEAN WaitForD0,
1482     __in FxPowerReferenceFlags Flags,
1483     __in_opt PVOID Tag,
1484     __in_opt LONG Line,
1485     __in_opt PSTR File
1486     )
1487 /*++
1488 
1489 Routine Description:
1490     Caller wants to move the device into D0 manually.  The caller may optionally
1491     wait synchronously for the transition to occur if the device is currently in
1492     Dx.
1493 
1494 Arguments:
1495     WaitForD0 - TRUE if the caller wants to synchronously wait for the Dx to D0
1496                 transition
1497 
1498     QueryPnpPending - TRUE if we are being called to bring the device back to
1499                 working state when a QueryRemove or a QueryStop
1500 
1501 Return Value:
1502     NTSTATUS
1503 
1504     STATUS_SUCCESS - success
1505     STATUS_PENDING - transition is occurring
1506     STATUS_POWER_STATE_INVALID - ower transition has failed
1507 
1508   --*/
1509 {
1510     NTSTATUS status;
1511     KIRQL irql;
1512     ULONG count = 0;
1513 
1514 
1515 
1516 
1517 
1518 
1519 
1520 
1521 
1522 
1523 
1524 
1525 
1526 
1527 
1528 
1529 
1530 
1531 
1532     //
1533     // Poke the state machine
1534     //
1535     status = IoIncrementWithFlags(Flags, &count);
1536 
1537     //
1538     // STATUS_PENDING indicates a Dx to D0 transition is occurring right now
1539     //
1540     if (status == STATUS_PENDING) {
1541         if (WaitForD0) {
1542             FxPkgPnp* pPkgPnp;
1543 
1544             ASSERT(Mx::MxGetCurrentIrql() <= APC_LEVEL);
1545 
1546             //
1547             // With the current usage, if WaitForD0 is TRUE, then the only
1548             // acceptable flag is FxPowerReferenceDefault.
1549             //
1550             // If the usage changes in the future such that it is acceptable to
1551             // have WaitForD0 set to TRUE and some flag(s) set, then the ASSERT
1552             // below should be updated accordingly (or removed altogether).
1553             //
1554             ASSERT(FxPowerReferenceDefault == Flags);
1555 
1556             pPkgPnp = GetPnpPkg(this);
1557 
1558             DoTraceLevelMessage(
1559                 pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1560                 "WDFDEVICE %p in thread %p waiting synchronously for Dx to D0 "
1561                 "transition",
1562                 pPkgPnp->GetDevice()->GetHandle(),
1563                 Mx::MxGetCurrentThread());
1564 
1565             //
1566             // Returns success always
1567             //
1568             (void) FxPowerIdleMachine::WaitForD0();
1569 
1570             m_Lock.Acquire(&irql);
1571 
1572             //
1573             // If WaitForD0 is TRUE, then the FxPowerIdleSendPnpPowerUpEvent
1574             // flag can't be set. That flag is only used when the PnP state
1575             // machine waits asynchronously for the device to power up during
1576             // query-remove.
1577             //
1578             ASSERT(0 == (m_Flags & FxPowerIdleSendPnpPowerUpEvent));
1579 
1580             if ((m_Flags & FxPowerIdlePowerFailed) != 0x0 ||
1581                 (m_Flags & FxPowerIdleIsStarted) == 0x0) {
1582 
1583                 //
1584                 // Event was set because a power up or down failure occurred
1585                 //
1586                 status = STATUS_POWER_STATE_INVALID;
1587 
1588                 if (m_Flags & FxPowerIdlePowerFailed) {
1589                     DoTraceLevelMessage(
1590                         pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1591                         "WDFDEVICE %p waiting for D0 in thread %p failed because of "
1592                         "power failure, %!STATUS!",
1593                         pPkgPnp->GetDevice()->GetHandle(),
1594                         Mx::MxGetCurrentThread(),
1595                         status);
1596                 }
1597                 else {
1598                     COVERAGE_TRAP();
1599                     DoTraceLevelMessage(
1600                         pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1601                         "WDFDEVICE %p waiting for D0 in thread %p failed because of "
1602                         "invalid state , %!STATUS!",
1603                         pPkgPnp->GetDevice()->GetHandle(),
1604                         Mx::MxGetCurrentThread(), status);
1605                 }
1606 
1607                 //
1608                 // Decrement the io count that was taken above
1609                 //
1610                 ASSERT(m_IoCount > 0);
1611                 m_IoCount--;
1612                 ProcessEventLocked(PowerIdleEventIoDecrement);
1613             }
1614             else {
1615                 //
1616                 // Successfully returned to D0
1617                 //
1618                 status = STATUS_SUCCESS;
1619             }
1620             m_Lock.Release(irql);
1621         }
1622     }
1623 
1624     if (m_TagTracker != NULL) {
1625         //
1626         // Only track the reference if the call was successful
1627         // and the counter was actually incremented.
1628         //
1629         if (status == STATUS_SUCCESS || status == STATUS_PENDING) {
1630             m_TagTracker->UpdateTagHistory(Tag, Line, File, TagAddRef, count);
1631         }
1632     }
1633 
1634     return status;
1635 }
1636 
1637 _Must_inspect_result_
1638 NTSTATUS
IoIncrement(VOID)1639 FxPowerIdleMachine::IoIncrement(
1640     VOID
1641     )
1642 /*++
1643 
1644 Routine Description:
1645     Public function for any component to increment the io count.  The increment
1646     may cause the state machine to move out of the enabled state depending on
1647     the current state.
1648 
1649 Arguments:
1650     None
1651 
1652 Return Value:
1653     STATUS_PENDING if the state machine is transition from idle to non idle
1654 
1655     STATUS_SUCCESS otherwise
1656 
1657   --*/
1658 {
1659     return IoIncrementWithFlags(FxPowerReferenceDefault);
1660 }
1661 
1662 _Must_inspect_result_
1663 NTSTATUS
IoIncrementWithFlags(__in FxPowerReferenceFlags Flags,__out_opt PULONG Count)1664 FxPowerIdleMachine::IoIncrementWithFlags(
1665     __in FxPowerReferenceFlags Flags,
1666     __out_opt PULONG Count
1667     )
1668 /*++
1669 
1670 Routine Description:
1671     An enchanced version of FxPowerIdleMachine::IoIncrement that has special
1672     behavior based on flags passed in by the caller. Please read the routine
1673     description of FxPowerIdleMachine::IoIncrement as well.
1674 
1675 Arguments:
1676     Flags - The following flags are defined -
1677          FxPowerReferenceDefault - No special behavior
1678          FxPowerReferenceSendPnpPowerUpEvent - Set the
1679            FxPowerIdleSendPnpPowerUpEvent flag in the idle state machine flags.
1680            This will indicate to the idle state machine that when the device
1681            powers up, it needs to send the PnpEventDeviceInD0 event to the PnP
1682            state machine.
1683 
1684 Return Value:
1685     STATUS_PENDING if the state machine is transition from idle to non idle
1686 
1687     STATUS_SUCCESS otherwise
1688 
1689   --*/
1690 {
1691     NTSTATUS status;
1692     KIRQL irql;
1693 
1694     m_Lock.Acquire(&irql);
1695 
1696     if (m_Flags & FxPowerIdlePowerFailed) {
1697         //
1698         // fail without incrementing the count because we are in an
1699         // invalid power state
1700         //
1701         status = STATUS_POWER_STATE_INVALID;
1702         COVERAGE_TRAP();
1703     }
1704     else if ((m_Flags & FxPowerIdleIsStarted) == 0x0) {
1705         //
1706         // The state machine is not yet in a started state
1707         //
1708         status = STATUS_POWER_STATE_INVALID;
1709     }
1710     else {
1711         m_IoCount++;
1712         if (Count != NULL) {
1713             *Count = m_IoCount;
1714         }
1715 
1716         ProcessEventLocked(PowerIdleEventIoIncrement);
1717 
1718         if (InD0Locked()) {
1719             status = STATUS_SUCCESS;
1720         }
1721         else {
1722             status = STATUS_PENDING;
1723             if (Flags & FxPowerReferenceSendPnpPowerUpEvent) {
1724                 m_Flags |= FxPowerIdleSendPnpPowerUpEvent;
1725             }
1726         }
1727     }
1728     m_Lock.Release(irql);
1729 
1730     return status;
1731 }
1732 
1733 
1734 VOID
IoDecrement(__in_opt PVOID Tag,__in_opt LONG Line,__in_opt PSTR File)1735 FxPowerIdleMachine::IoDecrement(
1736     __in_opt PVOID Tag,
1737     __in_opt LONG Line,
1738     __in_opt PSTR File
1739     )
1740 /*++
1741 
1742 Routine Description:
1743     Public function which allows the caller decrement the pending io count on
1744     this state machine.  If the count goes to zero and idle is enabled, then
1745     the timer is started.
1746 
1747 Arguments:
1748     None
1749 
1750 Return Value:
1751     None
1752 
1753   --*/
1754 {
1755     KIRQL irql;
1756     FxPkgPnp* pPkgPnp;
1757     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1758     ULONG count;
1759 
1760     pPkgPnp = GetPnpPkg(this);
1761     pFxDriverGlobals = pPkgPnp->GetDriverGlobals();
1762 
1763     m_Lock.Acquire(&irql);
1764 
1765     if (m_IoCount == 0) {
1766         //
1767         // We can get here for the following reasons:
1768         // 1. Driver called WdfDevicveStopIdle/WdfDeviceResumeIdle in a mismatched
1769         //    manner. This is a driver bug.
1770         // 2. Framework did power deref without a corresponding power ref.
1771         //    This would be a framework bug.
1772         //
1773         // We will break into debugger if verifier is turned on. This will allow
1774         // developers to catch this problem during develeopment.
1775         // We limit this break to version 1.11+ because otherwise older drivers
1776         // may hit this, and if they cannot be fixed for some reason, then
1777         // verifier would need to be turned off to avoid the break which is not
1778         // desirable.
1779         //
1780         DoTraceLevelMessage(
1781             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1782             "WDFDEVICE 0x%p !devobj 0x%p The device is being power-dereferenced"
1783             " without a matching power-reference. This could occur if driver"
1784             " incorrectly calls WdfDeviceResumeIdle without a matching call to"
1785             " WdfDeviceStopIdle.",
1786             pPkgPnp->GetDevice()->GetHandle(),
1787             pPkgPnp->GetDevice()->GetDeviceObject());
1788 
1789         if (pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
1790            FxVerifierDbgBreakPoint(pFxDriverGlobals);
1791         }
1792     }
1793 
1794     ASSERT(m_IoCount > 0);
1795     count = --m_IoCount;
1796     ProcessEventLocked(PowerIdleEventIoDecrement);
1797     m_Lock.Release(irql);
1798 
1799     if (m_TagTracker != NULL) {
1800         m_TagTracker->UpdateTagHistory(Tag, Line, File, TagRelease, count);
1801     }
1802 }
1803 
1804 BOOLEAN
QueryReturnToIdle(VOID)1805 FxPowerIdleMachine::QueryReturnToIdle(
1806     VOID
1807     )
1808 /*++
1809 
1810 Routine Description:
1811     Public function which allows the caller to query the current io count on
1812     this state machine.  If the count non zero, the device will be brought back
1813     to D0.  If zero, the device will remain in Dx.
1814 
1815 Arguments:
1816     None
1817 
1818 Return Value:
1819     if TRUE is returned, there is an outstanding IO.
1820     if FALSE is returned, the device is idle.
1821 
1822   --*/
1823 {
1824     KIRQL irql;
1825     BOOLEAN result;
1826 
1827     //
1828     // To return to idle, the following must be true
1829     // 1)  the device must be in Dx (FxPowerIdleInDx)
1830     // 2)  the timer must *NOT*  be running
1831     // 3)  an IO count of zero
1832     //
1833     m_Lock.Acquire(&irql);
1834 
1835     //     1
1836     if ((m_Flags & FxPowerIdleInDx) == FxPowerIdleInDx &&
1837         // 2                                        3
1838         (m_Flags & FxPowerIdleTimerStarted) == 0 && m_IoCount == 0x0) {
1839         result = TRUE;
1840     }
1841     else {
1842         result = FALSE;
1843     }
1844 
1845     //
1846     // If the caller is querying about returning to idle, then they have
1847     // processed the io present event that we previously sent.  We must clear
1848     // the flag, otherwise the following can occur
1849     // 1)  new io arrives, send the io present message
1850     // 2)  return to idle (io count goes to zero)
1851     // 3)  get queried to return to idle, return TRUE
1852     // 4)  new io arrives, we do not send the new io present message because the
1853     //     FxPowerIdleIoPresentSent flag is set.
1854     //
1855     m_Flags &= ~FxPowerIdleIoPresentSent;
1856 
1857     m_Lock.Release(irql);
1858 
1859     return result;
1860 }
1861 
1862 VOID
SendD0Notification(VOID)1863 FxPowerIdleMachine::SendD0Notification(
1864     VOID
1865     )
1866 {
1867     m_D0NotificationEvent.Set();
1868 
1869     if (m_Flags & FxPowerIdleSendPnpPowerUpEvent) {
1870 
1871         m_Flags &= ~FxPowerIdleSendPnpPowerUpEvent;
1872 
1873         //
1874         // Send an event to the Pnp state machine indicating that the device is
1875         // now in D0.
1876         //
1877 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
1878         GetPnpPkg(this)->PnpProcessEvent(PnpEventDeviceInD0);
1879 #else
1880         GetPnpPkg(this)->PnpProcessEvent(
1881             PnpEventDeviceInD0,
1882             TRUE // ProcessEventOnDifferentThread
1883             );
1884 #endif
1885 
1886     }
1887     return;
1888 }
1889 
1890 VOID
ProcessPowerEvent(__in FxPowerIdleEvents Event)1891 FxPowerIdleMachine::ProcessPowerEvent(
1892     __in FxPowerIdleEvents Event
1893     )
1894 /*++
1895 
1896 Routine Description:
1897     Post a power related event to the state machine.
1898 
1899 Arguments:
1900     Event - the event to post
1901 
1902 Return Value:
1903     None
1904 
1905   --*/
1906 {
1907     KIRQL irql;
1908 
1909     //
1910     // All other event types have specialized public functions
1911     //
1912     ASSERT(Event == PowerIdleEventPowerUpComplete ||
1913            Event == PowerIdleEventPowerUpFailed ||
1914            Event == PowerIdleEventPowerDown ||
1915            Event == PowerIdleEventPowerDownFailed);
1916 
1917     m_Lock.Acquire(&irql);
1918     ProcessEventLocked(Event);
1919     m_Lock.Release(irql);
1920 }
1921 
1922 VOID
ProcessEventLocked(__in FxPowerIdleEvents Event)1923 FxPowerIdleMachine::ProcessEventLocked(
1924     __in FxPowerIdleEvents Event
1925     )
1926 /*++
1927 
1928 Routine Description:
1929     Processes an event and runs it through the state machine
1930 
1931 Arguments:
1932 
1933 
1934 Return Value:
1935 
1936 
1937   --*/
1938 
1939 {
1940     const FxIdleStateTable* entry;
1941     FxPowerIdleStates newState;
1942     FxPkgPnp* pPkgPnp;
1943 
1944     pPkgPnp = GetPnpPkg(this);
1945 
1946     m_EventHistory[m_EventHistoryIndex] = Event;
1947     m_EventHistoryIndex = (m_EventHistoryIndex + 1) %
1948                           (sizeof(m_EventHistory)/sizeof(m_EventHistory[0]));
1949 
1950     entry = &m_StateTable[m_CurrentIdleState-FxIdleStopped];
1951     newState = FxIdleMax;
1952 
1953     for (ULONG i = 0; i < entry->TargetStatesCount; i++) {
1954         if (entry->TargetStates[i].PowerIdleEvent == Event) {
1955             DO_EVENT_TRAP(&entry->TargetStates[i]);
1956             newState = entry->TargetStates[i].PowerIdleState;
1957             break;
1958         }
1959     }
1960 
1961     if (newState == FxIdleMax) {
1962         switch (Event) {
1963         case PowerIdleEventIoIncrement:
1964         case PowerIdleEventIoDecrement:
1965             //
1966             // We always can handle io increment, io decrement, and query return
1967             // to idle from any state...
1968             //
1969             break;
1970 
1971         case PowerIdleEventEnabled:
1972             if (m_Flags & FxPowerIdleTimerEnabled) {
1973                 //
1974                 // Getting an enable event while enabled is OK
1975                 //
1976                 break;
1977             }
1978             //  ||   ||  Fall    ||  ||
1979             //  \/   \/  through \/  \/
1980 
1981         default:
1982             //
1983             // ...but we should not be dropping any other events from this state.
1984             //
1985 
1986             //
1987             DoTraceLevelMessage(
1988                 pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1989                 "WDFDEVICE 0x%p !devobj 0x%p power idle state %!FxPowerIdleStates!"
1990                 " dropping event %!FxPowerIdleEvents!",
1991                 pPkgPnp->GetDevice()->GetHandle(),
1992                 pPkgPnp->GetDevice()->GetDeviceObject(),
1993                 m_CurrentIdleState, Event);
1994 
1995             COVERAGE_TRAP();
1996         }
1997     }
1998 
1999     while (newState != FxIdleMax) {
2000 
2001         DoTraceLevelMessage(
2002             pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNPPOWERSTATES,
2003             "WDFDEVICE 0x%p !devobj 0x%p entering power idle state "
2004             "%!FxPowerIdleStates! from %!FxPowerIdleStates!",
2005             pPkgPnp->GetDevice()->GetHandle(),
2006             pPkgPnp->GetDevice()->GetDeviceObject(),
2007             newState, m_CurrentIdleState);
2008 
2009         m_StateHistory[m_StateHistoryIndex] = newState;
2010         m_StateHistoryIndex = (m_StateHistoryIndex + 1) %
2011                               (sizeof(m_StateHistory)/sizeof(m_StateHistory[0]));
2012 
2013         m_CurrentIdleState = newState;
2014         entry = &m_StateTable[m_CurrentIdleState-FxIdleStopped];
2015 
2016         if (entry->StateFunc != NULL) {
2017             newState = entry->StateFunc(this);
2018         }
2019         else {
2020             newState = FxIdleMax;
2021         }
2022     }
2023 }
2024