Use ASSERT macro instead of assert macro.
[reactos.git] / reactos / hal / halx86 / adapter.c
index 371ddc1..8c58a99 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.12 2004/10/22 20:08:21 ekohl Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
 
 /* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */
 
+\f
 NTSTATUS STDCALL
-HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject,
-                         PWAIT_CONTEXT_BLOCK WaitContextBlock,
-                         ULONG NumberOfMapRegisters,
-                         PDRIVER_CONTROL ExecutionRoutine)
+HalAllocateAdapterChannel(
+  PADAPTER_OBJECT AdapterObject,
+  PWAIT_CONTEXT_BLOCK WaitContextBlock,
+  ULONG NumberOfMapRegisters,
+  PDRIVER_CONTROL ExecutionRoutine)
 /*
  * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
  * ARGUMENTS:
@@ -39,154 +41,443 @@ 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 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->DeviceQueue, &WaitContextBlock->WaitQueueEntry))
+    return STATUS_SUCCESS;
 
+  /* 24-bit max address due to 16-bit dma controllers */
+  MinAddress.QuadPart = 0x0000000;
   MaxAddress.QuadPart = 0x1000000;
+  BoundryAddressMultiple.QuadPart = 0;
 
   /* 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.
+   *
+   * 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,
+      MinAddress,
+      MaxAddress,
+      BoundryAddressMultiple,
+      MmCached,
+      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;
 }
 
-
+\f
 BOOLEAN STDCALL
-IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject,
-                      PMDL             Mdl,
-                      PVOID            MapRegisterBase,
-                      PVOID            CurrentVa,
-                      ULONG            Length,
-                      BOOLEAN          WriteToDevice)
+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.
+ */
 {
-  // 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 );
+  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->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;
+    
+  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;
+  LARGE_INTEGER MinAddress;
+  LARGE_INTEGER BoundryAddressMultiple;
+  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 */
+      MinAddress.QuadPart = 0x0000000;
+      MaxAddress.QuadPart = 0x1000000;
+      BoundryAddressMultiple.QuadPart = 0;
+
+      AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory( 
+          WaitContextBlock->NumberOfMapRegisters * PAGE_SIZE,
+          MinAddress,
+          MaxAddress,
+          BoundryAddressMultiple,
+          MmCached,
+          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)
+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
+ */
 {
-   UNIMPLEMENTED;
+  if( AdapterObject->AllocatedMapRegisters )
+    {
+      MmFreeContiguousMemory(AdapterObject->MapRegisterBase);
+      AdapterObject->MapRegisterBase = 0;
+    }
 }
 
-
+\f
 PHYSICAL_ADDRESS  STDCALL
-IoMapTransfer (PADAPTER_OBJECT AdapterObject,
-              PMDL             Mdl,
-              PVOID            MapRegisterBase,
-              PVOID            CurrentVa,
-              PULONG           Length,
-              BOOLEAN          WriteToDevice)
+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;
-  // program up the dma controller, and return
-  // if it is a write to the device, copy the caller buffer to the low buffer
-  if( WriteToDevice )
-    memcpy( MapRegisterBase,
-           MmGetSystemAddressForMdl( Mdl ) + ( (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl ) ),
-           *Length );
-  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, AdapterObject->Channel | 0x10 );
-  // 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, AdapterObject->Channel | ( WriteToDevice ? 0x8 : 0x4 ) );
-  // set the 64k page register for the channel
-  WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(((ULONG)Address.QuadPart)>>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) );
-  // unmask the channel to let it rip
-  WRITE_PORT_UCHAR( (PVOID)0x0A, AdapterObject->Channel );
-  Address.QuadPart = (DWORD)MapRegisterBase;
+  PVOID MaskReg, ClearReg, ModeReg;
+  UCHAR ModeMask, LengthShift;
+  KIRQL OldIrql;
+
+#if defined(__GNUC__)
+  Address.QuadPart = 0ULL;
+#else
+  Address.QuadPart = 0;
+#endif
+
+  /* Isa System (slave) DMA? */
+  if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
+  {
+#if 0
+    /* channel 0 is reserved for DRAM refresh */
+    ASSERT(AdapterObject->Channel != 0);
+    /* channel 4 is reserved for cascade */
+    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 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->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
+    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( 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)(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 >> LengthShift) - 1) );
+    WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(((*Length >> LengthShift) - 1)>>8) );
+    // unmask the channel to let it rip
+    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 
+    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->Master && 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->Master && !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);
+  }
+
+  DPRINT1("IoMapTransfer: Unsupported operation\n");
+  KEBUGCHECK(0);
   return Address;
 }