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; 27 CInterruptSync(IUnknown *OuterUnknown){} 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 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 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 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 130 CInterruptSync::GetKInterrupt() 131 { 132 DPRINT("CInterruptSynchronizedRoutine\n"); 133 134 return m_Interrupt; 135 } 136 137 BOOLEAN 138 NTAPI 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 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 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 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 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