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