From 3c640340fe682efe2f139bf5618f1f5c87fd4942 Mon Sep 17 00:00:00 2001 From: Vizzini Date: Thu, 23 Oct 2003 09:03:51 +0000 Subject: [PATCH] Fixes to get slave DMA working again, as well as more architectural changes that bring us closer to the Windows 2000 DMA model. adapter.c: significantly re-worked and heavily commented hal.h: more additions to ADAPTER_OBJECT; more pending dma.c: added initialization of ADAPTER_OBJECT ndis/io.c: minor DMA changes and addition of fixmes svn path=/trunk/; revision=6411 --- reactos/drivers/net/ndis/ndis/io.c | 11 +- reactos/hal/halx86/adapter.c | 246 ++++++++++++++++++++--------- reactos/hal/halx86/dma.c | 18 ++- reactos/hal/halx86/include/hal.h | 4 +- 4 files changed, 194 insertions(+), 85 deletions(-) diff --git a/reactos/drivers/net/ndis/ndis/io.c b/reactos/drivers/net/ndis/ndis/io.c index e08b8995a93..802e501b183 100644 --- a/reactos/drivers/net/ndis/ndis/io.c +++ b/reactos/drivers/net/ndis/ndis/io.c @@ -363,9 +363,8 @@ NdisMAllocateMapRegisters( Description.Version = DEVICE_DESCRIPTION_VERSION; Description.Master = TRUE; /* implied by calling this function */ - Description.ScatterGather = TRUE; /* All BM DMA are S/G (ms seems to do this) */ + Description.ScatterGather = TRUE; /* XXX UNTRUE: All BM DMA are S/G (ms seems to do this) */ Description.Dma32BitAddresses = DmaSize; - Description.Dma64BitAddresses = 0; /* FIXME figure this out based on input */ Description.BusNumber = Adapter->BusNumber; Description.InterfaceType = Adapter->BusType; Description.DmaChannel = DmaChannel; @@ -373,6 +372,7 @@ NdisMAllocateMapRegisters( if(Adapter->NdisMiniportBlock.AdapterType == Isa) { + /* system dma */ if(DmaChannel < 4) Description.DmaWidth = Width8Bits; else @@ -382,7 +382,6 @@ NdisMAllocateMapRegisters( } else if(Adapter->NdisMiniportBlock.AdapterType == PCIBus) { - /* Width and Speed are automatically determined on PCI */ if(DmaSize == NDIS_DMA_64BITS) Description.Dma64BitAddresses = TRUE; else @@ -394,12 +393,6 @@ NdisMAllocateMapRegisters( ASSERT(0); } - Description.Reserved1 = 0; /* Must Be Zero (ref DDK) */ - Description.DemandMode = 0; /* unused due to bus master */ - Description.AutoInitialize = 0; /* unused due to bus master */ - Description.IgnoreCount = 0; /* unused due to bus master */ - Description.DmaPort = 0; /* unused due to bus type */ - AvailableMapRegisters = MapRegistersRequired; AdapterObject = HalGetAdapter(&Description, &AvailableMapRegisters); diff --git a/reactos/hal/halx86/adapter.c b/reactos/hal/halx86/adapter.c index 371ddc1edec..e35d922d3aa 100644 --- a/reactos/hal/halx86/adapter.c +++ b/reactos/hal/halx86/adapter.c @@ -1,4 +1,4 @@ -/* $Id: adapter.c,v 1.6 2003/10/20 06:03:28 vizzini Exp $ +/* $Id: adapter.c,v 1.7 2003/10/23 09:03:51 vizzini Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -22,6 +22,7 @@ /* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */ + NTSTATUS STDCALL HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject, PWAIT_CONTEXT_BLOCK WaitContextBlock, @@ -39,76 +40,77 @@ HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject, * STATUS_SUCCESS in all other cases, including if the callbacak had * to be queued for later delivery * NOTES: - * - Map registers don't exist on X86 so we can just call the callback - * with a map register base of 0 * - the ADAPTER_OBJECT struct is undocumented; please make copious - * notes here if anything is changed or improved since there is - * no other documentation for this routine or its data structures - * - The original implementation of this function allocated a contiguous - * physical buffer the size of NumberOfMapRegisters * PAGE_SIZE, which - * is unnecessary and very expensive (contiguous memory is rare). It - * also leaked in some circumstances (drivers allocate and sometimes - * don't free map registers) + * notes in hal.h if anything is changed or improved since there is + * no other documentation for this data structure * BUGS: - * - This routine should check whether or not map registers are needed - * (rather than assuming they're not) and allocate them on platforms - * that support them. + * - it's possible that some of this code is in the wrong place + * - there are many unhandled cases */ { -#if 0 - KIRQL OldIrql; - PVOID Buffer; - int ret; LARGE_INTEGER MaxAddress; + IO_ALLOCATION_ACTION Retval; + /* 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->DeviceQueue, (PKDEVICE_QUEUE_ENTRY)WaitContextBlock)) + return STATUS_SUCCESS; + + /* 24-bit max address due to 16-bit dma controllers */ MaxAddress.QuadPart = 0x1000000; /* why 64K alignment? */ - Buffer = MmAllocateContiguousAlignedMemory( NumberOfMapRegisters * PAGE_SIZE, - MaxAddress, - 0x10000 ); - if( !Buffer ) + /* + * 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. + */ + AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory( + NumberOfMapRegisters * PAGE_SIZE, MaxAddress, 0x10000 ); + + if(!AdapterObject->MapRegisterBase) return STATUS_INSUFFICIENT_RESOURCES; - KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql ); - if( AdapterObject->Inuse ) - { - // someone is already using it, we need to wait - // create a wait block, and add it to the chain - UNIMPLEMENTED; - } - else { - AdapterObject->Inuse = TRUE; - KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql ); - ret = ExecutionRoutine( DeviceObject, - NULL, - Buffer, - WaitContextBlock->DriverContext ); - KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql ); - if( ret == DeallocateObject ) - { - MmFreeContiguousMemory( Buffer ); - AdapterObject->Inuse = FALSE; - } - else AdapterObject->Buffer = Buffer; - } - KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql ); -#endif - AdapterObject->MapRegisterBase = 0; - AdapterObject->AllocatedMapRegisters = 0; + AdapterObject->AllocatedMapRegisters = NumberOfMapRegisters; - IO_ALLOCATION_ACTION Retval = ExecutionRoutine(WaitContextBlock->DeviceObject, - WaitContextBlock->CurrentIrp, 0, WaitContextBlock->DeviceContext); + /* call the client's AdapterControl callback with its map registers and context */ + Retval = ExecutionRoutine(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp, + AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext); - if(Retval == DeallocateObject) - IoFreeAdapterChannel(AdapterObject); + /* + * 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) - AdapterObject->AllocatedMapRegisters = 0; + { + /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */ + AdapterObject->AllocatedMapRegisters = 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 STDCALL IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject, PMDL Mdl, @@ -116,44 +118,124 @@ IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject, 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. + */ { - // if this was a read from device, copy data back to caller buffer, otherwise, do nothing - if( !WriteToDevice ) - memcpy( (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )), MapRegisterBase, Length ); + /* FIXME we don't have ASSERT */ + //ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + /* this can happen if the card supports scatter/gather */ + if(!MapRegisterBase) + return TRUE; + + 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. + */ { - KIRQL OldIrql; - - KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql ); - if( AdapterObject->Inuse == FALSE ) + LARGE_INTEGER MaxAddress; + PWAIT_CONTEXT_BLOCK WaitContextBlock; + IO_ALLOCATION_ACTION Retval; + + while(1) { - DbgPrint( "Attempting to IoFreeAdapterChannel on a channel not in use\n" ); - KEBUGCHECK(0); + /* To keep map registers, call here with the following set to 0 */ + if(AdapterObject->AllocatedMapRegisters) + IoFreeMapRegisters(AdapterObject, AdapterObject->MapRegisterBase, AdapterObject->AllocatedMapRegisters); + + if(!(WaitContextBlock = (PWAIT_CONTEXT_BLOCK)KeRemoveDeviceQueue(&AdapterObject->DeviceQueue))) + break; + + /* + * the following should really be done elsewhere since this + * function really can't return an error code. FIXME. + */ + + /* 24-bit max address due to 16-bit dma controllers */ + MaxAddress.QuadPart = 0x1000000; + + AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory( + WaitContextBlock->NumberOfMapRegisters * PAGE_SIZE, MaxAddress, 0x10000 ); + + 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->AllocatedMapRegisters = 0; + } } - AdapterObject->Inuse = FALSE; - if( AdapterObject->Buffer ) - { - MmFreeContiguousMemory( AdapterObject->Buffer ); - AdapterObject->Buffer = 0; - } - KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql ); } - + VOID STDCALL IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject, PVOID MapRegisterBase, 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 + */ { - UNIMPLEMENTED; + if( AdapterObject->AllocatedMapRegisters ) + { + MmFreeContiguousMemory(AdapterObject->MapRegisterBase); + AdapterObject->MapRegisterBase = 0; + } } - + PHYSICAL_ADDRESS STDCALL IoMapTransfer (PADAPTER_OBJECT AdapterObject, PMDL Mdl, @@ -161,6 +243,24 @@ IoMapTransfer (PADAPTER_OBJECT AdapterObject, PVOID CurrentVa, PULONG Length, 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 + * Mdl: locked-down user buffer to DMA in to or out of + * MapRegisterBase: handle to map registers to use for this dma + * CurrentVa: index into Mdl to transfer into/out of + * Length: length of transfer in/out + * 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; // program up the dma controller, and return diff --git a/reactos/hal/halx86/dma.c b/reactos/hal/halx86/dma.c index a2e40615b88..87ae7e64a96 100644 --- a/reactos/hal/halx86/dma.c +++ b/reactos/hal/halx86/dma.c @@ -1,4 +1,4 @@ -/* $Id: dma.c,v 1.7 2003/09/11 11:45:28 ekohl Exp $ +/* $Id: dma.c,v 1.8 2003/10/23 09:03:51 vizzini Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -16,6 +16,9 @@ #include #include +/* XXX This initialization is out of date - ADAPTER_OBJECT has changed */ +/* NOTE: The following initializations have to be kept in synch with ADAPTER_OBJECT in hal.h */ +/* FIXME: we need the 16-bit dma channels */ ADAPTER_OBJECT IsaSlaveAdapterObjects[] = { { Isa, FALSE, 0, (PVOID)0x87, (PVOID)0x1, (PVOID)0x0, 0, NULL }, { Isa, FALSE, 1, (PVOID)0x83, (PVOID)0x3, (PVOID)0x2, 0, NULL }, @@ -25,6 +28,8 @@ ADAPTER_OBJECT IsaSlaveAdapterObjects[] = { ADAPTER_OBJECT PciBusMasterAdapterObjects[] = { { PCIBus, TRUE, 0, (PVOID)0, (PVOID)0, (PVOID)0x0, 0, NULL } }; +/* Global flag to tell whether or not the adapter's device queue should be initialized (first call only) */ +BOOLEAN AdaptersInitialized = FALSE; /* FUNCTIONS *****************************************************************/ @@ -112,6 +117,17 @@ HalGetAdapter (PDEVICE_DESCRIPTION DeviceDescription, * Figure out what to do with the commented-out cases */ { + /* TODO: find a better home for this */ + if(!AdaptersInitialized) + { + KeInitializeDeviceQueue(&PciBusMasterAdapterObjects[0].DeviceQueue); + KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[0].DeviceQueue); + KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[1].DeviceQueue); + KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[2].DeviceQueue); + KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[3].DeviceQueue); + AdaptersInitialized = TRUE; + } + /* Validate parameters in device description, and return a pointer to the adapter object for the requested dma channel */ if( DeviceDescription->Version != DEVICE_DESCRIPTION_VERSION ) diff --git a/reactos/hal/halx86/include/hal.h b/reactos/hal/halx86/include/hal.h index c9e3747ab7c..d6cce42a725 100644 --- a/reactos/hal/halx86/include/hal.h +++ b/reactos/hal/halx86/include/hal.h @@ -57,8 +57,8 @@ struct _ADAPTER_OBJECT { PVOID MapRegisterBase; ULONG AllocatedMapRegisters; PWAIT_CONTEXT_BLOCK WaitContextBlock; - PKDEVICE_QUEUE DeviceQueue; - BOOLEAN UsesPhysicalMapRegisters; + KDEVICE_QUEUE DeviceQueue; + BOOLEAN ScatterGather; }; /* sysinfo.c */