1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            drivers/wdm/audio/backpln/portcls/service_group.cpp
5  * PURPOSE:         ServiceGroup object implementation
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "private.hpp"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 VOID
15 NTAPI
16 IServiceGroupDpc(
17     IN struct _KDPC  *Dpc,
18     IN PVOID  DeferredContext,
19     IN PVOID  SystemArgument1,
20     IN PVOID  SystemArgument2
21     );
22 
23 typedef struct
24 {
25     LIST_ENTRY Entry;
26     IN PSERVICESINK pServiceSink;
27 }GROUP_ENTRY, *PGROUP_ENTRY;
28 
29 class CServiceGroup : public CUnknownImpl<IServiceGroup>
30 {
31 public:
32     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
33 
34     IMP_IServiceGroup;
35     CServiceGroup(IUnknown * OuterUnknown);
36     virtual ~CServiceGroup() {}
37 
38 protected:
39 
40     LIST_ENTRY m_ServiceSinkHead;
41 
42     BOOL m_TimerInitialized;
43     KTIMER m_Timer;
44     KDPC m_Dpc;
45     KSPIN_LOCK m_Lock;
46 
47     friend VOID NTAPI IServiceGroupDpc(IN struct _KDPC  *Dpc, IN PVOID  DeferredContext, IN PVOID  SystemArgument1, IN PVOID  SystemArgument2);
48 };
49 
50 //---------------------------------------------------------------
51 // IUnknown methods
52 //
53 
54 NTSTATUS
55 NTAPI
56 CServiceGroup::QueryInterface(
57     IN  REFIID refiid,
58     OUT PVOID* Output)
59 {
60     UNICODE_STRING GuidString;
61 
62     if (IsEqualGUIDAligned(refiid, IID_IServiceGroup) ||
63         IsEqualGUIDAligned(refiid, IID_IServiceSink) ||
64         IsEqualGUIDAligned(refiid, IID_IUnknown))
65     {
66         *Output = PVOID(PSERVICEGROUP(this));
67         PUNKNOWN(*Output)->AddRef();
68         return STATUS_SUCCESS;
69     }
70 
71     if (RtlStringFromGUID(refiid, &GuidString) == STATUS_SUCCESS)
72     {
73         DPRINT1("CServiceGroup::QueryInterface no interface!!! iface %S\n", GuidString.Buffer);
74         RtlFreeUnicodeString(&GuidString);
75     }
76 
77     return STATUS_UNSUCCESSFUL;
78 }
79 //---------------------------------------------------------------
80 // IServiceSink methods
81 //
82 
83 CServiceGroup::CServiceGroup(IUnknown * OuterUnknown)
84 {
85     // initialize dpc
86     KeInitializeDpc(&m_Dpc, IServiceGroupDpc, (PVOID)this);
87 
88     // set highest importance
89     KeSetImportanceDpc(&m_Dpc, HighImportance);
90 
91     // initialize service group list lock
92     KeInitializeSpinLock(&m_Lock);
93 
94     // initialize service group list
95     InitializeListHead(&m_ServiceSinkHead);
96 }
97 
98 VOID
99 NTAPI
100 CServiceGroup::RequestService()
101 {
102     KIRQL OldIrql;
103 
104     DPRINT("CServiceGroup::RequestService() Dpc at Level %u\n", KeGetCurrentIrql());
105 
106     if (m_TimerInitialized)
107     {
108         LARGE_INTEGER DueTime;
109 
110         // no due time
111         DueTime.QuadPart = 0LL;
112 
113         // delayed service requested
114         KeSetTimer(&m_Timer, DueTime, &m_Dpc);
115     }
116     else
117     {
118         // check current irql
119         if (KeGetCurrentIrql() > DISPATCH_LEVEL)
120         {
121             //insert dpc to queue
122             KeInsertQueueDpc(&m_Dpc, NULL, NULL);
123         }
124         else
125         {
126             // raise irql to dispatch level to make dpc fire immediately
127             KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
128             // insert dpc to queue
129             KeInsertQueueDpc(&m_Dpc, NULL, NULL);
130             // lower irql to old level
131             KeLowerIrql(OldIrql);
132         }
133     }
134 }
135 
136 //---------------------------------------------------------------
137 // IServiceGroup methods
138 //
139 
140 NTSTATUS
141 NTAPI
142 CServiceGroup::AddMember(
143     IN PSERVICESINK pServiceSink)
144 {
145     PGROUP_ENTRY Entry;
146     KIRQL OldLevel;
147 
148     // sanity check
149     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
150 
151     // allocate service sink entry
152     Entry = (PGROUP_ENTRY)AllocateItem(NonPagedPool, sizeof(GROUP_ENTRY), TAG_PORTCLASS);
153     if (!Entry)
154     {
155         // out of memory
156         return STATUS_INSUFFICIENT_RESOURCES;
157     }
158 
159     // initialize service sink entry
160     Entry->pServiceSink = pServiceSink;
161     // increment reference count
162     pServiceSink->AddRef();
163 
164     // acquire service group list lock
165     KeAcquireSpinLock(&m_Lock, &OldLevel);
166 
167     // insert into service sink list
168     InsertTailList(&m_ServiceSinkHead, &Entry->Entry);
169 
170     // release service group list lock
171     KeReleaseSpinLock(&m_Lock, OldLevel);
172 
173     return STATUS_SUCCESS;
174 }
175 
176 VOID
177 NTAPI
178 CServiceGroup::RemoveMember(
179     IN PSERVICESINK pServiceSink)
180 {
181     PLIST_ENTRY CurEntry;
182     PGROUP_ENTRY Entry;
183     KIRQL OldLevel;
184 
185     // sanity check
186     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
187 
188     // acquire service group list lock
189     KeAcquireSpinLock(&m_Lock, &OldLevel);
190 
191     // grab first entry
192     CurEntry = m_ServiceSinkHead.Flink;
193 
194     // loop list until the passed entry is found
195     while (CurEntry != &m_ServiceSinkHead)
196     {
197         // grab entry
198         Entry = CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry);
199 
200         // check if it matches the passed entry
201         if (Entry->pServiceSink == pServiceSink)
202         {
203             // remove entry from list
204             RemoveEntryList(&Entry->Entry);
205 
206             // release service sink reference
207             pServiceSink->Release();
208 
209             // free service sink entry
210             FreeItem(Entry, TAG_PORTCLASS);
211 
212             // leave loop
213             break;
214         }
215         // move to next entry
216         CurEntry = CurEntry->Flink;
217     }
218 
219     // release service group list lock
220     KeReleaseSpinLock(&m_Lock, OldLevel);
221 
222 }
223 
224 VOID
225 NTAPI
226 IServiceGroupDpc(
227     IN struct _KDPC  *Dpc,
228     IN PVOID  DeferredContext,
229     IN PVOID  SystemArgument1,
230     IN PVOID  SystemArgument2
231     )
232 {
233     PLIST_ENTRY CurEntry;
234     PGROUP_ENTRY Entry;
235     CServiceGroup * This = (CServiceGroup*)DeferredContext;
236 
237     // acquire service group list lock
238     KeAcquireSpinLockAtDpcLevel(&This->m_Lock);
239 
240     // grab first entry
241     CurEntry = This->m_ServiceSinkHead.Flink;
242 
243     // loop the list and call the attached service sink/group
244     while (CurEntry != &This->m_ServiceSinkHead)
245     {
246         //grab current entry
247         Entry = (PGROUP_ENTRY)CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry);
248 
249         // call service sink/group
250         Entry->pServiceSink->RequestService();
251 
252         // move to next entry
253         CurEntry = CurEntry->Flink;
254     }
255 
256     // release service group list lock
257     KeReleaseSpinLockFromDpcLevel(&This->m_Lock);
258 }
259 
260 VOID
261 NTAPI
262 CServiceGroup::SupportDelayedService()
263 {
264     PC_ASSERT_IRQL(DISPATCH_LEVEL);
265 
266     // initialize the timer
267     KeInitializeTimer(&m_Timer);
268 
269     // use the timer to perform service requests
270     m_TimerInitialized = TRUE;
271 }
272 
273 VOID
274 NTAPI
275 CServiceGroup::RequestDelayedService(
276     IN ULONGLONG ullDelay)
277 {
278     LARGE_INTEGER DueTime;
279 
280     // sanity check
281     PC_ASSERT_IRQL(DISPATCH_LEVEL);
282     PC_ASSERT(m_TimerInitialized);
283 
284     DueTime.QuadPart = ullDelay;
285 
286     // set the timer
287     KeSetTimer(&m_Timer, DueTime, &m_Dpc);
288 }
289 
290 VOID
291 NTAPI
292 CServiceGroup::CancelDelayedService()
293 {
294     PC_ASSERT_IRQL(DISPATCH_LEVEL);
295     PC_ASSERT(m_TimerInitialized);
296 
297     // cancel the timer
298     KeCancelTimer(&m_Timer);
299 }
300 
301 NTSTATUS
302 NTAPI
303 PcNewServiceGroup(
304     OUT PSERVICEGROUP* OutServiceGroup,
305     IN  PUNKNOWN OuterUnknown OPTIONAL)
306 {
307     CServiceGroup * This;
308     NTSTATUS Status;
309 
310     DPRINT("PcNewServiceGroup entered\n");
311 
312     //FIXME support aggregation
313     PC_ASSERT(OuterUnknown == NULL);
314 
315     // allocate a service group object
316     This = new(NonPagedPool, TAG_PORTCLASS)CServiceGroup(OuterUnknown);
317 
318     if (!This)
319     {
320         // out of memory
321         return STATUS_INSUFFICIENT_RESOURCES;
322     }
323 
324     // request IServiceSink interface
325     Status = This->QueryInterface(IID_IServiceSink, (PVOID*)OutServiceGroup);
326 
327     if (!NT_SUCCESS(Status))
328     {
329         // failed to acquire service sink interface
330         delete This;
331         return Status;
332     }
333 
334     // done
335     return Status;
336 }
337