mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 02:10:34 +00:00
804 lines
23 KiB
C++
804 lines
23 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
InterruptObjectUm.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements a frameworks managed interrupt object
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
User mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "fxmin.hpp"
|
|
#include "FxInterruptThreadpoolUm.hpp"
|
|
|
|
extern "C" {
|
|
#include "InterruptObjectUm.tmh"
|
|
}
|
|
|
|
#define STRSAFE_LIB
|
|
#include <strsafe.h>
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxInterrupt::InitializeInternal(
|
|
__in FxObject* Parent,
|
|
__in PWDF_INTERRUPT_CONFIG Configuration
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
FxInterruptThreadpool* pool = NULL;
|
|
CM_SHARE_DISPOSITION shareVector;
|
|
|
|
UNREFERENCED_PARAMETER(Parent);
|
|
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
switch (m_ShareVector) {
|
|
case WdfTrue:
|
|
//
|
|
// Override the bus driver's value, explicitly sharing this interrupt.
|
|
//
|
|
shareVector = CmResourceShareShared;
|
|
break;
|
|
|
|
case WdfFalse:
|
|
//
|
|
// Override the bus driver's value, explicitly claiming this interrupt
|
|
// as non-shared.
|
|
//
|
|
shareVector = CmResourceShareDeviceExclusive;
|
|
break;
|
|
|
|
case WdfUseDefault:
|
|
default:
|
|
//
|
|
// Leave the bus driver's value alone.
|
|
//
|
|
shareVector = CmResourceShareUndetermined;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a thread pool if not already created. An interrupt is created in
|
|
// one of the pnp callbacks (OnAddDevice, OnPrepareHarwdare etc) so there is
|
|
// no race in getting and setting the theradpool pointer.
|
|
//
|
|
pool = m_Device->GetInterruptThreadpool();
|
|
if (pool == NULL) {
|
|
hr = FxInterruptThreadpool::_CreateAndInit(GetDriverGlobals(),
|
|
&pool);
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
m_Device->SetInterruptThreadpool(pool);
|
|
}
|
|
|
|
//
|
|
// create an instance of interruypt wait block
|
|
//
|
|
hr = FxInterruptWaitblock::_CreateAndInit(pool,
|
|
this,
|
|
FxInterrupt::_InterruptThunk,
|
|
&m_InterruptWaitblock);
|
|
if (FAILED(hr)) {
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Waitblock creation failed for CWdfInterrupt object"
|
|
" %!hresult!", hr);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Send an IOCTL to redirector to create and initialize an interrupt object
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
hr = deviceStack->InitializeInterrupt((WUDF_INTERRUPT_CONTEXT) this,
|
|
m_InterruptWaitblock->GetEventHandle(),
|
|
shareVector,
|
|
&m_RdInterruptContext
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
PUMDF_VERSION_DATA driverVersion = deviceStack->GetMinDriverVersion();
|
|
|
|
BOOL preserveCompat =
|
|
deviceStack->ShouldPreserveIrpCompletionStatusCompatibility();
|
|
|
|
status = CHostFxUtil::NtStatusFromHr(
|
|
hr,
|
|
driverVersion->MajorNumber,
|
|
driverVersion->MinorNumber,
|
|
preserveCompat
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"failed to initialize interrupt "
|
|
"%!STATUS!", status);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
|
|
//
|
|
// Dispose will do cleanup. No need to cleanup here.
|
|
//
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FxInterrupt::ConnectInternal(
|
|
VOID
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS status;
|
|
IWudfDeviceStack2 *deviceStack;
|
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
|
BOOLEAN isRdConnectingOrConnected = FALSE;
|
|
|
|
pFxDriverGlobals = GetDriverGlobals();
|
|
deviceStack = m_Device->GetDeviceStack2();
|
|
|
|
//
|
|
// reset the interrupt event to non-signaled state to start with a
|
|
// clean slate.
|
|
//
|
|
m_InterruptWaitblock->ResetEvent();
|
|
|
|
//
|
|
// Open the queue and enqueue an interrupt event wait to the threadpool
|
|
// before connecting the interrupt in the reflector. This minimizes the
|
|
// processing delay for interrupts that fire as soon as they are connected,
|
|
// like GPIO button devices that have no means to explicitly enable and
|
|
// disable interrupts at the hardware level.
|
|
//
|
|
StartThreadpoolWaitQueue();
|
|
|
|
//
|
|
// Tell the PnP Manager to connect the interrupt. Send a message to
|
|
// redirector to do so. When ConnectInterrupt returns a failure code,
|
|
// we use isRdConnectingOrConnected to check if the failure was due
|
|
// to an already connected or currently connecting interrupt.
|
|
//
|
|
hr = deviceStack->ConnectInterrupt(m_RdInterruptContext,
|
|
&isRdConnectingOrConnected);
|
|
if (FAILED(hr))
|
|
{
|
|
if (isRdConnectingOrConnected) {
|
|
//
|
|
// The connect call failed because we asked the Reflector to connect
|
|
// an already connected or currently connecting interrupt. Perhaps the
|
|
// client made a redundant call to WdfInterruptEnable. In this case,
|
|
// we want to keep the threadpool active so that we continue to receive
|
|
// and acknowledge interrupts - otherwise RdIsrPassiveLevel may time out.
|
|
//
|
|
DoTraceLevelMessage(pFxDriverGlobals,
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Multiple connection attempts for !WDFINTERRUPT 0x%p",
|
|
GetHandle());
|
|
|
|
if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(2, 19)) {
|
|
FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO),
|
|
CHECK("Multiple interrupt connection attempts", FALSE),
|
|
pFxDriverGlobals->Public.DriverName);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Connecting the interrupt in the reflector failed, which means
|
|
// that IoConnectInterruptEx either failed or was not called at all.
|
|
// All we need to do in this case is revert the actions done by
|
|
// StartThreadpoolWaitQueue above, which are closing the queue
|
|
// and removing the enqueued interrupt event wait.
|
|
//
|
|
StopAndFlushThreadpoolWaitQueue();
|
|
}
|
|
|
|
PUMDF_VERSION_DATA driverVersion = deviceStack->GetMinDriverVersion();
|
|
BOOL preserveCompat =
|
|
deviceStack->ShouldPreserveIrpCompletionStatusCompatibility();
|
|
|
|
status = CHostFxUtil::NtStatusFromHr(
|
|
hr,
|
|
driverVersion->MajorNumber,
|
|
driverVersion->MinorNumber,
|
|
preserveCompat
|
|
);
|
|
|
|
DoTraceLevelMessage(pFxDriverGlobals,
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Connect message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
|
|
return status;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::DisconnectInternal(
|
|
VOID
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
InterruptControlType controlType;
|
|
|
|
//
|
|
// Tell the PnP Manager to disconnect the interrupt.
|
|
// Send a message to redirector to do so.
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
controlType = InterruptControlTypeDisconnect;
|
|
hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Disconnect message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("Disconnect message to reflector returned failure. "),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
}
|
|
|
|
//
|
|
// Now that interrupt has been disconnected, flush the threadpool. Note that
|
|
// we need to do this after disconnect because if we did it before disconnect,
|
|
// we might drop any spurious interrupts that were generated after
|
|
// the driver disabled interrupt generation in its Disable callback,
|
|
// and after the DPCs were flushed. Fx can't drop spurious interrupt
|
|
// because if the interrupt is level-triggered then refelctor would be waiting
|
|
// for acknowledgement. The fact that disconnect command to reflector has
|
|
// returned to fx guarantees that there are no more interrupts pending in
|
|
// reflector.
|
|
//
|
|
StopAndFlushThreadpoolWaitQueue();
|
|
|
|
//
|
|
// There might still be WorkItemForIsr running as a result of
|
|
// the handling of spurious interrupt by driver, so we need to flush the
|
|
// workitem as well.
|
|
//
|
|
FlushQueuedWorkitem();
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::SetPolicyInternal(
|
|
__in WDF_INTERRUPT_POLICY Policy,
|
|
__in WDF_INTERRUPT_PRIORITY Priority,
|
|
__in PGROUP_AFFINITY TargetProcessorSet
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
//
|
|
// Tell reflector to set the policy of interrupt.
|
|
//
|
|
hr = deviceStack->SetInterruptPolicy(m_RdInterruptContext,
|
|
Policy,
|
|
Priority,
|
|
TargetProcessorSet);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"SetPolicy message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
}
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName);
|
|
|
|
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
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(IoResourceDescriptor);
|
|
|
|
ASSERTMSG("Not implemented for UMDF\n", FALSE);
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::ResetInternal(
|
|
VOID
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
InterruptControlType controlType;
|
|
HRESULT hr;
|
|
|
|
if (m_RdInterruptContext == NULL) {
|
|
//
|
|
// Reflector hasn't yet created a partner interrupt object so nothing
|
|
// to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Send a message to redirector to reset interrupt info.
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
controlType = InterruptControlTypeResetInterruptInfo;
|
|
hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"ResetInterruptInfo message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
}
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::RevokeResourcesInternal(
|
|
VOID
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
InterruptControlType controlType;
|
|
HRESULT hr;
|
|
|
|
if (m_RdInterruptContext == NULL) {
|
|
//
|
|
// Reflector hasn't yet created a partner interrupt object so nothing
|
|
// to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Send a message to redirector to revoke interrupt resources.
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
controlType = InterruptControlTypeRevokeResources;
|
|
hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"RevokeResources message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
}
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::FlushQueuedDpcs(
|
|
VOID
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Send a message to redirector to flush DPCs.
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
hr = deviceStack->ControlInterrupt(m_RdInterruptContext,
|
|
InterruptControlTypeFlushQueuedDpcs);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"FlushQueuedDpcs message to reflector returned failure "
|
|
"%!hresult!", hr);
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL,
|
|
TRAPMSG("FlushQueuedDpcs message to reflector returned failure"),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::AssignResourcesInternal(
|
|
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,
|
|
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans,
|
|
__in PWDF_INTERRUPT_INFO InterruptInfo
|
|
)
|
|
{
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// level-triggered interrupt handling is supported only on win8 and newer.
|
|
//
|
|
if (IsLevelTriggered(CmDescTrans->Flags) &&
|
|
FxIsPassiveLevelInterruptSupported() == FALSE) {
|
|
hr = E_INVALIDARG;
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Failed to assign interrupt resource to interrupt object"
|
|
"because interrupt resource is for level-triggered interrupt"
|
|
"which is not supported on this platform. See the docs for info on"
|
|
"supported platforms. %!hresult!\n", hr);
|
|
|
|
FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), TRAPMSG(
|
|
"Failed to assign interrupt resource to interrupt object"
|
|
"because interrupt resource is for level-triggered interrupt"
|
|
"which is not supported on this platform. See the docs for info on"
|
|
"supported platforms."),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
}
|
|
|
|
//
|
|
// Sharing is only supported for level-triggered interrupts. We allow
|
|
// shared latched interrupts in order to accomodate incorrect device
|
|
// firmwares that mistakenly declare their exclusive resources as shared.
|
|
// Genuinely shared edge-triggered interrupts will cause a deadlock
|
|
// because of how the OS handles non-passive latched interrupts with
|
|
// multiple registered handlers. See RdInterrupt::AssignResources
|
|
// for details.
|
|
//
|
|
if (IsLevelTriggered(CmDescTrans->Flags) == FALSE &&
|
|
CmDescTrans->ShareDisposition != CmResourceShareDeviceExclusive) {
|
|
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
"The resource descriptor indicates that this is a shared "
|
|
"edge-triggered interrupt. UMDF only supports sharing of "
|
|
"level-triggered interrupts. Please check if your device "
|
|
"firmware mistakenly declares this resource as shared "
|
|
"instead of device exclusive. If the resource is in fact "
|
|
"shared, then UMDF does not support this device.\n");
|
|
}
|
|
|
|
//
|
|
// Tell the PnP Manager to assign resources to the interrupt.
|
|
// Send a message to redirector to do so.
|
|
//
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
hr = deviceStack->AssignInterruptResources(m_RdInterruptContext,
|
|
CmDescRaw,
|
|
CmDescTrans,
|
|
InterruptInfo,
|
|
m_PassiveHandlingByRedirector);
|
|
if (FAILED(hr))
|
|
{
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"AssignResources message to reflector returned failure "
|
|
"%!hresult!\n", hr);
|
|
}
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::ThreadpoolWaitCallback(
|
|
VOID
|
|
)
|
|
{
|
|
BOOLEAN claimed;
|
|
|
|
//
|
|
// ETW event for performance measurement
|
|
//
|
|
EventWriteEVENT_UMDF_FX_INTERRUPT_NOTIFICATION_RECEIVED(
|
|
m_InterruptInfo.MessageNumber
|
|
);
|
|
|
|
//
|
|
// Invoke the ISR callback under interrupt lock.
|
|
//
|
|
if (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
|
|
//
|
|
claimed = WakeInterruptIsr();
|
|
} else {
|
|
AcquireLock();
|
|
claimed = m_EvtInterruptIsr(GetHandle(),
|
|
m_InterruptInfo.MessageNumber
|
|
);
|
|
ReleaseLock();
|
|
}
|
|
|
|
//
|
|
// Queue another wait. MSDN: You must re-register the event with the
|
|
// wait object before signaling it each time to trigger the wait callback.
|
|
//
|
|
if (m_CanQueue) {
|
|
QueueSingleWaitOnInterruptEvent();
|
|
}
|
|
|
|
//
|
|
// Return acknowledgement to reflector if it's handled at passive level
|
|
// by reflector.
|
|
//
|
|
if (m_PassiveHandlingByRedirector) {
|
|
IWudfDeviceStack *deviceStack;
|
|
HRESULT hr;
|
|
|
|
deviceStack = m_Device->GetDeviceStack();
|
|
|
|
hr = deviceStack->AcknowledgeInterrupt(m_RdInterruptContext, claimed);
|
|
|
|
if (FAILED(hr)) {
|
|
DoTraceLevelMessage(GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"AcknowledgeInterrupt message to reflector returned "
|
|
"failure. Check UMDF log for failure reason. %!hresult!", hr);
|
|
|
|
FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("AcknowledgeInterrupt message to "
|
|
"reflector returned failure. Check UMDF log for failure reason. "),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::QueueSingleWaitOnInterruptEvent(
|
|
VOID
|
|
)
|
|
{
|
|
m_InterruptWaitblock->SetThreadpoolWait();
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::StartThreadpoolWaitQueue(
|
|
VOID
|
|
)
|
|
{
|
|
m_CanQueue = TRUE;
|
|
|
|
QueueSingleWaitOnInterruptEvent();
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::StopAndFlushThreadpoolWaitQueue(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// We need to stop the threadpool wait queue and accomplish the following:
|
|
// 1) Prevent any new waits from being queued.
|
|
// 2) Removed any waits already queued.
|
|
// 3) Wait for interrupt isr callback to complete.
|
|
//
|
|
|
|
//
|
|
// Prevent any more enquing now that interrupt has been disconnected.
|
|
//
|
|
m_CanQueue = FALSE;
|
|
|
|
//
|
|
// wait for isr callback
|
|
//
|
|
m_InterruptWaitblock->WaitForOutstandingCallbackToComplete();
|
|
|
|
//
|
|
// remove any waits already queued
|
|
//
|
|
m_InterruptWaitblock->ClearThreadpoolWait();
|
|
|
|
//
|
|
// wait for callback. This additional wait for callback is needed to
|
|
// handle the follwoing race:
|
|
// - CanQueue is set to false in this thread
|
|
// - Callback is executing at statement after CanQueue check so it did not
|
|
// see false.
|
|
// - this thread waits for callback
|
|
// - callback thread queues a wait and returns
|
|
// - the wait earlier queued is satisfied and callback runs
|
|
// - this thread clears the queue (there is nothing to clear) but there is
|
|
// still a callback runnning and this thread needs to wait.
|
|
//
|
|
m_InterruptWaitblock->WaitForOutstandingCallbackToComplete();
|
|
}
|
|
|
|
VOID
|
|
CALLBACK
|
|
FxInterrupt::_InterruptThunk(
|
|
PTP_CALLBACK_INSTANCE Instance,
|
|
PVOID Parameter,
|
|
PTP_WAIT Wait,
|
|
TP_WAIT_RESULT WaitResult
|
|
)
|
|
{
|
|
FxInterrupt* fxInterrupt = (FxInterrupt*) Parameter;
|
|
|
|
UNREFERENCED_PARAMETER(Instance);
|
|
UNREFERENCED_PARAMETER(Wait);
|
|
UNREFERENCED_PARAMETER(WaitResult);
|
|
|
|
fxInterrupt->ThreadpoolWaitCallback();
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::FlushAndRundownInternal(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// flush the threadpool callbacks
|
|
//
|
|
StopAndFlushThreadpoolWaitQueue();
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// waitblock destructor will ensure event and waitblock cleanup.
|
|
//
|
|
if (m_InterruptWaitblock != NULL) {
|
|
delete m_InterruptWaitblock;
|
|
m_InterruptWaitblock = NULL;
|
|
}
|
|
|
|
//
|
|
// No need to explicitly delete the COM wrapper (CWdfInterrupt).
|
|
// The COM wrapper will get deleted in fxInterrupt's destroy callback when
|
|
// the object tree reference taken during creation will be released.
|
|
//
|
|
}
|
|
|
|
BOOLEAN
|
|
FxInterrupt::QueueDpcForIsr(
|
|
VOID
|
|
)
|
|
{
|
|
FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("Not implemented"),
|
|
GetDriverGlobals()->Public.DriverName);
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
FxInterrupt::WorkItemHandler(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// For UMDF, we allow drivers to call WdfInterruptQueueDpcdForIsr, and
|
|
// internally we queue a workitem and invoke EvtInterruptDpc. Only
|
|
// one of the callbacks EvtInterruptDpc or EvtInterruptWorkitem is
|
|
// allowed.
|
|
//
|
|
ASSERT(m_EvtInterruptWorkItem != NULL || m_EvtInterruptDpc != NULL);
|
|
ASSERT((m_EvtInterruptWorkItem != NULL && m_EvtInterruptDpc != NULL) == FALSE);
|
|
|
|
FX_TRACK_DRIVER(GetDriverGlobals());
|
|
|
|
//
|
|
// Call the drivers registered WorkItemForIsr event callback
|
|
//
|
|
if (m_CallbackLock != NULL) {
|
|
KIRQL irql = 0;
|
|
|
|
m_CallbackLock->Lock(&irql);
|
|
if (m_EvtInterruptWorkItem != NULL) {
|
|
m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
|
|
}
|
|
else {
|
|
m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
|
|
}
|
|
m_CallbackLock->Unlock(irql);
|
|
}
|
|
else {
|
|
if (m_EvtInterruptWorkItem != NULL) {
|
|
m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle());
|
|
}
|
|
else {
|
|
m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle());
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
_SynchronizeExecution(
|
|
__in MdInterrupt Interrupt,
|
|
__in MdInterruptSynchronizeRoutine SynchronizeRoutine,
|
|
__in PVOID SynchronizeContext
|
|
)
|
|
{
|
|
FxInterruptEnableParameters* pParams;
|
|
BOOLEAN isPassive;
|
|
|
|
UNREFERENCED_PARAMETER(Interrupt);
|
|
|
|
pParams = (FxInterruptEnableParameters*) SynchronizeContext;
|
|
isPassive = pParams->Interrupt->IsPassiveHandling();
|
|
FX_VERIFY(INTERNAL, CHECK("Must be Passive Interrupt", isPassive));
|
|
|
|
//
|
|
// The internal synchronize routine will call the routine under lock
|
|
//
|
|
return SynchronizeRoutine(SynchronizeContext);
|
|
}
|