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