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