From a0b8ef87a862a0d1e2e97fcacf1f03fe6e2aed00 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 22 Aug 2005 08:39:42 +0000 Subject: [PATCH] Complete reimplementation of HAL DMA routines. The key changes are * Proper support for bus-master device adapters. * Real implementation of map registers. * Basic support for emulating scatter/gather DMA on devices that don't support it in hardware. * Support for transfers that aren't page aligned. * Proper detection and support of EISA DMA controllers. * Fixed prototype for HalFlushCommonBuffer. svn path=/trunk/; revision=17470 --- reactos/hal/hal/hal.c | 5 +- reactos/hal/hal/hal.def | 2 +- reactos/hal/halx86/generic/adapter.c | 717 -------- reactos/hal/halx86/generic/dma.c | 2255 +++++++++++++++++++----- reactos/hal/halx86/generic/generic.xml | 1 - reactos/hal/halx86/include/hal.h | 1 + reactos/hal/halx86/include/haldma.h | 389 ++++ reactos/hal/halx86/include/halp.h | 353 ---- 8 files changed, 2239 insertions(+), 1484 deletions(-) delete mode 100644 reactos/hal/halx86/generic/adapter.c create mode 100644 reactos/hal/halx86/include/haldma.h diff --git a/reactos/hal/hal/hal.c b/reactos/hal/hal/hal.c index c031a647acb..f0c51738cb5 100644 --- a/reactos/hal/hal/hal.c +++ b/reactos/hal/hal/hal.c @@ -211,10 +211,7 @@ HalFlushCommonBuffer( ULONG Unknown2, ULONG Unknown3, ULONG Unknown4, - ULONG Unknown5, - ULONG Unknown6, - ULONG Unknown7, - ULONG Unknown8) + ULONG Unknown5) { UNIMPLEMENTED; diff --git a/reactos/hal/hal/hal.def b/reactos/hal/hal/hal.def index 9f9d7fdda2c..3b38a267076 100644 --- a/reactos/hal/hal/hal.def +++ b/reactos/hal/hal/hal.def @@ -20,7 +20,7 @@ HalDisableSystemInterrupt@8 HalDisplayString@4 HalEnableSystemInterrupt@12 HalEndSystemInterrupt@8 -HalFlushCommonBuffer@32 +HalFlushCommonBuffer@20 HalFreeCommonBuffer@24 HalGetAdapter@8 HalGetBusData@20 diff --git a/reactos/hal/halx86/generic/adapter.c b/reactos/hal/halx86/generic/adapter.c deleted file mode 100644 index 135d2da8b86..00000000000 --- a/reactos/hal/halx86/generic/adapter.c +++ /dev/null @@ -1,717 +0,0 @@ -/* $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 -#define NDEBUG -#include - -/* FUNCTIONS *****************************************************************/ - -/* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */ - - -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; -} - - -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; -} - - -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; - } - } -} - - -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; - } -} - - -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 */ - - - - diff --git a/reactos/hal/halx86/generic/dma.c b/reactos/hal/halx86/generic/dma.c index 2011eb14618..12eec1637c9 100644 --- a/reactos/hal/halx86/generic/dma.c +++ b/reactos/hal/halx86/generic/dma.c @@ -5,459 +5,1898 @@ * 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 #define NDEBUG #include -/* 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; + PHYSICAL_ADDRESS HighestAddress; - 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) + 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; } - } + } - BaseAddress = MmAllocateContiguousMemorySpecifyCache( - Length, - LowestAddress, - HighestAddress, - BoundryAddressMultiple, - CacheEnabled ? MmCached : MmNonCached); - if (!BaseAddress) - return 0; - - *LogicalAddress = MmGetPhysicalAddress(BaseAddress); - - return BaseAddress; + 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 -HalFlushCommonBuffer (ULONG Unknown1, - ULONG Unknown2, - ULONG Unknown3, - ULONG Unknown4, - ULONG Unknown5, - ULONG Unknown6, - ULONG Unknown7, - ULONG Unknown8) +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; + + SizeOfBitmap = MAX_MAP_REGISTERS; + Size = sizeof(ADAPTER_OBJECT); + Size += sizeof(RTL_BITMAP); + Size += (SizeOfBitmap + 7) >> 3; + + MasterAdapter = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_DMA); + if (MasterAdapter == NULL) + return NULL; + + RtlZeroMemory(MasterAdapter, Size); + + KeInitializeSpinLock(&MasterAdapter->SpinLock); + InitializeListHead(&MasterAdapter->AdapterQueue); + + MasterAdapter->MapRegisters = (PVOID)(MasterAdapter + 1); + RtlInitializeBitMap( + MasterAdapter->MapRegisters, + (PULONG)(MasterAdapter->MapRegisters + 1), + SizeOfBitmap); + RtlSetAllBits(MasterAdapter->MapRegisters); + MasterAdapter->NumberOfMapRegisters = 0; + MasterAdapter->CommittedMapRegisters = 0; + + MasterAdapter->MapRegisterBase = ExAllocatePoolWithTag( + NonPagedPool, + SizeOfBitmap * sizeof(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 +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 -HalFreeCommonBuffer (PADAPTER_OBJECT AdapterObject, - ULONG Length, - PHYSICAL_ADDRESS LogicalAddress, - PVOID VirtualAddress, - BOOLEAN CacheEnabled) +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) { MmFreeContiguousMemory(VirtualAddress); } -PADAPTER_OBJECT STDCALL -HalGetAdapter (PDEVICE_DESCRIPTION DeviceDescription, - PULONG NumberOfMapRegisters) -/* - * 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 HalpDmaGetDmaAlignment + * + * Internal routine to return the DMA alignment requirement. It's exported + * using the DMA_OPERATIONS interface by HalGetAdapter. + * + * @see HalGetAdapter */ -{ - PADAPTER_OBJECT AdapterObject; - DWORD ChannelSelect; - DWORD Controller; - ULONG MaximumLength; - BOOLEAN ChannelSetup = TRUE; - DMA_MODE DmaMode = {0}; - - DPRINT("Entered Function\n"); - - /* 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; - } - - /* Auto Initialize Enabled or Not*/ - DmaMode.AutoInitialize = DeviceDescription->AutoInitialize; - AdapterObject->AdapterMode = DmaMode; - return AdapterObject; -} ULONG STDCALL -HalReadDmaCounter (PADAPTER_OBJECT AdapterObject) +HalpDmaGetDmaAlignment( + PADAPTER_OBJECT AdapterObject) { - KIRQL OldIrql; - ULONG Count; + return 1; +} - KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql); +/* + * @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) { - - /* 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; - - } else { + /* Send the request to the specific controller. */ + if (AdapterObject->AdapterNumber == 1) + { + PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; - /* Set this for Ease */ - PDMA2_CONTROL DmaControl2 = 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); - /* 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; + Count++; + 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 = + (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) +{ + 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; + } + + 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; + } + + /* 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; + + if (WriteToDevice) + { + AdapterMode.TransferType = WRITE_TRANSFER; + } + else + { + AdapterMode.TransferType = READ_TRANSFER; + if (AdapterObject->IgnoreCount) + { + RtlZeroMemory((PUCHAR)RealMapRegisterBase[Counter].VirtualAddress + + PageOffset, TransferLength); + } + } + + TransferOffset = PhysicalAddress.LowPart & 0xFFFF; + if (AdapterObject->Width16Bits) + { + TransferLength >>= 1; + TransferOffset >>= 1; + } + + OldIrql = KfAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock); + + if (AdapterObject->AdapterNumber == 1) + { + PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; + + /* Reset Register */ + WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0); + /* Set the Mode */ + WRITE_PORT_UCHAR(&DmaControl1->Mode, AdapterMode.Byte); + /* Set the Offset Register */ + WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, + (UCHAR)(TransferOffset)); + WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, + (UCHAR)(TransferOffset >> 8)); + /* Set the Page Register */ + WRITE_PORT_UCHAR(AdapterObject->PagePort + + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages), + (UCHAR)(PhysicalAddress.LowPart >> 16)); + if (HalpEisaDma) + { + WRITE_PORT_UCHAR(AdapterObject->PagePort + + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages), + 0); + } + /* Set the Length */ + WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, + (UCHAR)(TransferLength - 1)); + WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, + (UCHAR)((TransferLength - 1) >> 8)); + /* Unmask the Channel */ + WRITE_PORT_UCHAR(&DmaControl1->SingleMask, + AdapterObject->ChannelNumber | DMA_CLEARMASK); + } + else + { + PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa; + + /* Reset Register */ + WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0); + /* Set the Mode */ + WRITE_PORT_UCHAR(&DmaControl2->Mode, AdapterMode.Byte); + /* Set the Offset Register */ + WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, + (UCHAR)(TransferOffset)); + WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, + (UCHAR)(TransferOffset >> 8)); + /* Set the Page Register */ + WRITE_PORT_UCHAR(AdapterObject->PagePort + + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages), + (UCHAR)(PhysicalAddress.u.LowPart >> 16)); + if (HalpEisaDma) + { + WRITE_PORT_UCHAR(AdapterObject->PagePort + + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages), + 0); + } + /* Set the Length */ + WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, + (UCHAR)(TransferLength - 1)); + WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, + (UCHAR)((TransferLength - 1) >> 8)); + /* Unmask the Channel */ + WRITE_PORT_UCHAR(&DmaControl2->SingleMask, + AdapterObject->ChannelNumber | DMA_CLEARMASK); + } + + KfReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql); + } + + /* + * Return physical address of the buffer with data that is used for the + * transfer. It can either point inside the Mdl that was passed by the + * caller or into the map registers if the Mdl buffer can't be used + * directly. + */ + + return PhysicalAddress; +} + +/** + * @name HalFlushCommonBuffer + * + * @implemented + */ + +BOOLEAN STDCALL +HalFlushCommonBuffer( + ULONG Unknown1, + ULONG Unknown2, + ULONG Unknown3, + ULONG Unknown4, + ULONG Unknown5) +{ + return TRUE; } /* EOF */ diff --git a/reactos/hal/halx86/generic/generic.xml b/reactos/hal/halx86/generic/generic.xml index fdc06ae753d..2566f0e6f0c 100644 --- a/reactos/hal/halx86/generic/generic.xml +++ b/reactos/hal/halx86/generic/generic.xml @@ -4,7 +4,6 @@ - adapter.c beep.c bus.c dma.c diff --git a/reactos/hal/halx86/include/hal.h b/reactos/hal/halx86/include/hal.h index 745260bc105..e4befee3e11 100644 --- a/reactos/hal/halx86/include/hal.h +++ b/reactos/hal/halx86/include/hal.h @@ -26,6 +26,7 @@ #include "apic.h" #include "bus.h" #include "halirq.h" +#include "haldma.h" #include "halp.h" #include "mps.h" #include "ioapic.h" diff --git a/reactos/hal/halx86/include/haldma.h b/reactos/hal/halx86/include/haldma.h new file mode 100644 index 00000000000..0c98a5a4fef --- /dev/null +++ b/reactos/hal/halx86/include/haldma.h @@ -0,0 +1,389 @@ +#ifndef HALDMA_H +#define HALDMA_H + +/* + * DMA Page Register Structure + * 080 DMA RESERVED + * 081 DMA Page Register (channel 2) + * 082 DMA Page Register (channel 3) + * 083 DMA Page Register (channel 1) + * 084 DMA RESERVED + * 085 DMA RESERVED + * 086 DMA RESERVED + * 087 DMA Page Register (channel 0) + * 088 DMA RESERVED + * 089 PS/2-DMA Page Register (channel 6) + * 08A PS/2-DMA Page Register (channel 7) + * 08B PS/2-DMA Page Register (channel 5) + * 08C PS/2-DMA RESERVED + * 08D PS/2-DMA RESERVED + * 08E PS/2-DMA RESERVED + * 08F PS/2-DMA Page Register (channel 4) + */ + +typedef struct _DMA_PAGE +{ + UCHAR Reserved1; + UCHAR Channel2; + UCHAR Channel3; + UCHAR Channel1; + UCHAR Reserved2[3]; + UCHAR Channel0; + UCHAR Reserved3; + UCHAR Channel6; + UCHAR Channel7; + UCHAR Channel5; + UCHAR Reserved4[3]; + UCHAR Channel4; +} DMA_PAGE, *PDMA_PAGE; + +/* + * DMA Channel Mask Register Structure + * + * MSB LSB + * x x x x x x x x + * ------------------- - ----- + * | | | 00 - Select channel 0 mask bit + * | | \---- 01 - Select channel 1 mask bit + * | | 10 - Select channel 2 mask bit + * | | 11 - Select channel 3 mask bit + * | | + * | \---------- 0 - Clear mask bit + * | 1 - Set mask bit + * | + * \----------------------- xx - Reserved + */ + +typedef struct _DMA_CHANNEL_MASK +{ + UCHAR Channel: 2; + UCHAR SetMask: 1; + UCHAR Reserved: 5; +} DMA_CHANNEL_MASK, *PDMA_CHANNEL_MASK; + +/* + * DMA Mask Register Structure + * + * MSB LSB + * x x x x x x x x + * \---/ - - ----- ----- + * | | | | | 00 - Channel 0 select + * | | | | \---- 01 - Channel 1 select + * | | | | 10 - Channel 2 select + * | | | | 11 - Channel 3 select + * | | | | + * | | | | 00 - Verify transfer + * | | | \------------ 01 - Write transfer + * | | | 10 - Read transfer + * | | | + * | | \-------------------- 0 - Autoinitialized + * | | 1 - Non-autoinitialized + * | | + * | \------------------------ 0 - Address increment select + * | + * | 00 - Demand mode + * \------------------------------ 01 - Single mode + * 10 - Block mode + * 11 - Cascade mode + */ + +typedef union _DMA_MODE +{ + struct + { + UCHAR Channel: 2; + UCHAR TransferType: 2; + UCHAR AutoInitialize: 1; + UCHAR AddressDecrement: 1; + UCHAR RequestMode: 2; + }; + UCHAR Byte; +} DMA_MODE, *PDMA_MODE; + +/* + * DMA Extended Mode Register Structure + * + * MSB LSB + * x x x x x x x x + * - - ----- ----- ----- + * | | | | | 00 - Channel 0 select + * | | | | \---- 01 - Channel 1 select + * | | | | 10 - Channel 2 select + * | | | | 11 - Channel 3 select + * | | | | + * | | | | 00 - 8-bit I/O, by bytes + * | | | \------------ 01 - 16-bit I/O, by words, address shifted + * | | | 10 - 32-bit I/O, by bytes + * | | | 11 - 16-bit I/O, by bytes + * | | | + * | | \---------------------- 00 - Compatible + * | | 01 - Type A + * | | 10 - Type B + * | | 11 - Burst + * | | + * | \---------------------------- 0 - Terminal Count is Output + * | + * \---------------------------------0 - Disable Stop Register + * 1 - Enable Stop Register + */ + +typedef union _DMA_EXTENDED_MODE +{ + struct + { + UCHAR ChannelNumber: 2; + UCHAR TransferSize: 2; + UCHAR TimingMode: 2; + UCHAR TerminalCountIsOutput: 1; + UCHAR EnableStopRegister: 1; + }; + UCHAR Byte; +} DMA_EXTENDED_MODE, *PDMA_EXTENDED_MODE; + +/* DMA Extended Mode Register Transfer Sizes */ +#define B_8BITS 0 +#define W_16BITS 1 +#define B_32BITS 2 +#define B_16BITS 3 + +/* DMA Extended Mode Register Timing */ +#define COMPATIBLE_TIMING 0 +#define TYPE_A_TIMING 1 +#define TYPE_B_TIMING 2 +#define BURST_TIMING 3 + +/* Channel Stop Registers for each Channel */ +typedef struct _DMA_CHANNEL_STOP +{ + UCHAR ChannelLow; + UCHAR ChannelMid; + UCHAR ChannelHigh; + UCHAR Reserved; +} DMA_CHANNEL_STOP, *PDMA_CHANNEL_STOP; + +/* Transfer Types */ +#define VERIFY_TRANSFER 0x00 +#define READ_TRANSFER 0x01 +#define WRITE_TRANSFER 0x02 + +/* Request Modes */ +#define DEMAND_REQUEST_MODE 0x00 +#define SINGLE_REQUEST_MODE 0x01 +#define BLOCK_REQUEST_MODE 0x02 +#define CASCADE_REQUEST_MODE 0x03 + +#define DMA_SETMASK 4 +#define DMA_CLEARMASK 0 +#define DMA_READ 4 +#define DMA_WRITE 8 +#define DMA_SINGLE_TRANSFER 0x40 +#define DMA_AUTO_INIT 0x10 + +typedef struct _DMA1_ADDRESS_COUNT +{ + UCHAR DmaBaseAddress; + UCHAR DmaBaseCount; +} DMA1_ADDRESS_COUNT, *PDMA1_ADDRESS_COUNT; + +typedef struct _DMA2_ADDRESS_COUNT +{ + UCHAR DmaBaseAddress; + UCHAR Reserved1; + UCHAR DmaBaseCount; + UCHAR Reserved2; +} DMA2_ADDRESS_COUNT, *PDMA2_ADDRESS_COUNT; + +typedef struct _DMA1_CONTROL +{ + DMA1_ADDRESS_COUNT DmaAddressCount[4]; + UCHAR DmaStatus; + UCHAR DmaRequest; + UCHAR SingleMask; + UCHAR Mode; + UCHAR ClearBytePointer; + UCHAR MasterClear; + UCHAR ClearMask; + UCHAR AllMask; +} DMA1_CONTROL, *PDMA1_CONTROL; + +typedef struct _DMA2_CONTROL +{ + DMA2_ADDRESS_COUNT DmaAddressCount[4]; + UCHAR DmaStatus; + UCHAR Reserved1; + UCHAR DmaRequest; + UCHAR Reserved2; + UCHAR SingleMask; + UCHAR Reserved3; + UCHAR Mode; + UCHAR Reserved4; + UCHAR ClearBytePointer; + UCHAR Reserved5; + UCHAR MasterClear; + UCHAR Reserved6; + UCHAR ClearMask; + UCHAR Reserved7; + UCHAR AllMask; + UCHAR Reserved8; +} DMA2_CONTROL, *PDMA2_CONTROL; + +/* This structure defines the I/O Map of the 82537 controller. */ +typedef struct _EISA_CONTROL +{ + /* DMA Controller 1 */ + DMA1_CONTROL DmaController1; /* 00h-0Fh */ + UCHAR Reserved1[16]; /* 0Fh-1Fh */ + + /* Interrupt Controller 1 (PIC) */ + UCHAR Pic1Operation; /* 20h */ + UCHAR Pic1Interrupt; /* 21h */ + UCHAR Reserved2[30]; /* 22h-3Fh */ + + /* Timer */ + UCHAR TimerCounter; /* 40h */ + UCHAR TimerMemoryRefresh; /* 41h */ + UCHAR Speaker; /* 42h */ + UCHAR TimerOperation; /* 43h */ + UCHAR TimerMisc; /* 44h */ + UCHAR Reserved3[2]; /* 45-46h */ + UCHAR TimerCounterControl; /* 47h */ + UCHAR TimerFailSafeCounter; /* 48h */ + UCHAR Reserved4; /* 49h */ + UCHAR TimerCounter2; /* 4Ah */ + UCHAR TimerOperation2; /* 4Bh */ + UCHAR Reserved5[20]; /* 4Ch-5Fh */ + + /* NMI / Keyboard / RTC */ + UCHAR Keyboard; /* 60h */ + UCHAR NmiStatus; /* 61h */ + UCHAR Reserved6[14]; /* 62h-6Fh */ + UCHAR NmiEnable; /* 70h */ + UCHAR Reserved7[15]; /* 71h-7Fh */ + + /* DMA Page Registers Controller 1 */ + DMA_PAGE DmaController1Pages; /* 80h-8Fh */ + UCHAR Reserved8[16]; /* 90h-9Fh */ + + /* Interrupt Controller 2 (PIC) */ + UCHAR Pic2Operation; /* 0A0h */ + UCHAR Pic2Interrupt; /* 0A1h */ + UCHAR Reserved9[30]; /* 0A2h-0BFh */ + + /* DMA Controller 2 */ + DMA1_CONTROL DmaController2; /* 0C0h-0CFh */ + + /* System Reserved Ports */ + UCHAR SystemReserved[816]; /* 0D0h-3FFh */ + + /* Extended DMA Registers, Controller 1 */ + UCHAR DmaHighByteCount1[8]; /* 400h-407h */ + UCHAR Reserved10[2]; /* 408h-409h */ + UCHAR DmaChainMode1; /* 40Ah */ + UCHAR DmaExtendedMode1; /* 40Bh */ + UCHAR DmaBufferControl; /* 40Ch */ + UCHAR Reserved11[84]; /* 40Dh-460h */ + UCHAR ExtendedNmiControl; /* 461h */ + UCHAR NmiCommand; /* 462h */ + UCHAR Reserved12; /* 463h */ + UCHAR BusMaster; /* 464h */ + UCHAR Reserved13[27]; /* 465h-47Fh */ + + /* DMA Page Registers Controller 2 */ + DMA_PAGE DmaController2Pages; /* 480h-48Fh */ + UCHAR Reserved14[48]; /* 490h-4BFh */ + + /* Extended DMA Registers, Controller 2 */ + UCHAR DmaHighByteCount2[16]; /* 4C0h-4CFh */ + + /* Edge/Level Control Registers */ + UCHAR Pic1EdgeLevel; /* 4D0h */ + UCHAR Pic2EdgeLevel; /* 4D1h */ + UCHAR Reserved15[2]; /* 4D2h-4D3h */ + + /* Extended DMA Registers, Controller 2 */ + UCHAR DmaChainMode2; /* 4D4h */ + UCHAR Reserved16; /* 4D5h */ + UCHAR DmaExtendedMode2; /* 4D6h */ + UCHAR Reserved17[9]; /* 4D7h-4DFh */ + + /* DMA Stop Registers */ + DMA_CHANNEL_STOP DmaChannelStop[8]; /* 4E0h-4FFh */ +} EISA_CONTROL, *PEISA_CONTROL; + +typedef struct _MAP_REGISTER_ENTRY +{ + PVOID VirtualAddress; + PHYSICAL_ADDRESS PhysicalAddress; + ULONG Counter; +} MAP_REGISTER_ENTRY, *PMAP_REGISTER_ENTRY; + +struct _ADAPTER_OBJECT { + /* + * New style DMA object definition. The fact that it is at the beginning + * of the ADAPTER_OBJECT structure allows us to easily implement the + * fallback implementation of IoGetDmaAdapter. + */ + DMA_ADAPTER DmaHeader; + + /* + * For normal adapter objects pointer to master adapter that takes care + * of channel allocation. For master adapter set to NULL. + */ + struct _ADAPTER_OBJECT *MasterAdapter; + + ULONG MapRegistersPerChannel; + PVOID AdapterBaseVa; + PMAP_REGISTER_ENTRY MapRegisterBase; + + ULONG NumberOfMapRegisters; + ULONG CommittedMapRegisters; + + PWAIT_CONTEXT_BLOCK CurrentWcb; + KDEVICE_QUEUE ChannelWaitQueue; + PKDEVICE_QUEUE RegisterWaitQueue; + LIST_ENTRY AdapterQueue; + KSPIN_LOCK SpinLock; + PRTL_BITMAP MapRegisters; + PUCHAR PagePort; + UCHAR ChannelNumber; + UCHAR AdapterNumber; + USHORT DmaPortAddress; + DMA_MODE AdapterMode; + BOOLEAN NeedsMapRegisters; + BOOLEAN MasterDevice; + BOOLEAN Width16Bits; + BOOLEAN ScatterGather; + BOOLEAN IgnoreCount; + BOOLEAN Dma32BitAddresses; + BOOLEAN Dma64BitAddresses; + LIST_ENTRY AdapterList; +} ADAPTER_OBJECT; + +typedef struct _GROW_WORK_ITEM { + WORK_QUEUE_ITEM WorkQueueItem; + PADAPTER_OBJECT AdapterObject; + ULONG NumberOfMapRegisters; +} GROW_WORK_ITEM, *PGROW_WORK_ITEM; + +#define MAP_BASE_SW_SG 1 + +PADAPTER_OBJECT STDCALL +HalpDmaAllocateMasterAdapter(VOID); + +VOID STDCALL +HalPutDmaAdapter( + PADAPTER_OBJECT AdapterObject); + +ULONG STDCALL +HalpDmaGetDmaAlignment( + PADAPTER_OBJECT AdapterObject); + +NTSTATUS STDCALL +IoAllocateAdapterChannel( + IN PADAPTER_OBJECT AdapterObject, + IN PDEVICE_OBJECT DeviceObject, + IN ULONG NumberOfMapRegisters, + IN PDRIVER_CONTROL ExecutionRoutine, + IN PVOID Context); + +#endif /* HALDMA_H */ diff --git a/reactos/hal/halx86/include/halp.h b/reactos/hal/halx86/include/halp.h index 2e5c168d83d..dbee3f29ba8 100644 --- a/reactos/hal/halx86/include/halp.h +++ b/reactos/hal/halx86/include/halp.h @@ -40,358 +40,6 @@ PVOID HalpMapPhysMemory(ULONG PhysAddr, ULONG Size); /* Non-generic initialization */ VOID HalpInitPhase0 (PLOADER_PARAMETER_BLOCK LoaderBlock); -/* DMA Page Register Structure - 080 DMA RESERVED - 081 DMA Page Register (channel 2) - 082 DMA Page Register (channel 3) - 083 DMA Page Register (channel 1) - 084 DMA RESERVED - 085 DMA RESERVED - 086 DMA RESERVED - 087 DMA Page Register (channel 0) - 088 DMA RESERVED - 089 PS/2-DMA Page Register (channel 6) - 08A PS/2-DMA Page Register (channel 7) - 08B PS/2-DMA Page Register (channel 5) - 08C PS/2-DMA RESERVED - 08D PS/2-DMA RESERVED - 08E PS/2-DMA RESERVED - 08F PS/2-DMA Page Register (channel 4) -*/ - typedef struct _DMA_PAGE{ - UCHAR Reserved1; - UCHAR Channel2; - UCHAR Channel3; - UCHAR Channel1; - UCHAR Reserved2[3]; - UCHAR Channel0; - UCHAR Reserved3; - UCHAR Channel6; - UCHAR Channel7; - UCHAR Channel5; - UCHAR Reserved4[3]; - UCHAR Channel4; -} DMA_PAGE, *PDMA_PAGE; - -/* DMA Channel Mask Register Structure - -MSB LSB - x x x x x x x x - ------------------- - ----- - | | | 00 - Select channel 0 mask bit - | | \---- 01 - Select channel 1 mask bit - | | 10 - Select channel 2 mask bit - | | 11 - Select channel 3 mask bit - | | - | \---------- 0 - Clear mask bit - | 1 - Set mask bit - | - \----------------------- xx - Reserved -*/ -typedef struct _DMA_CHANNEL_MASK { - UCHAR Channel : 2; - UCHAR SetMask : 1; - UCHAR Reserved : 5; -} DMA_CHANNEL_MASK, *PDMA_CHANNEL_MASK; - -/* DMA Mask Register Structure - - MSB LSB - x x x x x x x x - \---/ - - ----- ----- - | | | | | 00 - Channel 0 select - | | | | \---- 01 - Channel 1 select - | | | | 10 - Channel 2 select - | | | | 11 - Channel 3 select - | | | | - | | | | 00 - Verify transfer - | | | \------------ 01 - Write transfer - | | | 10 - Read transfer - | | | - | | \-------------------- 0 - Autoinitialized - | | 1 - Non-autoinitialized - | | - | \------------------------ 0 - Address increment select - | - | 00 - Demand mode - \------------------------------ 01 - Single mode - 10 - Block mode - 11 - Cascade mode -*/ -typedef struct _DMA_MODE { - UCHAR Channel : 2; - UCHAR TransferType : 2; - UCHAR AutoInitialize : 1; - UCHAR AddressDecrement : 1; - UCHAR RequestMode : 2; -} DMA_MODE, *PDMA_MODE; - - -/* DMA Extended Mode Register Structure - - MSB LSB - x x x x x x x x - - - ----- ----- ----- - | | | | | 00 - Channel 0 select - | | | | \---- 01 - Channel 1 select - | | | | 10 - Channel 2 select - | | | | 11 - Channel 3 select - | | | | - | | | | 00 - 8-bit I/O, by bytes - | | | \------------ 01 - 16-bit I/O, by words, address shifted - | | | 10 - 32-bit I/O, by bytes - | | | 11 - 16-bit I/O, by bytes - | | | - | | \---------------------- 00 - Compatible - | | 01 - Type A - | | 10 - Type B - | | 11 - Burst - | | - | \---------------------------- 0 - Terminal Count is Output - | - \---------------------------------0 - Disable Stop Register - 1 - Enable Stop Register -*/ -typedef struct _DMA_EXTENDED_MODE { - UCHAR ChannelNumber : 2; - UCHAR TransferSize : 2; - UCHAR TimingMode : 2; - UCHAR TerminalCountIsOutput : 1; - UCHAR EnableStopRegister : 1; -}DMA_EXTENDED_MODE, *PDMA_EXTENDED_MODE; - -/* DMA Extended Mode Register Transfer Sizes */ -#define B_8BITS 0 -#define W_16BITS 1 -#define B_32BITS 2 -#define B_16BITS 3 - -/* DMA Extended Mode Register Timing */ -#define COMPATIBLE_TIMING 0 -#define TYPE_A_TIMING 1 -#define TYPE_B_TIMING 2 -#define BURST_TIMING 3 - -/* Channel Stop Registers for each Channel */ -typedef struct _DMA_CHANNEL_STOP { - UCHAR ChannelLow; - UCHAR ChannelMid; - UCHAR ChannelHigh; - UCHAR Reserved; -} DMA_CHANNEL_STOP, *PDMA_CHANNEL_STOP; - -/* Transfer Types */ -#define VERIFY_TRANSFER 0x00 -#define READ_TRANSFER 0x01 -#define WRITE_TRANSFER 0x02 - -/* Request Modes */ -#define DEMAND_REQUEST_MODE 0x00 -#define SINGLE_REQUEST_MODE 0x01 -#define BLOCK_REQUEST_MODE 0x02 -#define CASCADE_REQUEST_MODE 0x03 - -#define DMA_SETMASK 4 -#define DMA_CLEARMASK 0 -#define DMA_READ 4 -#define DMA_WRITE 8 -#define DMA_SINGLE_TRANSFER 0x40 -#define DMA_AUTO_INIT 0x10 - -typedef struct _DMA1_ADDRESS_COUNT { - UCHAR DmaBaseAddress; - UCHAR DmaBaseCount; -} DMA1_ADDRESS_COUNT, *PDMA1_ADDRESS_COUNT; - -typedef struct _DMA2_ADDRESS_COUNT { - UCHAR DmaBaseAddress; - UCHAR Reserved1; - UCHAR DmaBaseCount; - UCHAR Reserved2; -} DMA2_ADDRESS_COUNT, *PDMA2_ADDRESS_COUNT; - -typedef struct _DMA1_CONTROL { - DMA1_ADDRESS_COUNT DmaAddressCount[4]; - UCHAR DmaStatus; - UCHAR DmaRequest; - UCHAR SingleMask; - UCHAR Mode; - UCHAR ClearBytePointer; - UCHAR MasterClear; - UCHAR ClearMask; - UCHAR AllMask; -} DMA1_CONTROL, *PDMA1_CONTROL; - -typedef struct _DMA2_CONTROL { - DMA2_ADDRESS_COUNT DmaAddressCount[4]; - UCHAR DmaStatus; - UCHAR Reserved1; - UCHAR DmaRequest; - UCHAR Reserved2; - UCHAR SingleMask; - UCHAR Reserved3; - UCHAR Mode; - UCHAR Reserved4; - UCHAR ClearBytePointer; - UCHAR Reserved5; - UCHAR MasterClear; - UCHAR Reserved6; - UCHAR ClearMask; - UCHAR Reserved7; - UCHAR AllMask; - UCHAR Reserved8; -} DMA2_CONTROL, *PDMA2_CONTROL; - -/* This Structure Defines the I/O Map of the 82537 Controller - I've only defined the registers which are likely to be useful to us */ -typedef struct _EISA_CONTROL { - /* DMA Controller 1 */ - DMA1_CONTROL DmaController1; /* 00h-0Fh */ - UCHAR Reserved1[16]; /* 0Fh-1Fh */ - - /* Interrupt Controller 1 (PIC) */ - UCHAR Pic1Operation; /* 20h */ - UCHAR Pic1Interrupt; /* 21h */ - UCHAR Reserved2[30]; /* 22h-3Fh */ - - /* Timer */ - UCHAR TimerCounter; /* 40h */ - UCHAR TimerMemoryRefresh; /* 41h */ - UCHAR Speaker; /* 42h */ - UCHAR TimerOperation; /* 43h */ - UCHAR TimerMisc; /* 44h */ - UCHAR Reserved3[2]; /* 45-46h */ - UCHAR TimerCounterControl; /* 47h */ - UCHAR TimerFailSafeCounter; /* 48h */ - UCHAR Reserved4; /* 49h */ - UCHAR TimerCounter2; /* 4Ah */ - UCHAR TimerOperation2; /* 4Bh */ - UCHAR Reserved5[20]; /* 4Ch-5Fh */ - - /* NMI / Keyboard / RTC */ - UCHAR Keyboard; /* 60h */ - UCHAR NmiStatus; /* 61h */ - UCHAR Reserved6[14]; /* 62h-6Fh */ - UCHAR NmiEnable; /* 70h */ - UCHAR Reserved7[15]; /* 71h-7Fh */ - - /* DMA Page Registers Controller 1 */ - DMA_PAGE DmaController1Pages; /* 80h-8Fh */ - UCHAR Reserved8[16]; /* 90h-9Fh */ - - /* Interrupt Controller 2 (PIC) */ - UCHAR Pic2Operation; /* 0A0h */ - UCHAR Pic2Interrupt; /* 0A1h */ - UCHAR Reserved9[30]; /* 0A2h-0BFh */ - - /* DMA Controller 2 */ - DMA1_CONTROL DmaController2; /* 0C0h-0CFh */ - - /* System Reserved Ports */ - UCHAR SystemReserved[816]; /* 0D0h-3FFh */ - - /* Extended DMA Registers, Controller 1 */ - UCHAR DmaHighByteCount1[8]; /* 400h-407h */ - UCHAR Reserved10[2]; /* 408h-409h */ - UCHAR DmaChainMode1; /* 40Ah */ - UCHAR DmaExtendedMode1; /* 40Bh */ - UCHAR DmaBufferControl; /* 40Ch */ - UCHAR Reserved11[84]; /* 40Dh-460h */ - UCHAR ExtendedNmiControl; /* 461h */ - UCHAR NmiCommand; /* 462h */ - UCHAR Reserved12; /* 463h */ - UCHAR BusMaster; /* 464h */ - UCHAR Reserved13[27]; /* 465h-47Fh */ - - /* DMA Page Registers Controller 2 */ - DMA_PAGE DmaController2Pages; /* 480h-48Fh */ - UCHAR Reserved14[48]; /* 490h-4BFh */ - - /* Extended DMA Registers, Controller 2 */ - UCHAR DmaHighByteCount2[16]; /* 4C0h-4CFh */ - - /* Edge/Level Control Registers */ - UCHAR Pic1EdgeLevel; /* 4D0h */ - UCHAR Pic2EdgeLevel; /* 4D1h */ - UCHAR Reserved15[2]; /* 4D2h-4D3h */ - - /* Extended DMA Registers, Controller 2 */ - UCHAR DmaChainMode2; /* 4D4h */ - UCHAR Reserved16; /* 4D5h */ - UCHAR DmaExtendedMode2; /* 4D6h */ - UCHAR Reserved17[9]; /* 4D7h-4DFh */ - - /* DMA Stop Registers */ - DMA_CHANNEL_STOP DmaChannelStop[8]; /* 4E0h-4FFh */ -} EISA_CONTROL, *PEISA_CONTROL; - -extern ULONG HalpEisaDma; -extern PADAPTER_OBJECT MasterAdapter; - -ULONG HalpEisaDma; -PADAPTER_OBJECT MasterAdapter; - -/* - * ADAPTER_OBJECT - Track a busmaster DMA adapter and its associated resources - * - * NOTES: - * - I've updated this to the Windows Object Defintion. - */ -struct _ADAPTER_OBJECT { - DMA_ADAPTER DmaHeader; - struct _ADAPTER_OBJECT *MasterAdapter; - ULONG MapRegistersPerChannel; - PVOID AdapterBaseVa; - PVOID MapRegisterBase; - ULONG NumberOfMapRegisters; - ULONG CommittedMapRegisters; - PWAIT_CONTEXT_BLOCK CurrentWcb; - KDEVICE_QUEUE ChannelWaitQueue; - PKDEVICE_QUEUE RegisterWaitQueue; - LIST_ENTRY AdapterQueue; - ULONG SpinLock; - PRTL_BITMAP MapRegisters; - PUCHAR PagePort; - UCHAR ChannelNumber; - UCHAR AdapterNumber; - USHORT DmaPortAddress; - union { - DMA_MODE AdapterMode; - UCHAR AdapterModeByte; - }; - BOOLEAN NeedsMapRegisters; - BOOLEAN MasterDevice; - UCHAR Width16Bits; - UCHAR ScatterGather; - UCHAR IgnoreCount; - UCHAR Dma32BitAddresses; - UCHAR Dma64BitAddresses; - BOOLEAN LegacyAdapter; - LIST_ENTRY AdapterList; -} ADAPTER_OBJECT; - -/* -struct _ADAPTER_OBJECT { - INTERFACE_TYPE InterfaceType; - BOOLEAN Master; - int Channel; - PVOID PagePort; - PVOID CountPort; - PVOID OffsetPort; - KSPIN_LOCK SpinLock; - PVOID Buffer; - BOOLEAN Inuse; - ULONG AvailableMapRegisters; - PVOID MapRegisterBase; - ULONG AllocatedMapRegisters; - PWAIT_CONTEXT_BLOCK WaitContextBlock; - KDEVICE_QUEUE DeviceQueue; - BOOLEAN ScatterGather; - BOOLEAN DemandMode; - BOOLEAN AutoInitialize; -}; -*/ - /* sysinfo.c */ NTSTATUS STDCALL HalpQuerySystemInformation(IN HAL_QUERY_INFORMATION_CLASS InformationClass, @@ -399,7 +47,6 @@ HalpQuerySystemInformation(IN HAL_QUERY_INFORMATION_CLASS InformationClass, IN OUT PVOID Buffer, OUT PULONG ReturnedLength); - /* Non-standard functions */ VOID STDCALL HalReleaseDisplayOwnership();