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
FxWakeInterruptMachine(__in FxInterrupt * Interrupt)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
ProcessEvent(__in FxWakeInterruptEvents Event)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
_ProcessEventInner(__inout FxPkgPnp * PkgPnp,__inout FxPostProcessInfo * Info,__in PVOID WorkerContext)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
ProcessEventInner(__inout FxPostProcessInfo * Info)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
Waking(__in FxWakeInterruptMachine * This)408 FxWakeInterruptMachine::Waking(
409 __in FxWakeInterruptMachine* This
410 )
411 {
412 This->m_PkgPnp->PowerPolicyProcessEvent(PwrPolWakeInterruptFired);
413
414 return WakeInterruptMax;
415 }
416
417 FxWakeInterruptStates
Dx(__in FxWakeInterruptMachine * This)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
DxNotArmedForWake(__in FxWakeInterruptMachine * This)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
InvokingEvtIsrInDxNotArmedForWake(__in FxWakeInterruptMachine * This)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
InvokingEvtIsrPostWake(__in FxWakeInterruptMachine * This)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
CompletingD0(__in FxWakeInterruptMachine * This)486 FxWakeInterruptMachine::CompletingD0(
487 __in FxWakeInterruptMachine* This
488 )
489 {
490 This->m_PkgPnp->AckPendingWakeInterruptOperation(FALSE);
491
492 return WakeInterruptD0;
493 }
494
495 FxWakeInterruptStates
InvokingEvtIsrInD0(__in FxWakeInterruptMachine * This)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
Failed(__in FxWakeInterruptMachine * This)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