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