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

📄 dma.c

📁 WinCE 3.0 BSP, 包含Inter SA1110, Intel_815E, Advantech_PCM9574 等
💻 C
字号:
//
// Permedia3 Sample Display Driver
// dma.c
//
// Copyright (c) 2000 Microsoft Corporation. All rights reserved.
//
// This module manages the Permedia3's use of DMA. This includes the
// command buffering, which the card DMAs, as well as DMA transfers
// of data (bitmaps mostly) from system meory.

#include "pch.h"  // Precompiled header support.
#include "debug.h"
#include "register.h"
#include "const.h"
#include "struct.h"
#include "global.h"
#include "proto.h"
#include <ceddk.h>

// This is the current position in a DMA buffer that we are writing to.

static ULONG * l_CurrentPosition;

// This points to the last address that we wrote a command to in the previous
// command bundle.

static ULONG * l_WriterPosition;

// These pointers identify the physical and virtual addresses of the
// beginning and the end of the DMA buffer.

static ULONG * l_VirtualDMABuffer;
static ULONG * l_VirtualDMABufferEnd;
static ULONG   l_PhysicalDMABuffer;
static ULONG   l_PhysicalDMABufferEnd;

// This is the current Hostin ID number. We attach unique IDs to each DMA
// packet. This allows us to monitor the progress of the chip in the
// command stream.

static ULONG l_HostinId;

// This variable indicates if we need to monitor the DMA status from the
// vertical retrace interrupt.

static BOOL l_NeedToCheckDMA;

// This event is set by the DMA interrupt to signify that it is idle.

static HANDLE l_DMADone;

// Internal prototypes. These functions are not exposed outside this module.

static void RestartDMA();
static PVOID AllocatePhysicalMemory(UINT cbSize, PULONG ppaBuffer);
static BOOL FreePhysicalMemory(PVOID lpMemory, UINT cbSize);

BOOL
InitializeDMA()
{
  // InitializeDMA
  // This function initializes the DMA command buffering scheme. This function
  // is the only one in this module which does not access shared variables
  // with Interlocked* functions. (Interrupts are enbaled after DMA in the
  // intialization scheme. See init.c.)

  // Local variables.

  ULONG DMAMemoryControl;
  BOOL FnRetVal = FALSE;  // Return value for this function.

  // We assme the DMA buffer is some number of ULONGSs. We use this
  // assumption later.

  Assert(SIZEOF_DMA_BUFFER % 4 == 0);

  Enter(L"InitializeDMA");

  // Set initial Hostin ID.

  l_HostinId = 0;

  // Enable bus-mastering on the PCI bus.

  g_Config.PciCommonConfig.Command |= b_CFGCommand_BusMasterEnable;

  if (HalSetBusDataByOffset(PCIConfiguration,
                            g_Config.PciBusNumber,
                            g_Config.PciSlotNumber.u.AsULONG,
                            &g_Config.PciCommonConfig.Command,
                            r_CFGCommand,
                            sizeof(ULONG)) == sizeof(ULONG)) {

    // Setup the appropriate DMA control registers.

    WaitForInputFIFO(3);

    DMAMemoryControl = 0;

    DMAMemoryControl |= 1 << 2;  // Align host-in DMA to 64 bit boundaries
    DMAMemoryControl |= 1 << 31; // Align host-out DMA to 64 bit boundaries

    // !TODO! Burst size?
    //DMAMemoryControl |= (0 & 0x1f) << 24; // burst size n == (1 << 7+n)? Spec indicates n * 128

    WriteRegUlong(r_DMAMemoryControl, DMAMemoryControl);

    // Allocate buffers for DMA and setup pointers. Use special CE
    // API to get contigous physical memory buffers, suitable for DMA.

    // !TODO! Should we enable caching on DMA buffers?

    l_VirtualDMABuffer = AllocatePhysicalMemory(SIZEOF_DMA_BUFFER, &l_PhysicalDMABuffer);

    if (l_VirtualDMABuffer != NULL) {

      l_DMADone = CreateEvent(NULL,  // No security descriptors in CE
                              FALSE, // Auto reset
                              FALSE, // Initially not signaled
                              NULL); // No name
      if (l_DMADone != NULL) {

        l_PhysicalDMABufferEnd = l_PhysicalDMABuffer + SIZEOF_DMA_BUFFER;
        l_VirtualDMABufferEnd = (ULONG *)((BYTE *)l_VirtualDMABuffer + SIZEOF_DMA_BUFFER);

        RestartDMA();

        FnRetVal = TRUE;

        Message(L"DMA Command stream enabled.\n");
      }
      else {
        Error(L"Unable to create event for DMA completion.\n");
      }
    }
    else {

      Error(L"Unable to allocate contiguous memory for DMA buffer.\n");
    }
  }
  else {

    Error(L"Unable to enable bus mastering on PCI bus.\n");
  }

  Exit(L"InitializeDMA");

  return FnRetVal;
}

ULONG
RegisterToTag(
  ULONG Register
  )
{
  // RegisterToTag
  // This function converts a register address (16 bits) to a DMA register 
  // tag. No Enter/Exit semantics for inlined functions.

  return (Register >> 3);
}

void
WaitForDMASpace(
  ULONG Ulongs
  )
{
  // WaitForDMASpace
  // This function should be called before writing a set of commands into the
  // DMA buffer system. It verifies that enough space is available for the
  // commands.

  // Check parameters.

  Assert(Ulongs < SIZEOF_DMA_BUFFER);

  Enter(L"WaitForDMASpace");

  // Compute the number of Ulongs left in the DMA buffer. If we do not have
  // enough space left, we wait for the current DMA to complete to restart
  // the buffer. Note the use of the +2 to allow us to prepend a HostIdTag
  // to the coming buffer.

  if ((ULONG)(l_VirtualDMABufferEnd - l_CurrentPosition) < Ulongs + 2) {

    WaitNotBusy();

    // Restart the buffer. Also sets host in tag for this packet.

    RestartDMA();
  }
  else {

    // Write in the new HostInId.

    QueueDMATag(r_HostinID, l_HostinId++);
  }

  Exit(L"WaitForDMASpace");
}

void
QueueDMATag(
  ULONG Register,
  ULONG Data
  )
{
  // QueueDMATag
  // This function adds a tag and data pair to the command stream. There is
  // no Enter/Exit commands becuase this function is intended to be inlined.

  // This function assumes that there is enough space in the current DMA
  // buffer for the data passed.

  *l_CurrentPosition++ = RegisterToTag(Register);
  *l_CurrentPosition++ = Data;
}

void
QueueDMAHold(
  ULONG Register,
  ULONG Count
  )
{
  // QueueDMAHold
  // This function adds a 'hold' packet to the command stream. A 'hold' packet
  // tells the the DMA engine to hold the tag constant for a certain number
  // of consecutive data elements. Useful for consecutively writing different
  // values to the same register. No Enter/Exit semantics for inlines.

  // This function assumes that there is enough space in the current DMA
  // buffer for the data passed.

  *l_CurrentPosition++ = (RegisterToTag(Register) | ((Count - 1) << 16));
}

void
QueueDMAIncrement(
  ULONG Register,
  ULONG Count
  )
{
  // QueueDMAIncrement
  // This function adds and 'increment' packet to the command stream. This
  // packet instructs the DMA engine to increment the tag value for each
  // consecutive data element. No Enter/Exit semantics.

  // This function assumes that there is enough space in the current DMA
  // buffer for the data passed.

  *l_CurrentPosition++ = (RegisterToTag(Register) | ((Count - 1) << 16) | (1 << 14));
}

void
QueueDMAIndex(
  ULONG Register,
  ULONG Mask
  )
{
  // QueueDMAIndex
  // This function adds a DMA 'index' packet to the command stream. This packet
  // identifies an 11 bit group, and then holds a 16 bit mask that identifies
  // which of the registers in the group are to be written. The data following the
  // index packet is paired up to the tags, starting at the low bit of the mask.

  // This function assumes that there is enough space in the current DMA
  // buffer for the data passed.

  *l_CurrentPosition++ = ((RegisterToTag(Register) & 0xFF0) | (Mask << 16) | (2 << 14));
}

void
QueueDMAUlong(
  ULONG Data
  )
{
  // QueueDMAUlong
  // Add a Ulong (32 bit value) to the command stream. No Enter/Exit semantics
  // for inlined functions.

  // This function assumes that there is enough space in the current DMA
  // buffer for the data passed.
         
  *l_CurrentPosition++ = Data;
}

void
EndDMA()
{
  // EndDMA
  // This function is called to signify that an entire meaningful group of
  // commands have been written into the current buffer, and that we should
  // DMA them. No Enter/Exit semantics for inlined functions.

  WriteRegUlong(r_DMAContinue, l_CurrentPosition - l_WriterPosition);
  l_WriterPosition = l_CurrentPosition;
}

void
DMAInterrupt()
{
  // DMAInterrupt
  // This function is called to process a DMA interrupt. It mearly sets the
  // DMA done event if someone is listening (l_NeedToCheckDMA) and if the
  // DMA engine is idle.

  Enter(L"DMAInterrupt");

  if (l_NeedToCheckDMA) {

    if (!IsBusy()) {
      SetEvent(l_DMADone);
    }
  }

  Exit(L"DMAInterrupt");
}

void
RestartDMA()
{
  // RestartDMA
  // This function kicks the DMA buffer off by loading the DMAAddr and
  // DMACount registers. It resets the Writer and Current position
  // pointers to the start of the buffer as well. Assumes that the
  // current DMA is done.

  Enter(L"RestartDMA");

  l_WriterPosition = l_VirtualDMABuffer;
  l_CurrentPosition = l_WriterPosition;

  // Start a DMA operation in the first buffer. We will use DMA continues
  // each time a packet is completed.

  // Write in the new HostInId. No need to check for space or call EndDMA.

  QueueDMATag(r_HostinID, l_HostinId++);

  WriteRegUlong(r_DMAAddr, l_PhysicalDMABuffer);

  WriteRegUlong(r_DMACount, l_CurrentPosition - l_WriterPosition);

  l_WriterPosition = l_CurrentPosition;

  Exit(L"RestartDMA");
}

void
WaitNotBusy()
{
  // WaitNotBusy
  // This function will cause the current thread to sleep until the DMA buffer
  // is empty. That's to say, all pending operations are complete.

  Enter(L"WaitNotBusy");

  l_NeedToCheckDMA = TRUE;
  WaitForSingleObject(l_DMADone, INFINITE);
  l_NeedToCheckDMA = FALSE;

  Exit(L"WaitNotBusy");
}

BOOL
IsBusy()
{
  // IsBusy
  // This function will check the chip against our local HostIn ID to
  // determine if all pending DMA operations are complete. Returns TRUE if
  // there are still pending operations.

  // Local variables.

  ULONG CurrentHostInId;
  ULONG BufferHostInId;
  BOOL FnRetVal;  // This is the value returned from this function.

  Enter(L"IsBusy");

  CurrentHostInId = ReadRegUlong(r_HostinID);
  BufferHostInId  = l_HostinId - 1;

  FnRetVal = (BOOL)(CurrentHostInId != BufferHostInId);

  Exit(L"IsBusy");

  return FnRetVal;
}

BOOL
FreePhysicalMemory(PVOID lpMemory, UINT cbSize)
{
    UnlockPages(lpMemory, cbSize);
    VirtualFree(lpMemory,0, MEM_RELEASE);

    return(TRUE);
}

PVOID
AllocatePhysicalMemory(UINT cbSize, PULONG ppaBuffer)
{
    LPVOID pBuffer = NULL;
    LPVOID pLastBuffer = NULL;
    LPVOID pNextBuffer = NULL;
    ULONG paBuffer = 0;
    BOOL fDone = FALSE;
    BOOL fResult = FALSE;
    UINT i = 0;
    //let's first find the maximum # of pages that could be locked
    UINT cPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(NULL, cbSize);
    PULONG aPages = NULL;

    //allocate an array for the maximum # of pages
    aPages = SystemAlloc(sizeof(ULONG) * cPages);
    if (!aPages)
    {
        Error(L"ddi_perm3:AllocatePhysicalMemory error allocating memory.\r\n");
        return(NULL);
    }

    // we need to allocate at least 1 byte
    if(cbSize == 0)
        cbSize = 1;

    do
    {
        fDone = TRUE;
        pLastBuffer = pBuffer;

        pBuffer = VirtualAlloc(NULL, cbSize,
                MEM_COMMIT, PAGE_NOCACHE | PAGE_READWRITE);

        // our allocated block must be on a page boundary!
        ASSERT(((DWORD)pBuffer & (PAGE_SIZE - 1)) == 0);

        Message(L"Allocated possibly continuous memory block: %%08X\r\n", pBuffer);

        if(pBuffer)
        {
            fResult = LockPages(pBuffer, cbSize, aPages, LOCKFLAG_WRITE);

            if(fResult)
            {
                PULONG pPage;

                ASSERT(cPages ==
                        ADDRESS_AND_SIZE_TO_SPAN_PAGES(pBuffer, cbSize));
                pPage = aPages;

                // We want page numbers and the kernel gives us the physical address
                // of each page, adjust the list.  Except on MIPS where the kernel
                // gives us the physical address shifted right 6 bits.

                for(i = 0 ; i < cPages ; ++i)
                {
#if defined(MIPS)
#if defined(R4000) | defined(R5230)
#if defined(R4100)
                    *pPage >>= PAGE_SHIFT - 4;
#else
                    *pPage >>= PAGE_SHIFT - 6;
#endif
#else
                    *pPage >>= PAGE_SHIFT;
#endif
#elif defined(SHx) | defined(x86) | defined(PPC) | defined(ARM)
                    *pPage >>= PAGE_SHIFT;
#else
#error Unsupported Processor
#endif

                    ++pPage;
                }

                pPage = aPages;

                ASSERT(cPages > 0);

                for(i = 0 ; i < cPages - 1 && fDone ; ++i)
                {
                    if(*pPage + 1 != *(pPage + 1))
                        fDone = FALSE;

                    ++pPage;
                }
                if(fDone)
                    paBuffer = *aPages << PAGE_SHIFT;
            }
            if(!fDone)
                *(PPVOID)pBuffer = pLastBuffer;
        }
    } while(!fDone);

    while(pLastBuffer)
    {
        pNextBuffer = *(PPVOID)pLastBuffer;

        UnlockPages(pLastBuffer, cbSize);
        VirtualFree(pLastBuffer,0, MEM_RELEASE);

        pLastBuffer = pNextBuffer;
    }

    // our allocated block must be on a page boundary or we will miss
    // part of the physical address and the interrupt table will not be aligned!
    ASSERT(((DWORD)pBuffer & (PAGE_SIZE - 1)) == 0);

    ASSERT(ppaBuffer);
    *ppaBuffer = paBuffer;

    ASSERT((*ppaBuffer & (PAGE_SIZE - 1)) == 0);

    if(aPages)
        SystemFree(aPages);

    Message(L"ddi_perm3:AllocatePhysicalMemory Continuous memory block VA: %08X PA:%08X \r\n", pBuffer, *ppaBuffer);

    return(pBuffer);
}


⌨️ 快捷键说明

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