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