Fixes to get slave DMA working again, as well as more architectural changes
authorVizzini <vizzini@plasmic.com>
Thu, 23 Oct 2003 09:03:51 +0000 (09:03 +0000)
committerVizzini <vizzini@plasmic.com>
Thu, 23 Oct 2003 09:03:51 +0000 (09:03 +0000)
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
reactos/hal/halx86/adapter.c
reactos/hal/halx86/dma.c
reactos/hal/halx86/include/hal.h

index e08b899..802e501 100644 (file)
@@ -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);
 
index 371ddc1..e35d922 100644 (file)
@@ -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 */
 
+\f
 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 )
+
+  AdapterObject->AllocatedMapRegisters = 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)
     {
-      // someone is already using it, we need to wait
-      // create a wait block, and add it to the chain
-      UNIMPLEMENTED;
+      /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */
+      AdapterObject->AllocatedMapRegisters = 0;
+      IoFreeAdapterChannel(AdapterObject);
     }
-  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;
-
-  IO_ALLOCATION_ACTION Retval = ExecutionRoutine(WaitContextBlock->DeviceObject,
-      WaitContextBlock->CurrentIrp, 0, WaitContextBlock->DeviceContext);
-
-  if(Retval == DeallocateObject)
-    IoFreeAdapterChannel(AdapterObject);
-  else if(Retval == DeallocateObjectKeepRegisters)
-    AdapterObject->AllocatedMapRegisters = 0;
+
+  /*
+   * if we don't call IoFreeAdapterChannel, the next device won't get de-queued,
+   * which is what we want.
+   */
 
   return STATUS_SUCCESS;
 }
 
-
+\f
 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;
 }
 
-
+\f
 VOID STDCALL
 IoFreeAdapterChannel (PADAPTER_OBJECT  AdapterObject)
+/*
+ * FUNCTION: frees DMA resources allocated by IoAllocateAdapterChannel
+ * ARGUMENTS:
+ *     AdapterObject: Adapter object with resources to free
+ * NOTES:
+ *     - This function releases the DMA adapter and optionally the map registers
+ *     - After releasing the adapter, it checks the adapter's queue and runs
+ *       each queued device object in series until the queue is empty
+ *     - This is the only way the device queue is emptied.
+ */
 {
-  KIRQL OldIrql;
-  
-  KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
-  if( AdapterObject->Inuse == FALSE )
-    {
-      DbgPrint( "Attempting to IoFreeAdapterChannel on a channel not in use\n" );
-      KEBUGCHECK(0);
-    }
-  AdapterObject->Inuse = FALSE;
-  if( AdapterObject->Buffer )
+  LARGE_INTEGER MaxAddress;
+  PWAIT_CONTEXT_BLOCK WaitContextBlock;
+  IO_ALLOCATION_ACTION Retval;
+
+  while(1)
     {
-      MmFreeContiguousMemory( AdapterObject->Buffer );
-      AdapterObject->Buffer = 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;
+        }
     }
-  KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
 }
 
-
+\f
 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;
+    }
 }
 
-
+\f
 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
index a2e4061..87ae7e6 100644 (file)
@@ -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 <internal/debug.h>
 #include <hal.h>
 
+/* 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 )
index c9e3747..d6cce42 100644 (file)
@@ -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 */