1 //
2 //    Copyright (C) Microsoft.  All rights reserved.
3 //
4 #ifndef _FXPOWERIDLESTATEMACHINE_H_
5 #define _FXPOWERIDLESTATEMACHINE_H_
6 
7 //
8 // This is a magical number based on inspection.  If the queue overflows,
9 // it is OK to increase these numbers without fear of either dependencies or
10 // weird side affects.
11 //
12 const UCHAR FxPowerIdleEventQueueDepth = 8;
13 
14 enum FxPowerIdleEvents {
15     // CAN BE REUSED                        = 0x0001,
16     PowerIdleEventPowerUpFailed             = 0x0002,
17     PowerIdleEventPowerUpComplete           = 0x0004,
18     PowerIdleEventPowerDown                 = 0x0008,
19     PowerIdleEventPowerDownFailed           = 0x0010,
20     PowerIdleEventTimerExpired              = 0x0020,
21     PowerIdleEventEnabled                   = 0x0040,
22     PowerIdleEventDisabled                  = 0x0080,
23     PowerIdleEventIoDecrement               = 0x0100,
24     PowerIdleEventIoIncrement               = 0x0200,
25     PowerIdleEventStart                     = 0x0400,
26     PowerIdleEventStop                      = 0x0800,
27     PowerIdleNull                           = 0x0000,
28 };
29 
30 // begin_wpp config
31 // CUSTOM_TYPE(FxPowerIdleEvents, ItemEnum(FxPowerIdleEvents));
32 // end_wpp
33 
34 enum FxPowerIdleStates {
35     FxIdleStopped = 1,
36     FxIdleStarted,
37     FxIdleStartedPowerUp,
38     FxIdleStartedPowerFailed,
39     FxIdleDisabled,
40     FxIdleCheckIoCount,
41     FxIdleBusy,
42     FxIdleDecrementIo,
43     FxIdleStartTimer,
44     FxIdleTimerRunning,
45     FxIdleTimingOut,
46     FxIdleTimedOut,
47     FxIdleTimedOutIoIncrement,
48     FxIdleTimedOutPowerDown,
49     FxIdleTimedOutPowerDownFailed,
50     FxIdleGoingToDx,
51     FxIdleInDx,
52     FxIdleInDxIoIncrement,
53     FxIdleInDxPowerUpFailure,
54     FxIdleInDxStopped,
55     FxIdleInDxDisabled,
56     FxIdleInDxEnabled,
57     FxIdlePowerUp,
58     FxIdlePowerUpComplete,
59     FxIdleTimedOutDisabled,
60     FxIdleTimedOutEnabled,
61     FxIdleCancelTimer,
62     FxIdleWaitForTimeout,
63     FxIdleTimerExpired,
64     FxIdleDisabling,
65     FxIdleDisablingWaitForTimeout,
66     FxIdleDisablingTimerExpired,
67     FxIdlePowerFailedWaitForTimeout,
68     FxIdlePowerFailed,
69     FxIdleMax,
70 };
71 
72 //
73 // NOTE:  if you change these flags (order, values, etc), you must also modify
74 //        m_FlagsByName to match your changes.
75 //
76 enum FxPowerIdleFlags {
77     FxPowerIdleTimerEnabled        = 0x01,
78     FxPowerIdleInDx                = 0x02,
79     FxPowerIdleTimerCanceled       = 0x04,
80     FxPowerIdleTimerStarted        = 0x08,
81     FxPowerIdlePowerFailed         = 0x10,
82     FxPowerIdleIsStarted           = 0x20,
83     FxPowerIdleIoPresentSent       = 0x40,
84     FxPowerIdleSendPnpPowerUpEvent = 0x80,
85 };
86 
87 enum FxPowerReferenceFlags {
88     FxPowerReferenceDefault             = 0x0,
89     FxPowerReferenceSendPnpPowerUpEvent = 0x1
90 };
91 
92 typedef
93 FxPowerIdleStates
94 (*PFN_POWER_IDLE_STATE_ENTRY_FUNCTION)(
95     FxPowerIdleMachine*
96     );
97 
98 struct FxPowerIdleTargetState {
99     FxPowerIdleEvents PowerIdleEvent;
100 
101     FxPowerIdleStates PowerIdleState;
102 
103 #if FX_SUPER_DBG
104     BOOLEAN EventDebugged;
105 #endif
106 };
107 
108 struct FxIdleStateTable {
109     PFN_POWER_IDLE_STATE_ENTRY_FUNCTION StateFunc;
110 
111     const FxPowerIdleTargetState* TargetStates;
112 
113     ULONG TargetStatesCount;
114 };
115 
116 class FxPowerIdleMachine : public FxStump {
117 
118 public:
119     FxPowerIdleMachine(
120         VOID
121         );
122 
123     ~FxPowerIdleMachine(
124         VOID
125         );
126 
127     _Must_inspect_result_
128     NTSTATUS
129     Init(
130         VOID
131         );
132 
133     VOID
134     EnableTimer(
135         VOID
136         );
137 
138     BOOLEAN
139     DisableTimer(
140         VOID
141         );
142 
143     VOID
144     Start(
145         VOID
146         );
147 
148     VOID
149     Stop(
150         VOID
151         );
152 
153     VOID
154     Reset(
155         VOID
156         );
157 
158     _Must_inspect_result_
159     NTSTATUS
160     IoIncrement(
161         VOID
162         );
163 
164     _Must_inspect_result_
165     NTSTATUS
166     IoIncrementWithFlags(
167         __in FxPowerReferenceFlags Flags,
168         __out_opt PULONG Count = NULL
169         );
170 
171     VOID
172     IoDecrement(
173         __in_opt PVOID Tag = NULL,
174         __in_opt LONG Line = 0,
175         __in_opt PSTR File = NULL
176         );
177 
178     BOOLEAN
179     QueryReturnToIdle(
180         VOID
181         );
182 
183     VOID
WaitForD0(VOID)184     WaitForD0(
185         VOID
186         )
187     {
188         m_D0NotificationEvent.EnterCRAndWaitAndLeave();
189     }
190 
191     _Must_inspect_result_
192     NTSTATUS
PowerReference(__in BOOLEAN WaitForD0,__in_opt PVOID Tag=NULL,__in_opt LONG Line=0,__in_opt PSTR File=NULL)193     PowerReference(
194         __in BOOLEAN WaitForD0,
195         __in_opt PVOID Tag = NULL,
196         __in_opt LONG Line = 0,
197         __in_opt PSTR File = NULL
198         )
199     {
200         return PowerReferenceWorker(WaitForD0, FxPowerReferenceDefault, Tag, Line, File);
201     }
202 
203     _Must_inspect_result_
204     NTSTATUS
PowerReferenceWithFlags(__in FxPowerReferenceFlags Flags)205     PowerReferenceWithFlags(
206         __in FxPowerReferenceFlags Flags
207         )
208     {
209         return PowerReferenceWorker(FALSE, // WaitForD0
210                                     Flags);
211     }
212 
213     VOID
214     ProcessPowerEvent(
215         __in FxPowerIdleEvents Event
216         );
217 
218 protected:
219 
220     VOID
221     ProcessEventLocked(
222         __in FxPowerIdleEvents Event
223         );
224 
225     BOOLEAN
IsTransitioning(VOID)226     IsTransitioning(
227         VOID
228         )
229     {
230         return m_D0NotificationEvent.ReadState() ? FALSE : TRUE;
231     }
232 
233     BOOLEAN
CancelIdleTimer(VOID)234     CancelIdleTimer(
235         VOID
236         )
237     {
238         //
239         // If we are canceling the timer, be well sure it is started
240         //
241         ASSERT(m_Flags & FxPowerIdleTimerStarted);
242 
243         if (m_PowerTimeoutTimer.Stop()) {
244             m_Flags &= ~FxPowerIdleTimerStarted;
245             return TRUE;
246         }
247         else {
248             return FALSE;
249         }
250     }
251 
252     BOOLEAN
InD0Locked(VOID)253     InD0Locked(
254         VOID
255         )
256     {
257         return m_D0NotificationEvent.ReadState() ? TRUE : FALSE;
258     }
259 
260     VOID
261     SendD0Notification(
262         VOID
263         );
264 
265     _Must_inspect_result_
266     NTSTATUS
267     PowerReferenceWorker(
268         __in BOOLEAN WaitForD0,
269         __in FxPowerReferenceFlags Flags,
270         __in_opt PVOID Tag = NULL,
271         __in_opt LONG Line = 0,
272         __in_opt PSTR File = NULL
273         );
274 
275     static
276     MdDeferredRoutineType
277     _PowerTimeoutDpcRoutine;
278 
279     static
280     FxPowerIdleStates
281     Stopped(
282         __inout FxPowerIdleMachine* This
283         );
284 
285     static
286     FxPowerIdleStates
287     Started(
288         __inout FxPowerIdleMachine* This
289         );
290 
291     static
292     FxPowerIdleStates
293     StartedPowerUp(
294         __inout FxPowerIdleMachine* This
295         );
296 
297     static
298     FxPowerIdleStates
299     StartedPowerFailed(
300         __inout FxPowerIdleMachine* This
301         );
302 
303     static
304     FxPowerIdleStates
305     Disabled(
306         __inout FxPowerIdleMachine* This
307         );
308 
309     static
310     FxPowerIdleStates
311     CheckIoCount(
312         __inout FxPowerIdleMachine* This
313         );
314 
315     static
316     FxPowerIdleStates
317     DecrementIo(
318         __inout FxPowerIdleMachine* This
319         );
320 
321     static
322     FxPowerIdleStates
323     StartTimer(
324         __inout FxPowerIdleMachine* This
325         );
326 
327     static
328     FxPowerIdleStates
329     TimingOut(
330         __inout FxPowerIdleMachine* This
331         );
332 
333     static
334     FxPowerIdleStates
335     TimedOutIoIncrement(
336         __inout FxPowerIdleMachine* This
337         );
338 
339     static
340     FxPowerIdleStates
341     TimedOutPowerDown(
342         __inout FxPowerIdleMachine* This
343         );
344 
345     static
346     FxPowerIdleStates
347     TimedOutPowerDownFailed(
348         __inout FxPowerIdleMachine* This
349         );
350 
351     static
352     FxPowerIdleStates
353     GoingToDx(
354         __inout FxPowerIdleMachine* This
355         );
356 
357     static
358     FxPowerIdleStates
359     InDx(
360         __inout FxPowerIdleMachine* This
361         );
362 
363     static
364     FxPowerIdleStates
365     InDxIoIncrement(
366         __inout FxPowerIdleMachine* This
367         );
368 
369     static
370     FxPowerIdleStates
371     InDxPowerUpFailure(
372         __inout FxPowerIdleMachine* This
373         );
374 
375     static
376     FxPowerIdleStates
377     InDxStopped(
378         __inout FxPowerIdleMachine* This
379         );
380 
381     static
382     FxPowerIdleStates
383     InDxDisabled(
384         __inout FxPowerIdleMachine* This
385         );
386 
387     static
388     FxPowerIdleStates
389     InDxEnabled(
390         __inout FxPowerIdleMachine* This
391         );
392 
393     static
394     FxPowerIdleStates
395     PowerUp(
396         __inout FxPowerIdleMachine* This
397         );
398 
399     static
400     FxPowerIdleStates
401     PowerUpComplete(
402         __inout FxPowerIdleMachine* This
403         );
404 
405     static
406     FxPowerIdleStates
407     TimedOutDisabled(
408         __inout FxPowerIdleMachine* This
409         );
410 
411     static
412     FxPowerIdleStates
413     TimedOutEnabled(
414         __inout FxPowerIdleMachine* This
415         );
416 
417     static
418     FxPowerIdleStates
419     CancelTimer(
420         __inout FxPowerIdleMachine* This
421         );
422 
423     static
424     FxPowerIdleStates
425     TimerExpired(
426         __inout FxPowerIdleMachine* This
427         );
428 
429     static
430     FxPowerIdleStates
431     Disabling(
432         __inout FxPowerIdleMachine* This
433         );
434 
435     static
436     FxPowerIdleStates
437     DisablingTimerExpired(
438         __inout FxPowerIdleMachine* This
439         );
440 
441     static
442     FxPowerIdleStates
443     PowerFailed(
444         __inout FxPowerIdleMachine* This
445         );
446 
447 private:
448     VOID
449     CheckAssumptions(
450         VOID
451         );
452 
453 public:
454     LARGE_INTEGER m_PowerTimeout;
455 
456 protected:
457     //
458     // Lock which guards state
459     //
460     MxLock m_Lock;
461 
462     //
463     // Number of pending requests which require being in D0
464     //
465     ULONG m_IoCount;
466 
467     //
468     // Tracks power references and releases.
469     //
470     FxTagTracker* m_TagTracker;
471 
472     //
473     // Timer which will be set when the I/O count goes to zero
474     //
475     MxTimer m_PowerTimeoutTimer;
476 
477     //
478     // Event to wait on when transitioning from Dx to D0
479     //
480     FxCREvent m_D0NotificationEvent;
481 
482     union {
483         //
484         // Combintaion of FxPowerIdleFlags enum values
485         //
486         UCHAR m_Flags;
487 
488         //
489         // Not used in the code.  Here so that you can easily decode m_Flags in
490         // the debugger without needing the enum definition.
491         //
492         struct  {
493             UCHAR TimerEnabled : 1;
494             UCHAR InDx : 1;
495             UCHAR TimerCanceled : 1;
496             UCHAR TimerStarted : 1;
497             UCHAR TimerPowerFailed : 1;
498             UCHAR IsStarted : 1;
499             UCHAR IoPresentSent : 1;
500             UCHAR SendPnpPowerUpEvent : 1;
501         } m_FlagsByName;
502     };
503 
504     //
505     // Index into m_EventHistory where to place the next value
506     //
507     UCHAR m_EventHistoryIndex;
508 
509     //
510     // Index into m_StateHistory where to place the next value
511     UCHAR m_StateHistoryIndex;
512 
513     //
514     // our current state
515     //
516     FxPowerIdleStates m_CurrentIdleState;
517 
518     //
519     // Circular history of events fed into this state machine
520     //
521     FxPowerIdleEvents m_EventHistory[FxPowerIdleEventQueueDepth];
522 
523     //
524     // Circular history of states the state machine was in
525     //
526     FxPowerIdleStates m_StateHistory[FxPowerIdleEventQueueDepth];
527 
528     static const FxPowerIdleTargetState m_StoppedStates[];
529     static const FxPowerIdleTargetState m_StartedStates[];
530     static const FxPowerIdleTargetState m_DisabledStates[];
531     static const FxPowerIdleTargetState m_BusyStates[];
532     static const FxPowerIdleTargetState m_TimerRunningStates[];
533     static const FxPowerIdleTargetState m_TimedOutStates[];
534     static const FxPowerIdleTargetState m_InDxStates[];
535     static const FxPowerIdleTargetState m_WaitForTimeoutStates[];
536     static const FxPowerIdleTargetState m_DisablingWaitForTimeoutStates[];
537     static const FxPowerIdleTargetState m_PowerFailedWaitForTimeoutStates[];
538 
539     static const FxIdleStateTable m_StateTable[];
540 
541     //
542     // We use a coalescable timer for idle timeout. The tolerable delay for the
543     // idle timer is defined below. The value below is an arbitrary choice and
544     // can be changed if necessary. MSDN documentation suggests 100 ms as being
545     // a reasonable choice.
546     //
547     static const ULONG m_IdleTimerTolerableDelayMS = 100;
548 };
549 
550 #endif // _FXPOWERIDLESTATEMACHINE_H_
551