reactos/drivers/wdm/audio/backpln/portcls/service_group.cpp

363 lines
7.8 KiB
C++

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