📄 dma.c
字号:
/* $Id: dma.c 24759 2006-11-14 20:59:48Z ion $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/hal/x86/dma.c
* PURPOSE: DMA functions
* PROGRAMMERS: David Welch (welch@mcmail.com)
* Filip Navara (navaraf@reactos.com)
* UPDATE HISTORY:
* Created 22/05/98
*/
/**
* @page DMA Implementation Notes
*
* Concepts:
*
* - Map register
*
* Abstract encapsulation of physically contiguous buffer that resides
* in memory accessible by both the DMA device / controller and the system.
* The map registers are allocated and distributed on demand and are
* scarce resource.
*
* The actual use of map registers is to allow transfers from/to buffer
* located in physical memory at address inaccessible by the DMA device /
* controller directly. For such transfers the map register buffers
* are used as intermediate data storage.
*
* - Master adapter
*
* A container for map registers (typically corresponding to one physical
* bus connection type). There can be master adapters for 24-bit address
* ranges, 32-bit address ranges, etc. Every time a new DMA adapter is
* created it's associated with a corresponding master adapter that
* is used for any map register allocation requests.
*
* - Bus-master / Slave DMA
*
* Slave DMA is term used for DMA transfers done by the system (E)ISA
* controller as opposed to transfers mastered by the device itself
* (hence the name).
*
* For slave DMA special care is taken to actually access the system
* controller and handle the transfers. The relevant code is in
* HalpDmaInitializeEisaAdapter, HalReadDmaCounter, IoFlushAdapterBuffers
* and IoMapTransfer.
*
* Implementation:
*
* - Allocation of map registers
*
* Initial set of map registers is allocated on the system start to
* ensure that low memory won't get filled up later. Additional map
* registers are allocated as needed by HalpGrowMapBuffers. This
* routine is called on two places:
*
* - HalGetAdapter, since we're at PASSIVE_LEVEL and it's known that
* more map registers will probably be needed.
* - IoAllocateAdapterChannel (indirectly using HalpGrowMapBufferWorker
* since we're at DISPATCH_LEVEL and call HalpGrowMapBuffers directly)
* when no more map registers are free.
*
* Note that even if no more map registers can be allocated it's not
* the end of the world. The adapters waiting for free map registers
* are queued in the master adapter's queue and once one driver hands
* back it's map registers (using IoFreeMapRegisters or indirectly using
* the execution routine callback in IoAllocateAdapterChannel) the
* queue gets processed and the map registers are reassigned.
*/
/* INCLUDES *****************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
static KEVENT HalpDmaLock;
static LIST_ENTRY HalpDmaAdapterList;
static PADAPTER_OBJECT HalpEisaAdapter[8];
static BOOLEAN HalpEisaDma;
static PADAPTER_OBJECT HalpMasterAdapter;
static const ULONG_PTR HalpEisaPortPage[8] = {
FIELD_OFFSET(DMA_PAGE, Channel0),
FIELD_OFFSET(DMA_PAGE, Channel1),
FIELD_OFFSET(DMA_PAGE, Channel2),
FIELD_OFFSET(DMA_PAGE, Channel3),
0,
FIELD_OFFSET(DMA_PAGE, Channel5),
FIELD_OFFSET(DMA_PAGE, Channel6),
FIELD_OFFSET(DMA_PAGE, Channel7)
};
static DMA_OPERATIONS HalpDmaOperations = {
sizeof(DMA_OPERATIONS),
(PPUT_DMA_ADAPTER)HalPutDmaAdapter,
(PALLOCATE_COMMON_BUFFER)HalAllocateCommonBuffer,
(PFREE_COMMON_BUFFER)HalFreeCommonBuffer,
NULL, /* Initialized in HalpInitDma() */
NULL, /* Initialized in HalpInitDma() */
NULL, /* Initialized in HalpInitDma() */
NULL, /* Initialized in HalpInitDma() */
NULL, /* Initialized in HalpInitDma() */
(PGET_DMA_ALIGNMENT)HalpDmaGetDmaAlignment,
(PREAD_DMA_COUNTER)HalReadDmaCounter,
/* FIXME: Implement the S/G funtions. */
NULL /*(PGET_SCATTER_GATHER_LIST)HalGetScatterGatherList*/,
NULL /*(PPUT_SCATTER_GATHER_LIST)HalPutScatterGatherList*/,
NULL /*(PCALCULATE_SCATTER_GATHER_LIST_SIZE)HalCalculateScatterGatherListSize*/,
NULL /*(PBUILD_SCATTER_GATHER_LIST)HalBuildScatterGatherList*/,
NULL /*(PBUILD_MDL_FROM_SCATTER_GATHER_LIST)HalBuildMdlFromScatterGatherList*/
};
#define MAX_MAP_REGISTERS 64
#define TAG_DMA TAG('D','M','A',' ')
/* FUNCTIONS *****************************************************************/
VOID
HalpInitDma(VOID)
{
/*
* Initialize the DMA Operation table
*/
HalpDmaOperations.AllocateAdapterChannel = (PALLOCATE_ADAPTER_CHANNEL)IoAllocateAdapterChannel;
HalpDmaOperations.FlushAdapterBuffers = (PFLUSH_ADAPTER_BUFFERS)IoFlushAdapterBuffers;
HalpDmaOperations.FreeAdapterChannel = (PFREE_ADAPTER_CHANNEL)IoFreeAdapterChannel;
HalpDmaOperations.FreeMapRegisters = (PFREE_MAP_REGISTERS)IoFreeMapRegisters;
HalpDmaOperations.MapTransfer = (PMAP_TRANSFER)IoMapTransfer;
/*
* Check if Extended DMA is available. We're just going to do a random
* read and write.
*/
WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2), 0x2A);
if (READ_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2)) == 0x2A)
HalpEisaDma = TRUE;
/*
* Intialize all the global variables and allocate master adapter with
* first map buffers.
*/
InitializeListHead(&HalpDmaAdapterList);
KeInitializeEvent(&HalpDmaLock, NotificationEvent, TRUE);
HalpMasterAdapter = HalpDmaAllocateMasterAdapter();
/*
* Setup the HalDispatchTable callback for creating PnP DMA adapters. It's
* used by IoGetDmaAdapter in the kernel.
*/
HalGetDmaAdapter = HalpGetDmaAdapter;
}
/**
* @name HalpGetAdapterMaximumPhysicalAddress
*
* Get the maximum physical address acceptable by the device represented
* by the passed DMA adapter.
*/
PHYSICAL_ADDRESS STDCALL
HalpGetAdapterMaximumPhysicalAddress(
IN PADAPTER_OBJECT AdapterObject)
{
PHYSICAL_ADDRESS HighestAddress;
if (AdapterObject->MasterDevice)
{
if (AdapterObject->Dma64BitAddresses)
{
HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
return HighestAddress;
}
else if (AdapterObject->Dma32BitAddresses)
{
HighestAddress.QuadPart = 0xFFFFFFFF;
return HighestAddress;
}
}
HighestAddress.QuadPart = 0xFFFFFF;
return HighestAddress;
}
/**
* @name HalpGrowMapBuffers
*
* Allocate initial, or additional, map buffers for DMA master adapter.
*
* @param MasterAdapter
* DMA master adapter to allocate buffers for.
* @param SizeOfMapBuffers
* Size of the map buffers to allocate (not including the size
* already allocated).
*/
BOOLEAN STDCALL
HalpGrowMapBuffers(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG SizeOfMapBuffers)
{
PVOID VirtualAddress;
PHYSICAL_ADDRESS PhysicalAddress;
PHYSICAL_ADDRESS HighestAcceptableAddress;
PHYSICAL_ADDRESS LowestAcceptableAddress;
PHYSICAL_ADDRESS BoundryAddressMultiple;
KIRQL OldIrql;
ULONG MapRegisterCount;
/* FIXME: Check if enough map register slots are available. */
MapRegisterCount = BYTES_TO_PAGES(SizeOfMapBuffers);
/*
* Allocate memory for the new map registers. For 32-bit adapters we use
* two passes in order not to waste scare resource (low memory).
*/
HighestAcceptableAddress =
HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
LowestAcceptableAddress.HighPart = 0;
LowestAcceptableAddress.LowPart =
HighestAcceptableAddress.LowPart == 0xFFFFFFFF ? 0x1000000 : 0;
BoundryAddressMultiple.QuadPart = 0;
VirtualAddress = MmAllocateContiguousMemorySpecifyCache(
MapRegisterCount << PAGE_SHIFT, LowestAcceptableAddress,
HighestAcceptableAddress, BoundryAddressMultiple, MmNonCached);
if (VirtualAddress == NULL && LowestAcceptableAddress.LowPart != 0)
{
LowestAcceptableAddress.LowPart = 0;
VirtualAddress = MmAllocateContiguousMemorySpecifyCache(
MapRegisterCount << PAGE_SHIFT, LowestAcceptableAddress,
HighestAcceptableAddress, BoundryAddressMultiple, MmNonCached);
}
if (VirtualAddress == NULL)
return FALSE;
PhysicalAddress = MmGetPhysicalAddress(VirtualAddress);
/*
* All the following must be done with the master adapter lock held
* to prevent corruption.
*/
OldIrql = KfAcquireSpinLock(&AdapterObject->SpinLock);
/*
* Setup map register entries for the buffer allocated. Each entry has
* a virtual and physical address and corresponds to PAGE_SIZE large
* buffer.
*/
if (MapRegisterCount > 0)
{
PROS_MAP_REGISTER_ENTRY CurrentEntry, PreviousEntry;
CurrentEntry = AdapterObject->MapRegisterBase +
AdapterObject->NumberOfMapRegisters;
do
{
/*
* Leave one entry free for every non-contiguous memory region
* in the map register bitmap. This ensures that we can search
* using RtlFindClearBits for contiguous map register regions.
*
* Also for non-EISA DMA leave one free entry for every 64Kb
* break, because the DMA controller can handle only coniguous
* 64Kb regions.
*/
if (CurrentEntry != AdapterObject->MapRegisterBase)
{
PreviousEntry = CurrentEntry - 1;
if (PreviousEntry->PhysicalAddress.LowPart + PAGE_SIZE ==
PhysicalAddress.LowPart)
{
if (!HalpEisaDma)
{
if ((PreviousEntry->PhysicalAddress.LowPart ^
PhysicalAddress.LowPart) & 0xFFFF0000)
{
CurrentEntry++;
AdapterObject->NumberOfMapRegisters++;
}
}
}
else
{
CurrentEntry++;
AdapterObject->NumberOfMapRegisters++;
}
}
RtlClearBit(AdapterObject->MapRegisters,
CurrentEntry - AdapterObject->MapRegisterBase);
CurrentEntry->VirtualAddress = VirtualAddress;
CurrentEntry->PhysicalAddress = PhysicalAddress;
PhysicalAddress.LowPart += PAGE_SIZE;
VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
CurrentEntry++;
AdapterObject->NumberOfMapRegisters++;
MapRegisterCount--;
}
while (MapRegisterCount != 0);
}
KfReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
return TRUE;
}
/**
* @name HalpDmaAllocateMasterAdapter
*
* Helper routine to allocate and initialize master adapter object and it's
* associated map register buffers.
*
* @see HalpInitDma
*/
PADAPTER_OBJECT STDCALL
HalpDmaAllocateMasterAdapter(VOID)
{
PADAPTER_OBJECT MasterAdapter;
ULONG Size, SizeOfBitmap;
SizeOfBitmap = MAX_MAP_REGISTERS;
Size = sizeof(ADAPTER_OBJECT);
Size += sizeof(RTL_BITMAP);
Size += (SizeOfBitmap + 7) >> 3;
MasterAdapter = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_DMA);
if (MasterAdapter == NULL)
return NULL;
RtlZeroMemory(MasterAdapter, Size);
KeInitializeSpinLock(&MasterAdapter->SpinLock);
InitializeListHead(&MasterAdapter->AdapterQueue);
MasterAdapter->MapRegisters = (PVOID)(MasterAdapter + 1);
RtlInitializeBitMap(
MasterAdapter->MapRegisters,
(PULONG)(MasterAdapter->MapRegisters + 1),
SizeOfBitmap);
RtlSetAllBits(MasterAdapter->MapRegisters);
MasterAdapter->NumberOfMapRegisters = 0;
MasterAdapter->CommittedMapRegisters = 0;
MasterAdapter->MapRegisterBase = ExAllocatePoolWithTag(
NonPagedPool,
SizeOfBitmap * sizeof(ROS_MAP_REGISTER_ENTRY),
TAG_DMA);
if (MasterAdapter->MapRegisterBase == NULL)
{
ExFreePool(MasterAdapter);
return NULL;
}
RtlZeroMemory(MasterAdapter->MapRegisterBase,
SizeOfBitmap * sizeof(ROS_MAP_REGISTER_ENTRY));
if (!HalpGrowMapBuffers(MasterAdapter, 0x10000))
{
ExFreePool(MasterAdapter);
return NULL;
}
return MasterAdapter;
}
/**
* @name HalpDmaAllocateChildAdapter
*
* Helper routine of HalGetAdapter. Allocate child adapter object and
* fill out some basic fields.
*
* @see HalGetAdapter
*/
PADAPTER_OBJECT STDCALL
HalpDmaAllocateChildAdapter(
ULONG NumberOfMapRegisters,
PDEVICE_DESCRIPTION DeviceDescription)
{
PADAPTER_OBJECT AdapterObject;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE Handle;
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
OBJ_KERNEL_HANDLE | OBJ_PERMANENT,
NULL,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -