2020-09-24 20:51:15 +00:00
|
|
|
/*++
|
|
|
|
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
|
|
|
|
InterruptObject.cpp
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
This module implements a frameworks managed interrupt object
|
|
|
|
|
|
|
|
Author:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
|
|
|
|
Kernel mode only
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "pnppriv.hpp"
|
|
|
|
|
|
|
|
// Tracing support
|
2020-10-16 03:30:51 +00:00
|
|
|
// #include "InterruptObjectKm.tmh"
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxInterrupt::InitializeInternal(
|
|
|
|
__in FxObject* Parent,
|
|
|
|
__in PWDF_INTERRUPT_CONFIG Configuration
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Parent);
|
|
|
|
UNREFERENCED_PARAMETER(Configuration);
|
|
|
|
|
|
|
|
DO_NOTHING();
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::DpcHandler(
|
|
|
|
__in_opt PVOID SystemArgument1,
|
|
|
|
__in_opt PVOID SystemArgument2
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
|
|
|
|
ASSERT(m_EvtInterruptDpc != NULL);
|
|
|
|
|
|
|
|
FX_TRACK_DRIVER(GetDriverGlobals());
|
|
|
|
|
|
|
|
//
|
|
|
|
// Call the drivers registered DpcForIsr event callback
|
|
|
|
//
|
|
|
|
if (m_CallbackLock != NULL) {
|
|
|
|
KIRQL irql = 0;
|
|
|
|
|
|
|
|
m_CallbackLock->Lock(&irql);
|
|
|
|
m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
|
|
|
|
m_CallbackLock->Unlock(irql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FxInterrupt::QueueDpcForIsr(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
BOOLEAN queued;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Using this function is optional,
|
|
|
|
// but the caller better have registered a handler
|
|
|
|
//
|
|
|
|
ASSERT(m_EvtInterruptDpc != NULL);
|
|
|
|
|
|
|
|
queued = KeInsertQueueDpc(&m_Dpc, this, NULL);
|
|
|
|
|
|
|
|
return queued;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::WorkItemHandler(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT(m_EvtInterruptWorkItem != NULL );
|
|
|
|
|
|
|
|
FX_TRACK_DRIVER(GetDriverGlobals());
|
|
|
|
|
|
|
|
//
|
|
|
|
// Call the drivers registered WorkItemForIsr event callback
|
|
|
|
//
|
|
|
|
if (m_CallbackLock != NULL) {
|
|
|
|
KIRQL irql = 0;
|
|
|
|
|
|
|
|
m_CallbackLock->Lock(&irql);
|
|
|
|
|
|
|
|
FxPerfTraceWorkItem(&m_EvtInterruptWorkItem);
|
|
|
|
m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
|
|
|
|
m_CallbackLock->Unlock(irql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
FxPerfTraceWorkItem(&m_EvtInterruptWorkItem);
|
|
|
|
m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
FxInterrupt::ConnectInternal(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IO_CONNECT_INTERRUPT_PARAMETERS connectParams;
|
|
|
|
FxPkgPnp* fxPkgPnp;
|
|
|
|
|
|
|
|
fxPkgPnp = m_Device->m_PkgPnp;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Tell the PnP Manager to connect the interrupt.
|
|
|
|
//
|
|
|
|
ASSERT(fxPkgPnp->m_IoConnectInterruptEx != NULL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// We're running on Longhorn or later (or somebody backported the new
|
|
|
|
// interrupt code,) so tell the PnP manager everything we can about this
|
|
|
|
// device.
|
|
|
|
//
|
|
|
|
RtlZeroMemory(&connectParams, sizeof(connectParams));
|
|
|
|
|
|
|
|
if (FxIsProcessorGroupSupported()) {
|
|
|
|
connectParams.Version = CONNECT_FULLY_SPECIFIED_GROUP;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
connectParams.Version = CONNECT_FULLY_SPECIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
connectParams.FullySpecified.PhysicalDeviceObject = m_Device->GetPhysicalDevice();
|
|
|
|
connectParams.FullySpecified.InterruptObject = &m_Interrupt;
|
|
|
|
connectParams.FullySpecified.ServiceRoutine = _InterruptThunk;
|
|
|
|
connectParams.FullySpecified.ServiceContext = this;
|
|
|
|
connectParams.FullySpecified.SpinLock = m_SpinLock;
|
|
|
|
connectParams.FullySpecified.FloatingSave = m_FloatingSave;
|
|
|
|
connectParams.FullySpecified.Vector = m_InterruptInfo.Vector;
|
|
|
|
connectParams.FullySpecified.Irql = m_InterruptInfo.Irql;
|
|
|
|
connectParams.FullySpecified.ProcessorEnableMask = m_InterruptInfo.TargetProcessorSet;
|
|
|
|
connectParams.FullySpecified.Group = m_InterruptInfo.Group;
|
|
|
|
connectParams.FullySpecified.InterruptMode = m_InterruptInfo.Mode;
|
|
|
|
connectParams.FullySpecified.ShareVector =
|
|
|
|
m_InterruptInfo.ShareDisposition == CmResourceShareShared ? TRUE : FALSE;
|
|
|
|
connectParams.FullySpecified.SynchronizeIrql = m_SynchronizeIrql;
|
|
|
|
|
|
|
|
return fxPkgPnp->m_IoConnectInterruptEx(&connectParams);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::DisconnectInternal(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IO_DISCONNECT_INTERRUPT_PARAMETERS params;
|
|
|
|
PKINTERRUPT interruptObject;
|
|
|
|
FxPkgPnp* fxPkgPnp;
|
|
|
|
|
|
|
|
fxPkgPnp = m_Device->m_PkgPnp;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now null these pointers so that we can catch anyone trying to use them
|
|
|
|
// erroneously.
|
|
|
|
//
|
|
|
|
interruptObject = m_Interrupt;
|
|
|
|
m_Interrupt = NULL;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Disconnect the interrupt.
|
|
|
|
//
|
|
|
|
ASSERT(fxPkgPnp->m_IoDisconnectInterruptEx != NULL);
|
|
|
|
|
|
|
|
RtlZeroMemory(¶ms, sizeof(params));
|
|
|
|
|
|
|
|
if (FxIsProcessorGroupSupported()) {
|
|
|
|
params.Version = CONNECT_FULLY_SPECIFIED_GROUP;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
params.Version = CONNECT_FULLY_SPECIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.ConnectionContext.InterruptObject = interruptObject;
|
|
|
|
|
|
|
|
fxPkgPnp->m_IoDisconnectInterruptEx(¶ms);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::FilterResourceRequirements(
|
|
|
|
__inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
This function allows an interrupt object to change the
|
|
|
|
IoResourceRequirementsList that the PnP Manager sends during
|
|
|
|
IRP_MN_FILTER_RESOURCE_REQUIREMENTS. This function takes a single
|
|
|
|
IoResourceDescriptor and applies default or user specified policy.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
IoResourceDescriptor - Pointer to descriptor that matches this interrupt object
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Set sharing policy.
|
|
|
|
//
|
|
|
|
switch (m_ShareVector) {
|
|
|
|
case WdfTrue:
|
|
|
|
//
|
|
|
|
// Override the bus driver's value, explicitly sharing this interrupt.
|
|
|
|
//
|
|
|
|
IoResourceDescriptor->ShareDisposition = CmResourceShareShared;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WdfFalse:
|
|
|
|
//
|
|
|
|
// Override the bus driver's value, explicitly claiming this interrupt
|
|
|
|
// as non-shared.
|
|
|
|
//
|
|
|
|
IoResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WdfUseDefault:
|
|
|
|
default:
|
|
|
|
//
|
|
|
|
// Leave the bus driver's value alone.
|
|
|
|
//
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Apply policy. Only do this if we are running on an OS which supports
|
|
|
|
// the notion of Interrupt Policy and if the policy is not already included
|
|
|
|
// by the bus driver based on registry settings.
|
|
|
|
//
|
|
|
|
if (FxLibraryGlobals.IoConnectInterruptEx != NULL &&
|
|
|
|
m_SetPolicy &&
|
|
|
|
(IoResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_POLICY_INCLUDED) == 0x0) {
|
|
|
|
|
|
|
|
IoResourceDescriptor->Flags |= CM_RESOURCE_INTERRUPT_POLICY_INCLUDED;
|
|
|
|
IoResourceDescriptor->u.Interrupt.AffinityPolicy = (IRQ_DEVICE_POLICY)m_Policy;
|
|
|
|
IoResourceDescriptor->u.Interrupt.PriorityPolicy = (IRQ_PRIORITY)m_Priority;
|
|
|
|
IoResourceDescriptor->u.Interrupt.TargetedProcessors = m_Processors.Mask;
|
|
|
|
IoResourceDescriptor->u.Interrupt.Group = m_Processors.Group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::FlushQueuedDpcs(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
KeFlushQueuedDpcs();
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FxInterrupt::_InterruptThunk(
|
|
|
|
__in struct _KINTERRUPT *Interrupt,
|
|
|
|
__in PVOID ServiceContext
|
|
|
|
)
|
|
|
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
This is the C routine called by the kernels INTERRUPT handler
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
{
|
|
|
|
FxInterrupt* interrupt;
|
|
|
|
BOOLEAN result;
|
|
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Interrupt);
|
|
|
|
|
|
|
|
interrupt = (FxInterrupt*)ServiceContext;
|
|
|
|
ASSERT( interrupt->m_EvtInterruptIsr != NULL );
|
|
|
|
|
|
|
|
if (interrupt->m_IsEdgeTriggeredNonMsiInterrupt == TRUE) {
|
|
|
|
//
|
|
|
|
// If KMDF is in the process of disconnecting this interrupt, discard it.
|
|
|
|
//
|
|
|
|
if (interrupt->m_Disconnecting == TRUE) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Captures the interrupt object as a backup in case interrupts start
|
|
|
|
// to arrive before IoConnectInterruptEx sets FxInterrupt.m_Interrupt.
|
|
|
|
//
|
|
|
|
interrupt->m_InterruptCaptured = Interrupt;
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// If the interrupt is not connected, treat this as spurious interrupt.
|
|
|
|
//
|
|
|
|
else if (NULL == interrupt->m_Interrupt) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interrupt->IsWakeCapable()) {
|
|
|
|
//
|
|
|
|
// if it is a wake capable interrupt, we will hand it off
|
|
|
|
// to the state machine so that it can power up the device
|
|
|
|
// if required and then invoke the ISR callback
|
|
|
|
//
|
|
|
|
ASSERT(interrupt->m_PassiveHandling);
|
|
|
|
FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr);
|
|
|
|
return interrupt->WakeInterruptIsr();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interrupt->m_PassiveHandling) {
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Acquire our internal passive-lock after the kernel acquired its own
|
|
|
|
// passive-lock and before invoking the callback.
|
|
|
|
//
|
|
|
|
interrupt->AcquireLock();
|
|
|
|
|
|
|
|
FxPerfTracePassiveInterrupt(&interrupt->m_EvtInterruptIsr);
|
|
|
|
|
|
|
|
result = interrupt->m_EvtInterruptIsr(
|
|
|
|
interrupt->GetHandle(),
|
|
|
|
interrupt->m_InterruptInfo.MessageNumber);
|
|
|
|
interrupt->ReleaseLock();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
FxPerfTraceInterrupt(&interrupt->m_EvtInterruptIsr);
|
|
|
|
|
|
|
|
result = interrupt->m_EvtInterruptIsr(
|
|
|
|
interrupt->GetHandle(),
|
|
|
|
interrupt->m_InterruptInfo.MessageNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::_InterruptDpcThunk(
|
|
|
|
__in PKDPC Dpc,
|
|
|
|
__in_opt PVOID DeferredContext,
|
|
|
|
__in_opt PVOID SystemArgument1,
|
|
|
|
__in_opt PVOID SystemArgument2
|
|
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Thunk used to invoke EvtInterruptDpc at DPC-level, or to queue a work-item
|
|
|
|
for invoking EvtInterruptWorkItem at passive-level.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FxInterrupt* interrupt;
|
|
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
|
|
|
|
|
|
ASSERT(DeferredContext != NULL);
|
|
|
|
interrupt = (FxInterrupt*) DeferredContext;
|
|
|
|
|
|
|
|
if (interrupt->m_SystemWorkItem == NULL) {
|
|
|
|
|
|
|
|
FxPerfTraceDpc(&interrupt->m_EvtInterruptDpc);
|
|
|
|
|
|
|
|
interrupt->DpcHandler(SystemArgument1, SystemArgument2);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
interrupt->m_SystemWorkItem->TryToEnqueue(_InterruptWorkItemCallback,
|
|
|
|
interrupt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::FlushAndRundownInternal(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Rundown the workitem.
|
|
|
|
//
|
|
|
|
if (m_SystemWorkItem != NULL) {
|
|
|
|
m_SystemWorkItem->DeleteObject();
|
|
|
|
m_SystemWorkItem = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// If present, delete the default passive-lock.
|
|
|
|
//
|
|
|
|
if (m_DisposeWaitLock) {
|
|
|
|
ASSERT(m_WaitLock != NULL);
|
|
|
|
m_WaitLock->DeleteObject();
|
|
|
|
m_WaitLock = NULL;
|
|
|
|
m_DisposeWaitLock = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FxInterrupt::_InterruptMarkDisconnecting(
|
|
|
|
_In_opt_ PVOID SyncContext
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FxInterrupt* pFxInterrupt;
|
|
|
|
|
|
|
|
ASSERT(SyncContext != NULL);
|
|
|
|
pFxInterrupt = (FxInterrupt*)SyncContext;
|
|
|
|
|
|
|
|
//
|
|
|
|
// This callback is invoked only if m_IsEdgeTriggeredNonMsiInterrupt
|
|
|
|
// is TRUE. This will cause _InterruptThunk to discard subsequent
|
|
|
|
// interrupts until m_Disconnecting is reset to FALSE.
|
|
|
|
//
|
|
|
|
pFxInterrupt->m_Disconnecting = TRUE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::ReportActive(
|
|
|
|
_In_ BOOLEAN Internal
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters;
|
|
|
|
FxPkgPnp* fxPkgPnp;
|
|
|
|
|
|
|
|
fxPkgPnp = m_Device->m_PkgPnp;
|
|
|
|
|
|
|
|
if (Internal == FALSE) {
|
|
|
|
//
|
|
|
|
// if the interrupt is not connected, you can't report active or inactive
|
|
|
|
//
|
|
|
|
if(m_Connected == FALSE || m_Interrupt == NULL) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
"Driver is reporting WDFINTERRUPT %p as being active even though"
|
|
|
|
" it is not connected.", GetHandle());
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fxPkgPnp->m_IoReportInterruptActive == NULL) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
"Driver is calling DDI WdfInterruptReportActive() on an OS that "
|
|
|
|
"doesn't support the DDI.");
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// No need to report active if interrupt is already active
|
|
|
|
//
|
|
|
|
if (m_Active) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RtlZeroMemory(¶meters, sizeof(parameters));
|
|
|
|
|
|
|
|
if (FxIsProcessorGroupSupported()) {
|
|
|
|
parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parameters.Version = CONNECT_FULLY_SPECIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
parameters.ConnectionContext.InterruptObject = m_Interrupt;
|
|
|
|
|
|
|
|
fxPkgPnp->m_IoReportInterruptActive(¶meters);
|
|
|
|
m_Active = TRUE;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FxInterrupt::ReportInactive(
|
|
|
|
_In_ BOOLEAN Internal
|
|
|
|
)
|
|
|
|
{
|
|
|
|
IO_REPORT_INTERRUPT_ACTIVE_STATE_PARAMETERS parameters;
|
|
|
|
FxPkgPnp* fxPkgPnp;
|
|
|
|
|
|
|
|
fxPkgPnp = m_Device->m_PkgPnp;
|
|
|
|
|
|
|
|
if (Internal == FALSE) {
|
|
|
|
//
|
|
|
|
// if the interrupt is not connected, you can't report active or inactive
|
|
|
|
//
|
|
|
|
if(m_Connected == FALSE || m_Interrupt == NULL) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
"Driver is reporting WDFINTERRUPT %p as being inactive even though"
|
|
|
|
" it is not connected.", GetHandle());
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fxPkgPnp->m_IoReportInterruptInactive == NULL) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
|
|
"Driver is calling DDI WdfInterruptReportInactive() on an OS that "
|
|
|
|
"doesn't support the DDI.");
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// No need to report Inactive if interrupt is already Inactive
|
|
|
|
//
|
|
|
|
if (m_Active == FALSE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RtlZeroMemory(¶meters, sizeof(parameters));
|
|
|
|
|
|
|
|
if (FxIsProcessorGroupSupported()) {
|
|
|
|
parameters.Version = CONNECT_FULLY_SPECIFIED_GROUP;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parameters.Version = CONNECT_FULLY_SPECIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
parameters.ConnectionContext.InterruptObject = m_Interrupt;
|
|
|
|
|
|
|
|
fxPkgPnp->m_IoReportInterruptInactive(¶meters);
|
|
|
|
m_Active = FALSE;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|