/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel Streaming * FILE: drivers/wdm/audio/backpln/portcls/dma_init.c * PURPOSE: portcls dma support object * PROGRAMMER: Johannes Anderwald */ #include "private.hpp" #ifndef YDEBUG #define NDEBUG #endif #include class CDmaChannelInit : public IDmaChannelInit { public: inline PVOID operator new( size_t Size, POOL_TYPE PoolType, ULONG Tag) { PVOID P = ExAllocatePoolWithTag(PoolType, Size, Tag); if (P) RtlZeroMemory(P, Size); return P; } STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); STDMETHODIMP_(ULONG) AddRef() { InterlockedIncrement(&m_Ref); return m_Ref; } STDMETHODIMP_(ULONG) Release() { InterlockedDecrement(&m_Ref); if (!m_Ref) { delete this; return 0; } return m_Ref; } IMP_IDmaChannelInit; CDmaChannelInit(IUnknown * OuterUnknown){} virtual ~CDmaChannelInit(){} protected: PDEVICE_OBJECT m_pDeviceObject; PDMA_ADAPTER m_pAdapter; BOOL m_DmaStarted; ULONG m_MapSize; PVOID m_MapRegisterBase; ULONG m_LastTransferCount; ULONG m_MaximumBufferSize; ULONG m_MaxMapRegisters; ULONG m_AllocatedBufferSize; ULONG m_BufferSize; PHYSICAL_ADDRESS m_Address; PVOID m_Buffer; PMDL m_Mdl; BOOLEAN m_WriteToDevice; friend IO_ALLOCATION_ACTION NTAPI AdapterControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context); LONG m_Ref; }; //--------------------------------------------------------------- // IUnknown methods // extern GUID IID_IDmaChannelSlave; NTSTATUS NTAPI CDmaChannelInit::QueryInterface( IN REFIID refiid, OUT PVOID* Output) { if (IsEqualGUIDAligned(refiid, IID_IUnknown) || IsEqualGUIDAligned(refiid, IID_IDmaChannel)) //IsEqualGUIDAligned(refiid, IID_IDmaChannelSlave)) // HACK { *Output = PVOID(PUNKNOWN(this)); PUNKNOWN(*Output)->AddRef(); return STATUS_SUCCESS; } DPRINT("No interface!!!\n"); return STATUS_UNSUCCESSFUL; } //--------------------------------------------------------------- // IDmaChannel methods // NTSTATUS NTAPI CDmaChannelInit::AllocateBuffer( IN ULONG BufferSize, IN PPHYSICAL_ADDRESS PhysicalAddressConstraint OPTIONAL) { PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); // Did the caller already allocate a buffer ?*/ if (m_Buffer) { DPRINT("CDmaChannelInit_AllocateBuffer free common buffer first \n"); return STATUS_UNSUCCESSFUL; } m_Buffer = m_pAdapter->DmaOperations->AllocateCommonBuffer(m_pAdapter, BufferSize, &m_Address, FALSE); if (!m_Buffer) { DPRINT("CDmaChannelInit_AllocateBuffer fAllocateCommonBuffer failed \n"); return STATUS_UNSUCCESSFUL; } m_BufferSize = BufferSize; m_AllocatedBufferSize = BufferSize; DPRINT("CDmaChannelInit::AllocateBuffer Success Buffer %p BufferSize %u Address %x\n", m_Buffer, BufferSize, m_Address); return STATUS_SUCCESS; } ULONG NTAPI CDmaChannelInit::AllocatedBufferSize() { DPRINT("CDmaChannelInit_AllocatedBufferSize: this %p BufferSize %u\n", this, m_BufferSize); return m_AllocatedBufferSize; } VOID NTAPI CDmaChannelInit::CopyFrom( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { DPRINT("CDmaChannelInit_CopyFrom: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount); CopyTo(Destination, Source, ByteCount); } VOID NTAPI CDmaChannelInit::CopyTo( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { DPRINT("CDmaChannelInit_CopyTo: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount); RtlCopyMemory(Destination, Source, ByteCount); } VOID NTAPI CDmaChannelInit::FreeBuffer() { DPRINT("CDmaChannelInit_FreeBuffer: this %p\n", this); PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); if (!m_Buffer) { DPRINT("CDmaChannelInit_FreeBuffer allocate common buffer first \n"); return; } m_pAdapter->DmaOperations->FreeCommonBuffer(m_pAdapter, m_AllocatedBufferSize, m_Address, m_Buffer, FALSE); m_Buffer = NULL; m_AllocatedBufferSize = 0; m_Address.QuadPart = 0LL; if (m_Mdl) { IoFreeMdl(m_Mdl); m_Mdl = NULL; } } PADAPTER_OBJECT NTAPI CDmaChannelInit::GetAdapterObject() { DPRINT("CDmaChannelInit_GetAdapterObject: this %p\n", this); return (PADAPTER_OBJECT)m_pAdapter; } ULONG NTAPI CDmaChannelInit::MaximumBufferSize() { DPRINT("CDmaChannelInit_MaximumBufferSize: this %p\n", this); return m_MaximumBufferSize; } #ifdef _MSC_VER PHYSICAL_ADDRESS NTAPI CDmaChannelInit::PhysicalAddress() { DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart); return m_Address; } #else PHYSICAL_ADDRESS NTAPI CDmaChannelInit::PhysicalAddress( PPHYSICAL_ADDRESS Address) { DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart); PHYSICAL_ADDRESS Result; Address->QuadPart = m_Address.QuadPart; Result.QuadPart = (ULONG_PTR)Address; return Result; } #endif VOID NTAPI CDmaChannelInit::SetBufferSize( IN ULONG BufferSize) { DPRINT("CDmaChannelInit_SetBufferSize: this %p\n", this); m_BufferSize = BufferSize; } ULONG NTAPI CDmaChannelInit::BufferSize() { DPRINT("BufferSize %u\n", m_BufferSize); PC_ASSERT(m_BufferSize); return m_BufferSize; } PVOID NTAPI CDmaChannelInit::SystemAddress() { DPRINT("CDmaChannelInit_SystemAddress: this %p\n", this); return m_Buffer; } ULONG NTAPI CDmaChannelInit::TransferCount() { DPRINT("CDmaChannelInit_TransferCount: this %p\n", this); return m_LastTransferCount; } ULONG NTAPI CDmaChannelInit::ReadCounter() { ULONG Counter; PC_ASSERT_IRQL(DISPATCH_LEVEL); Counter = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); if (!m_DmaStarted || Counter >= m_LastTransferCount) Counter = 0; DPRINT("ReadCounter %u\n", Counter); return Counter; } IO_ALLOCATION_ACTION NTAPI AdapterControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context) { ULONG Length; CDmaChannelInit * This = (CDmaChannelInit*)Context; Length = This->m_MapSize; This->m_MapRegisterBase = MapRegisterBase; This->m_pAdapter->DmaOperations->MapTransfer(This->m_pAdapter, This->m_Mdl, MapRegisterBase, (PVOID)((ULONG_PTR)This->m_Mdl->StartVa + This->m_Mdl->ByteOffset), &Length, This->m_WriteToDevice); if (Length == This->m_BufferSize) { This->m_DmaStarted = TRUE; } return KeepObject; } NTSTATUS NTAPI CDmaChannelInit::Start( ULONG MapSize, BOOLEAN WriteToDevice) { NTSTATUS Status; ULONG MapRegisters; KIRQL OldIrql; DPRINT("CDmaChannelInit_Start: this %p\n", this); PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); if (m_DmaStarted) return STATUS_UNSUCCESSFUL; if (!m_Mdl) { m_Mdl = IoAllocateMdl(m_Buffer, m_MaximumBufferSize, FALSE, FALSE, NULL); if (!m_Mdl) { return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(m_Mdl); } m_MapSize = MapSize; m_WriteToDevice = WriteToDevice; m_LastTransferCount = MapSize; //FIXME // synchronize access // KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); MapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(m_Buffer, MapSize); Status = m_pAdapter->DmaOperations->AllocateAdapterChannel(m_pAdapter, m_pDeviceObject, MapRegisters, AdapterControl, (PVOID)this); KeLowerIrql(OldIrql); if(!NT_SUCCESS(Status)) m_LastTransferCount = 0; return Status; } NTSTATUS NTAPI CDmaChannelInit::Stop() { KIRQL OldIrql; DPRINT("CDmaChannelInit::Stop: this %p\n", this); PC_ASSERT_IRQL(DISPATCH_LEVEL); if (!m_DmaStarted) return STATUS_SUCCESS; m_pAdapter->DmaOperations->FlushAdapterBuffers(m_pAdapter, m_Mdl, m_MapRegisterBase, (PVOID)((ULONG_PTR)m_Mdl->StartVa + m_Mdl->ByteOffset), m_MapSize, m_WriteToDevice); KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); m_pAdapter->DmaOperations->FreeAdapterChannel(m_pAdapter); KeLowerIrql(OldIrql); m_DmaStarted = FALSE; IoFreeMdl(m_Mdl); m_Mdl = NULL; return STATUS_SUCCESS; } NTSTATUS NTAPI CDmaChannelInit::WaitForTC( ULONG Timeout) { ULONG RetryCount; ULONG BytesRemaining; ULONG PrevBytesRemaining; PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); if (!BytesRemaining) { return STATUS_SUCCESS; } RetryCount = Timeout / 10; PrevBytesRemaining = 0xFFFFFFFF; do { BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter); if (!BytesRemaining) break; if (PrevBytesRemaining == BytesRemaining) break; KeStallExecutionProcessor(10); PrevBytesRemaining = BytesRemaining; }while(RetryCount-- >= 1); if (BytesRemaining) { return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } NTSTATUS NTAPI CDmaChannelInit::Init( IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject) { INTERFACE_TYPE BusType; NTSTATUS Status; PDMA_ADAPTER Adapter; PPCLASS_DEVICE_EXTENSION DeviceExt; ULONG MapRegisters; ULONG ResultLength; // Get bus type Status = IoGetDeviceProperty(DeviceObject, DevicePropertyLegacyBusType, sizeof(BusType), (PVOID)&BusType, &ResultLength); if (NT_SUCCESS(Status)) { DeviceDescription->InterfaceType = BusType; } // Fetch device extension DeviceExt = (PPCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension; // Acquire dma adapter Adapter = IoGetDmaAdapter(DeviceExt->PhysicalDeviceObject, DeviceDescription, &MapRegisters); if (!Adapter) { FreeItem(this, TAG_PORTCLASS); return STATUS_DEVICE_CONFIGURATION_ERROR; } // initialize object m_pAdapter = Adapter; m_pDeviceObject = DeviceObject; m_MaximumBufferSize = DeviceDescription->MaximumLength; m_MaxMapRegisters = MapRegisters; return STATUS_SUCCESS; } NTSTATUS NTAPI PcNewDmaChannel( OUT PDMACHANNEL* OutDmaChannel, IN PUNKNOWN OuterUnknown OPTIONAL, IN POOL_TYPE PoolType, IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject) { NTSTATUS Status; CDmaChannelInit * This; DPRINT("OutDmaChannel %p OuterUnknown %p PoolType %p DeviceDescription %p DeviceObject %p\n", OutDmaChannel, OuterUnknown, PoolType, DeviceDescription, DeviceObject); This = new(PoolType, TAG_PORTCLASS)CDmaChannelInit(OuterUnknown); if (!This) return STATUS_INSUFFICIENT_RESOURCES; Status = This->QueryInterface(IID_IDmaChannel, (PVOID*)OutDmaChannel); if (!NT_SUCCESS(Status)) { delete This; return Status; } Status = This->Init(DeviceDescription, DeviceObject); if (!NT_SUCCESS(Status)) { delete This; return Status; } return Status; }