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