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