/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel Streaming * FILE: drivers/wdm/audio/backpln/portcls/service_group.cpp * PURPOSE: ServiceGroup object implementation * PROGRAMMER: Johannes Anderwald */ #include "private.hpp" #ifndef YDEBUG #define NDEBUG #endif #include VOID NTAPI IServiceGroupDpc( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); typedef struct { LIST_ENTRY Entry; IN PSERVICESINK pServiceSink; }GROUP_ENTRY, *PGROUP_ENTRY; class CServiceGroup : public IServiceGroup { public: STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); STDMETHODIMP_(ULONG) AddRef() { InterlockedIncrement(&m_Ref); return m_Ref; } STDMETHODIMP_(ULONG) Release() { InterlockedDecrement(&m_Ref); if (!m_Ref) { delete this; return 0; } return m_Ref; } IMP_IServiceGroup; CServiceGroup(IUnknown * OuterUnknown); virtual ~CServiceGroup() {} protected: LIST_ENTRY m_ServiceSinkHead; BOOL m_TimerInitialized; KTIMER m_Timer; KDPC m_Dpc; KSPIN_LOCK m_Lock; friend VOID NTAPI IServiceGroupDpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); LONG m_Ref; }; //--------------------------------------------------------------- // IUnknown methods // NTSTATUS NTAPI CServiceGroup::QueryInterface( IN REFIID refiid, OUT PVOID* Output) { UNICODE_STRING GuidString; if (IsEqualGUIDAligned(refiid, IID_IServiceGroup) || IsEqualGUIDAligned(refiid, IID_IServiceSink) || IsEqualGUIDAligned(refiid, IID_IUnknown)) { *Output = PVOID(PSERVICEGROUP(this)); PUNKNOWN(*Output)->AddRef(); return STATUS_SUCCESS; } if (RtlStringFromGUID(refiid, &GuidString) == STATUS_SUCCESS) { DPRINT1("CServiceGroup::QueryInterface no interface!!! iface %S\n", GuidString.Buffer); RtlFreeUnicodeString(&GuidString); } return STATUS_UNSUCCESSFUL; } //--------------------------------------------------------------- // IServiceSink methods // CServiceGroup::CServiceGroup(IUnknown * OuterUnknown) { // initialize dpc KeInitializeDpc(&m_Dpc, IServiceGroupDpc, (PVOID)this); // set highest importance KeSetImportanceDpc(&m_Dpc, HighImportance); // initialize service group list lock KeInitializeSpinLock(&m_Lock); // initialize service group list InitializeListHead(&m_ServiceSinkHead); } VOID NTAPI CServiceGroup::RequestService() { KIRQL OldIrql; DPRINT("CServiceGroup::RequestService() Dpc at Level %u\n", KeGetCurrentIrql()); if (m_TimerInitialized) { LARGE_INTEGER DueTime; // no due time DueTime.QuadPart = 0LL; // delayed service requested KeSetTimer(&m_Timer, DueTime, &m_Dpc); } else { // check current irql if (KeGetCurrentIrql() > DISPATCH_LEVEL) { //insert dpc to queue KeInsertQueueDpc(&m_Dpc, NULL, NULL); } else { // raise irql to dispatch level to make dpc fire immediately KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); // insert dpc to queue KeInsertQueueDpc(&m_Dpc, NULL, NULL); // lower irql to old level KeLowerIrql(OldIrql); } } } //--------------------------------------------------------------- // IServiceGroup methods // NTSTATUS NTAPI CServiceGroup::AddMember( IN PSERVICESINK pServiceSink) { PGROUP_ENTRY Entry; KIRQL OldLevel; // sanity check PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); // allocate service sink entry Entry = (PGROUP_ENTRY)AllocateItem(NonPagedPool, sizeof(GROUP_ENTRY), TAG_PORTCLASS); if (!Entry) { // out of memory return STATUS_INSUFFICIENT_RESOURCES; } // initialize service sink entry Entry->pServiceSink = pServiceSink; // increment reference count pServiceSink->AddRef(); // acquire service group list lock KeAcquireSpinLock(&m_Lock, &OldLevel); // insert into service sink list InsertTailList(&m_ServiceSinkHead, &Entry->Entry); // release service group list lock KeReleaseSpinLock(&m_Lock, OldLevel); return STATUS_SUCCESS; } VOID NTAPI CServiceGroup::RemoveMember( IN PSERVICESINK pServiceSink) { PLIST_ENTRY CurEntry; PGROUP_ENTRY Entry; KIRQL OldLevel; // sanity check PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); // acquire service group list lock KeAcquireSpinLock(&m_Lock, &OldLevel); // grab first entry CurEntry = m_ServiceSinkHead.Flink; // loop list until the passed entry is found while (CurEntry != &m_ServiceSinkHead) { // grab entry Entry = CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); // check if it matches the passed entry if (Entry->pServiceSink == pServiceSink) { // remove entry from list RemoveEntryList(&Entry->Entry); // release service sink reference pServiceSink->Release(); // free service sink entry FreeItem(Entry, TAG_PORTCLASS); // leave loop break; } // move to next entry CurEntry = CurEntry->Flink; } // release service group list lock KeReleaseSpinLock(&m_Lock, OldLevel); } VOID NTAPI IServiceGroupDpc( IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PLIST_ENTRY CurEntry; PGROUP_ENTRY Entry; CServiceGroup * This = (CServiceGroup*)DeferredContext; // acquire service group list lock KeAcquireSpinLockAtDpcLevel(&This->m_Lock); // grab first entry CurEntry = This->m_ServiceSinkHead.Flink; // loop the list and call the attached service sink/group while (CurEntry != &This->m_ServiceSinkHead) { //grab current entry Entry = (PGROUP_ENTRY)CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); // call service sink/group Entry->pServiceSink->RequestService(); // move to next entry CurEntry = CurEntry->Flink; } // release service group list lock KeReleaseSpinLockFromDpcLevel(&This->m_Lock); } VOID NTAPI CServiceGroup::SupportDelayedService() { PC_ASSERT_IRQL(DISPATCH_LEVEL); // initialize the timer KeInitializeTimer(&m_Timer); // use the timer to perform service requests m_TimerInitialized = TRUE; } VOID NTAPI CServiceGroup::RequestDelayedService( IN ULONGLONG ullDelay) { LARGE_INTEGER DueTime; // sanity check PC_ASSERT_IRQL(DISPATCH_LEVEL); PC_ASSERT(m_TimerInitialized); DueTime.QuadPart = ullDelay; // set the timer KeSetTimer(&m_Timer, DueTime, &m_Dpc); } VOID NTAPI CServiceGroup::CancelDelayedService() { PC_ASSERT_IRQL(DISPATCH_LEVEL); PC_ASSERT(m_TimerInitialized); // cancel the timer KeCancelTimer(&m_Timer); } NTSTATUS NTAPI PcNewServiceGroup( OUT PSERVICEGROUP* OutServiceGroup, IN PUNKNOWN OuterUnknown OPTIONAL) { CServiceGroup * This; NTSTATUS Status; DPRINT("PcNewServiceGroup entered\n"); //FIXME support aggregation PC_ASSERT(OuterUnknown == NULL); // allocate a service group object This = new(NonPagedPool, TAG_PORTCLASS)CServiceGroup(OuterUnknown); if (!This) { // out of memory return STATUS_INSUFFICIENT_RESOURCES; } // request IServiceSink interface Status = This->QueryInterface(IID_IServiceSink, (PVOID*)OutServiceGroup); if (!NT_SUCCESS(Status)) { // failed to acquire service sink interface delete This; return Status; } // done return Status; }