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