1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel Streaming 4 * FILE: drivers/wdm/audio/backpln/portcls/dma_init.c 5 * PURPOSE: portcls dma support object 6 * PROGRAMMER: Johannes Anderwald 7 */ 8 9 #include "private.hpp" 10 11 #define NDEBUG 12 #include <debug.h> 13 14 class CDmaChannelInit : public CUnknownImpl<IDmaChannelInit> 15 { 16 public: 17 inline 18 PVOID 19 operator new( 20 size_t Size, 21 POOL_TYPE PoolType, 22 ULONG Tag) 23 { 24 return ExAllocatePoolWithTag(PoolType, Size, Tag); 25 } 26 27 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); 28 29 IMP_IDmaChannelInit; 30 CDmaChannelInit(IUnknown * OuterUnknown) : 31 m_pDeviceObject(nullptr), 32 m_pAdapter(nullptr), 33 m_DmaStarted(FALSE), 34 m_MapSize(0), 35 m_MapRegisterBase(nullptr), 36 m_LastTransferCount(0), 37 m_MaximumBufferSize(0), 38 m_MaxMapRegisters(0), 39 m_AllocatedBufferSize(0), 40 m_BufferSize(0), 41 m_Address({0}), 42 m_Buffer(nullptr), 43 m_Mdl(nullptr), 44 m_WriteToDevice(FALSE) 45 { 46 } 47 virtual ~CDmaChannelInit(){} 48 49 protected: 50 51 PDEVICE_OBJECT m_pDeviceObject; 52 PDMA_ADAPTER m_pAdapter; 53 54 BOOL m_DmaStarted; 55 56 ULONG m_MapSize; 57 PVOID m_MapRegisterBase; 58 59 ULONG m_LastTransferCount; 60 61 ULONG m_MaximumBufferSize; 62 ULONG m_MaxMapRegisters; 63 ULONG m_AllocatedBufferSize; 64 ULONG m_BufferSize; 65 66 PHYSICAL_ADDRESS m_Address; 67 PVOID m_Buffer; 68 PMDL m_Mdl; 69 BOOLEAN m_WriteToDevice; 70 71 friend IO_ALLOCATION_ACTION NTAPI AdapterControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context); 72 }; 73 74 //--------------------------------------------------------------- 75 // IUnknown methods 76 // 77 78 extern GUID IID_IDmaChannelSlave; 79 80 NTSTATUS 81 NTAPI 82 CDmaChannelInit::QueryInterface( 83 IN REFIID refiid, 84 OUT PVOID* Output) 85 { 86 if (IsEqualGUIDAligned(refiid, IID_IUnknown) || 87 IsEqualGUIDAligned(refiid, IID_IDmaChannel)) 88 //IsEqualGUIDAligned(refiid, IID_IDmaChannelSlave)) // HACK 89 { 90 *Output = PVOID(PUNKNOWN(this)); 91 PUNKNOWN(*Output)->AddRef(); 92 return STATUS_SUCCESS; 93 } 94 DPRINT("No interface!!!\n"); 95 return STATUS_UNSUCCESSFUL; 96 } 97 98 //--------------------------------------------------------------- 99 // IDmaChannel methods 100 // 101 102 NTSTATUS 103 NTAPI 104 CDmaChannelInit::AllocateBuffer( 105 IN ULONG BufferSize, 106 IN PPHYSICAL_ADDRESS PhysicalAddressConstraint OPTIONAL) 107 { 108 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 109 110 // Did the caller already allocate a buffer ?*/ 111 if (m_Buffer) 112 { 113 DPRINT("CDmaChannelInit_AllocateBuffer free common buffer first \n"); 114 return STATUS_UNSUCCESSFUL; 115 } 116 117 m_Buffer = m_pAdapter->DmaOperations->AllocateCommonBuffer(m_pAdapter, BufferSize, &m_Address, FALSE); 118 if (!m_Buffer) 119 { 120 DPRINT("CDmaChannelInit_AllocateBuffer fAllocateCommonBuffer failed \n"); 121 return STATUS_UNSUCCESSFUL; 122 } 123 124 m_BufferSize = BufferSize; 125 m_AllocatedBufferSize = BufferSize; 126 DPRINT("CDmaChannelInit::AllocateBuffer Success Buffer %p BufferSize %u Address %x\n", m_Buffer, BufferSize, m_Address); 127 128 return STATUS_SUCCESS; 129 } 130 131 ULONG 132 NTAPI 133 CDmaChannelInit::AllocatedBufferSize() 134 { 135 DPRINT("CDmaChannelInit_AllocatedBufferSize: this %p BufferSize %u\n", this, m_BufferSize); 136 return m_AllocatedBufferSize; 137 } 138 139 VOID 140 NTAPI 141 CDmaChannelInit::CopyFrom( 142 IN PVOID Destination, 143 IN PVOID Source, 144 IN ULONG ByteCount 145 ) 146 { 147 DPRINT("CDmaChannelInit_CopyFrom: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount); 148 149 CopyTo(Destination, Source, ByteCount); 150 } 151 152 VOID 153 NTAPI 154 CDmaChannelInit::CopyTo( 155 IN PVOID Destination, 156 IN PVOID Source, 157 IN ULONG ByteCount 158 ) 159 { 160 DPRINT("CDmaChannelInit_CopyTo: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount); 161 RtlCopyMemory(Destination, Source, ByteCount); 162 } 163 164 VOID 165 NTAPI 166 CDmaChannelInit::FreeBuffer() 167 { 168 DPRINT("CDmaChannelInit_FreeBuffer: this %p\n", this); 169 170 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 171 172 if (!m_Buffer) 173 { 174 DPRINT("CDmaChannelInit_FreeBuffer allocate common buffer first \n"); 175 return; 176 } 177 178 m_pAdapter->DmaOperations->FreeCommonBuffer(m_pAdapter, m_AllocatedBufferSize, m_Address, m_Buffer, FALSE); 179 m_Buffer = NULL; 180 m_AllocatedBufferSize = 0; 181 m_Address.QuadPart = 0LL; 182 183 if (m_Mdl) 184 { 185 IoFreeMdl(m_Mdl); 186 m_Mdl = NULL; 187 } 188 } 189 190 PADAPTER_OBJECT 191 NTAPI 192 CDmaChannelInit::GetAdapterObject() 193 { 194 DPRINT("CDmaChannelInit_GetAdapterObject: this %p\n", this); 195 return (PADAPTER_OBJECT)m_pAdapter; 196 } 197 198 ULONG 199 NTAPI 200 CDmaChannelInit::MaximumBufferSize() 201 { 202 DPRINT("CDmaChannelInit_MaximumBufferSize: this %p\n", this); 203 return m_MaximumBufferSize; 204 } 205 206 #ifdef _MSC_VER 207 208 PHYSICAL_ADDRESS 209 NTAPI 210 CDmaChannelInit::PhysicalAddress() 211 { 212 DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart); 213 214 return m_Address; 215 } 216 217 #else 218 219 PHYSICAL_ADDRESS 220 NTAPI 221 CDmaChannelInit::PhysicalAddress( 222 PPHYSICAL_ADDRESS Address) 223 { 224 DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart); 225 226 PHYSICAL_ADDRESS Result; 227 228 Address->QuadPart = m_Address.QuadPart; 229 Result.QuadPart = (ULONG_PTR)Address; 230 return Result; 231 } 232 233 #endif 234 235 VOID 236 NTAPI 237 CDmaChannelInit::SetBufferSize( 238 IN ULONG BufferSize) 239 { 240 DPRINT("CDmaChannelInit_SetBufferSize: this %p\n", this); 241 m_BufferSize = BufferSize; 242 243 } 244 245 ULONG 246 NTAPI 247 CDmaChannelInit::BufferSize() 248 { 249 DPRINT("BufferSize %u\n", m_BufferSize); 250 PC_ASSERT(m_BufferSize); 251 return m_BufferSize; 252 } 253 254 PVOID 255 NTAPI 256 CDmaChannelInit::SystemAddress() 257 { 258 DPRINT("CDmaChannelInit_SystemAddress: this %p\n", this); 259 return m_Buffer; 260 } 261 262 ULONG 263 NTAPI 264 CDmaChannelInit::TransferCount() 265 { 266 DPRINT("CDmaChannelInit_TransferCount: this %p\n", this); 267 return m_LastTransferCount; 268 } 269 270 ULONG 271 NTAPI 272 CDmaChannelInit::ReadCounter() 273 { 274 ULONG Counter; 275 276 PC_ASSERT_IRQL(DISPATCH_LEVEL); 277 278 Counter = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); 279 280 if (!m_DmaStarted || Counter >= m_LastTransferCount) 281 Counter = 0; 282 283 DPRINT("ReadCounter %u\n", Counter); 284 285 return Counter; 286 } 287 288 IO_ALLOCATION_ACTION 289 NTAPI 290 AdapterControl( 291 IN PDEVICE_OBJECT DeviceObject, 292 IN PIRP Irp, 293 IN PVOID MapRegisterBase, 294 IN PVOID Context) 295 { 296 ULONG Length; 297 CDmaChannelInit * This = (CDmaChannelInit*)Context; 298 299 Length = This->m_MapSize; 300 This->m_MapRegisterBase = MapRegisterBase; 301 302 This->m_pAdapter->DmaOperations->MapTransfer(This->m_pAdapter, 303 This->m_Mdl, 304 MapRegisterBase, 305 (PVOID)((ULONG_PTR)This->m_Mdl->StartVa + This->m_Mdl->ByteOffset), 306 &Length, 307 This->m_WriteToDevice); 308 309 if (Length == This->m_BufferSize) 310 { 311 This->m_DmaStarted = TRUE; 312 } 313 314 return KeepObject; 315 } 316 317 NTSTATUS 318 NTAPI 319 CDmaChannelInit::Start( 320 ULONG MapSize, 321 BOOLEAN WriteToDevice) 322 { 323 NTSTATUS Status; 324 ULONG MapRegisters; 325 KIRQL OldIrql; 326 327 DPRINT("CDmaChannelInit_Start: this %p\n", this); 328 329 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 330 331 if (m_DmaStarted) 332 return STATUS_UNSUCCESSFUL; 333 334 if (!m_Mdl) 335 { 336 m_Mdl = IoAllocateMdl(m_Buffer, m_MaximumBufferSize, FALSE, FALSE, NULL); 337 if (!m_Mdl) 338 { 339 return STATUS_INSUFFICIENT_RESOURCES; 340 } 341 MmBuildMdlForNonPagedPool(m_Mdl); 342 } 343 344 m_MapSize = MapSize; 345 m_WriteToDevice = WriteToDevice; 346 m_LastTransferCount = MapSize; 347 348 //FIXME 349 // synchronize access 350 // 351 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 352 353 MapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(m_Buffer, MapSize); 354 Status = m_pAdapter->DmaOperations->AllocateAdapterChannel(m_pAdapter, m_pDeviceObject, MapRegisters, AdapterControl, (PVOID)this); 355 KeLowerIrql(OldIrql); 356 357 if(!NT_SUCCESS(Status)) 358 m_LastTransferCount = 0; 359 360 return Status; 361 } 362 363 NTSTATUS 364 NTAPI 365 CDmaChannelInit::Stop() 366 { 367 KIRQL OldIrql; 368 369 DPRINT("CDmaChannelInit::Stop: this %p\n", this); 370 PC_ASSERT_IRQL(DISPATCH_LEVEL); 371 372 if (!m_DmaStarted) 373 return STATUS_SUCCESS; 374 375 m_pAdapter->DmaOperations->FlushAdapterBuffers(m_pAdapter, 376 m_Mdl, 377 m_MapRegisterBase, 378 (PVOID)((ULONG_PTR)m_Mdl->StartVa + m_Mdl->ByteOffset), 379 m_MapSize, 380 m_WriteToDevice); 381 382 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 383 384 m_pAdapter->DmaOperations->FreeAdapterChannel(m_pAdapter); 385 386 KeLowerIrql(OldIrql); 387 388 m_DmaStarted = FALSE; 389 390 IoFreeMdl(m_Mdl); 391 m_Mdl = NULL; 392 393 return STATUS_SUCCESS; 394 } 395 396 NTSTATUS 397 NTAPI 398 CDmaChannelInit::WaitForTC( 399 ULONG Timeout) 400 { 401 ULONG RetryCount; 402 ULONG BytesRemaining; 403 ULONG PrevBytesRemaining; 404 405 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 406 407 BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); 408 if (!BytesRemaining) 409 { 410 return STATUS_SUCCESS; 411 } 412 413 RetryCount = Timeout / 10; 414 PrevBytesRemaining = 0xFFFFFFFF; 415 do 416 { 417 BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); 418 419 if (!BytesRemaining) 420 break; 421 422 if (PrevBytesRemaining == BytesRemaining) 423 break; 424 425 KeStallExecutionProcessor(10); 426 PrevBytesRemaining = BytesRemaining; 427 428 }while(RetryCount-- >= 1); 429 430 if (BytesRemaining) 431 { 432 return STATUS_UNSUCCESSFUL; 433 } 434 435 return STATUS_SUCCESS; 436 437 } 438 439 NTSTATUS 440 NTAPI 441 CDmaChannelInit::Init( 442 IN PDEVICE_DESCRIPTION DeviceDescription, 443 IN PDEVICE_OBJECT DeviceObject) 444 { 445 INTERFACE_TYPE BusType; 446 NTSTATUS Status; 447 PDMA_ADAPTER Adapter; 448 PPCLASS_DEVICE_EXTENSION DeviceExt; 449 ULONG MapRegisters; 450 ULONG ResultLength; 451 452 // Get bus type 453 Status = IoGetDeviceProperty(DeviceObject, DevicePropertyLegacyBusType, sizeof(BusType), (PVOID)&BusType, &ResultLength); 454 if (NT_SUCCESS(Status)) 455 { 456 DeviceDescription->InterfaceType = BusType; 457 } 458 // Fetch device extension 459 DeviceExt = (PPCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension; 460 // Acquire dma adapter 461 Adapter = IoGetDmaAdapter(DeviceExt->PhysicalDeviceObject, DeviceDescription, &MapRegisters); 462 if (!Adapter) 463 { 464 FreeItem(this, TAG_PORTCLASS); 465 return STATUS_DEVICE_CONFIGURATION_ERROR; 466 } 467 468 // initialize object 469 m_pAdapter = Adapter; 470 m_pDeviceObject = DeviceObject; 471 m_MaximumBufferSize = DeviceDescription->MaximumLength; 472 m_MaxMapRegisters = MapRegisters; 473 474 return STATUS_SUCCESS; 475 } 476 477 NTSTATUS 478 NTAPI 479 PcNewDmaChannel( 480 OUT PDMACHANNEL* OutDmaChannel, 481 IN PUNKNOWN OuterUnknown OPTIONAL, 482 IN POOL_TYPE PoolType, 483 IN PDEVICE_DESCRIPTION DeviceDescription, 484 IN PDEVICE_OBJECT DeviceObject) 485 { 486 NTSTATUS Status; 487 CDmaChannelInit * This; 488 489 DPRINT("OutDmaChannel %p OuterUnknown %p PoolType %p DeviceDescription %p DeviceObject %p\n", 490 OutDmaChannel, OuterUnknown, PoolType, DeviceDescription, DeviceObject); 491 492 This = new(PoolType, TAG_PORTCLASS)CDmaChannelInit(OuterUnknown); 493 if (!This) 494 return STATUS_INSUFFICIENT_RESOURCES; 495 496 Status = This->QueryInterface(IID_IDmaChannel, (PVOID*)OutDmaChannel); 497 498 if (!NT_SUCCESS(Status)) 499 { 500 delete This; 501 return Status; 502 } 503 504 Status = This->Init(DeviceDescription, DeviceObject); 505 506 if (!NT_SUCCESS(Status)) 507 { 508 delete This; 509 return Status; 510 } 511 512 return Status; 513 } 514