⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dma.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $Id: dma.c 24759 2006-11-14 20:59:48Z ion $
 *
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS kernel
 * 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 <hal.h>
#define NDEBUG
#include <debug.h>

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,
   NULL, /* Initialized in HalpInitDma() */
   NULL, /* Initialized in HalpInitDma() */
   NULL, /* Initialized in HalpInitDma() */
   NULL, /* Initialized in HalpInitDma() */
   NULL, /* Initialized in HalpInitDma() */
   (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)
{
   /*
    * Initialize the DMA Operation table
    */
   HalpDmaOperations.AllocateAdapterChannel = (PALLOCATE_ADAPTER_CHANNEL)IoAllocateAdapterChannel;
   HalpDmaOperations.FlushAdapterBuffers = (PFLUSH_ADAPTER_BUFFERS)IoFlushAdapterBuffers;
   HalpDmaOperations.FreeAdapterChannel = (PFREE_ADAPTER_CHANNEL)IoFreeAdapterChannel;
   HalpDmaOperations.FreeMapRegisters = (PFREE_MAP_REGISTERS)IoFreeMapRegisters;
   HalpDmaOperations.MapTransfer = (PMAP_TRANSFER)IoMapTransfer;

   /*
    * 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();

   /*
    * Setup the HalDispatchTable callback for creating PnP DMA adapters. It's
    * used by IoGetDmaAdapter in the kernel.
    */

   HalGetDmaAdapter = HalpGetDmaAdapter;
}

/**
 * @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 HighestAddress;

   if (AdapterObject->MasterDevice)
   {
      if (AdapterObject->Dma64BitAddresses)
      {
         HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
         return HighestAddress;
      }
      else if (AdapterObject->Dma32BitAddresses)
      {
         HighestAddress.QuadPart = 0xFFFFFFFF;
         return HighestAddress;
      }
   }

   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
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;

   PhysicalAddress = MmGetPhysicalAddress(VirtualAddress);

   /*
    * 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)
   {
      PROS_MAP_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(ROS_MAP_REGISTER_ENTRY),
      TAG_DMA);
   if (MasterAdapter->MapRegisterBase == NULL)
   {
      ExFreePool(MasterAdapter);
      return NULL;
   }

   RtlZeroMemory(MasterAdapter->MapRegisterBase,
                 SizeOfBitmap * sizeof(ROS_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,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -