[ros-kernel] DMA patch

Filip Navara xnavara at volny.cz
Mon Jul 19 23:22:14 CEST 2004


Hi,

    after few days of working on the sound support I finally got it 
working up to the state where 3rd party sound drivers should work. I 
tested it only with a pure Sound Blaster 16 driver, but since all the 
NT4 drivers are based on common libraries I guess a lot of these drivers 
will work. In order to get the sound working I had to dig into HAL and 
implement some DMA options. Since I'm not a DMA kind of guy and all the 
info I got was from QEMU sources and 15 minute reading of DMA for 
dummies tutorial I'm posting the patch here for review first. And 
comments are welcome.

Regards,
Filip
-------------- next part --------------
Changelog:
- Implementation of HalReadDmaCounter.
- Cleaned up implementation of HalGetAdapter. Moved the initialization
  stuff from there to HalpInitDma that is called by HalInitSystem.
- Let HalAllocateCommonBuffer allocated 64K aligned memory.
- Change IoMapTransfer to support auto initialize and single transfer mode,
  16-bit DMA and common buffers.
- Stop DMA transfer in IoFlushAdapterBuffers.

Index: halx86/adapter.c
===================================================================
--- halx86/adapter.c	(revision 105)
+++ halx86/adapter.c	(working copy)
@@ -66,7 +66,7 @@
   WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters;
 
   /* returns true if queued, else returns false and sets the queue to busy */
-  if(KeInsertDeviceQueue(&AdapterObject->DeviceQueue, (PKDEVICE_QUEUE_ENTRY)WaitContextBlock))
+  if(KeInsertDeviceQueue(&AdapterObject->DeviceQueue, &WaitContextBlock->WaitQueueEntry))
     return STATUS_SUCCESS;
 
   /* 24-bit max address due to 16-bit dma controllers */
@@ -79,6 +79,10 @@
    * 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.
    */
   AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory( 
       NumberOfMapRegisters * PAGE_SIZE,
@@ -159,6 +163,12 @@
   if(!MapRegisterBase)
     return TRUE;
 
+  /* mask out (disable) the dma channel */
+  if (AdapterObject->Channel < 4)
+    WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)(AdapterObject->Channel | 0x4) );
+  else
+    WRITE_PORT_UCHAR( (PVOID)0xD4, (UCHAR)((AdapterObject->Channel - 4) | 0x4) );
+
   if(WriteToDevice)
     return TRUE;
     
@@ -166,19 +176,6 @@
         (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )), 
       MapRegisterBase, Length );
 
-  /*
-  FIXME: mask off (disable) channel if doing System DMA?
-  
-    From linux:
-    
-    if (dmanr<=3)
-      dma_outb(dmanr | 4,  DMA1_MASK_REG 0x0A) ;
-    else
-      dma_outb((dmanr & 3) | 4,  DMA2_MASK_REG 0x0A);
-
-  */
-  
-
   return TRUE;
 }
 
@@ -304,14 +301,18 @@
  *     - If the controller supports scatter/gather, the copyover should not happen
  */
 {
-	PHYSICAL_ADDRESS Address;
+  PHYSICAL_ADDRESS Address;
+  PVOID MaskReg, ClearReg, ModeReg;
+  UCHAR ModeMask, LengthShift;
+  KIRQL OldIrql;
+
 #if defined(__GNUC__)
-	Address.QuadPart = 0ULL;
+  Address.QuadPart = 0ULL;
 #else
-	Address.QuadPart = 0;
+  Address.QuadPart = 0;
 #endif
 
-	/* Isa System (slave) DMA? */
+  /* Isa System (slave) DMA? */
   if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
   {
 #if 0
@@ -321,39 +322,77 @@
     assert(AdapterObject->Channel != 4);
 #endif
 
+    KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
+
     /*
-    FIXME: Handle case when doing common-buffer System DMA. In this case, the buffer described
-    by MDL is allready phys. contiguous and below 16 mega. Driver makes a one-shot call to 
-    IoMapTransfer during init. to program controller with the common-buffer.
-    */
-    
+     * 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 )
+    if( WriteToDevice && !AdapterObject->AutoInitialize )
     {
       memcpy(MapRegisterBase,
              (char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
 	           *Length );
     }
              
+    // 16-bit DMA
+    if( AdapterObject->Channel >= 4 )
+    {
+      MaskReg = (PVOID)0xD4; ClearReg = (PVOID)0xD8; ModeReg = (PVOID)0xD6;
+      LengthShift = 1;
+    }
+    else
+    {
+      MaskReg = (PVOID)0x0A; ClearReg = (PVOID)0x0C; ModeReg = (PVOID)0x0B;
+      LengthShift = 0;
+    }
+
+    // calculate the mask we will later set to the mode register
+    ModeMask = (AdapterObject->Channel & 3) | ( WriteToDevice ? 0x8 : 0x4 );
+    // FIXME: if not demand mode, which mode to use? 0x40 for single mode
+    if (!AdapterObject->DemandMode)
+      ModeMask |= 0x40;
+    if (AdapterObject->AutoInitialize)
+      ModeMask |= 0x10;
+
     // program up the dma controller, and return
-    Address = MmGetPhysicalAddress( MapRegisterBase );
-    // port 0xA is the dma mask register, or a 0x10 on to the channel number to mask it
-    WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)(AdapterObject->Channel | 0x10));
+    if (!AdapterObject->AutoInitialize)
+      Address = MmGetPhysicalAddress( MapRegisterBase );
+    else
+      Address = MmGetPhysicalAddress( CurrentVa );
+    // disable and select the channel number
+    WRITE_PORT_UCHAR( MaskReg, (UCHAR)((AdapterObject->Channel & 3) | 0x4) );
     // write zero to the reset register
-    WRITE_PORT_UCHAR( (PVOID)0x0C, 0 );
-    // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for non auto initialize
-    WRITE_PORT_UCHAR( (PVOID)0x0B, (UCHAR)(AdapterObject->Channel | ( WriteToDevice ? 0x8 : 0x4 )) );
+    WRITE_PORT_UCHAR( ClearReg, 0 );
+    // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for auto initialize
+    WRITE_PORT_UCHAR( ModeReg, ModeMask);
     // set the 64k page register for the channel
-    WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(((ULONG)Address.QuadPart)>>16) );
+    WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(Address.u.LowPart >> 16) );
     // low, then high address byte, which is always 0 for us, because we have a 64k alligned address
     WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
     WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
     // count is 1 less than length, low then high
-    WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(*Length - 1) );
-    WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length - 1)>>8) );
+    WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length >> LengthShift) - 1) );
+    WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(((*Length >> LengthShift) - 1)>>8) );
     // unmask the channel to let it rip
-    WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)AdapterObject->Channel );
+    WRITE_PORT_UCHAR( MaskReg, AdapterObject->Channel & 3 );
 
+    KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
+
     /* 
     NOTE: Return value should be ignored when doing System DMA.
     Maybe return some more obvious invalid address here (thou returning 
@@ -436,7 +475,7 @@
 
     return MmGetPhysicalAddress(MapRegisterBase);
   }
-  
+
   DPRINT1("IoMapTransfer: Unsupported operation\n");
   KEBUGCHECK(0);
   return Address;
Index: halx86/halinit.c
===================================================================
--- halx86/halinit.c	(revision 1)
+++ halx86/halinit.c	(working copy)
@@ -63,6 +63,7 @@
   else if (BootPhase == 1)
     {
       HalpInitBusHandlers();
+      HalpInitDma();
       HalpCalibrateStallExecution();
 
       /* Enumerate the devices on the motherboard */
Index: halx86/dma.c
===================================================================
--- halx86/dma.c	(revision 105)
+++ halx86/dma.c	(working copy)
@@ -18,21 +18,38 @@
 
 /* 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 },
   { Isa, FALSE, 2, (PVOID)0x81, (PVOID)0x5, (PVOID)0x4, 0, NULL },
-  { Isa, FALSE, 3, (PVOID)0x82, (PVOID)0x7, (PVOID)0x6, 0, NULL } };
+  { Isa, FALSE, 3, (PVOID)0x82, (PVOID)0x7, (PVOID)0x6, 0, NULL },
+  /* 16-bit DMA */
+  { Isa, FALSE, 4, (PVOID)0x8F, (PVOID)0xC2, (PVOID)0xC0, 0, NULL },
+  { Isa, FALSE, 5, (PVOID)0x8B, (PVOID)0xC6, (PVOID)0xC4, 0, NULL },
+  { Isa, FALSE, 6, (PVOID)0x89, (PVOID)0xCA, (PVOID)0xC8, 0, NULL },
+  { Isa, FALSE, 7, (PVOID)0x8A, (PVOID)0xCE, (PVOID)0xCC, 0, NULL } };
 
 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 *****************************************************************/
 
+VOID
+HalpInitDma (VOID)
+{
+  ULONG Index;
+
+  KeInitializeDeviceQueue(&PciBusMasterAdapterObjects[0].DeviceQueue);
+  KeInitializeSpinLock(&PciBusMasterAdapterObjects[0].SpinLock);
+  PciBusMasterAdapterObjects[0].Inuse = FALSE;
+  for (Index = 0; Index < 8; Index++)
+    {
+      KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[Index].DeviceQueue);
+      KeInitializeSpinLock(&IsaSlaveAdapterObjects[Index].SpinLock);
+      IsaSlaveAdapterObjects[Index].Inuse = FALSE;
+    }
+}
+
 PVOID STDCALL
 HalAllocateCommonBuffer (PADAPTER_OBJECT    AdapterObject,
 			 ULONG              Length,
@@ -54,9 +71,11 @@
  *      CacheEnabled is ignored - it's all cache-disabled (like in NT)
  */
 {
-  PHYSICAL_ADDRESS HighestAddress;
+  PHYSICAL_ADDRESS LowestAddress, HighestAddress, BoundryAddressMultiple;
   PVOID BaseAddress;
 
+  LowestAddress.QuadPart = 0;
+  BoundryAddressMultiple.QuadPart = 0;
   HighestAddress.u.HighPart = 0;
   if (AdapterObject->InterfaceType == Isa ||
       (AdapterObject->InterfaceType == MicroChannel && AdapterObject->Master == FALSE))
@@ -68,7 +87,13 @@
       HighestAddress.u.LowPart = 0xFFFFFFFF; /* 32Bit: 4GB address range */
     }
 
-  BaseAddress = MmAllocateContiguousMemory(Length, HighestAddress);
+  BaseAddress = MmAllocateContiguousAlignedMemory(
+      Length,
+      LowestAddress,
+      HighestAddress,
+      BoundryAddressMultiple,
+      MmCached,
+      0x10000 );
   if (!BaseAddress)
     return 0;
 
@@ -114,56 +139,80 @@
  * RETURNS: The allocated adapter object on success
  *          NULL on failure
  * TODO:
- *    Figure out what to do with the commented-out cases
+ *        Honour all the fields in DeviceDescription structure.
  */
 {
-  /* 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;
-    }
+  PADAPTER_OBJECT AdapterObject;
 
   /* Validate parameters in device description, and return a pointer to
      the adapter object for the requested dma channel */
   if( DeviceDescription->Version != DEVICE_DESCRIPTION_VERSION )
     return NULL;
 
-  if (DeviceDescription->InterfaceType == PCIBus)
+  switch (DeviceDescription->InterfaceType)
     {
-      if (DeviceDescription->Master == FALSE)
-	return NULL;
+      case PCIBus:
+        if (DeviceDescription->Master == FALSE)
+          return NULL;
+        return &PciBusMasterAdapterObjects[0];
 
-      return &PciBusMasterAdapterObjects[0];
+      case Isa:
+        /* There are only 8 DMA channels on ISA. */
+        if (DeviceDescription->DmaChannel >= 8)
+          return NULL;
+        /* Channels 1-4 are for 8-bit transfers... */
+        if (DeviceDescription->DmaWidth != Width8Bits &&
+            DeviceDescription->DmaChannel < 4)
+          return NULL;
+        /* ...and the rest is for 16-bit transfers. */
+        if (DeviceDescription->DmaWidth != Width16Bits &&
+            DeviceDescription->DmaChannel >= 4)
+          return NULL;
+        AdapterObject = &IsaSlaveAdapterObjects[DeviceDescription->DmaChannel];
+        AdapterObject->Master = DeviceDescription->Master;
+        AdapterObject->ScatterGather = DeviceDescription->ScatterGather;
+        AdapterObject->AutoInitialize = DeviceDescription->AutoInitialize;
+        AdapterObject->DemandMode = DeviceDescription->DemandMode;
+        AdapterObject->Buffer = 0;
+        /* FIXME: Is this correct? */
+        *NumberOfMapRegisters = 16;
+        return AdapterObject;
+
+      default:
+        /* Unsupported bus. */
+        return NULL;
     }
-
-  /*
-  if( DeviceDescription->Master )
-    return NULL;
-  if( DeviceDescription->ScatterGather )
-    return NULL;
-  if( DeviceDescription->AutoInitialize )
-    return NULL;
-  if( DeviceDescription->Dma32BitAddresses )
-    return NULL;
-  if( DeviceDescription->InterfaceType != Isa )
-     return NULL;
-     */
-  /*  if( DeviceDescription->DmaWidth != Width8Bits )
-      return NULL;*/
-  *NumberOfMapRegisters = 0x10;
-  IsaSlaveAdapterObjects[DeviceDescription->DmaChannel].Buffer = 0;
-  return &IsaSlaveAdapterObjects[DeviceDescription->DmaChannel];
 }
 
 ULONG STDCALL
 HalReadDmaCounter (PADAPTER_OBJECT	AdapterObject)
 {
-   UNIMPLEMENTED;
+  KIRQL OldIrql;
+  ULONG Count;
+
+  if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
+  {
+    KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
+
+    /* Clear the flip/flop register */
+    WRITE_PORT_UCHAR( AdapterObject->Channel < 4 ? (PVOID)0x0C : (PVOID)0xD8, 0 );
+    /* Read the offset */
+    Count = READ_PORT_UCHAR( AdapterObject->CountPort );
+    Count |= READ_PORT_UCHAR( AdapterObject->CountPort ) << 8;
+
+    KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
+
+    /*
+     * We must return twice the sound for channel >= 4 because it's the size
+     * of words (16-bit) and not bytes.
+     */
+    if (AdapterObject->Channel < 4)
+      return Count;
+    else
+      return Count << 1;
+  }
+
+  return 0;
 }
 
 /* EOF */
Index: halx86/include/hal.h
===================================================================
--- halx86/include/hal.h	(revision 105)
+++ halx86/include/hal.h	(working copy)
@@ -19,6 +19,7 @@
 VOID FASTCALL HalInitializeDisplay (PLOADER_PARAMETER_BLOCK LoaderBlock);
 VOID FASTCALL HalClearDisplay (UCHAR CharAttribute);
 
+/* bus.c */
 VOID HalpInitBusHandlers (VOID);
 
 /* irql.c */
@@ -33,6 +34,9 @@
 /* enum.c */
 VOID HalpStartEnumerator (VOID);
 
+/* dma.c */
+VOID HalpInitDma (VOID);
+
 /*
  * ADAPTER_OBJECT - Track a busmaster DMA adapter and its associated resources
  *
@@ -59,6 +63,15 @@
   PWAIT_CONTEXT_BLOCK WaitContextBlock;
   KDEVICE_QUEUE DeviceQueue;
   BOOLEAN ScatterGather;
+
+  /*
+   * 18/07/04: Added these members. It's propably not the exact place where
+   * this should be stored, but I can't find better one. I haven't checked
+   * how Windows handles this.
+   * -- Filip Navara
+   */
+  BOOLEAN DemandMode;
+  BOOLEAN AutoInitialize;
 };
 
 /* sysinfo.c */


More information about the Ros-kernel mailing list