You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
18 KiB
677 lines
18 KiB
/*****************************************************************************
|
|
* sync.cpp - synchronization
|
|
*****************************************************************************
|
|
* Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "private.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
* IInterruptSyncInit
|
|
*****************************************************************************
|
|
* Interface for interrupt synchronizer with Init.
|
|
*/
|
|
DECLARE_INTERFACE_(IInterruptSyncInit,IInterruptSync)
|
|
{
|
|
DEFINE_ABSTRACT_UNKNOWN() // For IUnknown
|
|
|
|
// For IInterruptSync
|
|
STDMETHOD_(NTSTATUS,CallSynchronizedRoutine)
|
|
( THIS_
|
|
IN PINTERRUPTSYNCROUTINE Routine,
|
|
IN PVOID DynamicContext
|
|
) PURE;
|
|
STDMETHOD_(PKINTERRUPT,GetKInterrupt)
|
|
( THIS
|
|
) PURE;
|
|
STDMETHOD_(NTSTATUS,Connect)
|
|
( THIS
|
|
) PURE;
|
|
STDMETHOD_(void,Disconnect)
|
|
( THIS
|
|
) PURE;
|
|
STDMETHOD_(NTSTATUS,RegisterServiceRoutine)
|
|
( THIS_
|
|
IN PINTERRUPTSYNCROUTINE Routine,
|
|
IN PVOID DynamicContext,
|
|
IN BOOLEAN First
|
|
) PURE;
|
|
|
|
// For IInterruptSyncInit
|
|
STDMETHOD_(NTSTATUS,Init)
|
|
( THIS_
|
|
IN PRESOURCELIST ResourceList,
|
|
IN ULONG ResourceIndex,
|
|
IN INTERRUPTSYNCMODE Mode
|
|
) PURE;
|
|
};
|
|
|
|
typedef IInterruptSyncInit *PINTERRUPTSYNCINIT;
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync
|
|
*****************************************************************************
|
|
* Interrupt synchronizer implementation.
|
|
*/
|
|
class CInterruptSync
|
|
: public IInterruptSyncInit,
|
|
public CUnknown
|
|
{
|
|
private:
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR m_descriptor;
|
|
INTERRUPTSYNCMODE m_mode;
|
|
PKINTERRUPT m_pKInterrupt;
|
|
LIST_ENTRY m_listEntry;
|
|
KSPIN_LOCK m_kSpinLock;
|
|
KIRQL m_kIrql;
|
|
|
|
public:
|
|
DECLARE_STD_UNKNOWN();
|
|
CInterruptSync(PUNKNOWN pUnknownOuter);
|
|
~CInterruptSync();
|
|
|
|
STDMETHODIMP_(NTSTATUS) Init
|
|
(
|
|
IN PRESOURCELIST ResourceList,
|
|
IN ULONG ResourceIndex,
|
|
IN INTERRUPTSYNCMODE Mode
|
|
);
|
|
|
|
IMP_IInterruptSync;
|
|
|
|
friend
|
|
BOOLEAN
|
|
CInterruptSyncServiceRoutine
|
|
(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID PVoidContext
|
|
);
|
|
friend
|
|
BOOLEAN
|
|
CInterruptSyncWrapperRoutine
|
|
(
|
|
IN PVOID PVoidContext
|
|
);
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* ISRLISTENTRY
|
|
*****************************************************************************
|
|
* Entry in the list of ISRs.
|
|
*/
|
|
typedef struct
|
|
{
|
|
LIST_ENTRY ListEntry;
|
|
PINTERRUPTSYNCROUTINE Routine;
|
|
PVOID DynamicContext;
|
|
}
|
|
ISRLISTENTRY, *PISRLISTENTRY;
|
|
|
|
/*****************************************************************************
|
|
* WRAPPERROUTINECONTEXT
|
|
*****************************************************************************
|
|
* Context for synchronized routine wrapper function.
|
|
*/
|
|
typedef struct
|
|
{
|
|
PINTERRUPTSYNCROUTINE Routine;
|
|
PVOID DynamicContext;
|
|
CInterruptSync * InterruptSync;
|
|
NTSTATUS NtStatus;
|
|
}
|
|
WRAPPERROUTINECONTEXT, *PWRAPPERROUTINECONTEXT;
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Factory
|
|
*/
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CreateInterruptSync()
|
|
*****************************************************************************
|
|
* Creates an interrupt synchronization object.
|
|
*/
|
|
NTSTATUS
|
|
CreateInterruptSync
|
|
(
|
|
OUT PUNKNOWN * Unknown,
|
|
IN REFCLSID,
|
|
IN PUNKNOWN UnknownOuter OPTIONAL,
|
|
IN POOL_TYPE PoolType
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Unknown);
|
|
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Creating INTERRUPTSYNC"));
|
|
|
|
STD_CREATE_BODY_
|
|
(
|
|
CInterruptSync,
|
|
Unknown,
|
|
UnknownOuter,
|
|
PoolType,
|
|
PINTERRUPTSYNC
|
|
);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcNewInterruptSync()
|
|
*****************************************************************************
|
|
* Creates and initializes an interrupt-level synchronization object.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcNewInterruptSync
|
|
(
|
|
OUT PINTERRUPTSYNC * OutInterruptSync,
|
|
IN PUNKNOWN OuterUnknown OPTIONAL,
|
|
IN PRESOURCELIST ResourceList,
|
|
IN ULONG ResourceIndex,
|
|
IN INTERRUPTSYNCMODE Mode
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(OutInterruptSync);
|
|
ASSERT(ResourceList);
|
|
|
|
//
|
|
// Invalidate Parameters.
|
|
//
|
|
if (NULL == OutInterruptSync ||
|
|
NULL == ResourceList)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcInterruptSync : Invalid Parameter"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PUNKNOWN unknown;
|
|
NTSTATUS ntStatus = CreateInterruptSync( &unknown,
|
|
GUID_NULL,
|
|
OuterUnknown,
|
|
NonPagedPool );
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
PINTERRUPTSYNCINIT interruptSync;
|
|
ntStatus = unknown->QueryInterface( IID_IInterruptSync,
|
|
(PVOID *) &interruptSync );
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = interruptSync->Init( ResourceList,
|
|
ResourceIndex,
|
|
Mode );
|
|
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
*OutInterruptSync = interruptSync;
|
|
}
|
|
else
|
|
{
|
|
interruptSync->Release();
|
|
}
|
|
}
|
|
|
|
unknown->Release();
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync member functions
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::CInterruptSync()
|
|
*****************************************************************************
|
|
* Constructor.
|
|
*/
|
|
CInterruptSync::
|
|
CInterruptSync
|
|
( IN PUNKNOWN pUnknownOuter
|
|
)
|
|
: CUnknown(pUnknownOuter)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
KeInitializeSpinLock(&m_kSpinLock);
|
|
InitializeListHead(&m_listEntry);
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::~CInterruptSync()
|
|
*****************************************************************************
|
|
* Destructor.
|
|
*/
|
|
CInterruptSync::~CInterruptSync()
|
|
{
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Destroying INTERRUPTSYNC (0x%08x)",this));
|
|
|
|
//
|
|
// Make sure we're disconnected.
|
|
//
|
|
Disconnect();
|
|
|
|
//
|
|
// Delete the list of ISRs.
|
|
//
|
|
if (! IsListEmpty(&m_listEntry))
|
|
{
|
|
KIRQL kIrqlOld;
|
|
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
|
|
|
|
//
|
|
// Get rid of any remaining members.
|
|
//
|
|
while (! IsListEmpty(&m_listEntry))
|
|
{
|
|
PLIST_ENTRY pListEntry = RemoveHeadList(&m_listEntry);
|
|
|
|
delete PISRLISTENTRY(pListEntry);
|
|
}
|
|
|
|
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
|
|
}
|
|
}
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CDmaChannel::NonDelegatingQueryInterface()
|
|
*****************************************************************************
|
|
* Obtains an interface.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CInterruptSync::
|
|
NonDelegatingQueryInterface
|
|
(
|
|
REFIID Interface,
|
|
PVOID * Object
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Object);
|
|
|
|
if (IsEqualGUIDAligned(Interface,IID_IUnknown))
|
|
{
|
|
*Object = PVOID(PUNKNOWN(this));
|
|
}
|
|
else if (IsEqualGUIDAligned(Interface,IID_IInterruptSync))
|
|
{
|
|
*Object = PVOID(PINTERRUPTSYNCINIT(this));
|
|
}
|
|
else
|
|
{
|
|
*Object = NULL;
|
|
}
|
|
|
|
if (*Object)
|
|
{
|
|
PUNKNOWN(*Object)->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::Init()
|
|
*****************************************************************************
|
|
* Initializes the synchronization object.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CInterruptSync::
|
|
Init
|
|
(
|
|
IN PRESOURCELIST ResourceList,
|
|
IN ULONG ResourceIndex,
|
|
IN INTERRUPTSYNCMODE Mode
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Initializing INTERRUPTSYNC (0x%08x)",this));
|
|
|
|
ASSERT(ResourceList);
|
|
|
|
m_mode = Mode;
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor = ResourceList->FindTranslatedInterrupt(ResourceIndex);
|
|
|
|
NTSTATUS ntStatus = ( pDescriptor ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES );
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
m_descriptor = *pDescriptor;
|
|
m_pKInterrupt = NULL;
|
|
|
|
m_kIrql = KIRQL(m_descriptor.u.Interrupt.Level);
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSyncServiceRoutine()
|
|
*****************************************************************************
|
|
* Wrapper for service routine.
|
|
*/
|
|
static
|
|
BOOLEAN
|
|
CInterruptSyncServiceRoutine
|
|
(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID PVoidContext
|
|
)
|
|
{
|
|
CInterruptSync *pCInterruptSync = (CInterruptSync *)(PVoidContext);
|
|
|
|
BOOLEAN bResult = FALSE;
|
|
|
|
//
|
|
// Call ISRs as indicated by mode.
|
|
//
|
|
while (1)
|
|
{
|
|
BOOLEAN bResultThisPass = FALSE;
|
|
|
|
for
|
|
( PLIST_ENTRY pListEntry = pCInterruptSync->m_listEntry.Flink;
|
|
pListEntry != &pCInterruptSync->m_listEntry;
|
|
pListEntry = pListEntry->Flink
|
|
)
|
|
{
|
|
PISRLISTENTRY pIsrListEntry = PISRLISTENTRY(pListEntry);
|
|
|
|
ASSERT(pIsrListEntry->Routine);
|
|
|
|
if( NT_SUCCESS( pIsrListEntry->Routine( PINTERRUPTSYNC(pCInterruptSync),
|
|
pIsrListEntry->DynamicContext ) ) )
|
|
{
|
|
bResult = TRUE;
|
|
bResultThisPass = TRUE;
|
|
|
|
if (pCInterruptSync->m_mode == InterruptSyncModeNormal)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( (pCInterruptSync->m_mode != InterruptSyncModeRepeat) ||
|
|
(! bResultThisPass) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::Connect()
|
|
*****************************************************************************
|
|
* Initializes the synchronization object.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CInterruptSync::
|
|
Connect
|
|
( void
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Connect"));
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
KAFFINITY InterruptAffinity;
|
|
|
|
//
|
|
// Don't even think about connecting if we don't have any
|
|
// ISR's in our list
|
|
//
|
|
KIRQL oldIrql;
|
|
KeAcquireSpinLock( &m_kSpinLock, &oldIrql );
|
|
if( IsListEmpty( &m_listEntry ) )
|
|
{
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
KeReleaseSpinLock( &m_kSpinLock, oldIrql );
|
|
|
|
//
|
|
// Connect if not already connected
|
|
//
|
|
if ( (NT_SUCCESS(ntStatus)) && (!m_pKInterrupt) )
|
|
{
|
|
|
|
InterruptAffinity = m_descriptor.u.Interrupt.Affinity;
|
|
|
|
//
|
|
// If an interrupt affinity override was specified in the registry, and
|
|
// we are running on an MP machine, and at least one active processor
|
|
// will be able to handle our devices interrupt if only processors
|
|
// in both the device interrupt affinity specification AND the registry
|
|
// interrupt affinity are allowed to handle the interrupts, then
|
|
// limit the device interrupt affinity to the subset of both affinity
|
|
// masks.
|
|
//
|
|
if (gInterruptAffinity &&
|
|
KeNumberProcessors > 1 &&
|
|
(InterruptAffinity&gInterruptAffinity&KeQueryActiveProcessors()) ) {
|
|
InterruptAffinity &= gInterruptAffinity;
|
|
}
|
|
|
|
ntStatus = IoConnectInterrupt( &m_pKInterrupt,
|
|
CInterruptSyncServiceRoutine,
|
|
PVOID(this),
|
|
&m_kSpinLock, // TODO: Spin lock sharing?
|
|
m_descriptor.u.Interrupt.Vector,
|
|
m_kIrql,
|
|
m_kIrql, // TODO: Different for >1 interrupt?
|
|
((m_descriptor.Flags & CM_RESOURCE_INTERRUPT_LATCHED) ?
|
|
Latched : LevelSensitive),
|
|
(m_descriptor.ShareDisposition != CmResourceShareDeviceExclusive),
|
|
InterruptAffinity,
|
|
FALSE );
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
ASSERT(m_pKInterrupt);
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::Disconnect()
|
|
*****************************************************************************
|
|
* Disconnect from the interrupt.
|
|
*/
|
|
STDMETHODIMP_(void)
|
|
CInterruptSync::
|
|
Disconnect
|
|
( void
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Disconnect"));
|
|
|
|
PAGED_CODE();
|
|
|
|
if (m_pKInterrupt)
|
|
{
|
|
IoDisconnectInterrupt(m_pKInterrupt);
|
|
m_pKInterrupt = NULL;
|
|
}
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* CServiceGroup::RegisterServiceRoutine()
|
|
*****************************************************************************
|
|
* Add a service routine.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CInterruptSync::
|
|
RegisterServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNCROUTINE Routine,
|
|
IN PVOID DynamicContext,
|
|
IN BOOLEAN First
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::RegisterServiceRoutine"));
|
|
|
|
ASSERT(Routine);
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// ensure we really have a routine
|
|
//
|
|
if( !Routine )
|
|
{
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if( NT_SUCCESS(ntStatus) )
|
|
{
|
|
//
|
|
// Create a new member.
|
|
//
|
|
PISRLISTENTRY pIsrListEntry = new(NonPagedPool,'lIcP') ISRLISTENTRY;
|
|
|
|
if (pIsrListEntry)
|
|
{
|
|
pIsrListEntry->Routine = Routine;
|
|
pIsrListEntry->DynamicContext = DynamicContext;
|
|
|
|
//
|
|
// Add the member to the list.
|
|
//
|
|
KIRQL kIrqlOld;
|
|
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
|
|
|
|
if (First)
|
|
{
|
|
InsertHeadList( &m_listEntry, &pIsrListEntry->ListEntry );
|
|
}
|
|
else
|
|
{
|
|
InsertTailList( &m_listEntry, &pIsrListEntry->ListEntry );
|
|
}
|
|
|
|
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSyncWrapperRoutine()
|
|
*****************************************************************************
|
|
* Wrapper for synchronized routines.
|
|
*/
|
|
static
|
|
BOOLEAN
|
|
CInterruptSyncWrapperRoutine
|
|
(
|
|
IN PVOID PVoidContext
|
|
)
|
|
{
|
|
PWRAPPERROUTINECONTEXT pContext = PWRAPPERROUTINECONTEXT(PVoidContext);
|
|
|
|
pContext->NtStatus = pContext->Routine( PINTERRUPTSYNC(pContext->InterruptSync),
|
|
pContext->DynamicContext);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::CallSynchronizedRoutine()
|
|
*****************************************************************************
|
|
* Call a synchronized routine.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CInterruptSync::
|
|
CallSynchronizedRoutine
|
|
(
|
|
IN PINTERRUPTSYNCROUTINE Routine,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
WRAPPERROUTINECONTEXT context;
|
|
|
|
context.Routine = Routine;
|
|
context.DynamicContext = DynamicContext;
|
|
context.InterruptSync = this;
|
|
context.NtStatus = STATUS_SUCCESS;
|
|
|
|
if (m_pKInterrupt)
|
|
{
|
|
if (!KeSynchronizeExecution(m_pKInterrupt,CInterruptSyncWrapperRoutine,&context ) )
|
|
{
|
|
context.NtStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
else if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, using spinlock"));
|
|
|
|
KIRQL kIrqlOld;
|
|
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
|
|
|
|
// we have no interrupt yet, so synchronize the best you can
|
|
(void)CInterruptSyncWrapperRoutine(&context);
|
|
|
|
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
|
|
}
|
|
else
|
|
{
|
|
context.NtStatus = STATUS_UNSUCCESSFUL;
|
|
_DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, but IRQL > DISPATCH_LEVEL"));
|
|
}
|
|
|
|
return context.NtStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CInterruptSync::GetKInterrupt()
|
|
*****************************************************************************
|
|
* Get a WDM InterruptObject from a portcls sync object.
|
|
*/
|
|
STDMETHODIMP_(PKINTERRUPT)
|
|
CInterruptSync::
|
|
GetKInterrupt
|
|
( void
|
|
)
|
|
{
|
|
return m_pKInterrupt;
|
|
}
|
|
|