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