/***************************************************************************** * dma.cpp - dma channel ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved. */ #include "private.h" /***************************************************************************** * IDmaChannelInit ***************************************************************************** * Interface for dma channel with Init. */ DECLARE_INTERFACE_(IDmaChannelInit,IDmaChannelSlave) { DEFINE_ABSTRACT_UNKNOWN() // For IUnknown DEFINE_ABSTRACT_DMACHANNEL() // For IDmaChannel DEFINE_ABSTRACT_DMACHANNELSLAVE() // For IDmaChannelSlave STDMETHOD_(NTSTATUS,Init) ( THIS_ IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject ) PURE; }; typedef IDmaChannelInit *PDMACHANNELINIT; /***************************************************************************** * CDmaChannel ***************************************************************************** * DMA channel implementation. */ class CDmaChannel : public IDmaChannelInit, public CUnknown { private: PDEVICE_OBJECT m_DeviceObject; PDEVICE_OBJECT m_PhysicalDeviceObject; BOOLEAN m_Slave; BOOLEAN m_WriteToDevice; BOOLEAN m_ChannelActive; BOOLEAN m_TimedOut; PDMA_ADAPTER m_DmaAdapter; PMDL m_Mdl; PVOID m_MapRegisterBase; ULONG m_MaxBufferSize; ULONG m_AllocatedBufferSize; ULONG m_UsedBufferSize; ULONG m_MapSize; PVOID m_VirtualAddress; PHYSICAL_ADDRESS m_PhysicalAddress; PVOID m_UserAddress; ULONG m_TransferCount; KMUTEX m_DMALock; public: DECLARE_STD_UNKNOWN(); DEFINE_STD_CONSTRUCTOR(CDmaChannel); ~CDmaChannel(); IMP_IDmaChannelSlave; STDMETHODIMP_(NTSTATUS) Init ( IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject ); friend IO_ALLOCATION_ACTION AllocateAdapterCallback ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context ); }; /***************************************************************************** * Factory */ #pragma code_seg("PAGE") /***************************************************************************** * CreateDmaChannel() ***************************************************************************** * Creates a DMA channel. */ NTSTATUS CreateDmaChannel ( OUT PUNKNOWN * Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ) { PAGED_CODE(); ASSERT(Unknown); _DbgPrintF(DEBUGLVL_LIFETIME,("Creating DMA")); STD_CREATE_BODY_ ( CDmaChannel, Unknown, UnknownOuter, PoolType, PDMACHANNEL ); } /***************************************************************************** * PcNewDmaChannel() ***************************************************************************** * Creates a DMA channel. */ PORTCLASSAPI NTSTATUS NTAPI PcNewDmaChannel ( OUT PDMACHANNEL * OutDmaChannel, IN PUNKNOWN OuterUnknown OPTIONAL, IN POOL_TYPE PoolType, IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject OPTIONAL ) { PAGED_CODE(); ASSERT(OutDmaChannel); ASSERT(DeviceDescription); PUNKNOWN unknown; NTSTATUS ntStatus = CreateDmaChannel ( &unknown, GUID_NULL, OuterUnknown, PoolType ); if (NT_SUCCESS(ntStatus)) { PDMACHANNELINIT dmaChannel; ntStatus = unknown->QueryInterface ( IID_IDmaChannel, (PVOID *) &dmaChannel ); if (NT_SUCCESS(ntStatus)) { ntStatus = dmaChannel->Init ( DeviceDescription, DeviceObject ); if (NT_SUCCESS(ntStatus)) { *OutDmaChannel = dmaChannel; } else { dmaChannel->Release(); } } unknown->Release(); } return ntStatus; } /***************************************************************************** * Member functions */ #pragma code_seg() /***************************************************************************** * CDmaChannel::~CDmaChannel() ***************************************************************************** * Destructor. * Must put in non-paged code for raising IRQL for calling put adapter. */ CDmaChannel::~CDmaChannel() { ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL)); KIRQL irqlOld; _DbgPrintF(DEBUGLVL_LIFETIME,("Destroying DMA (0x%08x)",this)); FreeBuffer(); if (m_DmaAdapter) { KeRaiseIrql(DISPATCH_LEVEL,&irqlOld); m_DmaAdapter->DmaOperations->PutDmaAdapter(m_DmaAdapter); KeLowerIrql(irqlOld); m_DmaAdapter = NULL; } } #pragma code_seg("PAGE") /***************************************************************************** * CDmaChannel::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. */ STDMETHODIMP_(NTSTATUS) CDmaChannel:: NonDelegatingQueryInterface ( REFIID Interface, PVOID * Object ) { PAGED_CODE(); ASSERT(Object); if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { *Object = PVOID(PUNKNOWN(this)); } else if (IsEqualGUIDAligned(Interface,IID_IDmaChannel)) { *Object = PVOID(PDMACHANNELINIT(this)); } else if (IsEqualGUIDAligned(Interface,IID_IDmaChannelSlave) && m_Slave) { *Object = PVOID(PDMACHANNELINIT(this)); } else { *Object = NULL; } if (*Object) { PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } /***************************************************************************** * PcDmaSlaveDescription() ***************************************************************************** * Fills in a DMA device description for a slave device based on a resource. */ PORTCLASSAPI NTSTATUS NTAPI PcDmaSlaveDescription ( IN PRESOURCELIST ResourceList, IN ULONG ResourceIndex, IN BOOLEAN DemandMode, IN BOOLEAN AutoInitialize, IN DMA_SPEED DmaSpeed, IN ULONG MaximumLength, IN ULONG DmaPort, OUT PDEVICE_DESCRIPTION DeviceDescription ) { PAGED_CODE(); ASSERT(ResourceList); ASSERT(DeviceDescription); _DbgPrintF(DEBUGLVL_BLAB,("DmaSlaveDescription")); NTSTATUS ntStatus = STATUS_SUCCESS; PCM_PARTIAL_RESOURCE_DESCRIPTOR dmaDescriptor = ResourceList->FindTranslatedDma(ResourceIndex); if (! dmaDescriptor) { ntStatus = STATUS_INVALID_PARAMETER; } else { RtlZeroMemory(DeviceDescription,sizeof(DEVICE_DESCRIPTION)); DeviceDescription->Version = DEVICE_DESCRIPTION_VERSION; DeviceDescription->DmaChannel = dmaDescriptor->u.Dma.Channel; DeviceDescription->DmaWidth = (DeviceDescription->DmaChannel > 3) ? Width16Bits : Width8Bits; DeviceDescription->DemandMode = DemandMode; DeviceDescription->AutoInitialize = AutoInitialize; DeviceDescription->DmaSpeed = DmaSpeed; DeviceDescription->MaximumLength = MaximumLength; DeviceDescription->DmaPort = DmaPort; // fill in default interface bus type, Init() will query PnP DeviceDescription->InterfaceType = Isa; } return ntStatus; } /***************************************************************************** * PcDmaMasterDescription() ***************************************************************************** * Fills in a DMA device description for a master device based on a resource * list. */ PORTCLASSAPI void NTAPI PcDmaMasterDescription ( IN PRESOURCELIST ResourceList OPTIONAL, IN BOOLEAN ScatterGather, IN BOOLEAN Dma32BitAddresses, IN BOOLEAN IgnoreCount, IN BOOLEAN Dma64BitAddresses, IN DMA_WIDTH DmaWidth, IN DMA_SPEED DmaSpeed, IN ULONG MaximumLength, IN ULONG DmaPort, OUT PDEVICE_DESCRIPTION DeviceDescription ) { PAGED_CODE(); ASSERT(DeviceDescription); _DbgPrintF(DEBUGLVL_BLAB,("DmaMasterDescription")); ASSERT(DeviceDescription); RtlZeroMemory(DeviceDescription,sizeof(DEVICE_DESCRIPTION)); DeviceDescription->Version = IgnoreCount ? DEVICE_DESCRIPTION_VERSION1 : DEVICE_DESCRIPTION_VERSION; DeviceDescription->Master = TRUE; DeviceDescription->ScatterGather = ScatterGather; DeviceDescription->Dma32BitAddresses = Dma32BitAddresses; DeviceDescription->IgnoreCount = IgnoreCount; DeviceDescription->Dma64BitAddresses = Dma64BitAddresses; DeviceDescription->DmaWidth = DmaWidth; DeviceDescription->DmaSpeed = DmaSpeed; DeviceDescription->MaximumLength = MaximumLength; DeviceDescription->DmaPort = DmaPort; // fill in default interface bus type, Init() will query PnP DeviceDescription->InterfaceType = PCIBus; } /***************************************************************************** * CDmaChannel::Init() ***************************************************************************** * Initializes the dma channel. */ STDMETHODIMP_(NTSTATUS) CDmaChannel:: Init ( IN PDEVICE_DESCRIPTION DeviceDescription, IN PDEVICE_OBJECT DeviceObject ) { PAGED_CODE(); ASSERT(DeviceDescription); ASSERT(DeviceObject); _DbgPrintF(DEBUGLVL_LIFETIME,("Initializing DMA (0x%08x)",this)); PDEVICE_CONTEXT deviceContext = PDEVICE_CONTEXT(DeviceObject->DeviceExtension); PDEVICE_OBJECT PhysicalDeviceObject = deviceContext->PhysicalDeviceObject; m_DeviceObject = DeviceObject; m_PhysicalDeviceObject = PhysicalDeviceObject; m_Slave = !DeviceDescription->ScatterGather; m_ChannelActive = FALSE; KeInitializeMutex(&m_DMALock,0); // determine bus interface type INTERFACE_TYPE InterfaceType; ULONG BytesReturned; NTSTATUS ntStatus = IoGetDeviceProperty( m_PhysicalDeviceObject, DevicePropertyLegacyBusType, sizeof(INTERFACE_TYPE), &InterfaceType, &BytesReturned ); if(NT_SUCCESS(ntStatus)) { DeviceDescription->InterfaceType = InterfaceType; } else { // default values were already filled in by PcDmaSlaveDescription (Isa) // and PcDmaMasterDescription (PCIBus), so we'll just use those. ntStatus = STATUS_SUCCESS; } ULONG mapRegisters = DeviceDescription->MaximumLength / PAGE_SIZE + 1; m_DmaAdapter = IoGetDmaAdapter( PhysicalDeviceObject, DeviceDescription, &mapRegisters ); if (! m_DmaAdapter) { ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } else if (! mapRegisters) { _DbgPrintF(DEBUGLVL_TERSE, ("zero map registers")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { if (mapRegisters * PAGE_SIZE < DeviceDescription->MaximumLength) { m_MaxBufferSize = mapRegisters * PAGE_SIZE; } else { m_MaxBufferSize = DeviceDescription->MaximumLength; } } return ntStatus; } /***************************************************************************** * CDmaChannel::AllocateBuffer() ***************************************************************************** * Allocate a buffer for this DMA channel. */ STDMETHODIMP_(NTSTATUS) CDmaChannel:: AllocateBuffer ( IN ULONG BufferSize, IN PPHYSICAL_ADDRESS PhysicalAddressConstraint OPTIONAL ) { PAGED_CODE(); if (!BufferSize) { _DbgPrintF(DEBUGLVL_TERSE,("AllocateBuffer: NULL BufferSize!")); return STATUS_INVALID_PARAMETER; } if (PhysicalAddressConstraint) { DebugLog((ULONG_PTR)0x02,(ULONG_PTR)BufferSize,(ULONG_PTR)PhysicalAddressConstraint->HighPart,(ULONG_PTR)PhysicalAddressConstraint->LowPart); if ( (BufferSize > PhysicalAddressConstraint->QuadPart + 1) || (PhysicalAddressConstraint->QuadPart & (PhysicalAddressConstraint->QuadPart + 1))) { ASSERT(BufferSize <= PhysicalAddressConstraint->QuadPart + 1); // Physical address contraint should be power of 2 (minus 1) ASSERT(0 == (PhysicalAddressConstraint->QuadPart & (PhysicalAddressConstraint->QuadPart + 1))); return STATUS_INVALID_PARAMETER; } } else { DebugLog((ULONG_PTR)0x03,(ULONG_PTR)BufferSize,0,0); } DebugLog((ULONG_PTR)0x04,(ULONG_PTR)m_DmaAdapter,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_MaxBufferSize); ASSERT(! m_VirtualAddress); NTSTATUS ntStatus = STATUS_SUCCESS; if (BufferSize > m_MaxBufferSize) { ntStatus = STATUS_INVALID_PARAMETER; } else { m_UsedBufferSize = m_AllocatedBufferSize = BufferSize; #define MAX_REJECTED 40 ULONG rejected = 0; PHYSICAL_ADDRESS rejectedPA[MAX_REJECTED]; PVOID rejectedVA[MAX_REJECTED]; ULONG rejectedSize[MAX_REJECTED]; ULONG paBuffsize = BufferSize; ULONG rumpAmount; BOOLEAN matchingAllocation,alignmentFixup; matchingAllocation = TRUE; alignmentFixup = FALSE; while (! m_VirtualAddress) { PVOID virtualAddress = HalAllocateCommonBuffer ( m_DmaAdapter, paBuffsize, &m_PhysicalAddress, FALSE ); DebugLog((ULONG_PTR)0x11111111,(ULONG_PTR)virtualAddress,(ULONG_PTR)paBuffsize,(ULONG_PTR)m_PhysicalAddress.LowPart); if (! virtualAddress) { break; } if (PhysicalAddressConstraint) { PHYSICAL_ADDRESS beginConstraint,endConstraint,endBuffer,nextConstraint; endBuffer.QuadPart = m_PhysicalAddress.QuadPart + paBuffsize; beginConstraint.QuadPart = m_PhysicalAddress.QuadPart & ~PhysicalAddressConstraint->QuadPart; endConstraint.QuadPart = (m_PhysicalAddress.QuadPart + m_AllocatedBufferSize - 1) & ~PhysicalAddressConstraint->QuadPart; matchingAllocation = (paBuffsize == m_AllocatedBufferSize) && (beginConstraint.QuadPart == endConstraint.QuadPart); DebugLog((ULONG_PTR)m_PhysicalAddress.LowPart,(ULONG_PTR)endBuffer.LowPart, (ULONG_PTR)beginConstraint.LowPart, (ULONG_PTR)endConstraint.LowPart); nextConstraint.QuadPart = endConstraint.QuadPart + PhysicalAddressConstraint->QuadPart + 1; rumpAmount = (ULONG)((nextConstraint.QuadPart - endBuffer.QuadPart) & ~(PAGE_SIZE-1)); DebugLog((ULONG_PTR)m_AllocatedBufferSize,(ULONG_PTR)rumpAmount,0,(ULONG_PTR)nextConstraint.LowPart); if (rumpAmount > m_AllocatedBufferSize) { rumpAmount = m_AllocatedBufferSize; } } if (matchingAllocation) { m_VirtualAddress = virtualAddress; } else { if (rejected == MAX_REJECTED) { HalFreeCommonBuffer ( m_DmaAdapter, paBuffsize, m_PhysicalAddress, virtualAddress, FALSE ); DebugLog((ULONG_PTR)0x01111,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart); break; } rejectedPA[rejected] = m_PhysicalAddress; rejectedVA[rejected] = virtualAddress; rejectedSize[rejected] = paBuffsize; rejected++; alignmentFixup = (!alignmentFixup); // get ready for next time, when we if (alignmentFixup) // either fill the rest of this zone... { paBuffsize = rumpAmount; } else // ... or go back to being truthful { paBuffsize = m_AllocatedBufferSize; } } } while (rejected--) { HalFreeCommonBuffer ( m_DmaAdapter, rejectedSize[rejected], rejectedPA[rejected], rejectedVA[rejected], FALSE ); DebugLog((ULONG_PTR)0x02222,(ULONG_PTR)rejectedVA[rejected],(ULONG_PTR)rejectedSize[rejected],(ULONG_PTR)rejectedPA[rejected].LowPart); } if (! m_VirtualAddress) { _DbgPrintF(DEBUGLVL_TERSE,("unable to allocate common buffer")); m_AllocatedBufferSize = 0; ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } DebugLog((ULONG_PTR)0x0b,(ULONG_PTR)ntStatus,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart); DebugLog((ULONG_PTR)0x0c,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_DmaAdapter,(ULONG_PTR)m_AllocatedBufferSize); return ntStatus; } /***************************************************************************** * CDmaChannel::FreeBuffer() ***************************************************************************** * Free the buffer for this DMA channel. */ STDMETHODIMP_(void) CDmaChannel:: FreeBuffer ( void ) { PAGED_CODE(); if (m_VirtualAddress) { if (m_Mdl) { IoFreeMdl(m_Mdl); m_Mdl = NULL; } HalFreeCommonBuffer ( m_DmaAdapter, m_AllocatedBufferSize, m_PhysicalAddress, m_VirtualAddress, FALSE ); DebugLog((ULONG_PTR)0x03333,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart); m_VirtualAddress = NULL; m_PhysicalAddress.HighPart = 0; m_PhysicalAddress.LowPart = 0; } } #pragma code_seg() /***************************************************************************** * AllocateAdapterCallback() ***************************************************************************** * Fixed by MartinP 1/29/00 on suggestions from ForrestF. * Removed spinlock and event. * */ static IO_ALLOCATION_ACTION AllocateAdapterCallback ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp OPTIONAL, IN PVOID MapRegisterBase, IN PVOID Context ) { ASSERT(DeviceObject); ASSERT(MapRegisterBase); ASSERT(Context); CDmaChannel *that = (CDmaChannel *)Context; if( FALSE == that->m_TimedOut ) { ULONG MapLength = that->m_MapSize; that->m_MapRegisterBase = MapRegisterBase; IoMapTransfer( that->m_DmaAdapter, that->m_Mdl, that->m_MapRegisterBase, MmGetMdlVirtualAddress(that->m_Mdl), &MapLength, that->m_WriteToDevice ); if (that->m_MapSize == MapLength) { that->m_ChannelActive = TRUE; } else { _DbgPrintF(DEBUGLVL_TERSE,("***** MapSize Requested (0x%x) != MapLength (0x%x)",that->m_MapSize,MapLength)); that->m_TransferCount = 0; } } return KeepObject; } /***************************************************************************** * CDmaChannel::Start() ***************************************************************************** * Fixed by MartinP 1/29/00 on suggestions from ForrestF. Removed spinlock * and event, replaced by single mutex. Must be in non-pageable code, since * IRQL is raised to DISPATCH_LEVEL. * */ STDMETHODIMP_(NTSTATUS) CDmaChannel:: Start ( IN ULONG MapSize, IN BOOLEAN WriteToDevice ) { ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL)); ASSERT(MapSize <= m_AllocatedBufferSize); NTSTATUS ntStatus = STATUS_SUCCESS; // don't try to start a channel that is already started if( TRUE == m_ChannelActive ) { ASSERT(!"Nested DMA Starts"); return STATUS_UNSUCCESSFUL; } if (! m_Mdl) { if (m_VirtualAddress) { m_Mdl = IoAllocateMdl ( m_VirtualAddress, m_MaxBufferSize, FALSE, FALSE, NULL ); if (m_Mdl) { MmBuildMdlForNonPagedPool(m_Mdl); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; _DbgPrintF(DEBUGLVL_TERSE, ("CDmaChannel::Start, IoAllocateMdl() == NULL")); } } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; _DbgPrintF(DEBUGLVL_TERSE, ("CDmaChannel::Start, m_VirtualAddress == NULL")); } } if (NT_SUCCESS(ntStatus)) { m_WriteToDevice = WriteToDevice; m_MapSize = MapSize; #if DBG if (m_TransferCount) { _DbgPrintF(DEBUGLVL_TERSE, ("m_TransferCount == 0x%x in CDmaChannel::Start()",m_TransferCount)); } #endif m_TransferCount = MapSize; m_TimedOut = FALSE; // // Allocate an adapter channel. When the system is ready, // we'll process in the callback and then continue after // the event is signalled. // // grab the global DMA lock that serializes IoAllocateAdapterChannel calls // setup for 10 second timeout (PASSIVE_LEVEL only!!) LARGE_INTEGER Timeout = RtlConvertLongToLargeInteger( -10L * 10000000L ); ntStatus = KeWaitForMutexObject( &m_DMALock, Executive, KernelMode, FALSE, &Timeout); if (STATUS_SUCCESS == ntStatus) // STATUS_TIMEOUT is a success code { _DbgPrintF(DEBUGLVL_VERBOSE, ("allocating adapter channel")); // // IoAllocateAdapterChannel must be called at DISPATCH_LEVEL // KIRQL irqlOld; KeRaiseIrql(DISPATCH_LEVEL,&irqlOld); ntStatus = IoAllocateAdapterChannel( m_DmaAdapter, m_DeviceObject, BYTES_TO_PAGES(m_AllocatedBufferSize), AllocateAdapterCallback, PVOID(this) ); KeLowerIrql(irqlOld); // OK to continue on our merry way KeReleaseMutex(&m_DMALock,FALSE); if (!NT_SUCCESS(ntStatus)) { _DbgPrintF(DEBUGLVL_TERSE,("Cannot allocate DMA adapter channel")); m_TransferCount = 0; } } else { ASSERT(ntStatus == STATUS_TIMEOUT); ntStatus = STATUS_UNSUCCESSFUL; _DbgPrintF(DEBUGLVL_VERBOSE, ("DMA lock timeout, can't allocate DMA channel")); } } return ntStatus; } /***************************************************************************** * CDmaChannel::Stop() ***************************************************************************** * TODO */ STDMETHODIMP_(NTSTATUS) CDmaChannel:: Stop ( void ) { ASSERT((KeGetCurrentIrql() <= DISPATCH_LEVEL)); if (InterlockedExchange(PLONG(&m_TransferCount),0)) { (void) IoFlushAdapterBuffers ( m_DmaAdapter, m_Mdl, m_MapRegisterBase, m_VirtualAddress, m_AllocatedBufferSize, m_WriteToDevice ); KIRQL irqlOld; KeRaiseIrql(DISPATCH_LEVEL,&irqlOld); IoFreeAdapterChannel(m_DmaAdapter); m_ChannelActive = FALSE; KeLowerIrql(irqlOld); } return STATUS_SUCCESS; } /***************************************************************************** * CDmaChannel::ReadCounter() ***************************************************************************** * TODO */ STDMETHODIMP_(ULONG) CDmaChannel:: ReadCounter ( void ) { ULONG ulResult = HalReadDmaCounter(m_DmaAdapter); if ( !m_ChannelActive ) { ulResult = 0; } else { if (ulResult == m_TransferCount) { ulResult = 0; } else if (ulResult > m_TransferCount) { _DbgPrintF(DEBUGLVL_TERSE,("HalReadDmaCounter returned value out of range (0x%x >= 0x%x)",ulResult,m_TransferCount)); ulResult = 0; } } return ulResult; } /***************************************************************************** * CDmaChannel::TransferCount() ***************************************************************************** * Return the amount of data to be transfered via DMA. */ STDMETHODIMP_(ULONG) CDmaChannel:: TransferCount ( void ) { return m_TransferCount; } /***************************************************************************** * CDmaChannel::MaximumBufferSize() ***************************************************************************** * Return the maximum size that can be allocated to this DMA buffer. */ STDMETHODIMP_(ULONG) CDmaChannel:: MaximumBufferSize ( void ) { return m_MaxBufferSize; } /***************************************************************************** * CDmaChannel::AllocatedBufferSize() ***************************************************************************** * Return the original size allocated to this DMA buffer -- the maximum value * that can be sent to SetBufferSize(). */ STDMETHODIMP_(ULONG) CDmaChannel:: AllocatedBufferSize ( void ) { return m_AllocatedBufferSize; } /***************************************************************************** * CDmaChannel::BufferSize() ***************************************************************************** * Return the current size of the DMA buffer. */ STDMETHODIMP_(ULONG) CDmaChannel:: BufferSize ( void ) { return m_UsedBufferSize; } /***************************************************************************** * CDmaChannel::SetBufferSize() ***************************************************************************** * Change the size of the DMA buffer. This cannot exceed the initial * buffer size returned by AllocatedBufferSize(). */ STDMETHODIMP_(void) CDmaChannel:: SetBufferSize ( IN ULONG BufferSize ) { ASSERT(BufferSize <= m_AllocatedBufferSize); m_UsedBufferSize = BufferSize; } /***************************************************************************** * CDmaChannel::SystemAddress() ***************************************************************************** * Return the virtual address of this DMA buffer. */ STDMETHODIMP_(PVOID) CDmaChannel:: SystemAddress ( void ) { return m_VirtualAddress; } /***************************************************************************** * CDmaChannel::PhysicalAddress() ***************************************************************************** * Return the actual physical address of this DMA buffer. */ STDMETHODIMP_(PHYSICAL_ADDRESS) CDmaChannel:: PhysicalAddress ( void ) { ASSERT(m_VirtualAddress); return m_PhysicalAddress; } /***************************************************************************** * CDmaChannel::GetAdapterObject() ***************************************************************************** * Return the DMA adapter object (defined in wdm.h). */ STDMETHODIMP_(PADAPTER_OBJECT) CDmaChannel:: GetAdapterObject ( void ) { return m_DmaAdapter; } STDMETHODIMP_(NTSTATUS) CDmaChannel::WaitForTC( ULONG Timeout ) /*++ Routine Description: Waits for the DMA transfer to complete, else times out. Arguments: Timeout - Specifies the timeout in microseconds to wait for the transfer to complete. This is rounded down to the nearest 10 microsecond increment. Return: STATUS_SUCCESS if the transfer completed, else an error code. --*/ { ULONG Count; if (Count = HalReadDmaCounter(m_DmaAdapter)) { ULONG LastCount = Count; Timeout /= 10; while ((LastCount != (Count = HalReadDmaCounter( m_DmaAdapter ))) && Timeout) { LastCount = Count; KeStallExecutionProcessor( 10 ); Timeout--; } return (Timeout > 0) ? STATUS_SUCCESS : STATUS_IO_TIMEOUT; } else { return STATUS_SUCCESS; } } /***************************************************************************** * CDmaChannel::CopyTo() ***************************************************************************** * Copy data into the DMA buffer. This can be overridden if a client needs * to massage the data on output. */ STDMETHODIMP_(void) CDmaChannel:: CopyTo ( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { #ifndef _X86_ RtlCopyMemory(Destination,Source,ByteCount); #else // // Jeff says this is the way to go. // _asm { mov esi, Source mov ecx, ByteCount mov edi, Destination add edi, ecx neg ecx sub edi, 16 jmp Next16 Loop16: mov eax, DWORD PTR [esi] mov ebx, DWORD PTR [esi+4] mov DWORD PTR [edi+ecx], eax mov DWORD PTR [edi+ecx+4], ebx mov eax, DWORD PTR [esi+8] mov ebx, DWORD PTR [esi+12] mov DWORD PTR [edi+ecx+8], eax mov DWORD PTR [edi+ecx+12], ebx add esi, 16 Next16: add ecx, 16 jle Loop16 sub ecx, 16 jmp Next4 Loop4: mov eax, DWORD PTR [esi] add esi, 4 mov DWORD PTR [edi+ecx+12], eax Next4: add ecx, 4 jle Loop4 sub ecx, 4 jz Done1 Final1: mov al, BYTE PTR [esi] inc esi mov BYTE PTR [edi+ecx+16], al inc ecx jnz Final1 Done1: } #endif } /***************************************************************************** * CDmaChannel::CopyFrom() ***************************************************************************** * Copy data out of the DMA buffer. This can be overridden if a client needs * to massage the data on input. */ STDMETHODIMP_(void) CDmaChannel:: CopyFrom ( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { CopyTo(Destination,Source,ByteCount); }