Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1163 lines
33 KiB

/*****************************************************************************
* 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);
}