📄 dma.c
字号:
{
if (AdapterObject->ChannelNumber == 0xFF)
{
KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode,
FALSE, NULL);
RemoveEntryList(&AdapterObject->AdapterList);
KeSetEvent(&HalpDmaLock, 0, 0);
}
ObfDereferenceObject(AdapterObject);
}
/**
* @name HalAllocateCommonBuffer
*
* Allocates memory that is visible to both the processor(s) and the DMA
* device.
*
* @param AdapterObject
* Adapter object representing the bus master or system dma controller.
* @param Length
* Number of bytes to allocate.
* @param LogicalAddress
* Logical address the driver can use to access the buffer.
* @param CacheEnabled
* Specifies if the memory can be cached.
*
* @return The base virtual address of the memory allocated or NULL on failure.
*
* @remarks
* On real NT x86 systems the CacheEnabled parameter is ignored, we honour
* it. If it proves to cause problems change it.
*
* @see HalFreeCommonBuffer
*
* @implemented
*/
PVOID STDCALL
HalAllocateCommonBuffer(
PADAPTER_OBJECT AdapterObject,
ULONG Length,
PPHYSICAL_ADDRESS LogicalAddress,
BOOLEAN CacheEnabled)
{
PHYSICAL_ADDRESS LowestAcceptableAddress;
PHYSICAL_ADDRESS HighestAcceptableAddress;
PHYSICAL_ADDRESS BoundryAddressMultiple;
PVOID VirtualAddress;
LowestAcceptableAddress.QuadPart = 0;
HighestAcceptableAddress =
HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
BoundryAddressMultiple.QuadPart = 0;
/*
* For bus-master DMA devices the buffer mustn't cross 4Gb boundary. For
* slave DMA devices the 64Kb boundary mustn't be crossed since the
* controller wouldn't be able to handle it.
*/
if (AdapterObject->MasterDevice)
BoundryAddressMultiple.HighPart = 1;
else
BoundryAddressMultiple.LowPart = 0x10000;
VirtualAddress = MmAllocateContiguousMemorySpecifyCache(
Length, LowestAcceptableAddress, HighestAcceptableAddress,
BoundryAddressMultiple, CacheEnabled ? MmCached : MmNonCached);
if (VirtualAddress == NULL)
return NULL;
*LogicalAddress = MmGetPhysicalAddress(VirtualAddress);
return VirtualAddress;
}
/**
* @name HalFreeCommonBuffer
*
* Free common buffer allocated with HalAllocateCommonBuffer.
*
* @see HalAllocateCommonBuffer
*
* @implemented
*/
VOID STDCALL
HalFreeCommonBuffer(
PADAPTER_OBJECT AdapterObject,
ULONG Length,
PHYSICAL_ADDRESS LogicalAddress,
PVOID VirtualAddress,
BOOLEAN CacheEnabled)
{
MmFreeContiguousMemory(VirtualAddress);
}
/**
* @name HalpDmaGetDmaAlignment
*
* Internal routine to return the DMA alignment requirement. It's exported
* using the DMA_OPERATIONS interface by HalGetAdapter.
*
* @see HalGetAdapter
*/
ULONG STDCALL
HalpDmaGetDmaAlignment(
PADAPTER_OBJECT AdapterObject)
{
return 1;
}
/*
* @name HalReadDmaCounter
*
* Read DMA operation progress counter.
*
* @implemented
*/
ULONG STDCALL
HalReadDmaCounter(
PADAPTER_OBJECT AdapterObject)
{
KIRQL OldIrql;
ULONG Count, OldCount;
ASSERT(!AdapterObject->MasterDevice);
/*
* Acquire the master adapter lock since we're going to mess with the
* system DMA controller registers and we really don't want anyone
* to do the same at the same time.
*/
KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql);
/* Send the request to the specific controller. */
if (AdapterObject->AdapterNumber == 1)
{
PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
Count = 0xffff00;
do
{
OldCount = Count;
/* Send Reset */
WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0);
/* Read Count */
Count = READ_PORT_UCHAR(&DmaControl1->DmaAddressCount
[AdapterObject->ChannelNumber].DmaBaseCount);
Count |= READ_PORT_UCHAR(&DmaControl1->DmaAddressCount
[AdapterObject->ChannelNumber].DmaBaseCount) << 8;
}
while (0xffff00 & (OldCount ^ Count));
}
else
{
PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
Count = 0xffff00;
do
{
OldCount = Count;
/* Send Reset */
WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0);
/* Read Count */
Count = READ_PORT_UCHAR(&DmaControl2->DmaAddressCount
[AdapterObject->ChannelNumber].DmaBaseCount);
Count |= READ_PORT_UCHAR(&DmaControl2->DmaAddressCount
[AdapterObject->ChannelNumber].DmaBaseCount) << 8;
}
while (0xffff00 & (OldCount ^ Count));
}
KeReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql);
Count++;
Count &= 0xffff;
if (AdapterObject->Width16Bits)
Count *= 2;
return Count;
}
/**
* @name HalpGrowMapBufferWorker
*
* Helper routine of HalAllocateAdapterChannel for allocating map registers
* at PASSIVE_LEVEL in work item.
*/
VOID STDCALL
HalpGrowMapBufferWorker(PVOID DeferredContext)
{
PGROW_WORK_ITEM WorkItem = (PGROW_WORK_ITEM)DeferredContext;
KIRQL OldIrql;
BOOLEAN Succeeded;
/*
* Try to allocate new map registers for the adapter.
*
* NOTE: The NT implementation actually tries to allocate more map
* registers than needed as an optimization.
*/
KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode,
FALSE, NULL);
Succeeded = HalpGrowMapBuffers(WorkItem->AdapterObject->MasterAdapter,
WorkItem->NumberOfMapRegisters);
KeSetEvent(&HalpDmaLock, 0, 0);
if (Succeeded)
{
/*
* Flush the adapter queue now that new map registers are ready. The
* easiest way to do that is to call IoFreeMapRegisters to not free
* any registers. Note that we use the magic (PVOID)2 map register
* base to bypass the parameter checking.
*/
OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
IoFreeMapRegisters(WorkItem->AdapterObject, (PVOID)2, 0);
KfLowerIrql(OldIrql);
}
ExFreePool(WorkItem);
}
/**
* @name HalAllocateAdapterChannel
*
* Setup map registers for an adapter object.
*
* @param AdapterObject
* Pointer to an ADAPTER_OBJECT to set up.
* @param WaitContextBlock
* Context block to be used with ExecutionRoutine.
* @param NumberOfMapRegisters
* Number of map registers requested.
* @param ExecutionRoutine
* Callback to call when map registers are allocated.
*
* @return
* If not enough map registers can be allocated then
* STATUS_INSUFFICIENT_RESOURCES is returned. If the function
* succeeds or the callback is queued for later delivering then
* STATUS_SUCCESS is returned.
*
* @see IoFreeAdapterChannel
*
* @implemented
*/
NTSTATUS STDCALL
HalAllocateAdapterChannel(
PADAPTER_OBJECT AdapterObject,
PWAIT_CONTEXT_BLOCK WaitContextBlock,
ULONG NumberOfMapRegisters,
PDRIVER_CONTROL ExecutionRoutine)
{
PADAPTER_OBJECT MasterAdapter;
PGROW_WORK_ITEM WorkItem;
ULONG Index = ~0;
ULONG Result;
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
/* Set up the wait context block in case we can't run right away. */
WaitContextBlock->DeviceRoutine = ExecutionRoutine;
WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters;
/* Returns true if queued, else returns false and sets the queue to busy */
if (KeInsertDeviceQueue(&AdapterObject->ChannelWaitQueue, &WaitContextBlock->WaitQueueEntry))
return STATUS_SUCCESS;
MasterAdapter = AdapterObject->MasterAdapter;
AdapterObject->NumberOfMapRegisters = NumberOfMapRegisters;
AdapterObject->CurrentWcb = WaitContextBlock;
if (NumberOfMapRegisters && AdapterObject->NeedsMapRegisters)
{
if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel)
{
AdapterObject->NumberOfMapRegisters = 0;
IoFreeAdapterChannel(AdapterObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
/*
* Get the map registers. This is partly complicated by the fact
* that new map registers can only be allocated at PASSIVE_LEVEL
* and we're currently at DISPATCH_LEVEL. The following code has
* two code paths:
*
* - If there is no adapter queued for map register allocation,
* try to see if enough contiguous map registers are present.
* In case they're we can just get them and proceed further.
*
* - If some adapter is already present in the queue we must
* respect the order of adapters asking for map registers and
* so the fast case described above can't take place.
* This case is also entered if not enough coniguous map
* registers are present.
*
* A work queue item is allocated and queued, the adapter is
* also queued into the master adapter queue. The worker
* routine does the job of allocating the map registers at
* PASSIVE_LEVEL and calling the ExecutionRoutine.
*/
OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock);
if (IsListEmpty(&MasterAdapter->AdapterQueue))
{
Index = RtlFindClearBitsAndSet(
MasterAdapter->MapRegisters, NumberOfMapRegisters, 0);
if (Index != ~0)
{
AdapterObject->MapRegisterBase =
MasterAdapter->MapRegisterBase + Index;
if (!AdapterObject->ScatterGather)
{
AdapterObject->MapRegisterBase =
(PROS_MAP_REGISTER_ENTRY)(
(ULONG_PTR)AdapterObject->MapRegisterBase |
MAP_BASE_SW_SG);
}
}
}
if (Index == ~0)
{
WorkItem = ExAllocatePoolWithTag(
NonPagedPool, sizeof(GROW_WORK_ITEM), TAG_DMA);
if (WorkItem == NULL)
{
KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
AdapterObject->NumberOfMapRegisters = 0;
IoFreeAdapterChannel(AdapterObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue);
ExInitializeWorkItem(
&WorkItem->WorkQueueItem, HalpGrowMapBufferWorker, WorkItem);
WorkItem->AdapterObject = AdapterObject;
WorkItem->NumberOfMapRegisters = NumberOfMapRegisters;
ExQueueWorkItem(&WorkItem->WorkQueueItem, DelayedWorkQueue);
KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
return STATUS_SUCCESS;
}
KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
}
else
{
AdapterObject->MapRegisterBase = NULL;
AdapterObject->NumberOfMapRegisters = 0;
}
AdapterObject->CurrentWcb = WaitContextBlock;
Result = ExecutionRoutine(
WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
/*
* Possible return values:
*
* - KeepObject
* Don't free any resources, the ADAPTER_OBJECT is still in use and
* the caller will call IoFreeAdapterChannel later.
*
* - DeallocateObject
* Deallocate the map registers and release the ADAPTER_OBJECT, so
* someone else can use it.
*
* - DeallocateObjectKeepRegisters
* Release the ADAPTER_OBJECT, but hang on to the map registers. The
* client will later call IoFreeMapRegisters.
*
* NOTE:
* IoFreeAdapterChannel runs the queue, so it must be called unless
* the adapter object is not to be freed.
*/
if (Result == DeallocateObject)
{
IoFreeAdapterChannel(AdapterObject);
}
else if (Result == DeallocateObjectKeepRegisters)
{
AdapterObject->NumberOfMapRegisters = 0;
IoFreeAdapterChannel(AdapterObject);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -