+++ /dev/null
-/* $Id$
- *
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS kernel
- * FILE: hal/x86/adapter.c (from ntoskrnl/io/adapter.c)
- * PURPOSE: DMA handling
- * PROGRAMMERS: David Welch (welch@mcmail.com)
- * Vizzini (vizzini@plasmic.com)
- * UPDATE HISTORY:
- * Created 22/05/98
- * 18-Oct-2003 Vizzini DMA support modifications
- */
-
-/* INCLUDES *****************************************************************/
-
-#include <hal.h>
-#define NDEBUG
-#include <debug.h>
-
-/* FUNCTIONS *****************************************************************/
-
-/* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */
-
-\f
-NTSTATUS STDCALL
-HalAllocateAdapterChannel(
- PADAPTER_OBJECT AdapterObject,
- PWAIT_CONTEXT_BLOCK WaitContextBlock,
- ULONG NumberOfMapRegisters,
- PDRIVER_CONTROL ExecutionRoutine)
-/*
- * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
- * ARGUMENTS:
- * - AdapterObject: pointer to an ADAPTER_OBJECT to set up
- * - WaitContextBlock: Context block to be used with ExecutionRoutine
- * - NumberOfMapRegisters: number of map registers requested
- * - ExecutionRoutine: callback to call when map registers are allocated
- * RETURNS:
- * STATUS_INSUFFICIENT_RESOURCES if map registers cannot be allocated
- * STATUS_SUCCESS in all other cases, including if the callbacak had
- * to be queued for later delivery
- * NOTES:
- * - the ADAPTER_OBJECT struct is undocumented; please make copious
- * notes in hal.h if anything is changed or improved since there is
- * no other documentation for this data structure
- * BUGS:
- * - it's possible that some of this code is in the wrong place
- * - there are many unhandled cases
- */
-{
- LARGE_INTEGER MinAddress;
- LARGE_INTEGER MaxAddress;
- LARGE_INTEGER BoundryAddressMultiple;
- IO_ALLOCATION_ACTION Retval;
-
- ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-
- /*
- FIXME: return STATUS_INSUFFICIENT_RESOURCES if the NumberOfMapRegisters
- requested is larger than the value returned by IoGetDmaAdapter.
- */
-
- /* 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;
-
- /* why 64K alignment? */
- /*
- * X86 lacks map registers, so for now, we allocate a contiguous
- * block of physical memory <16MB and copy all DMA buffers into
- * that. This can be optimized.
- *
- * FIXME: We propably shouldn't allocate the memory here for common
- * buffer transfers. See a comment in IoMapTransfer about common buffer
- * support.
- */
-
- MinAddress.QuadPart = 0;
- BoundryAddressMultiple.QuadPart = 0;
- if ((AdapterObject->Dma64BitAddresses) && (AdapterObject->MasterDevice))
- {
- MaxAddress.QuadPart = 0xFFFFFFFFFFFFFFFFLL; /* 64Bit: >4GB address range */
- }
- else if ((AdapterObject->Dma32BitAddresses) && (AdapterObject->MasterDevice))
- {
- MaxAddress.QuadPart = 0xFFFFFFFF; /* 32Bit: 4GB address range */
- }
- else
- {
- MaxAddress.QuadPart = 0x00FFFFFF; /* 24Bit: 16MB address range */
- if (AdapterObject->Width16Bits)
- {
- BoundryAddressMultiple.QuadPart = 0x20000; /* 128k boundary */
- }
- else
- {
- BoundryAddressMultiple.QuadPart = 0x10000; /* 64k boundary */
- }
- }
- AdapterObject->MapRegisterBase = MmAllocateContiguousMemorySpecifyCache(
- NumberOfMapRegisters * PAGE_SIZE,
- MinAddress,
- MaxAddress,
- BoundryAddressMultiple,
- MmCached);
-
- if(!AdapterObject->MapRegisterBase)
- return STATUS_INSUFFICIENT_RESOURCES;
-
- AdapterObject->CommittedMapRegisters = NumberOfMapRegisters;
-
- /* call the client's AdapterControl callback with its map registers and context */
- Retval = ExecutionRoutine(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
- AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
-
- /*
- * 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( Retval == DeallocateObject )
- IoFreeAdapterChannel(AdapterObject);
- else if(Retval == DeallocateObjectKeepRegisters)
- {
- /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */
- AdapterObject->CommittedMapRegisters = 0;
- IoFreeAdapterChannel(AdapterObject);
- }
-
- /*
- * if we don't call IoFreeAdapterChannel, the next device won't get de-queued,
- * which is what we want.
- */
-
- return STATUS_SUCCESS;
-}
-
-
-BOOLEAN
-HalpGrowMapBuffers(
- IN PADAPTER_OBJECT AdapterObject,
- IN ULONG SizeOfMapBuffers)
-/*
- * FUNCTION: Allocate initial, or additional, map buffers for IO adapters.
- * ARGUMENTS:
- * AdapterObject: DMA adapter to allocate buffers for.
- * SizeOfMapBuffers: Size of the map buffers to allocate
- * NOTES:
- * - Needs to be tested...
- */
-{
- //ULONG PagesToAllocate = BYTES_TO_PAGES(SizeOfMapBuffers);
-
- /* TODO: Allocation */
-
- return TRUE;
-}
-
-PADAPTER_OBJECT STDCALL
-HalpAllocateAdapterEx(
- ULONG NumberOfMapRegisters,
- BOOLEAN IsMaster,
- BOOLEAN Dma32BitAddresses)
-/*
- * FUNCTION: Allocates an ADAPTER_OBJECT, optionally creates the Master Adapter.
- * ARGUMENTS:
- * - NumberOfMapRegisters: Number of map registers to allocate
- * - IsMaster: Wether this is a Master Device or not
- * - Dma32BitAddresses: Wether 32-bit Addresses are supported
- * RETURNS:
- * - Pointer to Adapter Object, or NULL if failure.
- * BUGS:
- * - Some stuff is unhandled/incomplete
- */
-{
- OBJECT_ATTRIBUTES ObjectAttributes;
- ULONG ObjectSize;
- ULONG BitmapSize;
- NTSTATUS Status;
- ULONG AllowedMapRegisters = 64;
- PADAPTER_OBJECT AdapterObject;
- HANDLE Handle;
-
- /* Allocate the Master Adapter if we haven't already
- but make sure we're not asked to do it now, and also check if we need it */
- if ((MasterAdapter == NULL) && (!IsMaster) && (NumberOfMapRegisters)) {
-
- /* Allocate and Save */
- DPRINT("Allocating the Master Adapter Object\n");
- MasterAdapter = HalpAllocateAdapterEx(NumberOfMapRegisters,
- TRUE,
- Dma32BitAddresses);
-
- /* Cancel on Failure */
- DPRINT("Checking if Master Adapter was allocated properly\n");
- if (!MasterAdapter) return NULL;
- }
-
- /* Initialize the Object Attributes for the Adapter Object */
- InitializeObjectAttributes(&ObjectAttributes,
- NULL,
- OBJ_PERMANENT,
- NULL,
- NULL);
-
- /* Check if this is the Master Adapter, in which case we need to allocate the bitmap */
- if (IsMaster) {
- /* Size due to the Bitmap + Bytes in the Bitmap Buffer (8 bytes, 64 bits)*/
- BitmapSize = sizeof(RTL_BITMAP) + (AllowedMapRegisters + 7) / 8;
-
- /* We will put the Bitmap Buffer after the Adapter Object for simplicity */
- ObjectSize = sizeof(ADAPTER_OBJECT) + BitmapSize;
- } else {
- ObjectSize = sizeof(ADAPTER_OBJECT);
- }
-
- /* Create and Allocate the Object */
- DPRINT("Creating the Object\n");
- Status = ObCreateObject(KernelMode,
- IoAdapterObjectType,
- &ObjectAttributes,
- KernelMode,
- NULL,
- ObjectSize,
- 0,
- 0,
- (PVOID)&AdapterObject);
-
- if (!NT_SUCCESS(Status)) return NULL;
-
- /* Add a Reference */
- DPRINT("Referencing the Object\n");
- Status = ObReferenceObjectByPointer(AdapterObject,
- FILE_READ_DATA | FILE_WRITE_DATA,
- IoAdapterObjectType,
- KernelMode);
-
- if (!NT_SUCCESS(Status)) return NULL;
-
- /* It's a Valid Object, so now we can play with the memory */
- RtlZeroMemory(AdapterObject, sizeof(ADAPTER_OBJECT));
-
- /* Insert it into the Object Table */
- DPRINT("Inserting the Object\n");
- Status = ObInsertObject(AdapterObject,
- NULL,
- FILE_READ_DATA | FILE_WRITE_DATA,
- 0,
- NULL,
- &Handle);
-
- if (!NT_SUCCESS(Status)) return NULL;
-
- /* We don't want the handle */
- NtClose(Handle);
-
- /* Set up the Adapter Object fields */
- AdapterObject->MapRegistersPerChannel = 1;
-
- /* Set the Master if needed (master only needed if we use Map Registers) */
- if (NumberOfMapRegisters) AdapterObject->MasterAdapter = MasterAdapter;
-
- /* Initalize the Channel Wait queue, which every adapter has */
- DPRINT("Initializing the Device Queue of the Object\n");
- KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue);
-
- /* Initialize the SpinLock, Queue and Bitmap, which are kept in the Master Adapter only */
- if (IsMaster) {
-
- DPRINT("Initializing the Master Adapter Stuff\n");
- KeInitializeSpinLock(&AdapterObject->SpinLock);
- InitializeListHead(&AdapterObject->AdapterQueue);
-
- /* As said previously, we put them here for simplicity */
- AdapterObject->MapRegisters = (PVOID)(AdapterObject + 1);
-
- /* Set up Bitmap */
- RtlInitializeBitMap(AdapterObject->MapRegisters,
- (PULONG)(AdapterObject->MapRegisters + 1),
- AllowedMapRegisters);
-
- /* Reset the Bitmap */
- RtlSetAllBits(AdapterObject->MapRegisters);
- AdapterObject->NumberOfMapRegisters = 0;
- AdapterObject->CommittedMapRegisters = 0;
-
- /* Allocate Memory for the Map Registers */
- AdapterObject->MapRegisterBase = ExAllocatePool(NonPagedPool,
- AllowedMapRegisters * sizeof(DWORD));
-
- /* Clear them */
- RtlZeroMemory(AdapterObject->MapRegisterBase, AllowedMapRegisters * sizeof(DWORD));
-
- /* Allocate the contigous memory */
- DPRINT("Allocating Buffers\n");
- HalpGrowMapBuffers(AdapterObject, 0x1000000);
- }
-
- DPRINT("Adapter Object allocated\n");
- return AdapterObject;
-}
-
-\f
-BOOLEAN STDCALL
-IoFlushAdapterBuffers (
- PADAPTER_OBJECT AdapterObject,
- PMDL Mdl,
- PVOID MapRegisterBase,
- PVOID CurrentVa,
- ULONG Length,
- BOOLEAN WriteToDevice)
-/*
- * FUNCTION: flush any data remaining in the dma controller's memory into the host memory
- * ARGUMENTS:
- * AdapterObject: the adapter object to flush
- * Mdl: original MDL to flush data into
- * MapRegisterBase: map register base that was just used by IoMapTransfer, etc
- * CurrentVa: offset into Mdl to be flushed into, same as was passed to IoMapTransfer
- * Length: length of the buffer to be flushed into
- * WriteToDevice: True if it's a write, False if it's a read
- * RETURNS:
- * TRUE in all cases
- * NOTES:
- * - This copies data from the map register-backed buffer to the user's target buffer.
- * Data is not in the user buffer until this is called.
- * - This is only meaningful on a read operation. Return immediately for a write.
- */
-{
- ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
-
- /* this can happen if the card supports scatter/gather */
- if(!MapRegisterBase)
- return TRUE;
-
- /* mask out (disable) the dma channel */
- if (AdapterObject->AdapterNumber == 1) {
-
- /* Set this for Ease */
- PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
-
- /* Set Channel */
- WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_SETMASK);
- } else {
- /* Set this for Ease */
- PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
-
- /* Set Channel */
- WRITE_PORT_UCHAR(&DmaControl2->SingleMask, (AdapterObject->ChannelNumber - 4) | DMA_SETMASK);
- }
-
- if(WriteToDevice)
- return TRUE;
-
- memcpy(
- (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )),
- MapRegisterBase, Length );
-
- return TRUE;
-}
-
-\f
-VOID STDCALL
-IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject)
-/*
- * FUNCTION: frees DMA resources allocated by IoAllocateAdapterChannel
- * ARGUMENTS:
- * AdapterObject: Adapter object with resources to free
- * NOTES:
- * - This function releases the DMA adapter and optionally the map registers
- * - After releasing the adapter, it checks the adapter's queue and runs
- * each queued device object in series until the queue is empty
- * - This is the only way the device queue is emptied.
- */
-{
- LARGE_INTEGER MaxAddress;
- LARGE_INTEGER MinAddress;
- LARGE_INTEGER BoundryAddressMultiple;
- PWAIT_CONTEXT_BLOCK WaitContextBlock;
- IO_ALLOCATION_ACTION Retval;
-
- while(1)
- {
- /* To keep map registers, call here with the following set to 0 */
- if(AdapterObject->CommittedMapRegisters)
- IoFreeMapRegisters(AdapterObject, AdapterObject->MapRegisterBase, AdapterObject->CommittedMapRegisters);
-
- if(!(WaitContextBlock = (PWAIT_CONTEXT_BLOCK)KeRemoveDeviceQueue(&AdapterObject->ChannelWaitQueue)))
- break;
-
- /*
- * the following should really be done elsewhere since this
- * function really can't return an error code. FIXME.
- */
-
- MinAddress.QuadPart = 0;
- BoundryAddressMultiple.QuadPart = 0;
- if ((AdapterObject->Dma64BitAddresses) && (AdapterObject->MasterDevice))
- {
- MaxAddress.QuadPart = 0xFFFFFFFFFFFFFFFFLL; /* 64Bit: >4GB address range */
- }
- else if ((AdapterObject->Dma32BitAddresses) && (AdapterObject->MasterDevice))
- {
- MaxAddress.QuadPart = 0xFFFFFFFF; /* 32Bit: 4GB address range */
- }
- else
- {
- MaxAddress.QuadPart = 0x00FFFFFF; /* 24Bit: 16MB address range */
- if (AdapterObject->Width16Bits)
- {
- BoundryAddressMultiple.QuadPart = 0x20000; /* 128k boundary */
- }
- else
- {
- BoundryAddressMultiple.QuadPart = 0x10000; /* 64k boundary */
- }
- }
-
- AdapterObject->MapRegisterBase = MmAllocateContiguousMemorySpecifyCache(
- WaitContextBlock->NumberOfMapRegisters * PAGE_SIZE,
- MinAddress,
- MaxAddress,
- BoundryAddressMultiple,
- MmCached);
-
- if(!AdapterObject->MapRegisterBase)
- return;
-
- /* call the adapter control routine */
- Retval = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
- AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
-
- if(Retval == KeepObject)
- {
- /* we're done until the caller manually calls IoFreeAdapterChannel */
- break;
- }
- else if(Retval == DeallocateObjectKeepRegisters)
- {
- /* hide the map registers so they aren't deallocated next time around */
- AdapterObject->CommittedMapRegisters = 0;
- }
- }
-}
-
-\f
-VOID STDCALL
-IoFreeMapRegisters (
- IN PADAPTER_OBJECT AdapterObject,
- IN PVOID MapRegisterBase,
- IN ULONG NumberOfMapRegisters)
-/*
- * FUNCTION: free map registers reserved by the system for a DMA
- * ARGUMENTS:
- * AdapterObject: dma adapter to free map registers on
- * MapRegisterBase: hadle to map registers to free
- * NumberOfRegisters: number of map registers to be freed
- * NOTES:
- * - XXX real windows has a funky interdependence between IoFreeMapRegisters
- * and IoFreeAdapterChannel
- * BUGS:
- * - needs to be improved to use a real map register implementation
- */
-{
- if( AdapterObject->CommittedMapRegisters )
- {
- MmFreeContiguousMemory(AdapterObject->MapRegisterBase);
- AdapterObject->MapRegisterBase = 0;
- }
-}
-
-\f
-PHYSICAL_ADDRESS STDCALL
-IoMapTransfer (
- IN PADAPTER_OBJECT AdapterObject,
- IN PMDL Mdl,
- IN PVOID MapRegisterBase,
- IN PVOID CurrentVa,
- IN OUT PULONG Length,
- IN BOOLEAN WriteToDevice)
-/*
- * FUNCTION: map a dma for transfer and do the dma if it's a slave
- * ARGUMENTS:
- * AdapterObject: adapter object to do the dma on. busmaster may pass NULL.
- * Mdl: locked-down user buffer to DMA in to or out of
- * MapRegisterBase: handle to map registers to use for this dma. allways NULL
- * when doing s/g.
- * CurrentVa: index into Mdl to transfer into/out of
- * Length: length of transfer in/out. Only modified on out when doing s/g.
- * WriteToDevice: TRUE if it's an output dma, FALSE otherwise
- * RETURNS:
- * If a busmaster: A logical address that can be used to program a dma controller
- * Otherwise: nothing meaningful
- * NOTES:
- * - This function does a copyover to contiguous memory <16MB
- * - If it's a slave transfer, this function actually performs it.
- * BUGS:
- * - If the controller supports scatter/gather, the copyover should not happen
- */
-{
- PHYSICAL_ADDRESS Address;
- KIRQL OldIrql;
- ULONG LengthShift = 0;
-
- Address.QuadPart = 0;
-
- /* Isa System (slave) DMA? */
- if (MapRegisterBase && !AdapterObject->MasterDevice)
- {
- KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
-
- /*
- * FIXME: Handle case when doing common-buffer System DMA. In this case,
- * the buffer described by MDL is already phys. contiguous and below
- * 16 mega. Driver makes a one-shot call to IoMapTransfer during init.
- * to program controller with the common-buffer.
- *
- * UPDATE: Common buffer support is in place, but it's not done in a
- * clean way. We use the buffer passed by the MDL in case that the
- * adapter object is marked as auto initialize. I'm not sure if this
- * is correct and if not, how to do it properly. Note that it's also
- * possible to allocate the common buffer with different adapter object
- * and IoMapTransfer must still work in this case. Eventually this should
- * be cleaned up somehow or at least this comment modified to reflect
- * the reality.
- * -- Filip Navara, 19/07/2004
- */
-
- /* if it is a write to the device, copy the caller buffer to the low buffer */
- if (WriteToDevice && !AdapterObject->AdapterMode.AutoInitialize)
- {
- memcpy(MapRegisterBase,
- (char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
- *Length );
- }
-
- /* Writer Adapter Mode, transfer type */
- AdapterObject->AdapterMode.TransferType = (WriteToDevice ? WRITE_TRANSFER : READ_TRANSFER);
-
- /* program up the dma controller, and return */
- if (!AdapterObject->AdapterMode.AutoInitialize) {
- Address = MmGetPhysicalAddress( MapRegisterBase );
- } else {
- Address = MmGetPhysicalAddress( CurrentVa );
- }
-
- /* 16-bit DMA has a shifted length */
- if (AdapterObject->Width16Bits) {
- LengthShift = 1;
- }
-
- /* Make the Transfer */
- if (AdapterObject->AdapterNumber == 1) {
-
- PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; /* For Writing Less Code */
-
- /* Mask the Channel */
- WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_SETMASK);
-
- /* Reset Register */
- WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0);
-
- /* Set the Mode */
- WRITE_PORT_UCHAR(&DmaControl1->Mode, AdapterObject->AdapterModeByte);
-
- /* Set the Page Register */
- WRITE_PORT_UCHAR(AdapterObject->PagePort, (UCHAR)(Address.u.LowPart >> 16));
-
- /* Set the Offset Register (apparently always 0 for us if I trust the previous comment) */
- WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, 0);
- WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, 0);
-
- /* Set the Length */
- WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
- (UCHAR)((*Length >> LengthShift) - 1));
- WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
- (UCHAR)(((*Length >> LengthShift) - 1) >> 8));
-
- /* Unmask the Channel */
- WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK);
- } else {
- PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa; /* For Writing Less Code */
-
- /* Mask the Channel */
- WRITE_PORT_UCHAR(&DmaControl2->SingleMask, AdapterObject->ChannelNumber | DMA_SETMASK);
-
- /* Reset Register */
- WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0);
-
- /* Set the Mode */
- WRITE_PORT_UCHAR(&DmaControl2->Mode, AdapterObject->AdapterModeByte);
-
- /* Set the Page Register */
- WRITE_PORT_UCHAR(AdapterObject->PagePort, (UCHAR)(Address.u.LowPart >> 16));
-
- /* Set the Offset Register (apparently always 0 for us if I trust the previous comment) */
- WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, 0);
- WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, 0);
-
- /* Set the Length */
- WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
- (UCHAR)((*Length >> LengthShift) - 1));
- WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
- (UCHAR)(((*Length >> LengthShift) - 1) >> 8));
-
- /* Unmask the Channel */
- WRITE_PORT_UCHAR(&DmaControl2->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK);
- }
-
- /* Release Spinlock */
- KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
-
- /*
- NOTE: Return value should be ignored when doing System DMA.
- Maybe return some more obvious invalid address here (thou returning
- MapRegisterBase is also wrong;-)to catch invalid use?
- */
- Address.QuadPart = (ULONG)MapRegisterBase;
- return Address;
- }
-
-
- /*
- Busmaster with s/g support?
- NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
- being NULL is used to detect a s/g busmaster.
- */
- if ((!AdapterObject && !MapRegisterBase) ||
- (AdapterObject && AdapterObject->MasterDevice && AdapterObject->ScatterGather))
- {
- /*
- Just return the passed VA's corresponding phys. address.
- Update length to the number of phys. contiguous bytes found.
- */
-
- PULONG MdlPages;
- ULONG MdlPageIndex, PhysContiguousLen;
- ULONG PhysAddress;
-
- MdlPages = (PULONG)(Mdl + 1);
-
- /* Get VA's corresponding mdl phys. page index */
- MdlPageIndex = ((ULONG)CurrentVa - (ULONG)Mdl->StartVa) / PAGE_SIZE;
-
- /* Get phys. page containing the VA */
- PhysAddress = MdlPages[MdlPageIndex];
-
- PhysContiguousLen = PAGE_SIZE - BYTE_OFFSET(CurrentVa);
-
- /* VA to map may span several contiguous phys. pages (unlikely) */
- while (PhysContiguousLen < *Length &&
- MdlPages[MdlPageIndex++] + PAGE_SIZE == MdlPages[MdlPageIndex])
- {
- /*
- Note that allways adding PAGE_SIZE may make PhysContiguousLen greater
- than Length if buffer doesn't end on page boundary. Take this
- into consideration below.
- */
- PhysContiguousLen += PAGE_SIZE;
- }
-
- if (PhysContiguousLen < *Length)
- {
- *Length = PhysContiguousLen;
- }
-
- //add offset to phys. page address
- Address.QuadPart = PhysAddress + BYTE_OFFSET(CurrentVa);
- return Address;
- }
-
-
- /*
- Busmaster without s/g support?
- NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
- not being NULL is used to detect a non s/g busmaster.
- */
- if ((!AdapterObject && MapRegisterBase) ||
- (AdapterObject && AdapterObject->MasterDevice && !AdapterObject->ScatterGather))
- {
- /*
- NOTE: Busmasters doing common-buffer DMA shouldn't call IoMapTransfer, but I don't
- know if it's illegal... Maybe figure out what to do in this case...
- */
-
- if( WriteToDevice )
- {
- memcpy(MapRegisterBase,
- (char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
- *Length );
- }
-
- return MmGetPhysicalAddress(MapRegisterBase);
- }
-
- DPRINT("IoMapTransfer: Unsupported operation\n");
- KEBUGCHECK(0);
- return Address;
-}
-
-
-/* EOF */
-
-
-
-
* 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>
-/* Adapters for each channel */
-PADAPTER_OBJECT HalpEisaAdapter[8];
+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,
+ (PALLOCATE_ADAPTER_CHANNEL)IoAllocateAdapterChannel,
+ (PFLUSH_ADAPTER_BUFFERS)IoFlushAdapterBuffers,
+ (PFREE_ADAPTER_CHANNEL)IoFreeAdapterChannel,
+ (PFREE_MAP_REGISTERS)IoFreeMapRegisters,
+ (PMAP_TRANSFER)IoMapTransfer,
+ (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)
+HalpInitDma(VOID)
{
- /* TODO: Initialize the first Map Buffer */
+ /*
+ * 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();
}
-PVOID STDCALL
-HalAllocateCommonBuffer (PADAPTER_OBJECT AdapterObject,
- ULONG Length,
- PPHYSICAL_ADDRESS LogicalAddress,
- BOOLEAN CacheEnabled)
-/*
- * FUNCTION: Allocates memory that is visible to both the processor(s) and
- * a dma device
- * ARGUMENTS:
- * AdapterObject = Adapter object representing the bus master or
- * system dma controller
- * Length = Number of bytes to allocate
- * LogicalAddress = Logical address the driver can use to access the
- * buffer
- * CacheEnabled = Specifies if the memory can be cached
- * RETURNS: The base virtual address of the memory allocated
- * NULL on failure
- * NOTES:
- * CacheEnabled is ignored - it's all cache-disabled (like in NT)
- * UPDATE: It's not ignored now. If that's wrong just modify the
- * CacheEnabled comparsion below.
+/**
+ * @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 LowestAddress, HighestAddress, BoundryAddressMultiple;
- PVOID BaseAddress;
-
- LowestAddress.QuadPart = 0;
- BoundryAddressMultiple.QuadPart = 0;
- if ((AdapterObject->Dma64BitAddresses) && (AdapterObject->MasterDevice)) {
- HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFLL; /* 64Bit: >4GB address range */
-
- } else if ((AdapterObject->Dma32BitAddresses) && (AdapterObject->MasterDevice)) {
- HighestAddress.QuadPart = 0xFFFFFFFF; /* 32Bit: 4GB address range */
- } else {
- HighestAddress.QuadPart = 0x00FFFFFF; /* 24Bit: 16MB address range */
- if (AdapterObject->Width16Bits)
+ PHYSICAL_ADDRESS HighestAddress;
+
+ if (AdapterObject->MasterDevice)
+ {
+ if (AdapterObject->Dma64BitAddresses)
{
- BoundryAddressMultiple.QuadPart = 0x20000; /* 128k boundary */
+ HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
+ return HighestAddress;
}
- else
+ else if (AdapterObject->Dma32BitAddresses)
{
- BoundryAddressMultiple.QuadPart = 0x10000; /* 64k boundary */
+ 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;
+
+ /*
+ * 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)
+ {
+ PMAP_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;
- BaseAddress = MmAllocateContiguousMemorySpecifyCache(
- Length,
- LowestAddress,
- HighestAddress,
- BoundryAddressMultiple,
- CacheEnabled ? MmCached : MmNonCached);
- if (!BaseAddress)
- return 0;
+ SizeOfBitmap = MAX_MAP_REGISTERS;
+ Size = sizeof(ADAPTER_OBJECT);
+ Size += sizeof(RTL_BITMAP);
+ Size += (SizeOfBitmap + 7) >> 3;
- *LogicalAddress = MmGetPhysicalAddress(BaseAddress);
+ MasterAdapter = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_DMA);
+ if (MasterAdapter == NULL)
+ return NULL;
- return BaseAddress;
+ 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(MAP_REGISTER_ENTRY),
+ TAG_DMA);
+ if (MasterAdapter->MapRegisterBase == NULL)
+ {
+ ExFreePool(MasterAdapter);
+ return NULL;
+ }
+
+ RtlZeroMemory(MasterAdapter->MapRegisterBase,
+ SizeOfBitmap * sizeof(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,
+ NULL);
+
+ Status = ObCreateObject(
+ KernelMode,
+ IoAdapterObjectType,
+ &ObjectAttributes,
+ KernelMode,
+ NULL,
+ sizeof(ADAPTER_OBJECT),
+ 0,
+ 0,
+ (PVOID)&AdapterObject);
+ if (!NT_SUCCESS(Status))
+ return NULL;
+
+ Status = ObReferenceObjectByPointer(
+ AdapterObject,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ IoAdapterObjectType,
+ KernelMode);
+ if (!NT_SUCCESS(Status))
+ return NULL;
+
+ RtlZeroMemory(AdapterObject, sizeof(ADAPTER_OBJECT));
+
+ Status = ObInsertObject(
+ AdapterObject,
+ NULL,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ 0,
+ NULL,
+ &Handle);
+ if (!NT_SUCCESS(Status))
+ return NULL;
+
+ ZwClose(Handle);
+
+ AdapterObject->DmaHeader.Version = DeviceDescription->Version;
+ AdapterObject->DmaHeader.Size = sizeof(ADAPTER_OBJECT);
+ AdapterObject->DmaHeader.DmaOperations = &HalpDmaOperations;
+ AdapterObject->MapRegistersPerChannel = 1;
+ AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses;
+ AdapterObject->ChannelNumber = 0xFF;
+ AdapterObject->MasterAdapter = HalpMasterAdapter;
+ KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue);
+
+ return AdapterObject;
+}
+
+/**
+ * @name HalpDmaInitializeEisaAdapter
+ *
+ * Setup DMA modes and extended modes for (E)ISA DMA adapter object.
+ */
+
BOOLEAN STDCALL
-HalFlushCommonBuffer (ULONG Unknown1,
- ULONG Unknown2,
- ULONG Unknown3,
- ULONG Unknown4,
- ULONG Unknown5,
- ULONG Unknown6,
- ULONG Unknown7,
- ULONG Unknown8)
+HalpDmaInitializeEisaAdapter(
+ PADAPTER_OBJECT AdapterObject,
+ PDEVICE_DESCRIPTION DeviceDescription)
{
+ UCHAR Controller;
+ DMA_MODE DmaMode = { Byte: 0 };
+ DMA_EXTENDED_MODE ExtendedMode = { Byte: 0 };
+ PVOID AdapterBaseVa;
+
+ Controller = (DeviceDescription->DmaChannel & 4) ? 2 : 1;
+
+ if (Controller == 1)
+ AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController1);
+ else
+ AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController2);
+
+ AdapterObject->AdapterNumber = Controller;
+ AdapterObject->ChannelNumber = DeviceDescription->DmaChannel & 3;
+ AdapterObject->PagePort = (PUCHAR)HalpEisaPortPage[DeviceDescription->DmaChannel];
+ AdapterObject->Width16Bits = FALSE;
+ AdapterObject->AdapterBaseVa = AdapterBaseVa;
+
+ if (HalpEisaDma)
+ {
+ ExtendedMode.ChannelNumber = AdapterObject->ChannelNumber;
+
+ switch (DeviceDescription->DmaSpeed)
+ {
+ case Compatible: ExtendedMode.TimingMode = COMPATIBLE_TIMING; break;
+ case TypeA: ExtendedMode.TimingMode = TYPE_A_TIMING; break;
+ case TypeB: ExtendedMode.TimingMode = TYPE_B_TIMING; break;
+ case TypeC: ExtendedMode.TimingMode = BURST_TIMING; break;
+ default:
+ return FALSE;
+ }
+
+ switch (DeviceDescription->DmaWidth)
+ {
+ case Width8Bits: ExtendedMode.TransferSize = B_8BITS; break;
+ case Width16Bits: ExtendedMode.TransferSize = B_16BITS; break;
+ case Width32Bits: ExtendedMode.TransferSize = B_32BITS; break;
+ default:
+ return FALSE;
+ }
+
+ if (Controller == 1)
+ WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode1),
+ ExtendedMode.Byte);
+ else
+ WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode2),
+ ExtendedMode.Byte);
+ }
+ else
+ {
+ /*
+ * Validate setup for non-busmaster DMA adapter. Secondary controller
+ * supports only 16-bit transfers and main controller supports only
+ * 8-bit transfers. Anything else is invalid.
+ */
+
+ if (!DeviceDescription->Master)
+ {
+ if (Controller == 2 && DeviceDescription->DmaWidth == Width16Bits)
+ AdapterObject->Width16Bits = TRUE;
+ else if (Controller != 1 || DeviceDescription->DmaWidth != Width8Bits)
+ return FALSE;
+ }
+ }
+
+ DmaMode.Channel = AdapterObject->ChannelNumber;
+ DmaMode.AutoInitialize = DeviceDescription->AutoInitialize;
+
+ /*
+ * Set the DMA request mode.
+ *
+ * For (E)ISA bus master devices just unmask (enable) the DMA channel
+ * and set it to cascade mode. Otherwise just select the right one
+ * bases on the passed device description.
+ */
+
+ if (DeviceDescription->Master)
+ {
+ DmaMode.RequestMode = CASCADE_REQUEST_MODE;
+ if (Controller == 1)
+ {
+ /* Set the Request Data */
+ WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->Mode,
+ DmaMode.Byte);
+ /* Unmask DMA Channel */
+ WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->SingleMask,
+ AdapterObject->ChannelNumber | DMA_CLEARMASK);
+ } else {
+ /* Set the Request Data */
+ WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->Mode,
+ DmaMode.Byte);
+ /* Unmask DMA Channel */
+ WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->SingleMask,
+ AdapterObject->ChannelNumber | DMA_CLEARMASK);
+ }
+ }
+ else
+ {
+ if (DeviceDescription->DemandMode)
+ DmaMode.RequestMode = DEMAND_REQUEST_MODE;
+ else
+ DmaMode.RequestMode = SINGLE_REQUEST_MODE;
+ }
+
+ AdapterObject->AdapterMode = DmaMode;
+
return TRUE;
}
+/**
+ * @name HalGetAdapter
+ *
+ * Allocate an adapter object for DMA device.
+ *
+ * @param DeviceDescription
+ * Structure describing the attributes of the device.
+ * @param NumberOfMapRegisters
+ * On return filled with the maximum number of map registers the
+ * device driver can allocate for DMA transfer operations.
+ *
+ * @return The DMA adapter on success, NULL otherwise.
+ *
+ * @implemented
+ */
+
+PADAPTER_OBJECT STDCALL
+HalGetAdapter(
+ PDEVICE_DESCRIPTION DeviceDescription,
+ PULONG NumberOfMapRegisters)
+{
+ PADAPTER_OBJECT AdapterObject = NULL;
+ PADAPTER_OBJECT MasterAdapter;
+ BOOLEAN EisaAdapter;
+ ULONG MapRegisters;
+ ULONG MaximumLength;
+
+ /* Validate parameters in device description */
+ if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION2)
+ return NULL;
+
+ /*
+ * See if we're going to use ISA/EISA DMA adapter. These adapters are
+ * special since they're reused.
+ *
+ * Also note that we check for channel number since there are only 8 DMA
+ * channels on ISA, so any request above this requires new adapter.
+ */
+
+ if (DeviceDescription->InterfaceType == Isa || !DeviceDescription->Master)
+ {
+ if (DeviceDescription->InterfaceType == Isa &&
+ DeviceDescription->DmaChannel >= 8)
+ EisaAdapter = FALSE;
+ else
+ EisaAdapter = TRUE;
+ }
+ else
+ {
+ EisaAdapter = FALSE;
+ }
+
+ /*
+ * Disallow creating adapter for ISA/EISA DMA channel 4 since it's used
+ * for cascading the controllers and it's not available for software use.
+ */
+
+ if (EisaAdapter && DeviceDescription->DmaChannel == 4)
+ return NULL;
+
+ /*
+ * Calculate the number of map registers.
+ *
+ * - For EISA and PCI scatter/gather no map registers are needed.
+ * - For ISA slave scatter/gather one map register is needed.
+ * - For all other cases the number of map registers depends on
+ * DeviceDescription->MaximumLength.
+ */
+
+ MaximumLength = DeviceDescription->MaximumLength & MAXLONG;
+ if (DeviceDescription->ScatterGather &&
+ (DeviceDescription->InterfaceType == Eisa ||
+ DeviceDescription->InterfaceType == PCIBus))
+ {
+ MapRegisters = 0;
+ }
+ else if (DeviceDescription->ScatterGather &&
+ !DeviceDescription->Master)
+ {
+ MapRegisters = 1;
+ }
+ else
+ {
+ /*
+ * In the equation below the additional map register added by
+ * the "+1" accounts for the case when a transfer does not start
+ * at a page-aligned address.
+ */
+ MapRegisters = BYTES_TO_PAGES(MaximumLength) + 1;
+ if (MapRegisters > 16)
+ MapRegisters = 16;
+ }
+
+ /*
+ * Acquire the DMA lock that is used to protect adapter lists and
+ * EISA adapter array.
+ */
+
+ KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode,
+ FALSE, NULL);
+
+ /*
+ * Now we must get ahold of the adapter object. For first eight ISA/EISA
+ * channels there are static adapter objects that are reused and updated
+ * on succesive HalGetAdapter calls. In other cases a new adapter object
+ * is always created and it's to the DMA adapter list (HalpDmaAdapterList).
+ */
+
+ if (EisaAdapter)
+ {
+ AdapterObject = HalpEisaAdapter[DeviceDescription->DmaChannel];
+ if (AdapterObject != NULL)
+ {
+ if (AdapterObject->NeedsMapRegisters &&
+ MapRegisters > AdapterObject->MapRegistersPerChannel)
+ AdapterObject->MapRegistersPerChannel = MapRegisters;
+ }
+ }
+
+ if (AdapterObject == NULL)
+ {
+ AdapterObject = HalpDmaAllocateChildAdapter(
+ MapRegisters, DeviceDescription);
+ if (AdapterObject == NULL)
+ {
+ KeSetEvent(&HalpDmaLock, 0, 0);
+ return NULL;
+ }
+
+ if (EisaAdapter)
+ {
+ HalpEisaAdapter[DeviceDescription->DmaChannel] = AdapterObject;
+ }
+
+ if (MapRegisters > 0)
+ {
+ AdapterObject->NeedsMapRegisters = TRUE;
+ MasterAdapter = HalpMasterAdapter;
+ AdapterObject->MapRegistersPerChannel = MapRegisters;
+
+ /*
+ * FIXME: Verify that the following makes sense. Actually
+ * MasterAdapter->NumberOfMapRegisters contains even the number
+ * of gaps, so this will not work correctly all the time. It
+ * doesn't matter much since it's only optimization to avoid
+ * queuing work items in HalAllocateAdapterChannel.
+ */
+
+ MasterAdapter->CommittedMapRegisters += MapRegisters;
+ if (MasterAdapter->CommittedMapRegisters > MasterAdapter->NumberOfMapRegisters)
+ HalpGrowMapBuffers(MasterAdapter, 0x10000);
+ }
+ else
+ {
+ AdapterObject->NeedsMapRegisters = FALSE;
+ if (DeviceDescription->Master)
+ AdapterObject->MapRegistersPerChannel = BYTES_TO_PAGES(MaximumLength) + 1;
+ else
+ AdapterObject->MapRegistersPerChannel = 1;
+ }
+ }
+
+ if (!EisaAdapter)
+ InsertTailList(&HalpDmaAdapterList, &AdapterObject->AdapterList);
+
+ /*
+ * Release the DMA lock. HalpDmaAdapterList and HalpEisaAdapter will
+ * no longer be touched, so we don't need it.
+ */
+
+ KeSetEvent(&HalpDmaLock, 0, 0);
+
+ /*
+ * Setup the values in the adapter object that are common for all
+ * types of buses.
+ */
+
+ if (DeviceDescription->Version >= DEVICE_DESCRIPTION_VERSION1)
+ AdapterObject->IgnoreCount = DeviceDescription->IgnoreCount;
+ else
+ AdapterObject->IgnoreCount = 0;
+
+ AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses;
+ AdapterObject->Dma64BitAddresses = DeviceDescription->Dma64BitAddresses;
+ AdapterObject->ScatterGather = DeviceDescription->ScatterGather;
+ AdapterObject->MasterDevice = DeviceDescription->Master;
+ *NumberOfMapRegisters = AdapterObject->MapRegistersPerChannel;
+
+ /*
+ * For non-(E)ISA adapters we have already done all the work. On the
+ * other hand for (E)ISA adapters we must still setup the DMA modes
+ * and prepare the controller.
+ */
+
+ if (EisaAdapter)
+ {
+ if (!HalpDmaInitializeEisaAdapter(AdapterObject, DeviceDescription))
+ {
+ ObfDereferenceObject(AdapterObject);
+ return NULL;
+ }
+ }
+
+ return AdapterObject;
+}
+
+/**
+ * @name HalPutDmaAdapter
+ *
+ * Internal routine to free DMA adapter and resources for reuse. It's exported
+ * using the DMA_OPERATIONS interface by HalGetAdapter.
+ *
+ * @see HalGetAdapter
+ */
+
+VOID STDCALL
+HalPutDmaAdapter(
+ PADAPTER_OBJECT AdapterObject)
+{
+ 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)
+HalFreeCommonBuffer(
+ PADAPTER_OBJECT AdapterObject,
+ ULONG Length,
+ PHYSICAL_ADDRESS LogicalAddress,
+ PVOID VirtualAddress,
+ BOOLEAN CacheEnabled)
{
MmFreeContiguousMemory(VirtualAddress);
}
-PADAPTER_OBJECT STDCALL
-HalGetAdapter (PDEVICE_DESCRIPTION DeviceDescription,
- PULONG NumberOfMapRegisters)
+/**
+ * @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;
+}
+
/*
- * FUNCTION: Returns a pointer to an adapter object for the DMA device
- * defined in the device description structure
- * ARGUMENTS:
- * DeviceDescription = Structure describing the attributes of the device
- * NumberOfMapRegisters (OUT) = Returns the maximum number of map
- * registers the device driver can
- * allocate for DMA transfer operations
- * RETURNS: The allocated adapter object on success
- * NULL on failure
- * TODO:
- * Testing
+ * @name HalReadDmaCounter
+ *
+ * Read DMA operation progress counter.
+ *
+ * @implemented
*/
+
+ULONG STDCALL
+HalReadDmaCounter(
+ PADAPTER_OBJECT AdapterObject)
{
- PADAPTER_OBJECT AdapterObject;
- DWORD ChannelSelect;
- DWORD Controller;
- ULONG MaximumLength;
- BOOLEAN ChannelSetup = TRUE;
- DMA_MODE DmaMode = {0};
-
- DPRINT("Entered Function\n");
+ 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);
- /* Validate parameters in device description, and return a pointer to
- the adapter object for the requested dma channel */
- if(DeviceDescription->Version != DEVICE_DESCRIPTION_VERSION) {
- DPRINT("Invalid Adapter version!\n");
- return NULL;
- }
-
- DPRINT("Checking Interface Type: %x \n", DeviceDescription->InterfaceType);
- if (DeviceDescription->InterfaceType == PCIBus) {
- if (DeviceDescription->Master == FALSE) {
- DPRINT("Invalid request!\n");
- return NULL;
- }
- ChannelSetup = FALSE;
- }
-
- /* There are only 8 DMA channels on ISA, so any request above this
- should not get any channel setup */
- if (DeviceDescription->DmaChannel >= 8) {
- ChannelSetup = FALSE;
- }
-
- /* Channel 4 is Reserved for Chaining, so you cant use it */
- if (DeviceDescription->DmaChannel == 4 && ChannelSetup) {
- DPRINT("Invalid request!\n");
- return NULL;
- }
-
- /* Devices that support Scatter/Gather do not need Map Registers */
- if (DeviceDescription->ScatterGather ||
- DeviceDescription->InterfaceType == PCIBus) {
- *NumberOfMapRegisters = 0;
- }
-
- /* Check if Extended DMA is available (we're just going to do a random read/write
- I picked Channel 2 because it's the first Channel in the Register */
- WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController1Pages.Channel2), 0x2A);
- if (READ_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController1Pages.Channel2)) == 0x2A) {
- HalpEisaDma = TRUE;
- }
-
- /* Find out how many Map Registers we need */
- DPRINT("Setting up Adapter Settings!\n");
- MaximumLength = DeviceDescription->MaximumLength & 0x7FFFFFFF;
- *NumberOfMapRegisters = BYTES_TO_PAGES(MaximumLength) + 1;
-
- /* Set the Channel Selection */
- ChannelSelect = DeviceDescription->DmaChannel & 0x03;
- DmaMode.Channel = ChannelSelect;
-
- /* Get the Controller Setup */
- Controller = (DeviceDescription->DmaChannel & 0x04) ? 2 : 1;
-
- /* Get the Adapter Object */
- if (HalpEisaAdapter[DeviceDescription->DmaChannel] != NULL) {
-
- /* Already allocated, return it */
- DPRINT("Getting an Adapter Object from the Cache\n");
- AdapterObject = HalpEisaAdapter[DeviceDescription->DmaChannel];
-
- /* Do we need more Map Registers this time? */
- if ((AdapterObject->NeedsMapRegisters) &&
- (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel)) {
- AdapterObject->MapRegistersPerChannel = *NumberOfMapRegisters;
- }
-
- } else {
-
- /* We have to allocate a new object! How exciting! */
- DPRINT("Allocating a new Adapter Object\n");
- AdapterObject = HalpAllocateAdapterEx(*NumberOfMapRegisters,
- FALSE,
- DeviceDescription->Dma32BitAddresses);
-
- if (AdapterObject == NULL) return NULL;
-
- HalpEisaAdapter[DeviceDescription->DmaChannel] = AdapterObject;
-
- if (!*NumberOfMapRegisters) {
- /* Easy case, no Map Registers needed */
- AdapterObject->NeedsMapRegisters = FALSE;
-
- /* If you're the master, you get all you want */
- if (DeviceDescription->Master) {
- AdapterObject->MapRegistersPerChannel= *NumberOfMapRegisters;
- } else {
- AdapterObject->MapRegistersPerChannel = 1;
- }
- } else {
- /* We Desire Registers */
- AdapterObject->NeedsMapRegisters = TRUE;
-
- /* The registers you want */
- AdapterObject->MapRegistersPerChannel = *NumberOfMapRegisters;
-
- /* Increase commitment */
- MasterAdapter->CommittedMapRegisters += *NumberOfMapRegisters;
- }
- }
-
- /* Set up DMA Structure */
- if (Controller == 1) {
- AdapterObject->AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController1);
- } else {
- AdapterObject->AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController2);
- }
-
- /* Set up Some Adapter Data */
- DPRINT("Setting up an Adapter Object\n");
- AdapterObject->IgnoreCount = DeviceDescription->IgnoreCount;
- AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses;
- AdapterObject->Dma64BitAddresses = DeviceDescription->Dma64BitAddresses;
- AdapterObject->ScatterGather = DeviceDescription->ScatterGather;
- AdapterObject->MasterDevice = DeviceDescription->Master;
- if (DeviceDescription->InterfaceType != PCIBus) AdapterObject->LegacyAdapter = TRUE;
-
- /* Everything below is not required if we don't need a channel */
- if (!ChannelSetup) {
- DPRINT("Retuning Adapter Object without Channel Setup\n");
- return AdapterObject;
- }
-
- AdapterObject->ChannelNumber = ChannelSelect;
-
-
- /* Set up the Page Port */
- if (Controller == 1) {
- switch (ChannelSelect) {
-
- case 0:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel0) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- case 1:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel1) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- case 2:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel2) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- case 3:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel3) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- }
-
- /* Set Controller Number */
- AdapterObject->AdapterNumber = 1;
- } else {
- switch (ChannelSelect) {
-
- case 1:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel5) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- case 2:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel6) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- case 3:
- AdapterObject->PagePort = (PUCHAR)(FIELD_OFFSET(DMA_PAGE, Channel7) +
- FIELD_OFFSET(EISA_CONTROL, DmaController1Pages));
- break;
- }
-
- /* Set Controller Number */
- AdapterObject->AdapterNumber = 2;
- }
-
- /* Set up the Extended Register */
- if (HalpEisaDma) {
- DMA_EXTENDED_MODE ExtendedMode;
-
- ExtendedMode.ChannelNumber = ChannelSelect;
-
- switch (DeviceDescription->DmaSpeed) {
-
- case Compatible:
- ExtendedMode.TimingMode = COMPATIBLE_TIMING;
- break;
-
- case TypeA:
- ExtendedMode.TimingMode = TYPE_A_TIMING;
- break;
-
- case TypeB:
- ExtendedMode.TimingMode = TYPE_B_TIMING;
- break;
-
- case TypeC:
- ExtendedMode.TimingMode = BURST_TIMING;
- break;
-
- default:
- return NULL;
- }
-
- switch (DeviceDescription->DmaWidth) {
-
- case Width8Bits:
- ExtendedMode.TransferSize = B_8BITS;
- break;
-
- case Width16Bits:
- ExtendedMode.TransferSize = B_16BITS;
- break;
-
- case Width32Bits:
- ExtendedMode.TransferSize = B_32BITS;
- break;
-
- default:
- return NULL;
- }
-
- if (Controller == 1) {
- WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode1),
- *((PUCHAR)&ExtendedMode));
- } else {
- WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode2),
- *((PUCHAR)&ExtendedMode));
- }
- }
-
- /* Do 8/16-bit validation */
- DPRINT("Validating an Adapter Object\n");
- if (!DeviceDescription->Master) {
- if ((DeviceDescription->DmaWidth == Width8Bits) && (Controller != 1)) {
- return NULL; /* 8-bit is only avalable on Controller 1 */
- } else if (DeviceDescription->DmaWidth == Width16Bits) {
- if (Controller != 2) {
- return NULL; /* 16-bit is only avalable on Controller 2 */
- } else {
- AdapterObject->Width16Bits = TRUE;
- }
- }
- }
-
- DPRINT("Final DMA Request Mode Setting of the Adapter Object\n");
-
- /* Set the DMA Request Modes */
- if (DeviceDescription->Master) {
- /* This is a cascade request */
- DmaMode.RequestMode = CASCADE_REQUEST_MODE;
-
- /* Send the request */
- if (AdapterObject->AdapterNumber == 1) {
- /* Set the Request Data */
- WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterObject->AdapterBaseVa)->Mode,
- AdapterObject->AdapterModeByte);
-
- /* Unmask DMA Channel */
- WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterObject->AdapterBaseVa)->SingleMask,
- AdapterObject->ChannelNumber | DMA_CLEARMASK);
- } else {
- /* Set the Request Data */
- WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterObject->AdapterBaseVa)->Mode,
- AdapterObject->AdapterModeByte);
-
- /* Unmask DMA Channel */
- WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterObject->AdapterBaseVa)->SingleMask,
- AdapterObject->ChannelNumber | DMA_CLEARMASK);
- }
- } else if (DeviceDescription->DemandMode) {
- /* This is a Demand request */
- DmaMode.RequestMode = DEMAND_REQUEST_MODE;
- } else {
- /* Normal Request */
- DmaMode.RequestMode = SINGLE_REQUEST_MODE;
- }
+ /* 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);
- /* Auto Initialize Enabled or Not*/
- DmaMode.AutoInitialize = DeviceDescription->AutoInitialize;
- AdapterObject->AdapterMode = DmaMode;
- return AdapterObject;
+ Count++;
+ if (AdapterObject->Width16Bits)
+ Count *= 2;
+
+ return Count;
}
-ULONG STDCALL
-HalReadDmaCounter (PADAPTER_OBJECT AdapterObject)
+/**
+ * @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 =
+ (PMAP_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);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/**
+ * @name IoFreeAdapterChannel
+ *
+ * Free DMA resources allocated by IoAllocateAdapterChannel.
+ *
+ * @param AdapterObject
+ * Adapter object with resources to free.
+ *
+ * @remarks
+ * This function releases map registers registers assigned to the DMA
+ * adapter. After releasing the adapter, it checks the adapter's queue
+ * and runs each queued device object in series until the queue is
+ * empty. This is the only way the device queue is emptied.
+ *
+ * @see IoAllocateAdapterChannel
+ *
+ * @implemented
+ */
+
+VOID STDCALL
+IoFreeAdapterChannel(
+ PADAPTER_OBJECT AdapterObject)
+{
+ PADAPTER_OBJECT MasterAdapter;
+ PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
+ PWAIT_CONTEXT_BLOCK WaitContextBlock;
+ ULONG Index = ~0;
+ ULONG Result;
+ KIRQL OldIrql;
+
+ MasterAdapter = AdapterObject->MasterAdapter;
+
+ for (;;)
+ {
+ /*
+ * To keep map registers, call here with AdapterObject->
+ * NumberOfMapRegisters set to zero. This trick is used in
+ * HalAllocateAdapterChannel for example.
+ */
+ if (AdapterObject->NumberOfMapRegisters)
+ {
+ IoFreeMapRegisters(
+ AdapterObject,
+ AdapterObject->MapRegisterBase,
+ AdapterObject->NumberOfMapRegisters);
+ }
+
+ DeviceQueueEntry = KeRemoveDeviceQueue(&AdapterObject->ChannelWaitQueue);
+ if (DeviceQueueEntry == NULL)
+ {
+ break;
+ }
+
+ WaitContextBlock = CONTAINING_RECORD(
+ DeviceQueueEntry,
+ WAIT_CONTEXT_BLOCK,
+ WaitQueueEntry);
+
+ AdapterObject->CurrentWcb = WaitContextBlock;
+ AdapterObject->NumberOfMapRegisters = WaitContextBlock->NumberOfMapRegisters;
+
+ if (WaitContextBlock->NumberOfMapRegisters &&
+ AdapterObject->MasterAdapter)
+ {
+ OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock);
+
+ if (IsListEmpty(&MasterAdapter->AdapterQueue))
+ {
+ Index = RtlFindClearBitsAndSet(
+ MasterAdapter->MapRegisters,
+ WaitContextBlock->NumberOfMapRegisters, 0);
+ if (Index != ~0)
+ {
+ AdapterObject->MapRegisterBase =
+ MasterAdapter->MapRegisterBase + Index;
+ if (!AdapterObject->ScatterGather)
+ {
+ AdapterObject->MapRegisterBase =
+ (PMAP_REGISTER_ENTRY)(
+ (ULONG_PTR)AdapterObject->MapRegisterBase |
+ MAP_BASE_SW_SG);
+ }
+ }
+ }
+
+ if (Index == ~0)
+ {
+ InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue);
+ KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
+ break;
+ }
+
+ KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
+ }
+ else
+ {
+ AdapterObject->MapRegisterBase = NULL;
+ AdapterObject->NumberOfMapRegisters = 0;
+ }
+
+ /* Call the adapter control routine. */
+ Result = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)(
+ WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
+ AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
+
+ switch (Result)
+ {
+ case KeepObject:
+ /*
+ * We're done until the caller manually calls IoFreeAdapterChannel
+ * or IoFreeMapRegisters.
+ */
+ return;
+
+ case DeallocateObjectKeepRegisters:
+ /*
+ * Hide the map registers so they aren't deallocated next time
+ * around.
+ */
+ AdapterObject->NumberOfMapRegisters = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ * @name IoFreeMapRegisters
+ *
+ * Free map registers reserved by the system for a DMA.
+ *
+ * @param AdapterObject
+ * DMA adapter to free map registers on.
+ * @param MapRegisterBase
+ * Handle to map registers to free.
+ * @param NumberOfRegisters
+ * Number of map registers to be freed.
+ *
+ * @implemented
+ */
+
+VOID STDCALL
+IoFreeMapRegisters(
+ IN PADAPTER_OBJECT AdapterObject,
+ IN PVOID MapRegisterBase,
+ IN ULONG NumberOfMapRegisters)
{
- KIRQL OldIrql;
- ULONG Count;
+ PADAPTER_OBJECT MasterAdapter = AdapterObject->MasterAdapter;
+ PLIST_ENTRY ListEntry;
+ KIRQL OldIrql;
+ ULONG Index;
+ ULONG Result;
+
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ if (MasterAdapter == NULL || MapRegisterBase == NULL)
+ return;
+
+ OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock);
+
+ if (NumberOfMapRegisters != 0)
+ {
+ PMAP_REGISTER_ENTRY RealMapRegisterBase;
+
+ RealMapRegisterBase =
+ (PMAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
+ RtlClearBits(MasterAdapter->MapRegisters,
+ RealMapRegisterBase - MasterAdapter->MapRegisterBase,
+ NumberOfMapRegisters);
+ }
+
+ /*
+ * Now that we freed few map registers it's time to look at the master
+ * adapter queue and see if there is someone waiting for map registers.
+ */
+
+ while (!IsListEmpty(&MasterAdapter->AdapterQueue))
+ {
+ ListEntry = RemoveHeadList(&MasterAdapter->AdapterQueue);
+ AdapterObject = CONTAINING_RECORD(
+ ListEntry, struct _ADAPTER_OBJECT, AdapterQueue);
+
+ Index = RtlFindClearBitsAndSet(
+ MasterAdapter->MapRegisters,
+ AdapterObject->NumberOfMapRegisters,
+ MasterAdapter->NumberOfMapRegisters);
+ if (Index == ~0)
+ {
+ InsertHeadList(&MasterAdapter->AdapterQueue, ListEntry);
+ break;
+ }
- KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql);
+ KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
+
+ AdapterObject->MapRegisterBase =
+ MasterAdapter->MapRegisterBase + Index;
+
+ Result = ((PDRIVER_CONTROL)AdapterObject->CurrentWcb->DeviceRoutine)(
+ AdapterObject->CurrentWcb->DeviceObject,
+ AdapterObject->CurrentWcb->CurrentIrp,
+ AdapterObject->MapRegisterBase,
+ AdapterObject->CurrentWcb->DeviceContext);
+
+ switch (Result)
+ {
+ case DeallocateObjectKeepRegisters:
+ AdapterObject->NumberOfMapRegisters = 0;
+ /* fall through */
+
+ case DeallocateObject:
+ if (AdapterObject->NumberOfMapRegisters)
+ {
+ OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock);
+ RtlClearBits(MasterAdapter->MapRegisters,
+ AdapterObject->MapRegisterBase -
+ MasterAdapter->MapRegisterBase,
+ AdapterObject->NumberOfMapRegisters);
+ KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
+ }
+ IoFreeAdapterChannel(AdapterObject);
+ break;
+
+ default:
+ break;
+ }
+
+ OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock);
+ }
+
+ KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
+}
+
+/**
+ * @name HalpCopyBufferMap
+ *
+ * Helper function for copying data from/to map register buffers.
+ *
+ * @see IoFlushAdapterBuffers, IoMapTransfer
+ */
+
+VOID STDCALL
+HalpCopyBufferMap(
+ PMDL Mdl,
+ PMAP_REGISTER_ENTRY MapRegisterBase,
+ PVOID CurrentVa,
+ ULONG Length,
+ BOOLEAN WriteToDevice)
+{
+ ULONG CurrentLength;
+ ULONG_PTR CurrentAddress;
+ ULONG PageOffset;
+ 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)CurrentVa - (ULONG_PTR)Mdl->StartVa -
+ Mdl->ByteOffset;
+
+ while (Length > 0)
+ {
+ PageOffset = CurrentAddress & (PAGE_SIZE - 1);
+ CurrentLength = PAGE_SIZE - PageOffset;
+ if (CurrentLength > Length)
+ CurrentLength = Length;
+
+ if (WriteToDevice)
+ {
+ RtlCopyMemory(
+ (PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + PageOffset),
+ (PVOID)CurrentAddress,
+ CurrentLength);
+ }
+ else
+ {
+ RtlCopyMemory(
+ (PVOID)CurrentAddress,
+ (PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + PageOffset),
+ 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;
+ PMAP_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;
+ }
- /* Send the Request to the specific controller */
- if (AdapterObject->AdapterNumber == 1) {
-
- /* Set this for Ease */
- PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
-
- /* 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;
+ /* This can happen if the device supports hardware scatter/gather. */
+ if (MapRegisterBase == NULL)
+ return TRUE;
+
+ RealMapRegisterBase =
+ (PMAP_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, 0);
+ }
+ 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_TYPE MdlPagesPtr;
+ PFN_TYPE MdlPage1, MdlPage2;
+ ULONG PageOffset;
+ ULONG TransferOffset;
+ ULONG TransferLength;
+ BOOLEAN UseMapRegisters;
+ PMAP_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.
+ *
+ * PageOffset 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.
+ */
+
+ PageOffset = (ULONG_PTR)CurrentVa & (PAGE_SIZE - 1);
+
+ MdlPagesPtr = (PPFN_TYPE)(Mdl + 1);
+ MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT;
+
+ PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT;
+ PhysicalAddress.QuadPart += PageOffset;
+
+ TransferLength = PAGE_SIZE - PageOffset;
+
+ /*
+ * 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 =
+ (PMAP_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 += PageOffset;
+ 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 +=
+ (PageOffset + TransferLength + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+ /*
+ * Check if the buffer doesn't exceed the highest phisical 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 += PageOffset;
+ }
+ }
+
+ /*
+ * 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;
- } else {
+ if (WriteToDevice)
+ {
+ AdapterMode.TransferType = WRITE_TRANSFER;
+ }
+ else
+ {
+ AdapterMode.TransferType = READ_TRANSFER;
+ if (AdapterObject->IgnoreCount)
+ {
+ RtlZeroMemory((PUCHAR)RealMapRegisterBase[Counter].VirtualAddress +
+ PageOffset, TransferLength);
+ }
+ }
- /* Set this for Ease */
- PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
-
- /* 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;
- }
-
- /* Play around with the count (add bias and multiply by 2 if 16-bit DMA) */
- Count ++;
- if (AdapterObject->Width16Bits) Count *=2 ;
-
- KeReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql);
-
- /* Return it */
- return Count;
+ 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 */