9fbafccc693137d21e0bdcebf11b86213e2b2470
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / interrupt.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/backpln/portcls/interrupt.c
5 * PURPOSE: portcls interrupt object
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9
10 #include "private.h"
11
12 typedef struct
13 {
14 LIST_ENTRY ListEntry;
15 PINTERRUPTSYNCROUTINE SyncRoutine;
16 PVOID DynamicContext;
17 }SYNC_ENTRY, *PSYNC_ENTRY;
18
19 typedef struct
20 {
21 IInterruptSyncVtbl *lpVtbl;
22
23 LONG ref;
24 KSPIN_LOCK Lock;
25 LIST_ENTRY ServiceRoutines;
26 PKINTERRUPT Interrupt;
27 INTERRUPTSYNCMODE Mode;
28 PRESOURCELIST ResourceList;
29 ULONG ResourceIndex;
30
31 PINTERRUPTSYNCROUTINE SyncRoutine;
32 PVOID DynamicContext;
33 }IInterruptSyncImpl;
34
35
36 //---------------------------------------------------------------
37 // IUnknown methods
38 //
39
40
41
42 NTSTATUS
43 NTAPI
44 IInterruptSync_fnQueryInterface(
45 IInterruptSync * iface,
46 IN REFIID refiid,
47 OUT PVOID* Output)
48 {
49 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
50
51 DPRINT("IInterruptSync_fnQueryInterface: This %p\n", This);
52
53 if (IsEqualGUIDAligned(refiid, &IID_IInterruptSync) ||
54 IsEqualGUIDAligned(refiid, &IID_IUnknown))
55 {
56 *Output = &This->lpVtbl;
57 InterlockedIncrement(&This->ref);
58 return STATUS_SUCCESS;
59 }
60 DPRINT1("IInterruptSync_fnQueryInterface: This %p UNKNOWN interface requested\n", This);
61 return STATUS_UNSUCCESSFUL;
62 }
63
64 ULONG
65 NTAPI
66 IInterruptSync_fnAddRef(
67 IInterruptSync * iface)
68 {
69 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
70
71 DPRINT("IInterruptSync_AddRef: This %p\n", This);
72
73 return InterlockedIncrement(&This->ref);
74 }
75
76 ULONG
77 NTAPI
78 IInterruptSync_fnRelease(
79 IInterruptSync* iface)
80 {
81 PLIST_ENTRY CurEntry;
82 PSYNC_ENTRY Entry;
83 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
84
85 InterlockedDecrement(&This->ref);
86
87 DPRINT("IInterruptSync_Release: This %p new ref %u\n", This, This->ref);
88
89 if (This->ref == 0)
90 {
91 while(!IsListEmpty(&This->ServiceRoutines))
92 {
93 CurEntry = RemoveHeadList(&This->ServiceRoutines);
94 Entry = CONTAINING_RECORD(CurEntry, SYNC_ENTRY, ListEntry);
95 FreeItem(Entry, TAG_PORTCLASS);
96 }
97
98 This->ResourceList->lpVtbl->Release(This->ResourceList);
99 FreeItem(This, TAG_PORTCLASS);
100 return 0;
101 }
102 /* Return new reference count */
103 return This->ref;
104 }
105
106 //---------------------------------------------------------------
107 // IInterruptSync methods
108 //
109
110
111 BOOLEAN
112 NTAPI
113 IInterruptSynchronizedRoutine(
114 IN PVOID ServiceContext)
115 {
116 IInterruptSyncImpl * This = (IInterruptSyncImpl*)ServiceContext;
117 DPRINT("IInterruptSynchronizedRoutine This %p SyncRoutine %p Context %p\n", This, This->SyncRoutine, This->DynamicContext);
118 return This->SyncRoutine((IInterruptSync*)&This->lpVtbl, This->DynamicContext);
119 }
120
121 NTSTATUS
122 NTAPI
123 IInterruptSync_fnCallSynchronizedRoutine(
124 IN IInterruptSync * iface,
125 IN PINTERRUPTSYNCROUTINE Routine,
126 IN PVOID DynamicContext)
127 {
128 KIRQL OldIrql;
129 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
130
131 DPRINT("IInterruptSync_fnCallSynchronizedRoutine This %p Routine %p DynamicContext %p Irql %x Interrupt %p\n", This, Routine, DynamicContext, KeGetCurrentIrql(), This->Interrupt);
132
133 if (!This->Interrupt)
134 {
135 DPRINT("IInterruptSync_CallSynchronizedRoutine %p no interrupt connected\n", This);
136 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
137 return STATUS_UNSUCCESSFUL;
138
139 KeAcquireSpinLock(&This->Lock, &OldIrql);
140 This->SyncRoutine = Routine;
141 This->DynamicContext = DynamicContext;
142 IInterruptSynchronizedRoutine((PVOID)This);
143 KeReleaseSpinLock(&This->Lock, OldIrql);
144
145 return STATUS_SUCCESS;
146 }
147
148 This->SyncRoutine = Routine;
149 This->DynamicContext = DynamicContext;
150
151 if (KeSynchronizeExecution(This->Interrupt, IInterruptSynchronizedRoutine, (PVOID)This))
152 return STATUS_SUCCESS;
153 else
154 return STATUS_UNSUCCESSFUL;
155 }
156
157 NTAPI
158 PKINTERRUPT
159 IInterruptSync_fnGetKInterrupt(
160 IN IInterruptSync * iface)
161 {
162 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
163 DPRINT("IInterruptSynchronizedRoutine\n");
164
165 return This->Interrupt;
166 }
167
168 BOOLEAN
169 NTAPI
170 IInterruptServiceRoutine(
171 IN PKINTERRUPT Interrupt,
172 IN PVOID ServiceContext)
173 {
174 PLIST_ENTRY CurEntry;
175 PSYNC_ENTRY Entry;
176 NTSTATUS Status;
177 BOOL Success;
178 IInterruptSyncImpl * This = (IInterruptSyncImpl*)ServiceContext;
179
180 //DPRINT("IInterruptServiceRoutine Mode %u\n", This->Mode);
181
182 if (This->Mode == InterruptSyncModeNormal)
183 {
184 CurEntry = This->ServiceRoutines.Flink;
185 while (CurEntry != &This->ServiceRoutines)
186 {
187 Entry = CONTAINING_RECORD(CurEntry, SYNC_ENTRY, ListEntry);
188 Status = Entry->SyncRoutine((IInterruptSync*)This, Entry->DynamicContext);
189 if (NT_SUCCESS(Status))
190 {
191 return TRUE;
192 }
193 CurEntry = CurEntry->Flink;
194 }
195 return FALSE;
196 }
197 else if (This->Mode == InterruptSyncModeAll)
198 {
199 CurEntry = This->ServiceRoutines.Flink;
200 while (CurEntry != &This->ServiceRoutines)
201 {
202 Entry = CONTAINING_RECORD(CurEntry, SYNC_ENTRY, ListEntry);
203 Status = Entry->SyncRoutine((IInterruptSync*)This, Entry->DynamicContext);
204 CurEntry = CurEntry->Flink;
205 }
206 DPRINT("Returning TRUE with mode InterruptSyncModeAll\n");
207 return TRUE; //FIXME
208 }
209 else if (This->Mode == InterruptSyncModeRepeat)
210 {
211 do
212 {
213 Success = FALSE;
214 CurEntry = This->ServiceRoutines.Flink;
215 while (CurEntry != &This->ServiceRoutines)
216 {
217 Entry = CONTAINING_RECORD(CurEntry, SYNC_ENTRY, ListEntry);
218 Status = Entry->SyncRoutine((IInterruptSync*)This, Entry->DynamicContext);
219 if (NT_SUCCESS(Status))
220 Success = TRUE;
221 CurEntry = CurEntry->Flink;
222 }
223 }while(Success);
224 DPRINT("Returning TRUE with mode InterruptSyncModeRepeat\n");
225 return TRUE; //FIXME
226 }
227 else
228 {
229 DPRINT("Unknown mode %u\n", This->Mode);
230 return FALSE; //FIXME
231 }
232 }
233
234
235 NTSTATUS
236 NTAPI
237 IInterruptSync_fnConnect(
238 IN IInterruptSync * iface)
239 {
240 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
241 NTSTATUS Status;
242 PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
243
244 DPRINT("IInterruptSync_fnConnect\n");
245 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
246
247 Descriptor = This->ResourceList->lpVtbl->FindTranslatedEntry(This->ResourceList, CmResourceTypeInterrupt, This->ResourceIndex);
248 if (!Descriptor)
249 return STATUS_UNSUCCESSFUL;
250
251 if (IsListEmpty(&This->ServiceRoutines))
252 return STATUS_UNSUCCESSFUL;
253
254 Status = IoConnectInterrupt(&This->Interrupt,
255 IInterruptServiceRoutine,
256 (PVOID)This,
257 &This->Lock,
258 Descriptor->u.Interrupt.Vector,
259 Descriptor->u.Interrupt.Level,
260 Descriptor->u.Interrupt.Level,
261 (Descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
262 (Descriptor->Flags != CM_RESOURCE_INTERRUPT_LATCHED),
263 Descriptor->u.Interrupt.Affinity,
264 FALSE);
265
266 DPRINT("IInterruptSync_fnConnect result %x\n", Status);
267 return Status;
268 }
269
270
271 VOID
272 NTAPI
273 IInterruptSync_fnDisconnect(
274 IN IInterruptSync * iface)
275 {
276 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
277
278 DPRINT("IInterruptSync_fnDisconnect\n");
279 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
280
281 if (!This->Interrupt)
282 {
283 DPRINT("IInterruptSync_Disconnect %p no interrupt connected\n", This);
284 return;
285 }
286
287 IoDisconnectInterrupt(This->Interrupt);
288 This->Interrupt = NULL;
289 }
290
291 NTSTATUS
292 NTAPI
293 IInterruptSync_fnRegisterServiceRoutine(
294 IN IInterruptSync * iface,
295 IN PINTERRUPTSYNCROUTINE Routine,
296 IN PVOID DynamicContext,
297 IN BOOLEAN First)
298 {
299 PSYNC_ENTRY NewEntry;
300 IInterruptSyncImpl * This = (IInterruptSyncImpl*)iface;
301
302 DPRINT("IInterruptSync_fnRegisterServiceRoutine\n");
303 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
304
305 NewEntry = AllocateItem(NonPagedPool, sizeof(SYNC_ENTRY), TAG_PORTCLASS);
306 if (!NewEntry)
307 return STATUS_INSUFFICIENT_RESOURCES;
308
309 NewEntry->SyncRoutine = Routine;
310 NewEntry->DynamicContext = DynamicContext;
311
312 if (First)
313 InsertHeadList(&This->ServiceRoutines, &NewEntry->ListEntry);
314 else
315 InsertTailList(&This->ServiceRoutines, &NewEntry->ListEntry);
316
317 return STATUS_SUCCESS;
318 }
319
320 static IInterruptSyncVtbl vt_IInterruptSyncVtbl =
321 {
322 /* IUnknown methods */
323 IInterruptSync_fnQueryInterface,
324 IInterruptSync_fnAddRef,
325 IInterruptSync_fnRelease,
326 /* IInterruptSync methods */
327 IInterruptSync_fnCallSynchronizedRoutine,
328 IInterruptSync_fnGetKInterrupt,
329 IInterruptSync_fnConnect,
330 IInterruptSync_fnDisconnect,
331 IInterruptSync_fnRegisterServiceRoutine
332 };
333
334 /*
335 * @implemented
336 */
337 NTSTATUS NTAPI
338 PcNewInterruptSync(
339 OUT PINTERRUPTSYNC* OutInterruptSync,
340 IN PUNKNOWN OuterUnknown OPTIONAL,
341 IN PRESOURCELIST ResourceList,
342 IN ULONG ResourceIndex,
343 IN INTERRUPTSYNCMODE Mode)
344 {
345 IInterruptSyncImpl * This;
346
347 DPRINT("PcNewInterruptSync entered OutInterruptSync %p OuterUnknown %p ResourceList %p ResourceIndex %u Mode %d\n",
348 OutInterruptSync, OuterUnknown, ResourceList, ResourceIndex, Mode);
349
350 if (!OutInterruptSync || !ResourceList || Mode < InterruptSyncModeNormal || Mode > InterruptSyncModeRepeat)
351 return STATUS_INVALID_PARAMETER;
352
353 if (ResourceIndex > ResourceList->lpVtbl->NumberOfEntriesOfType(ResourceList, CmResourceTypeInterrupt))
354 return STATUS_INVALID_PARAMETER;
355
356
357 ResourceList->lpVtbl->AddRef(ResourceList);
358
359 This = AllocateItem(NonPagedPool, sizeof(IInterruptSyncImpl), TAG_PORTCLASS);
360 if (!This)
361 return STATUS_INSUFFICIENT_RESOURCES;
362
363 /* initialize object */
364 This->lpVtbl = &vt_IInterruptSyncVtbl;
365 This->ref = 1;
366 This->Mode = Mode;
367 This->ResourceIndex = ResourceIndex;
368 This->ResourceList = ResourceList;
369 InitializeListHead(&This->ServiceRoutines);
370 KeInitializeSpinLock(&This->Lock);
371
372 *OutInterruptSync = (PINTERRUPTSYNC)&This->lpVtbl;
373 DPRINT("PcNewInterruptSync success %p\n", *OutInterruptSync);
374 return STATUS_SUCCESS;
375 }
376