dma.c
字号:
* @see IoFlushAdapterBuffers, IoMapTransfer
*/
VOID STDCALL
HalpCopyBufferMap(
PMDL Mdl,
PROS_MAP_REGISTER_ENTRY MapRegisterBase,
PVOID CurrentVa,
ULONG Length,
BOOLEAN WriteToDevice)
{
ULONG CurrentLength;
ULONG_PTR CurrentAddress;
ULONG ByteOffset;
PVOID VirtualAddress;
VirtualAddress = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
if (VirtualAddress == NULL)
{
/*
* NOTE: On real NT a mechanism with reserved pages is implemented
* to handle this case in a slow, but graceful non-fatal way.
*/
/* FIXME: The correct bug check code isn't defined. */
/* KEBUGCHECKEX(HAL_MEMORY_ALLOCATION, PAGE_SIZE, 0, (ULONG_PTR)__FILE__, 0); */
KEBUGCHECK(0);
}
CurrentAddress = (ULONG_PTR)VirtualAddress +
(ULONG_PTR)CurrentVa -
(ULONG_PTR)MmGetMdlVirtualAddress(Mdl);
while (Length > 0)
{
ByteOffset = BYTE_OFFSET(CurrentAddress);
CurrentLength = PAGE_SIZE - ByteOffset;
if (CurrentLength > Length)
CurrentLength = Length;
if (WriteToDevice)
{
RtlCopyMemory(
(PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset),
(PVOID)CurrentAddress,
CurrentLength);
}
else
{
RtlCopyMemory(
(PVOID)CurrentAddress,
(PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset),
CurrentLength);
}
Length -= CurrentLength;
CurrentAddress += CurrentLength;
MapRegisterBase++;
}
}
/**
* @name IoFlushAdapterBuffers
*
* Flush any data remaining in the DMA controller's memory into the host
* memory.
*
* @param AdapterObject
* The adapter object to flush.
* @param Mdl
* Original MDL to flush data into.
* @param MapRegisterBase
* Map register base that was just used by IoMapTransfer, etc.
* @param CurrentVa
* Offset into Mdl to be flushed into, same as was passed to
* IoMapTransfer.
* @param Length
* Length of the buffer to be flushed into.
* @param WriteToDevice
* TRUE if it's a write, FALSE if it's a read.
*
* @return TRUE in all cases.
*
* @remarks
* This copies data from the map register-backed buffer to the user's
* target buffer. Data are not in the user buffer until this function
* is called.
* For slave DMA transfers the controller channel is masked effectively
* stopping the current transfer.
*
* @unimplemented.
*/
BOOLEAN STDCALL
IoFlushAdapterBuffers(
PADAPTER_OBJECT AdapterObject,
PMDL Mdl,
PVOID MapRegisterBase,
PVOID CurrentVa,
ULONG Length,
BOOLEAN WriteToDevice)
{
BOOLEAN SlaveDma = FALSE;
PROS_MAP_REGISTER_ENTRY RealMapRegisterBase;
ASSERT_IRQL(DISPATCH_LEVEL);
if (AdapterObject != NULL && !AdapterObject->MasterDevice)
{
/* Mask out (disable) the DMA channel. */
if (AdapterObject->AdapterNumber == 1)
{
PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
WRITE_PORT_UCHAR(&DmaControl1->SingleMask,
AdapterObject->ChannelNumber | DMA_SETMASK);
}
else
{
PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
WRITE_PORT_UCHAR(&DmaControl2->SingleMask,
AdapterObject->ChannelNumber | DMA_SETMASK);
}
SlaveDma = TRUE;
}
/* This can happen if the device supports hardware scatter/gather. */
if (MapRegisterBase == NULL)
return TRUE;
RealMapRegisterBase =
(PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
if (!WriteToDevice)
{
if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG)
{
if (RealMapRegisterBase->Counter != ~0)
{
if (SlaveDma && !AdapterObject->IgnoreCount)
Length -= HalReadDmaCounter(AdapterObject);
}
HalpCopyBufferMap(Mdl, RealMapRegisterBase, CurrentVa, Length, FALSE);
}
else
{
/* FIXME: Unimplemented case */
ASSERT(FALSE);
}
}
RealMapRegisterBase->Counter = 0;
return TRUE;
}
/**
* @name IoMapTransfer
*
* Map a DMA for transfer and do the DMA if it's a slave.
*
* @param AdapterObject
* Adapter object to do the DMA on. Bus-master may pass NULL.
* @param Mdl
* Locked-down user buffer to DMA in to or out of.
* @param MapRegisterBase
* Handle to map registers to use for this dma.
* @param CurrentVa
* Index into Mdl to transfer into/out of.
* @param Length
* Length of transfer. Number of bytes actually transferred on
* output.
* @param WriteToDevice
* TRUE if it's an output DMA, FALSE otherwise.
*
* @return
* A logical address that can be used to program a DMA controller, it's
* not meaningful for slave DMA device.
*
* @remarks
* This function does a copyover to contiguous memory <16MB represented
* by the map registers if needed. If the buffer described by MDL can be
* used as is no copyover is done.
* If it's a slave transfer, this function actually performs it.
*
* @implemented
*/
PHYSICAL_ADDRESS STDCALL
IoMapTransfer(
IN PADAPTER_OBJECT AdapterObject,
IN PMDL Mdl,
IN PVOID MapRegisterBase,
IN PVOID CurrentVa,
IN OUT PULONG Length,
IN BOOLEAN WriteToDevice)
{
PPFN_NUMBER MdlPagesPtr;
PFN_NUMBER MdlPage1, MdlPage2;
ULONG ByteOffset;
ULONG TransferOffset;
ULONG TransferLength;
BOOLEAN UseMapRegisters;
PROS_MAP_REGISTER_ENTRY RealMapRegisterBase;
PHYSICAL_ADDRESS PhysicalAddress;
PHYSICAL_ADDRESS HighestAcceptableAddress;
ULONG Counter;
DMA_MODE AdapterMode;
KIRQL OldIrql;
/*
* Precalculate some values that are used in all cases.
*
* ByteOffset is offset inside the page at which the transfer starts.
* MdlPagesPtr is pointer inside the MDL page chain at the page where the
* transfer start.
* PhysicalAddress is physical address corresponding to the transfer
* start page and offset.
* TransferLength is the inital length of the transfer, which is reminder
* of the first page. The actual value is calculated below.
*
* Note that all the variables can change during the processing which
* takes place below. These are just initial values.
*/
ByteOffset = BYTE_OFFSET(CurrentVa);
MdlPagesPtr = MmGetMdlPfnArray(Mdl);
MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT;
PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT;
PhysicalAddress.QuadPart += ByteOffset;
TransferLength = PAGE_SIZE - ByteOffset;
/*
* Special case for bus master adapters with S/G support. We can directly
* use the buffer specified by the MDL, so not much work has to be done.
*
* Just return the passed VA's corresponding physical address and update
* length to the number of physically contiguous bytes found. Also
* pages crossing the 4Gb boundary aren't considered physically contiguous.
*/
if (MapRegisterBase == NULL)
{
while (TransferLength < *Length)
{
MdlPage1 = *MdlPagesPtr;
MdlPage2 = *(MdlPagesPtr + 1);
if (MdlPage1 + 1 != MdlPage2)
break;
if ((MdlPage1 ^ MdlPage2) & ~0xFFFFF)
break;
TransferLength += PAGE_SIZE;
MdlPagesPtr++;
}
if (TransferLength < *Length)
*Length = TransferLength;
return PhysicalAddress;
}
/*
* The code below applies to slave DMA adapters and bus master adapters
* without hardward S/G support.
*/
RealMapRegisterBase =
(PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
/*
* Try to calculate the size of the transfer. We can only transfer
* pages that are physically contiguous and that don't cross the
* 64Kb boundary (this limitation applies only for ISA controllers).
*/
while (TransferLength < *Length)
{
MdlPage1 = *MdlPagesPtr;
MdlPage2 = *(MdlPagesPtr + 1);
if (MdlPage1 + 1 != MdlPage2)
break;
if (!HalpEisaDma && ((MdlPage1 ^ MdlPage2) & ~0xF))
break;
TransferLength += PAGE_SIZE;
MdlPagesPtr++;
}
if (TransferLength > *Length)
TransferLength = *Length;
/*
* If we're about to simulate software S/G and not all the pages are
* physically contiguous then we must use the map registers to store
* the data and allow the whole transfer to proceed at once.
*/
if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG &&
TransferLength < *Length)
{
UseMapRegisters = TRUE;
PhysicalAddress = RealMapRegisterBase->PhysicalAddress;
PhysicalAddress.QuadPart += ByteOffset;
TransferLength = *Length;
RealMapRegisterBase->Counter = ~0;
Counter = 0;
}
else
{
/*
* This is ordinary DMA transfer, so just update the progress
* counters. These are used by IoFlushAdapterBuffers to track
* the transfer progress.
*/
UseMapRegisters = FALSE;
Counter = RealMapRegisterBase->Counter;
RealMapRegisterBase->Counter += BYTES_TO_PAGES(ByteOffset + TransferLength);
/*
* Check if the buffer doesn't exceed the highest physical address
* limit of the device. In that case we must use the map registers to
* store the data.
*/
HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
if (PhysicalAddress.QuadPart + TransferLength >
HighestAcceptableAddress.QuadPart)
{
UseMapRegisters = TRUE;
PhysicalAddress = RealMapRegisterBase->PhysicalAddress;
PhysicalAddress.QuadPart += ByteOffset;
if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG)
{
RealMapRegisterBase->Counter = ~0;
}
}
}
/*
* If we decided to use the map registers (see above) and we're about
* to transfer data to the device then copy the buffers into the map
* register memory.
*/
if (UseMapRegisters && WriteToDevice)
{
HalpCopyBufferMap(Mdl, RealMapRegisterBase + Counter,
CurrentVa, TransferLength, WriteToDevice);
}
/*
* Return the length of transfer that actually takes place.
*/
*Length = TransferLength;
/*
* If we're doing slave (system) DMA then program the (E)ISA controller
* to actually start the transfer.
*/
if (AdapterObject != NULL && !AdapterObject->MasterDevice)
{
AdapterMode = AdapterObject->AdapterMode;
if (WriteToDevice)
{
AdapterMode.TransferType = WRITE_TRANSFER;
}
else
{
AdapterMode.TransferType = READ_TRANSFER;
if (AdapterObject->IgnoreCount)
{
RtlZeroMemory((PUCHAR)RealMapRegisterBase[Counter].VirtualAddress +
ByteOffset, TransferLength);
}
}
TransferOffset = PhysicalAddress.LowPart & 0xFFFF;
if (AdapterObject->Width16Bits)
{
TransferLength >>= 1;
TransferOffset >>= 1;
}
OldIrql = KfAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock);
if (AdapterObject->AdapterNumber == 1)
{
PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
/* Reset Register */
WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0);
/* Set the Mode */
WRITE_PORT_UCHAR(&DmaControl1->Mode, AdapterMode.Byte);
/* Set the Offset Register */
WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
(UCHAR)(TransferOffset));
WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
(UCHAR)(TransferOffset >> 8));
/* Set the Page Register */
WRITE_PORT_UCHAR(AdapterObject->PagePort +
FIELD_OFFSET(EISA_CONTROL, DmaController1Pages),
(UCHAR)(PhysicalAddress.LowPart >> 16));
if (HalpEisaDma)
{
WRITE_PORT_UCHAR(AdapterObject->PagePort +
FIELD_OFFSET(EISA_CONTROL, DmaController2Pages),
0);
}
/* Set the Length */
WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
(UCHAR)(TransferLength - 1));
WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
(UCHAR)((TransferLength - 1) >> 8));
/* Unmask the Channel */
WRITE_PORT_UCHAR(&DmaControl1->SingleMask,
AdapterObject->ChannelNumber | DMA_CLEARMASK);
}
else
{
PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
/* Reset Register */
WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0);
/* Set the Mode */
WRITE_PORT_UCHAR(&DmaControl2->Mode, AdapterMode.Byte);
/* Set the Offset Register */
WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
(UCHAR)(TransferOffset));
WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
(UCHAR)(TransferOffset >> 8));
/* Set the Page Register */
WRITE_PORT_UCHAR(AdapterObject->PagePort +
FIELD_OFFSET(EISA_CONTROL, DmaController1Pages),
(UCHAR)(PhysicalAddress.u.LowPart >> 16));
if (HalpEisaDma)
{
WRITE_PORT_UCHAR(AdapterObject->PagePort +
FIELD_OFFSET(EISA_CONTROL, DmaController2Pages),
0);
}
/* Set the Length */
WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
(UCHAR)(TransferLength - 1));
WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
(UCHAR)((TransferLength - 1) >> 8));
/* Unmask the Channel */
WRITE_PORT_UCHAR(&DmaControl2->SingleMask,
AdapterObject->ChannelNumber | DMA_CLEARMASK);
}
KfReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql);
}
/*
* Return physical address of the buffer with data that is used for the
* transfer. It can either point inside the Mdl that was passed by the
* caller or into the map registers if the Mdl buffer can't be used
* directly.
*/
return PhysicalAddress;
}
/**
* @name HalFlushCommonBuffer
*
* @implemented
*/
BOOLEAN STDCALL
HalFlushCommonBuffer(
ULONG Unknown1,
ULONG Unknown2,
ULONG Unknown3,
ULONG Unknown4,
ULONG Unknown5)
{
return TRUE;
}
/* EOF */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -