/***************************************************************************** * 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; }