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