1 /*++
2 Copyright (c) Microsoft. All rights reserved.
3 
4 Module Name:
5 
6     WakeInterrupt.cpp
7 
8 Abstract:
9 
10     This module implements the wake interrupt logic in the framework.
11 
12 --*/
13 
14 #include "pnppriv.hpp"
15 
16 extern "C" {
17 #if defined(EVENT_TRACING)
18 #include "WakeInterruptStateMachine.tmh"
19 #endif
20 }
21 
22 const FxWakeInterruptTargetState
23 FxWakeInterruptMachine::m_FailedStates[] =
24 {
25     {WakeInterruptEventIsr, WakeInterruptFailed DEBUGGED_EVENT}
26 };
27 
28 const FxWakeInterruptTargetState
29 FxWakeInterruptMachine::m_D0States[] =
30 {
31     {WakeInterruptEventIsr, WakeInterruptInvokingEvtIsrInD0 DEBUGGED_EVENT},
32     {WakeInterruptEventLeavingD0, WakeInterruptDx DEBUGGED_EVENT},
33     {WakeInterruptEventLeavingD0NotArmedForWake, WakeInterruptDxNotArmedForWake DEBUGGED_EVENT}
34 };
35 
36 const FxWakeInterruptTargetState
37 FxWakeInterruptMachine::m_DxStates[] =
38 {
39     {WakeInterruptEventEnteringD0, WakeInterruptCompletingD0 DEBUGGED_EVENT},
40     {WakeInterruptEventIsr, WakeInterruptWaking DEBUGGED_EVENT},
41     {WakeInterruptEventD0EntryFailed, WakeInterruptFailed DEBUGGED_EVENT}
42 };
43 
44 const FxWakeInterruptTargetState
45 FxWakeInterruptMachine::m_DxNotArmedForWakeStates[] =
46 {
47     { WakeInterruptEventEnteringD0, WakeInterruptCompletingD0 DEBUGGED_EVENT },
48     { WakeInterruptEventIsr, WakeInterruptInvokingEvtIsrInDxNotArmedForWake DEBUGGED_EVENT },
49     { WakeInterruptEventD0EntryFailed, WakeInterruptFailed DEBUGGED_EVENT }
50 };
51 
52 const FxWakeInterruptTargetState
53 FxWakeInterruptMachine::m_WakingStates[] =
54 {
55     {WakeInterruptEventEnteringD0, WakeInterruptInvokingEvtIsrPostWake DEBUGGED_EVENT},
56     {WakeInterruptEventD0EntryFailed, WakeInterruptFailed DEBUGGED_EVENT}
57 };
58 
59 
60 const FxWakeInterruptStateTable
61 FxWakeInterruptMachine::m_StateTable[] =
62 {
63     // WakeInterruptFailed
64     {   FxWakeInterruptMachine::Failed,
65         FxWakeInterruptMachine::m_FailedStates,
66         ARRAY_SIZE(FxWakeInterruptMachine::m_FailedStates),
67     },
68 
69     // WakeInterruptD0
70     {   NULL,
71         FxWakeInterruptMachine::m_D0States,
72         ARRAY_SIZE(FxWakeInterruptMachine::m_D0States),
73     },
74 
75     // WakeInterruptDx
76     {   FxWakeInterruptMachine::Dx,
77         FxWakeInterruptMachine::m_DxStates,
78         ARRAY_SIZE(FxWakeInterruptMachine::m_DxStates),
79     },
80 
81     // WakeInterruptWaking
82     {   FxWakeInterruptMachine::Waking,
83         FxWakeInterruptMachine::m_WakingStates,
84         ARRAY_SIZE(FxWakeInterruptMachine::m_WakingStates),
85     },
86 
87     // WakeInterruptInvokingEvtIsrPostWakeStates
88     {   FxWakeInterruptMachine::InvokingEvtIsrPostWake,
89         NULL,
90         0,
91     },
92 
93     // WakeInterruptCompletingD0States
94     {   FxWakeInterruptMachine::CompletingD0,
95         NULL,
96         0,
97     },
98 
99     // WakeInterruptInvokingEvtIsrInD0
100     {   FxWakeInterruptMachine::InvokingEvtIsrInD0,
101         NULL,
102         0,
103     },
104 
105     // WakeInterruptDxNotArmedForWake
106     {   FxWakeInterruptMachine::DxNotArmedForWake,
107         FxWakeInterruptMachine::m_DxNotArmedForWakeStates,
108         ARRAY_SIZE(FxWakeInterruptMachine::m_DxNotArmedForWakeStates),
109     },
110 
111     // WakeInterruptInvokingEvtIsrInDxNotArmedForWake
112     { FxWakeInterruptMachine::InvokingEvtIsrInDxNotArmedForWake,
113       NULL,
114       0,
115     },
116 };
117 
118 FxWakeInterruptMachine::FxWakeInterruptMachine(
119     __in FxInterrupt * Interrupt
120     ) : FxThreadedEventQueue(FxWakeInterruptEventQueueDepth)
121 {
122     //
123     // Make sure we can fit the state into a byte
124     //
125     C_ASSERT(WakeInterruptMax <= 0xFF);
126 
127     m_CurrentState = WakeInterruptD0;
128 
129     RtlZeroMemory(&m_Queue, sizeof(m_Queue));
130     RtlZeroMemory(&m_States, sizeof(m_States));
131 
132     //
133     // Store the initial state in the state history array
134     //
135     m_States.History[IncrementHistoryIndex()] = m_CurrentState;
136     m_Interrupt = Interrupt;
137 }
138 
139 VOID
140 FxWakeInterruptMachine::ProcessEvent(
141     __in FxWakeInterruptEvents Event
142     )
143 {
144     NTSTATUS status;
145     KIRQL irql;
146     LONGLONG timeout = 0;
147 
148     //
149     // Acquire state machine *queue* lock, raising to DISPATCH_LEVEL
150     //
151     Lock(&irql);
152 
153     if (IsFull()) {
154         //
155         // The queue is full. This should never happen.
156         //
157         Unlock(irql);
158 
159         ASSERTMSG("The wake interrupt state machine queue is full\n",
160                   FALSE);
161         return;
162     }
163 
164     if (IsClosedLocked()) {
165         //
166         // The queue is closed. This should never happen.
167         //
168         DoTraceLevelMessage(
169           m_PkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
170           "WDFDEVICE 0x%p !devobj 0x%p current wake interrupt state"
171           " %!FxWakeInterruptStates! dropping event "
172           "%!FxWakeInterruptEvents! because of a closed queue",
173           m_PkgPnp->GetDevice()->GetHandle(),
174           m_PkgPnp->GetDevice()->GetDeviceObject(),
175           m_CurrentState,
176           Event);
177 
178         Unlock(irql);
179 
180         ASSERTMSG(
181             "The wake interrupt state machine queue is closed\n",
182             FALSE
183             );
184         return;
185     }
186 
187     //
188     // Enqueue the event
189     //
190     m_Queue[InsertAtTail()] = Event;
191 
192     //
193     // Drop the state machine *queue* lock
194     //
195     Unlock(irql);
196 
197     //
198     // Now, if we are running at PASSIVE_LEVEL, attempt to run the state machine
199     // on this thread. If we can't do that, then queue a work item.
200     //
201     if (irql == PASSIVE_LEVEL) {
202         //
203         // Try to acquire the state machine lock
204         //
205         status = m_StateMachineLock.AcquireLock(
206                     m_PkgPnp->GetDriverGlobals(),
207                     &timeout
208                     );
209         if (FxWaitLockInternal::IsLockAcquired(status)) {
210             FxPostProcessInfo info;
211 
212             //
213             // We now hold the state machine lock.  So call the function that
214             // dispatches the next state.
215             //
216             ProcessEventInner(&info);
217 
218             //
219             // The pnp state machine should be the only one deleting the object
220             //
221             ASSERT(info.m_DeleteObject == FALSE);
222 
223             //
224             // Release the state machine lock
225             //
226             m_StateMachineLock.ReleaseLock(
227                 m_PkgPnp->GetDriverGlobals()
228                 );
229 
230             info.Evaluate(m_PkgPnp);
231 
232             return;
233         }
234     }
235 
236     //
237     // For one reason or another, we couldn't run the state machine on this
238     // thread.  So queue a work item to do it.
239     //
240     QueueToThread();
241     return;
242 }
243 
244 VOID
245 FxWakeInterruptMachine::_ProcessEventInner(
246     __inout FxPkgPnp* PkgPnp,
247     __inout FxPostProcessInfo* Info,
248     __in PVOID WorkerContext
249     )
250 {
251 
252     UNREFERENCED_PARAMETER(PkgPnp);
253 
254     FxWakeInterruptMachine * pThis = (FxWakeInterruptMachine *) WorkerContext;
255 
256     //
257     // Take the state machine lock.
258     //
259     pThis->m_StateMachineLock.AcquireLock(
260                 pThis->m_PkgPnp->GetDriverGlobals()
261                 );
262 
263     //
264     // Call the function that will actually run the state machine.
265     //
266     pThis->ProcessEventInner(Info);
267 
268     //
269     // We are being called from the work item and m_WorkItemRunning is > 0, so
270     // we cannot be deleted yet.
271     //
272     ASSERT(Info->SomethingToDo() == FALSE);
273 
274     //
275     // Now release the state machine lock
276     //
277     pThis->m_StateMachineLock.ReleaseLock(
278                 pThis->m_PkgPnp->GetDriverGlobals()
279                 );
280 
281     return;
282 }
283 
284 VOID
285 FxWakeInterruptMachine::ProcessEventInner(
286     __inout FxPostProcessInfo* Info
287     )
288 {
289     KIRQL irql;
290     FxWakeInterruptEvents event;
291     const FxWakeInterruptStateTable* entry;
292     FxWakeInterruptStates newState;
293 
294     //
295     // Process as many events as we can
296     //
297     for ( ; ; ) {
298         //
299         // Acquire state machine *queue* lock
300         //
301         Lock(&irql);
302 
303         if (IsEmpty()) {
304             //
305             // The queue is empty.
306             //
307             GetFinishedState(Info);
308             Unlock(irql);
309             return;
310         }
311 
312         //
313         // Get the event from the queue
314         //
315         event = m_Queue[GetHead()];
316         IncrementHead();
317 
318         //
319         // Drop the state machine *queue* lock
320         //
321         Unlock(irql);
322 
323         //
324         // Get the state table entry for the current state
325         //
326         // NOTE: Prefast complains about buffer overflow if (m_CurrentState ==
327         // WakeInterruptMax), but that should never happen because WakeInterruptMax is not a real
328         // state. We just use it to represent the maximum value in the enum that
329         // defines the states.
330         //
331         __analysis_assume(m_CurrentState < WakeInterruptMax);
332         entry = &m_StateTable[m_CurrentState - WakeInterruptFailed];
333 
334         //
335         // Based on the event received, figure out the next state
336         //
337         newState = WakeInterruptMax;
338         for (ULONG i = 0; i < entry->TargetStatesCount; i++) {
339             if (entry->TargetStates[i].WakeInterruptEvent == event) {
340                 DO_EVENT_TRAP(&entry->TargetStates[i]);
341                 newState = entry->TargetStates[i].WakeInterruptState;
342                 break;
343             }
344         }
345 
346         if (newState == WakeInterruptMax) {
347             //
348             // Unexpected event for this state
349             //
350             DoTraceLevelMessage(
351                 m_PkgPnp->GetDriverGlobals(),
352                 TRACE_LEVEL_INFORMATION,
353                 TRACINGPNP,
354                 "WDFDEVICE 0x%p !devobj 0x%p wake interrupt state "
355                 "%!FxWakeInterruptStates! dropping event "
356                 "%!FxWakeInterruptEvents!",
357                 m_PkgPnp->GetDevice()->GetHandle(),
358                 m_PkgPnp->GetDevice()->GetDeviceObject(),
359                 m_CurrentState,
360                 event
361                 );
362 
363             COVERAGE_TRAP();
364         }
365 
366         while (newState != WakeInterruptMax) {
367             DoTraceLevelMessage(
368                 m_PkgPnp->GetDriverGlobals(),
369                 TRACE_LEVEL_INFORMATION,
370                 TRACINGPNPPOWERSTATES,
371                 "WDFDEVICE 0x%p !devobj 0x%p entering wake interrupt "
372                 "state %!FxWakeInterruptStates! from "
373                 "%!FxWakeInterruptStates!",
374                 m_PkgPnp->GetDevice()->GetHandle(),
375                 m_PkgPnp->GetDevice()->GetDeviceObject(),
376                 newState,
377                 m_CurrentState
378                 );
379 
380             //
381             // Update the state history array
382             //
383             m_States.History[IncrementHistoryIndex()] = (UCHAR) newState;
384 
385             //
386             // Move to the new state
387             //
388             m_CurrentState = (BYTE) newState;
389             entry = &m_StateTable[m_CurrentState-WakeInterruptFailed];
390 
391             //
392             // Invoke the state entry function (if present) for the new state
393             //
394             if (entry->StateFunc != NULL) {
395                 newState = entry->StateFunc(this);
396             }
397             else {
398                 newState = WakeInterruptMax;
399             }
400         }
401     }
402 
403     return;
404 }
405 
406 
407 FxWakeInterruptStates
408 FxWakeInterruptMachine::Waking(
409     __in FxWakeInterruptMachine* This
410     )
411 {
412     This->m_PkgPnp->PowerPolicyProcessEvent(PwrPolWakeInterruptFired);
413 
414     return WakeInterruptMax;
415 }
416 
417 FxWakeInterruptStates
418 FxWakeInterruptMachine::Dx(
419     __in FxWakeInterruptMachine* This
420     )
421 {
422     //
423     // Flush queued callbacks so that we know that nobody is still trying to
424     // synchronize against this interrupt. For KMDF this will flush DPCs and
425     // for UMDF this will send a message to reflector to flush queued DPCs.
426     //
427     This->m_Interrupt->FlushQueuedDpcs();
428 
429 #if FX_IS_KERNEL_MODE
430     //
431     // Rundown the workitem if present (passive-level interrupt support or KMDF).
432     // Not needed for UMDF since reflector doesn't use workitem for isr.
433     //
434     This->m_Interrupt->FlushQueuedWorkitem();
435 
436 #endif
437 
438     This->m_PkgPnp->AckPendingWakeInterruptOperation(FALSE);
439 
440     return WakeInterruptMax;
441 }
442 
443 FxWakeInterruptStates
444 FxWakeInterruptMachine::DxNotArmedForWake(
445     __in FxWakeInterruptMachine* This
446     )
447 {
448     //
449     // Ask power state machine to process the acknowledgement event
450     // on a different thread as we could be running the state machine's
451     // engine in the context of a wake ISR, and the power
452     // state machine will attempt to disconnect this interrupt when
453     // it processes the acknowledgement event.
454     //
455     This->m_PkgPnp->AckPendingWakeInterruptOperation(TRUE);
456 
457     return WakeInterruptMax;
458 }
459 
460 
461 FxWakeInterruptStates
462 FxWakeInterruptMachine::InvokingEvtIsrInDxNotArmedForWake(
463     __in FxWakeInterruptMachine* This
464     )
465 {
466     This->m_Interrupt->InvokeWakeInterruptEvtIsr();
467 
468     This->m_IsrEvent.Set();
469 
470     return WakeInterruptDxNotArmedForWake;
471 }
472 
473 FxWakeInterruptStates
474 FxWakeInterruptMachine::InvokingEvtIsrPostWake(
475     __in FxWakeInterruptMachine* This
476     )
477 {
478     This->m_Interrupt->InvokeWakeInterruptEvtIsr();
479 
480     This->m_IsrEvent.Set();
481 
482     return WakeInterruptCompletingD0;
483 }
484 
485 FxWakeInterruptStates
486 FxWakeInterruptMachine::CompletingD0(
487     __in FxWakeInterruptMachine* This
488     )
489 {
490     This->m_PkgPnp->AckPendingWakeInterruptOperation(FALSE);
491 
492     return WakeInterruptD0;
493 }
494 
495 FxWakeInterruptStates
496 FxWakeInterruptMachine::InvokingEvtIsrInD0(
497     __in FxWakeInterruptMachine* This
498     )
499 {
500     This->m_Interrupt->InvokeWakeInterruptEvtIsr();
501 
502     This->m_IsrEvent.Set();
503 
504     return WakeInterruptD0;
505 }
506 
507 FxWakeInterruptStates
508 FxWakeInterruptMachine::Failed(
509     __in FxWakeInterruptMachine* This
510     )
511 {
512     //
513     // Device failed to power up and we are not invoking the
514     // client driver's callback. So we cannot claim the
515     // interrupt
516     //
517     This->m_Claimed = FALSE;
518 
519     This->m_IsrEvent.Set();
520 
521     return WakeInterruptMax;
522 }
523 
524 
525