1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 InterruptObject.cpp 8 9 Abstract: 10 11 This module implements a frameworks managed interrupt object 12 13 Author: 14 15 16 17 18 Environment: 19 20 Kernel mode only 21 22 Revision History: 23 24 25 26 27 28 29 --*/ 30 31 #include "pnppriv.hpp" 32 33 // Tracing support 34 // #include "InterruptObjectKm.tmh" 35 36 _Must_inspect_result_ 37 NTSTATUS 38 FxInterrupt::InitializeInternal( 39 __in FxObject* Parent, 40 __in PWDF_INTERRUPT_CONFIG Configuration 41 ) 42 { 43 UNREFERENCED_PARAMETER(Parent); 44 UNREFERENCED_PARAMETER(Configuration); 45 46 DO_NOTHING(); 47 48 return STATUS_SUCCESS; 49 } 50 51 VOID 52 FxInterrupt::DpcHandler( 53 __in_opt PVOID SystemArgument1, 54 __in_opt PVOID SystemArgument2 55 ) 56 { 57 UNREFERENCED_PARAMETER(SystemArgument1); 58 UNREFERENCED_PARAMETER(SystemArgument2); 59 60 ASSERT(m_EvtInterruptDpc != NULL); 61 62 FX_TRACK_DRIVER(GetDriverGlobals()); 63 64 // 65 // Call the drivers registered DpcForIsr event callback 66 // 67 if (m_CallbackLock != NULL) { 68 KIRQL irql = 0; 69 70 m_CallbackLock->Lock(&irql); 71 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle()); 72 m_CallbackLock->Unlock(irql); 73 } 74 else { 75 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle()); 76 } 77 78 return; 79 } 80 81 BOOLEAN 82 FxInterrupt::QueueDpcForIsr( 83 VOID 84 ) 85 { 86 BOOLEAN queued; 87 88 // 89 // Using this function is optional, 90 // but the caller better have registered a handler 91 // 92 ASSERT(m_EvtInterruptDpc != NULL); 93 94 queued = KeInsertQueueDpc(&m_Dpc, this, NULL); 95 96 return queued; 97 } 98 99 VOID 100 FxInterrupt::WorkItemHandler( 101 VOID 102 ) 103 { 104 ASSERT(m_EvtInterruptWorkItem != NULL ); 105 106 FX_TRACK_DRIVER(GetDriverGlobals()); 107 108 // 109 // Call the drivers registered WorkItemForIsr event callback 110 // 111 if (m_CallbackLock != NULL) { 112 KIRQL irql = 0; 113 114 m_CallbackLock->Lock(&irql); 115 116 FxPerfTraceWorkItem(&m_EvtInterruptWorkItem); 117 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle()); 118 m_CallbackLock->Unlock(irql); 119 } 120 else { 121 FxPerfTraceWorkItem(&m_EvtInterruptWorkItem); 122 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle()); 123 } 124 125 return; 126 } 127 128 _Must_inspect_result_ 129 NTSTATUS 130 FxInterrupt::ConnectInternal( 131 VOID 132 ) 133 { 134 IO_CONNECT_INTERRUPT_PARAMETERS connectParams; 135 FxPkgPnp* fxPkgPnp; 136 137 fxPkgPnp = m_Device->m_PkgPnp; 138 139 // 140 // Tell the PnP Manager to connect the interrupt. 141 // 142 ASSERT(fxPkgPnp->m_IoConnectInterruptEx != NULL); 143 144 // 145 // We're running on Longhorn or later (or somebody backported the new 146 // interrupt code,) so tell the PnP manager everything we can about this 147 // device. 148 // 149 RtlZeroMemory(&connectParams, sizeof(connectParams)); 150 151 if (FxIsProcessorGroupSupported()) { 152 connectParams.Version = CONNECT_FULLY_SPECIFIED_GROUP; 153 } 154 else { 155 connectParams.Version = CONNECT_FULLY_SPECIFIED; 156 } 157 158 connectParams.FullySpecified.PhysicalDeviceObject = m_Device->GetPhysicalDevice(); 159 connectParams.FullySpecified.InterruptObject = &m_Interrupt; 160 connectParams.FullySpecified.ServiceRoutine = _InterruptThunk; 161 connectParams.FullySpecified.ServiceContext = this; 162 connectParams.FullySpecified.SpinLock = m_SpinLock; 163 connectParams.FullySpecified.FloatingSave = m_FloatingSave; 164 connectParams.FullySpecified.Vector = m_InterruptInfo.Vector; 165 connectParams.FullySpecified.Irql = m_InterruptInfo.Irql; 166 connectParams.FullySpecified.ProcessorEnableMask = m_InterruptInfo.TargetProcessorSet; 167 connectParams.FullySpecified.Group = m_InterruptInfo.Group; 168 connectParams.FullySpecified.InterruptMode = m_InterruptInfo.Mode; 169 connectParams.FullySpecified.ShareVector = 170 m_InterruptInfo.ShareDisposition == CmResourceShareShared ? TRUE : FALSE; 171 connectParams.FullySpecified.SynchronizeIrql = m_SynchronizeIrql; 172 173 return fxPkgPnp->m_IoConnectInterruptEx(&connectParams); 174 } 175 176 VOID 177 FxInterrupt::DisconnectInternal( 178 VOID 179 ) 180 { 181 IO_DISCONNECT_INTERRUPT_PARAMETERS params; 182 PKINTERRUPT interruptObject; 183 FxPkgPnp* fxPkgPnp; 184 185 fxPkgPnp = m_Device->m_PkgPnp; 186 187 // 188 // Now null these pointers so that we can catch anyone trying to use them 189 // erroneously. 190 // 191 interruptObject = m_Interrupt; 192 m_Interrupt = NULL; 193 194 // 195 // Disconnect the interrupt. 196 // 197 ASSERT(fxPkgPnp->m_IoDisconnectInterruptEx != NULL); 198 199 RtlZeroMemory(¶ms, sizeof(params)); 200 201 if (FxIsProcessorGroupSupported()) { 202 params.Version = CONNECT_FULLY_SPECIFIED_GROUP; 203 } 204 else { 205 params.Version = CONNECT_FULLY_SPECIFIED; 206 } 207 208 params.ConnectionContext.InterruptObject = interruptObject; 209 210 fxPkgPnp->m_IoDisconnectInterruptEx(¶ms); 211 212 return; 213 } 214 215 VOID 216 FxInterrupt::FilterResourceRequirements( 217 __inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor 218 ) 219 /*++ 220 221 Routine Description: 222 223 This function allows an interrupt object to change the 224 IoResourceRequirementsList that the PnP Manager sends during 225 IRP_MN_FILTER_RESOURCE_REQUIREMENTS. This function takes a single 226 IoResourceDescriptor and applies default or user specified policy. 227 228 Arguments: 229 230 IoResourceDescriptor - Pointer to descriptor that matches this interrupt object 231 232 Return Value: 233 234 VOID 235 236 --*/ 237 { 238 // 239 // Set sharing policy. 240 // 241 switch (m_ShareVector) { 242 case WdfTrue: 243 // 244 // Override the bus driver's value, explicitly sharing this interrupt. 245 // 246 IoResourceDescriptor->ShareDisposition = CmResourceShareShared; 247 break; 248 249 case WdfFalse: 250 // 251 // Override the bus driver's value, explicitly claiming this interrupt 252 // as non-shared. 253 // 254 IoResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; 255 break; 256 257 case WdfUseDefault: 258 default: 259 // 260 // Leave the bus driver's value alone. 261 // 262 break; 263 } 264 265 // 266 // Apply policy. Only do this if we are running on an OS which supports 267 // the notion of Interrupt Policy and if the policy is not already included 268 // by the bus driver based on registry settings. 269 // 270 if (FxLibraryGlobals.IoConnectInterruptEx != NULL && 271 m_SetPolicy && 272 (IoResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_POLICY_INCLUDED) == 0x0) { 273 274 IoResourceDescriptor->Flags |= CM_RESOURCE_INTERRUPT_POLICY_INCLUDED; 275 IoResourceDescriptor->u.Interrupt.AffinityPolicy = (IRQ_DEVICE_POLICY)m_Policy; 276 IoResourceDescriptor->u.Interrupt.PriorityPolicy = (IRQ_PRIORITY)m_Priority; 277 IoResourceDescriptor->u.Interrupt.TargetedProcessors = m_Processors.Mask; 278 IoResourceDescriptor->u.Interrupt.Group = m_Processors.Group; 279 } 280 } 281 282 VOID 283 FxInterrupt::FlushQueuedDpcs( 284 VOID 285 ) 286 { 287 KeFlushQueuedDpcs(); 288 } 289 290 BOOLEAN 291 FxInterrupt::_InterruptThunk( 292 __in struct _KINTERRUPT *Interrupt, 293 __in PVOID ServiceContext 294 ) 295 296 /*++ 297 298 Routine Description: 299 300 This is the C routine called by the kernels INTERRUPT handler 301 302 Arguments: 303 304 Return Value: 305 306 --*/ 307 308 { 309 FxInterrupt* interrupt; 310 BOOLEAN result; 311 312 UNREFERENCED_PARAMETER(Interrupt); 313 314 interrupt = (FxInterrupt*)ServiceContext; 315 ASSERT( interrupt->m_EvtInterruptIsr != NULL ); 316 317 if (interrupt->m_IsEdgeTriggeredNonMsiInterrupt == TRUE) { 318 // 319 // If KMDF is in the process of disconnecting this interrupt, discard it. 320 // 321 if (interrupt->m_Disconnecting == TRUE) { 322 return FALSE; 323 } 324 325 // 326 // Captures the interrupt object as a backup in case interrupts start 327 // to arrive before IoConnectInterruptEx sets FxInterrupt.m_Interrupt. 328 // 329 interrupt->m_InterruptCaptured = Interrupt; 330 } 331 // 332 // If the interrupt is not connected, treat this as spurious interrupt. 333 // 334 else if (NULL == interrupt->m_Interrupt) { 335 return FALSE; 336 } 337 338 if (interrupt->IsWakeCapable()) { 339 // 340 // if it is a wake capable interrupt, we will hand it off 341 // to the state machine so that it can power up the device 342 // if required and then invoke the ISR callback 343 // 344 ASSERT(interrupt->m_PassiveHandling); 345 FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr); 346 return interrupt->WakeInterruptIsr(); 347 } 348 349 if (interrupt->m_PassiveHandling) { 350 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 351 352 // 353 // Acquire our internal passive-lock after the kernel acquired its own 354 // passive-lock and before invoking the callback. 355 // 356 interrupt->AcquireLock(); 357 358 FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr); 359 360 result = interrupt->m_EvtInterruptIsr( 361 interrupt->GetHandle(), 362 interrupt->m_InterruptInfo.MessageNumber); 363 interrupt->ReleaseLock(); 364 } 365 else { 366 367 FxPerfTraceInterrupt(&interrupt->m_EvtInterruptIsr); 368 369 result = interrupt->m_EvtInterruptIsr( 370 interrupt->GetHandle(), 371 interrupt->m_InterruptInfo.MessageNumber); 372 } 373 374 return result; 375 } 376 377 VOID 378 FxInterrupt::_InterruptDpcThunk( 379 __in PKDPC Dpc, 380 __in_opt PVOID DeferredContext, 381 __in_opt PVOID SystemArgument1, 382 __in_opt PVOID SystemArgument2 383 ) 384 /*++ 385 386 Routine Description: 387 388 Thunk used to invoke EvtInterruptDpc at DPC-level, or to queue a work-item 389 for invoking EvtInterruptWorkItem at passive-level. 390 391 --*/ 392 { 393 FxInterrupt* interrupt; 394 395 UNREFERENCED_PARAMETER(Dpc); 396 397 ASSERT(DeferredContext != NULL); 398 interrupt = (FxInterrupt*) DeferredContext; 399 400 if (interrupt->m_SystemWorkItem == NULL) { 401 402 FxPerfTraceDpc(&interrupt->m_EvtInterruptDpc); 403 404 interrupt->DpcHandler(SystemArgument1, SystemArgument2); 405 } 406 else { 407 interrupt->m_SystemWorkItem->TryToEnqueue(_InterruptWorkItemCallback, 408 interrupt); 409 } 410 } 411 412 VOID 413 FxInterrupt::FlushAndRundownInternal( 414 VOID 415 ) 416 { 417 // 418 // Rundown the workitem. 419 // 420 if (m_SystemWorkItem != NULL) { 421 m_SystemWorkItem->DeleteObject(); 422 m_SystemWorkItem = NULL; 423 } 424 425 // 426 // If present, delete the default passive-lock. 427 // 428 if (m_DisposeWaitLock) { 429 ASSERT(m_WaitLock != NULL); 430 m_WaitLock->DeleteObject(); 431 m_WaitLock = NULL; 432 m_DisposeWaitLock = FALSE; 433 } 434 } 435 436 BOOLEAN 437 FxInterrupt::_InterruptMarkDisconnecting( 438 _In_opt_ PVOID SyncContext 439 ) 440 { 441 FxInterrupt* pFxInterrupt; 442 443 ASSERT(SyncContext != NULL); 444 pFxInterrupt = (FxInterrupt*)SyncContext; 445 446 // 447 // This callback is invoked only if m_IsEdgeTriggeredNonMsiInterrupt 448 // is TRUE. This will cause _InterruptThunk to discard subsequent 449 // interrupts until m_Disconnecting is reset to FALSE. 450 // 451 pFxInterrupt->m_Disconnecting = TRUE; 452 453 return TRUE; 454 } 455 456 VOID 457 FxInterrupt::ReportActive( 458 _In_ BOOLEAN Internal 459 ) 460 { 461 IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters; 462 FxPkgPnp* fxPkgPnp; 463 464 fxPkgPnp = m_Device->m_PkgPnp; 465 466 if (Internal == FALSE) { 467 // 468 // if the interrupt is not connected, you can't report active or inactive 469 // 470 if(m_Connected == FALSE || m_Interrupt == NULL) { 471 DoTraceLevelMessage( 472 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 473 "Driver is reporting WDFINTERRUPT %p as being active even though" 474 " it is not connected.", GetHandle()); 475 FxVerifierDbgBreakPoint(GetDriverGlobals()); 476 return; 477 } 478 479 if (fxPkgPnp->m_IoReportInterruptActive == NULL) { 480 DoTraceLevelMessage( 481 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 482 "Driver is calling DDI WdfInterruptReportActive() on an OS that " 483 "doesn't support the DDI."); 484 FxVerifierDbgBreakPoint(GetDriverGlobals()); 485 return; 486 } 487 } 488 489 // 490 // No need to report active if interrupt is already active 491 // 492 if (m_Active) { 493 return; 494 } 495 496 RtlZeroMemory(¶meters, sizeof(parameters)); 497 498 if (FxIsProcessorGroupSupported()) { 499 parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP; 500 } 501 else { 502 parameters.Version = CONNECT_FULLY_SPECIFIED; 503 } 504 505 parameters.ConnectionContext.InterruptObject = m_Interrupt; 506 507 fxPkgPnp->m_IoReportInterruptActive(¶meters); 508 m_Active = TRUE; 509 510 return; 511 } 512 513 VOID 514 FxInterrupt::ReportInactive( 515 _In_ BOOLEAN Internal 516 ) 517 { 518 IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters; 519 FxPkgPnp* fxPkgPnp; 520 521 fxPkgPnp = m_Device->m_PkgPnp; 522 523 if (Internal == FALSE) { 524 // 525 // if the interrupt is not connected, you can't report active or inactive 526 // 527 if(m_Connected == FALSE || m_Interrupt == NULL) { 528 DoTraceLevelMessage( 529 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 530 "Driver is reporting WDFINTERRUPT %p as being inactive even though" 531 " it is not connected.", GetHandle()); 532 FxVerifierDbgBreakPoint(GetDriverGlobals()); 533 return; 534 } 535 536 if (fxPkgPnp->m_IoReportInterruptInactive == NULL) { 537 DoTraceLevelMessage( 538 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 539 "Driver is calling DDI WdfInterruptReportInactive() on an OS that " 540 "doesn't support the DDI."); 541 FxVerifierDbgBreakPoint(GetDriverGlobals()); 542 return; 543 } 544 } 545 546 // 547 // No need to report Inactive if interrupt is already Inactive 548 // 549 if (m_Active == FALSE) { 550 return; 551 } 552 553 RtlZeroMemory(¶meters, sizeof(parameters)); 554 555 if (FxIsProcessorGroupSupported()) { 556 parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP; 557 } 558 else { 559 parameters.Version = CONNECT_FULLY_SPECIFIED; 560 } 561 562 parameters.ConnectionContext.InterruptObject = m_Interrupt; 563 564 fxPkgPnp->m_IoReportInterruptInactive(¶meters); 565 m_Active = FALSE; 566 567 return; 568 } 569 570