ddk_map.c

来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C语言 代码 · 共 323 行

C
323
字号
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-2000 Microsoft Corporation.  All rights reserved.

Module Name:  

    ddk_map.c

Abstract:  

    WINCE device driver support routines.  This file supports
    a very minimal subset of the routines from ntddk.h

    
Functions:


Notes:

--*/
#include <windows.h>
#include <types.h>
#include "ceddk.h"

#define VERBOSE 0

/*++
Routine Description:
  map the given physical address range to nonpaged system space

Arguments:
  PhysicalAddress - starting physical address of the I/O range to be mapped
  NumberOfBytes - number of bytes to be mapped
  CacheEnable - TRUE if the physical address range can be mapped as cached
                memory

Return Value:
  base virtual address that maps the base physical address for the range, or
  NULL if space for mapping the range is insufficient
--*/
PVOID
MmMapIoSpace (
             IN PHYSICAL_ADDRESS PhysicalAddress,
             IN ULONG NumberOfBytes,
             IN BOOLEAN CacheEnable
             )
{
    PVOID   pVirtualAddress;
    ULONG   SourcePhys;
    ULONG   SourceSize;
    BOOL    bSuccess;

    //
    // Page align source and adjust size to compensate
    //

    SourcePhys = PhysicalAddress.LowPart & ~(PAGE_SIZE - 1);
    SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1));

    pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);

    if ( pVirtualAddress != NULL ) {
        bSuccess = VirtualCopy(
                              pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize,
                              PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));

        if ( bSuccess ) {
            (ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1);
        } else {
            VirtualFree(pVirtualAddress, SourceSize, MEM_RELEASE);
            pVirtualAddress = NULL;
        }
    }

    return (pVirtualAddress);
}

/*++
Routine Description:
  unmap a specified range of physical addresses previously mapped by
  MmMapIoSpace

Arguments:
  BaseAddress - pointer to the base virtual address to which the physical
                pages were mapped
  NumberOfBytes - number of bytes that were mapped

Return Value:
  None
--*/
VOID
MmUnmapIoSpace (
               IN PVOID BaseAddress,
               IN ULONG NumberOfBytes
               )
{
    PVOID   pVirtualAddress = (PVOID)((ULONG)BaseAddress & ~(ULONG)(PAGE_SIZE - 1));
    ULONG   SourceSize = NumberOfBytes + ((ULONG)BaseAddress & (PAGE_SIZE - 1));

    VirtualFree(pVirtualAddress, SourceSize, MEM_RELEASE);
}


/*++

CeAllocPhysMem:

    Allocates a contiguous physical buffer on a page boundary so that it is simultaneously 
    accessible from both the processor and a device for DMA operations.

    This routine is similar to the NTDDK's HalAllocateCommonBuffer, but WinCE does not support Adapter Objects.

Arguments:

    StartAddress - Specifies the desired starting address of the region to allocate. 
        Usually NULL unless the device requires a specific start address.

    Length - Specifies the number of bytes to allocate.

    PhysicalAddress - Points to a variable that receives the physical address the 
        device can use to access the allocated buffer.

    CacheEnabled - Specifies whether the allocated memory should be cached;
        usually FALSE for drivers

Return Value:

    Base virtual address of the allocated range if successful, else NULL.

Notes:

    If a driver needs several pages of physical buffer space, but the pages need not be contiguous, the driver should make several 
    one-page requests rather than one large request. This approach conserves contiguous memory.

    Drivers typically call this routine during device initialization. After initialization, it is possible that only 
    one-page requests will succeed, if any.

    Memory can be released by calling CeFreePhysMem.

--*/
LPVOID
CeAllocPhysMem(
    IN  LPVOID  StartAddress,
    IN  ULONG   Length,
    OUT PULONG  PhysicalAddress,
    IN  BOOLEAN CacheEnabled
    )
{
    LPVOID pVirtualBuffer = NULL;
    LPVOID pLastBuffer;
    LPVOID pNextBuffer;
    ULONG  paBuffer = 0;
    BOOL   fDone;
    BOOL   fLocked;

    UINT   nPages;
    PULONG pPFNs;
    UINT i;

    if( !Length || !PhysicalAddress ) {
        DEBUGMSG(1, (TEXT("CeAllocPhysMem - invalid parameter\n")));
        return NULL;
    } else {
        //
        // find the maximum # of pages to be locked
        //
        nPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( NULL, Length );
        //
        // allocate an array of page Physical Frame Numbers 
        // representing the CPU-dependent physical addresses of the pages. 
        //
        pPFNs = LocalAlloc( LPTR, sizeof(ULONG) * nPages );  
        if ( !pPFNs ) {
            DEBUGMSG(1, (TEXT("CeAllocPhysMem - LocalAlloc Error:%d\n"), GetLastError() ));
            return NULL;
        }
    }

    do {
        fDone = TRUE;
        pLastBuffer = pVirtualBuffer;

        //
        // allocate physical storage in memory, or paging file
        //
        pVirtualBuffer = VirtualAlloc(  StartAddress,
                                        Length,
                                        MEM_COMMIT, 
                                        CacheEnabled ? PAGE_READWRITE : (PAGE_NOCACHE | PAGE_READWRITE) );

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

        if( pVirtualBuffer ) {
            //
            // lock into physical memory the specified region of 
            // virtual address space of the process, ensuring that 
            // subsequent access to the region will not incur a page fault.
            //            
            fLocked = LockPages( pVirtualBuffer, 
                                 Length, 
                                 pPFNs, 
                                 LOCKFLAG_WRITE );

            if ( fLocked ) {
                //
                // To determine the actual physical address, 
                // the value returned in pPFNs must be right-shifted by a number of bits. 
                // The shift count can be found in UserKInfo[KINX_PFN_SHIFT]. 
                // Right shift the returned values by UserKInfo[KINX_PFN_SHIFT] to get the actual physical address.
                //
                PULONG pPage;

                ASSERT( ADDRESS_AND_SIZE_TO_SPAN_PAGES(pVirtualBuffer, Length) == nPages );

                //
                // We want page numbers and NK gives us the physical address
                // of each page, adjust the list.  Except on MIPS where NK
                // gives us the physical address shifted right 6 bits.
                //
                for (i = 0, pPage = pPFNs; i < nPages ; ++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;
                }

                ASSERT( nPages > 0 );
                for(i = 0, pPage = pPFNs ; i < nPages - 1 && fDone ; ++i)
                {
                    if(*pPage + 1 != *(pPage + 1))
                        fDone = FALSE;

                    ++pPage;
                }

                if(fDone)
                    paBuffer = *pPFNs << PAGE_SHIFT;
            
            } else {
                 DEBUGMSG(1, (TEXT("CeAllocPhysMem - VirtualLock Error: %d\n"), GetLastError()));
            }

            if( !fDone ) {
                *(LPVOID *)pVirtualBuffer = pLastBuffer;
            }
        
        } else {
            DEBUGMSG(VERBOSE, (TEXT("CeAllocPhysMem - VirtualAlloc Error: %d\n"),GetLastError()));
        }

    } while(!fDone);

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

        UnlockPages(pLastBuffer, Length);
        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)pVirtualBuffer & (PAGE_SIZE - 1)) == 0);
    ASSERT((paBuffer & (PAGE_SIZE - 1)) == 0);

    ASSERT(PhysicalAddress);
    *PhysicalAddress = paBuffer;

    if(pPFNs)
        LocalFree(pPFNs);

    DEBUGMSG(VERBOSE, (TEXT("CeAllocPhysMem - VA: %08X PA:%08X \r\n"), pVirtualBuffer, paBuffer));

    return pVirtualBuffer;
}


/*

  Release memory previously acquired with CeAllocPhysMem.

*/
ULONG
CeFreePhysMem(
    IN PVOID VirtualAddress,
    IN ULONG Length 
    )
{
    ULONG status = STATUS_SUCCESS;

    if ( !UnlockPages( VirtualAddress, Length ) ) {
        status = GetLastError();
        DEBUGMSG(1, (TEXT("CeAllocPhysMem - UnlockPages Error:%d\n"), status ));
    }

    if ( !VirtualFree( VirtualAddress,0, MEM_RELEASE) ) {
        status = GetLastError();
        DEBUGMSG(1, (TEXT("CeAllocPhysMem - VirtualFree Error:%d\n"), status ));
    }

    return status;
}

⌨️ 快捷键说明

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