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