www.pudn.com > 9054Src.rar > CommonApi.c


/******************************************************************************* 
 * Copyright (c) 2006 PLX Technology, Inc. 
 * 
 * PLX Technology Inc. licenses this software under specific terms and 
 * conditions.  Use of any of the software or derviatives thereof in any 
 * product without a PLX Technology chip is strictly prohibited. 
 * 
 * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY, 
 * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF 
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee 
 * or representations regarding the use of, or the results of the use of, 
 * the software and documentation in terms of correctness, accuracy, 
 * reliability, currentness, or otherwise; and you rely on the software, 
 * documentation and results solely at your own risk. 
 * 
 * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS, 
 * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES 
 * OF ANY KIND.  IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM 
 * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER. 
 * 
 ******************************************************************************/ 
 
/****************************************************************************** 
 * 
 * File Name: 
 * 
 *      CommonApi.c 
 * 
 * Description: 
 * 
 *      API functions common to all PLX chips 
 * 
 * Revision History: 
 * 
 *      03-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "ApiFunctions.h" 
#include "CommonApi.h" 
#include "GlobalVars.h" 
#include "PciSupport.h" 
#include "PlxInterrupt.h" 
#include "SupportFunc.h" 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDeviceFind 
 * 
 * Description:  Search for a specific device using device location parameters 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDeviceFind( 
    DEVICE_EXTENSION *pdx, 
    DEVICE_LOCATION  *pDeviceCriteria, 
    U32              *pSelection 
    ) 
{ 
    U32            MatchFound; 
    BOOLEAN        ContinueComparison; 
    DEVICE_OBJECT *fdo; 
 
 
    // Verify pointers 
    if (pDeviceCriteria == NULL || pSelection == NULL) 
        return ApiNullParam; 
 
    if (pdx->pDeviceObject == NULL) 
        return ApiFailed; 
 
    if (pdx->pDeviceObject->DriverObject == NULL) 
        return ApiFailed; 
 
    MatchFound = 0; 
    fdo        = pdx->pDeviceObject->DriverObject->DeviceObject; 
 
    // Compare with items in device list 
    while (fdo != NULL) 
    { 
        // Get the device extension 
        pdx = fdo->DeviceExtension; 
 
        ContinueComparison = TRUE; 
 
        // Compare Serial Number 
        if ((pDeviceCriteria->SerialNumber[0] != '\0') && ContinueComparison) 
        { 
            if (_stricmp( 
                    pdx->Device.SerialNumber, 
                    pDeviceCriteria->SerialNumber 
                    ) != 0) 
            { 
                ContinueComparison = FALSE; 
            } 
        } 
 
        // Compare the Bus number 
        if ((pDeviceCriteria->BusNumber != (U8)-1) && ContinueComparison) 
        { 
            if (pDeviceCriteria->BusNumber != pdx->Device.BusNumber) 
                ContinueComparison = FALSE; 
        } 
 
        // Compare Slot Number 
        if ((pDeviceCriteria->SlotNumber != (U8)-1) && ContinueComparison) 
        { 
            if (pDeviceCriteria->SlotNumber != pdx->Device.SlotNumber) 
                ContinueComparison = FALSE; 
        } 
 
        // Compare Device ID 
        if ((pDeviceCriteria->DeviceId != (U16)-1) && ContinueComparison) 
        { 
            if (pDeviceCriteria->DeviceId != pdx->Device.DeviceId) 
                ContinueComparison = FALSE; 
        } 
 
        // Compare Vendor ID 
        if ((pDeviceCriteria->VendorId != (U16)-1) && ContinueComparison) 
        { 
            if (pDeviceCriteria->VendorId != pdx->Device.VendorId) 
                ContinueComparison = FALSE; 
        } 
 
        if (ContinueComparison == TRUE) 
        { 
            // Match found, check if it is the device we want 
            if (MatchFound++ == *pSelection) 
            { 
                // Copy the device information 
                RtlCopyMemory( 
                    pDeviceCriteria, 
                    &pdx->Device, 
                    sizeof(DEVICE_LOCATION) 
                    ); 
            } 
        } 
 
        fdo = fdo->NextDevice; 
    } 
 
    // Return the number of matched devices 
    *pSelection = MatchFound; 
 
    return ApiSuccess; 
} 
 
 
 
 
/********************************************************************* 
 * 
 * Function   :  PlxChipTypeGet 
 * 
 * Description:  Gets the PLX chip type and revision 
 * 
 *********************************************************************/ 
VOID 
PlxChipTypeGet( 
    DEVICE_EXTENSION *pdx, 
    U32              *pChipType, 
    U8               *pRevision 
    ) 
{ 
    U32 RegValue; 
 
 
    // Set chip type 
    *pChipType = PLX_CHIP_TYPE; 
 
    switch (*pChipType) 
    { 
        case 0x9080: 
        case 0x9054: 
        case 0x9056: 
        case 0x9656: 
            // Verify by reading hard-coded ID 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    0x70               // Hard-coded ID register 
                    ); 
 
            if (((RegValue & 0xFFFF) == 0x10b5) && 
                ((RegValue >> 16) == *pChipType)) 
            { 
                // Get revision 
                RegValue = 
                    PLX_REG_READ( 
                        pdx, 
                        0x74           // Revision ID 
                        ); 
 
                // 9054 requires additional verification 
                if (*pChipType != 0x9054) 
                { 
                    *pRevision = (U8)RegValue; 
                } 
                else 
                { 
                    // AA & AB versions have same revision ID 
                    if (RegValue == 0xA) 
                    { 
                        PLX_PCI_REG_READ( 
                            pdx, 
                            CFG_REV_ID, 
                            &RegValue 
                            ); 
 
                        if ((RegValue & 0xf) == 0xb) 
                            *pRevision = 0xAB; 
                        else 
                            *pRevision = 0xAA; 
                    } 
                    else 
                    { 
                        if (RegValue == 0xC) 
                        { 
                            // Set value for AC revision 
                            *pRevision = 0xAC; 
                        } 
                        else 
                        { 
                            *pRevision = (U8)RegValue; 
                        } 
                    } 
                } 
            } 
            break; 
 
        case 0x9050: 
        case 0x9030: 
            // 9050/52 require additional verification 
            if (*pChipType == 0x9030) 
            { 
                *pRevision = 0xAA; 
            } 
            else 
            { 
                // Read 9050/9052 PCI revision 
                PLX_PCI_REG_READ( 
                    pdx, 
                    CFG_REV_ID, 
                    &RegValue 
                    ); 
 
                if ((RegValue & 0xF) == 0x2) 
                { 
                    *pRevision = 2; 
                } 
                else 
                { 
                    *pRevision = 1; 
                } 
            } 
            break; 
 
        case 0x8311: 
            // Only AA revision exists 
            *pRevision = 0xAA; 
            break; 
 
        default: 
            *pChipType = 0x0; 
            *pRevision = 0x0; 
            break; 
    } 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxNotificationRegisterFor 
 * 
 * Description:  Registers a wait object for notification on interrupt(s) 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxNotificationRegisterFor( 
    DEVICE_EXTENSION  *pdx, 
    PLX_INTR          *pPlxIntr, 
    PLX_NOTIFY_OBJECT *pEvent, 
    VOID              *pOwner 
    ) 
{ 
    PKEVENT          pKEvent; 
    NTSTATUS         status; 
    PLX_WAIT_OBJECT *pWaitObject; 
 
 
    // Setup driver access to the user event handle 
    status = 
        ObReferenceObjectByHandle( 
            (HANDLE)pEvent->hEvent, // Handle 
            EVENT_MODIFY_STATE,     // Desired Access 
            *ExEventObjectType,     // Object type 
            KernelMode,             // Access Mode 
            &pKEvent,               // Object pointer 
            NULL                    // Handle information 
            ); 
 
    if (status != STATUS_SUCCESS) 
        return ApiInvalidHandle; 
 
    // Allocate a new wait object 
    pWaitObject = 
        ExAllocatePool( 
            NonPagedPool, 
            sizeof(PLX_WAIT_OBJECT) 
            ); 
 
    if (pWaitObject == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - memory allocation for interrupt wait object failed\n" 
            )); 
 
        // De-reference the object 
        ObDereferenceObject( 
            pKEvent 
            ); 
 
        return ApiInsufficientResources; 
    } 
 
    // Record the wait object 
    pEvent->pWaitObject = (PLX_UINT_PTR)pWaitObject; 
 
    // Record the owner 
    pWaitObject->pOwner = pOwner; 
 
    // Store kernel event handle 
    pWaitObject->pKEvent = pKEvent; 
 
    // Clear interrupt source 
    pWaitObject->IntSource = INTR_TYPE_NONE; 
 
    // Set interrupt notification flags 
    PlxChipSetInterruptNotifyFlags( 
        pPlxIntr, 
        pWaitObject 
        ); 
 
    // Add to list of waiting objects 
    ExInterlockedInsertTailList( 
        &(pdx->List_WaitObjects), 
        &(pWaitObject->ListEntry), 
        &(pdx->Lock_WaitObjectsList) 
        ); 
 
    DebugPrintf(( 
        "Registered interrupt wait object (%p)\n", 
        pWaitObject 
        )); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxNotificationWait 
 * 
 * Description:  Put the process to sleep until wake-up event occurs or timeout 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxNotificationWait( 
    DEVICE_EXTENSION  *pdx, 
    PLX_NOTIFY_OBJECT *pEvent, 
    U32                Timeout_ms 
    ) 
{ 
    // Implemented at API level in Windows driver 
    return ApiUnsupportedFunction; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxNotificationCancel 
 * 
 * Description:  Cancels a registered notification event 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxNotificationCancel( 
    DEVICE_EXTENSION  *pdx, 
    PLX_NOTIFY_OBJECT *pEvent, 
    VOID              *pOwner 
    ) 
{ 
    KIRQL            IrqL_Original; 
    BOOLEAN          bRemove; 
    PLIST_ENTRY      pEntry; 
    PLX_WAIT_OBJECT *pWaitObject; 
 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_WaitObjectsList), 
        &IrqL_Original 
        ); 
 
    pEntry = pdx->List_WaitObjects.Flink; 
 
    // Traverse list to find the desired list object 
    while (pEntry != &(pdx->List_WaitObjects)) 
    { 
        // Get the object 
        pWaitObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_WAIT_OBJECT, 
                ListEntry 
                ); 
 
        // Default to not remove 
        bRemove = FALSE; 
 
        // Determine if object should be removed 
        if (pOwner == pWaitObject->pOwner) 
        { 
            if (pEvent == NULL) 
            { 
                bRemove = TRUE; 
            } 
            else if ((PLX_UINT_PTR)pWaitObject == pEvent->pWaitObject) 
            { 
                bRemove = TRUE; 
            } 
        } 
 
        // Remove object 
        if (bRemove) 
        { 
            DebugPrintf(( 
                "Removing interrupt wait object (%p)...\n", 
                pWaitObject 
                )); 
 
            // Remove the object from the list 
            RemoveEntryList( 
                pEntry 
                ); 
 
            KeReleaseSpinLock( 
                &(pdx->Lock_WaitObjectsList), 
                IrqL_Original 
                ); 
 
            // De-reference the kernel event object 
            ObDereferenceObject( 
                pWaitObject->pKEvent 
                ); 
 
            // Release the list object 
            ExFreePool( 
                pWaitObject 
                ); 
 
            // Return if removing only a specific object 
            if (pEvent != NULL) 
                return ApiSuccess; 
 
            // Reset to beginning of list 
            KeAcquireSpinLock( 
                &(pdx->Lock_WaitObjectsList), 
                &IrqL_Original 
                ); 
 
            pEntry = pdx->List_WaitObjects.Flink; 
        } 
        else 
        { 
            // Jump to next item in the list 
            pEntry = pEntry->Flink; 
        } 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_WaitObjectsList), 
        IrqL_Original 
        ); 
 
    return ApiFailed; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBusMemTransfer 
 * 
 * Description:  Reads/Writes device memory to/from a buffer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciBusMemTransfer( 
    DEVICE_EXTENSION *pdx, 
    IOP_SPACE         IopSpace, 
    U32               LocalAddress, 
    BOOLEAN           bRemap, 
    U8               *pBuffer, 
    U32               ByteCount, 
    ACCESS_TYPE       AccessType, 
    BOOLEAN           bReadOperation 
    ) 
{ 
    U8           BarIndex; 
    U16          Offset_RegRemap; 
    U32          RegValue; 
    U32          SpaceRange; 
    U32          SpaceOffset; 
    U32          RemapOriginal; 
    U32          BytesToTransfer; 
    PLX_UINT_PTR SpaceVa; 
 
 
    // Added to prevent compiler warnings 
    RemapOriginal = 0; 
 
    // Verify data alignment 
    switch (AccessType) 
    { 
        case BitSize8: 
            break; 
 
        case BitSize16: 
            if (LocalAddress & 0x1) 
            { 
                DebugPrintf(("ERROR - Local address not aligned\n")); 
                return ApiInvalidAddress; 
            } 
 
            if (ByteCount & 0x1) 
            { 
                DebugPrintf(("ERROR - Byte count not aligned\n")); 
                return ApiInvalidSize; 
            } 
            break; 
 
        case BitSize32: 
            if (LocalAddress & 0x3) 
            { 
                DebugPrintf(("ERROR - Local address not aligned\n")); 
                return ApiInvalidAddress; 
            } 
 
            if (ByteCount & 0x3) 
            { 
                DebugPrintf(("ERROR - Byte count not aligned\n")); 
                return ApiInvalidSize; 
            } 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid access type\n")); 
            return ApiInvalidAccessType; 
    } 
 
    // Get and Verify the Local Space 
    PlxChipGetSpace( 
        pdx, 
        IopSpace, 
        &BarIndex, 
        &Offset_RegRemap 
        ); 
 
    if (BarIndex == (U8)-1) 
    { 
        return ApiInvalidIopSpace; 
    } 
 
    // Only memory spaces are supported by this function 
    if (pdx->PciBar[BarIndex].bIsIoSpace) 
    { 
        DebugPrintf(("ERROR - I/O spaces not supported by this function\n")); 
        return ApiInvalidIopSpace; 
    } 
 
    // Get kernel virtual address for the space 
    SpaceVa = (PLX_UINT_PTR)pdx->PciBar[BarIndex].pVa; 
 
    if (SpaceVa == 0) 
    { 
        DebugPrintf(( 
            "ERROR - Invalid kernel VA (0x%08lx) for PCI BAR\n", 
            SpaceVa 
            )); 
 
        return ApiInvalidAddress; 
    } 
 
    // Save the remap register 
    if (bRemap) 
    { 
        RemapOriginal = 
            PLX_REG_READ( 
                pdx, 
                Offset_RegRemap 
                ); 
    } 
    else 
    { 
        // Make sure requested area doesn't exceed our local space window boundary 
        if ((LocalAddress + ByteCount) > pdx->PciBar[BarIndex].Size) 
        { 
            DebugPrintf(("ERROR - requested area exceeds space range\n")); 
            return ApiInvalidSize; 
        } 
    } 
 
    // Get the range of the space 
    SpaceRange = ~(pdx->PciBar[BarIndex].Size - 1); 
 
    // Transfer data in blocks 
    while (ByteCount != 0) 
    { 
        // Adjust remap if necessary 
        if (bRemap) 
        { 
            // Clear upper bits of remap 
            RegValue = RemapOriginal & ~SpaceRange; 
 
            // Adjust window to local address 
            RegValue |= LocalAddress & SpaceRange; 
 
            PLX_REG_WRITE( 
                pdx, 
                Offset_RegRemap, 
                RegValue 
                ); 
        } 
 
        // Get current offset into space 
        SpaceOffset = LocalAddress & (~SpaceRange); 
 
        // Calculate bytes to transfer for next block 
        if (ByteCount <= (((~SpaceRange) + 1) - SpaceOffset)) 
        { 
            BytesToTransfer = ByteCount; 
        } 
        else 
        { 
            BytesToTransfer = ((~SpaceRange) + 1) - SpaceOffset; 
        } 
 
        if (bReadOperation) 
        { 
            // Copy block to user buffer 
            switch (AccessType) 
            { 
                case BitSize8: 
                    DEV_MEM_TO_USER_8( 
                        pBuffer, 
                        (SpaceVa + SpaceOffset), 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize16: 
                    DEV_MEM_TO_USER_16( 
                        pBuffer, 
                        (SpaceVa + SpaceOffset), 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize32: 
                    DEV_MEM_TO_USER_32( 
                        pBuffer, 
                        (SpaceVa + SpaceOffset), 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize64: 
                    // 64-bit not implemented yet 
                    break; 
            } 
        } 
        else 
        { 
            // Copy user buffer to device memory 
            switch (AccessType) 
            { 
                case BitSize8: 
                    USER_TO_DEV_MEM_8( 
                        (SpaceVa + SpaceOffset), 
                        pBuffer, 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize16: 
                    USER_TO_DEV_MEM_16( 
                        (SpaceVa + SpaceOffset), 
                        pBuffer, 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize32: 
                    USER_TO_DEV_MEM_32( 
                        (SpaceVa + SpaceOffset), 
                        pBuffer, 
                        BytesToTransfer 
                        ); 
                    break; 
 
                case BitSize64: 
                    // 64-bit not implemented yet 
                    break; 
            } 
        } 
 
        // Adjust for next block access 
        pBuffer      += BytesToTransfer; 
        LocalAddress += BytesToTransfer; 
        ByteCount    -= BytesToTransfer; 
    } 
 
    // Restore the remap register 
    if (bRemap) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            Offset_RegRemap, 
            RemapOriginal 
            ); 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciIoPortTransfer 
 * 
 * Description:  Read or Write from/to an I/O port 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciIoPortTransfer( 
    U64          IoPort, 
    ACCESS_TYPE  AccessType, 
    VOID        *pValue, 
    BOOLEAN      bReadOperation 
    ) 
{ 
    if (pValue == NULL) 
        return ApiNullParam; 
 
    if (bReadOperation) 
    { 
        switch (AccessType) 
        { 
            case BitSize8: 
                *(U8*)pValue = 
                    IO_PORT_READ_8( 
                        IoPort 
                        ); 
                break; 
 
            case BitSize16: 
                *(U16*)pValue = 
                    IO_PORT_READ_16( 
                        IoPort 
                        ); 
                break; 
 
            case BitSize32: 
                *(U32*)pValue = 
                    IO_PORT_READ_32( 
                        IoPort 
                        ); 
                break; 
 
            default: 
                return ApiInvalidAccessType; 
        } 
    } 
    else 
    { 
        switch (AccessType) 
        { 
            case BitSize8: 
                IO_PORT_WRITE_8( 
                    IoPort, 
                    *(U8*)pValue 
                    ); 
                break; 
 
            case BitSize16: 
                IO_PORT_WRITE_16( 
                    IoPort, 
                    *(U16*)pValue 
                    ); 
                break; 
 
            case BitSize32: 
                IO_PORT_WRITE_32( 
                    IoPort, 
                    *(U32*)pValue 
                    ); 
                break; 
 
            default: 
                return ApiInvalidAccessType; 
        } 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryAllocate 
 * 
 * Description:  Allocate physically contiguous page-locked memory 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPhysicalMemoryAllocate( 
    DEVICE_EXTENSION *pdx, 
    PLX_PHYSICAL_MEM *pPciMem, 
    BOOLEAN           bSmallerOk, 
    VOID             *pOwner 
    ) 
{ 
    U32                  DecrementAmount; 
    PLX_PHYS_MEM_OBJECT *pBufferObj; 
 
 
    // Initialize buffer information 
    pPciMem->UserAddr     = 0; 
    pPciMem->PhysicalAddr = 0; 
    pPciMem->CpuPhysical  = 0; 
 
    /******************************************************* 
     * Verify size 
     * 
     * A size of 0 is valid because this function may 
     * be called to allocate a common buffer of size 0; 
     * therefore, the information is reset & return sucess. 
     ******************************************************/ 
    if (pPciMem->Size == 0) 
    { 
        return ApiSuccess; 
    } 
 
    // Allocate memory for new list object 
    pBufferObj = 
        ExAllocatePool( 
            NonPagedPool, 
            sizeof(PLX_PHYS_MEM_OBJECT) 
            ); 
 
    if (pBufferObj == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - Memory allocation for list object failed\n" 
            )); 
 
        return ApiInsufficientResources; 
    } 
 
    // Set buffer request size 
    pBufferObj->Size = pPciMem->Size; 
 
    // Set Cacheability of the buffer 
    pBufferObj->bCacheable = Gbl_PhysicalMemoryCacheable; 
 
    // Setup amount to reduce on failure 
    DecrementAmount = (pPciMem->Size / 10); 
 
    DebugPrintf(( 
        "Attempting to allocate physical memory (%d Kb)...\n", 
        (pPciMem->Size >> 10) 
        )); 
 
    do 
    { 
        // Attempt to allocate the buffer 
        pBufferObj->pKernelVa = 
            Plx_dma_buffer_alloc( 
                pdx, 
                pBufferObj 
                ); 
 
        if (pBufferObj->pKernelVa == NULL) 
        { 
            // Reduce memory request size if requested 
            if (bSmallerOk && (pBufferObj->Size > PAGE_SIZE)) 
            { 
                pBufferObj->Size -= DecrementAmount; 
            } 
            else 
            { 
                // Release the list object 
                ExFreePool( 
                    pBufferObj 
                    ); 
 
                DebugPrintf(( 
                    "ERROR - Physical memory allocation failed\n" 
                    )); 
 
                pPciMem->Size = 0; 
 
                return ApiInsufficientResources; 
            } 
        } 
    } 
    while (pBufferObj->pKernelVa == NULL); 
 
    // Record buffer owner 
    pBufferObj->pOwner = pOwner; 
 
    // Allocate MDL for buffer for later mapping into user space 
    pBufferObj->pMdl = 
        IoAllocateMdl( 
            pBufferObj->pKernelVa, 
            pBufferObj->Size, 
            FALSE,          // Is this a secondary buffer? 
            FALSE,          // Charge quota? 
            NULL            // No IRP associated with MDL 
            ); 
 
    // Check if the MDL allocation succeeded 
    if (pBufferObj->pMdl == NULL) 
        DebugPrintf(("ERROR - failed to allocate an MDL for buffer\n")); 
    else 
    { 
        // Build the MDL 
        MmBuildMdlForNonPagedPool( 
            pBufferObj->pMdl 
            ); 
    } 
 
    // Initialize mappings list 
    InitializeListHead( 
        &(pBufferObj->List_Mappings) 
        ); 
 
    KeInitializeSpinLock( 
        &(pBufferObj->Lock_MappingsList) 
        ); 
 
    // Return buffer information 
    pPciMem->Size         = pBufferObj->Size; 
    pPciMem->PhysicalAddr = pBufferObj->BusPhysical; 
    pPciMem->CpuPhysical  = pBufferObj->CpuPhysical; 
 
    // Add buffer object to list 
    ExInterlockedInsertTailList( 
        &(pdx->List_PhysicalMem), 
        &(pBufferObj->ListEntry), 
        &(pdx->Lock_PhysicalMemList) 
        ); 
 
    // Check if this is the common buffer allocation 
    if (pdx == pOwner) 
    { 
        // Store common buffer information 
        pGbl_CommonBuffer = pBufferObj; 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryFree 
 * 
 * Description:  Free previously allocated physically contiguous page-locked memory 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPhysicalMemoryFree( 
    DEVICE_EXTENSION *pdx, 
    PLX_PHYSICAL_MEM *pPciMem 
    ) 
{ 
    KIRQL                IrqL_Original; 
    PLIST_ENTRY          pEntry; 
    PLX_PHYS_MEM_OBJECT *pBufferObj; 
 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_PhysicalMemList), 
        &IrqL_Original 
        ); 
 
    pEntry = pdx->List_PhysicalMem.Flink; 
 
    // Traverse list to find the desired list object 
    while (pEntry != &(pdx->List_PhysicalMem)) 
    { 
        // Get the object 
        pBufferObj = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_PHYS_MEM_OBJECT, 
                ListEntry 
                ); 
 
        // Check if the physical addresses matches 
        if (pBufferObj->BusPhysical == pPciMem->PhysicalAddr) 
        { 
            // Release lock until mappings are removed 
            KeReleaseSpinLock( 
                &(pdx->Lock_PhysicalMemList), 
                IrqL_Original 
                ); 
 
            // Make sure all mappings to this memory are unmapped 
            PlxPciPhysicalMemoryUnmapAll_ByOwner( 
                pdx, 
                pBufferObj, 
                NULL         // Ignore owner to remove all mappings 
                ); 
 
            // Remove the object from the list 
            KeAcquireSpinLock( 
                &(pdx->Lock_PhysicalMemList), 
                &IrqL_Original 
                ); 
 
            RemoveEntryList( 
                pEntry 
                ); 
 
            KeReleaseSpinLock( 
                &(pdx->Lock_PhysicalMemList), 
                IrqL_Original 
                ); 
 
            // Free the MDL 
            if (pBufferObj->pMdl != NULL) 
            { 
                IoFreeMdl( 
                    pBufferObj->pMdl 
                    ); 
            } 
 
            Plx_dma_buffer_free( 
                pdx, 
                pBufferObj 
                ); 
 
            // Release the list object 
            ExFreePool( 
                pBufferObj 
                ); 
 
            // Check if this is the common buffer release 
            if (pGbl_CommonBuffer == pBufferObj) 
            { 
                // Clear common buffer information 
                pGbl_CommonBuffer = NULL; 
            } 
 
            return ApiSuccess; 
        } 
 
        // Jump to next item in the list 
        pEntry = pEntry->Flink; 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_PhysicalMemList), 
        IrqL_Original 
        ); 
 
    DebugPrintf(( 
        "ERROR - buffer object not found in list\n" 
        )); 
 
    return ApiInvalidData; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryMap 
 * 
 * Description:  Maps physical memory to User virtual address space 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPhysicalMemoryMap( 
    DEVICE_EXTENSION *pdx, 
    PLX_PHYSICAL_MEM *pPciMem, 
    BOOLEAN           bDeviceMem, 
    VOID             *pOwner 
    ) 
{ 
    VOID                *pUserVa; 
    KIRQL                IrqL_Original; 
    BOOLEAN              bFound; 
    PLIST_ENTRY          pEntry; 
    PHYSICAL_ADDRESS     AddrPhysical; 
    PLX_USER_MAPPING    *pMapObject; 
    PLX_PHYS_MEM_OBJECT *pMemObject; 
    MEMORY_CACHING_TYPE  CacheMode; 
 
 
    // Set default return value 
    pPciMem->UserAddr = 0; 
 
    pMemObject = NULL; 
 
    // Check if memory object is common buffer 
    if (pGbl_CommonBuffer != NULL) 
    { 
        if (pPciMem->PhysicalAddr == pGbl_CommonBuffer->BusPhysical) 
        { 
            pMemObject = pGbl_CommonBuffer; 
        } 
    } 
 
    // Find the memory object to map 
    if (pMemObject == NULL) 
    { 
        // Find the object in the list 
        KeAcquireSpinLock( 
            &(pdx->Lock_PhysicalMemList), 
            &IrqL_Original 
            ); 
 
        pEntry = pdx->List_PhysicalMem.Flink; 
 
        bFound = FALSE; 
 
        // Traverse list to find the desired list object 
        while (!bFound && (pEntry != &(pdx->List_PhysicalMem))) 
        { 
            // Get the object 
            pMemObject = 
                CONTAINING_RECORD( 
                    pEntry, 
                    PLX_PHYS_MEM_OBJECT, 
                    ListEntry 
                    ); 
 
            // Check if the physical addresses matches 
            if (pMemObject->BusPhysical == pPciMem->PhysicalAddr) 
            { 
                bFound = TRUE; 
            } 
            else 
            { 
                // Jump to next item in the list 
                pEntry = pEntry->Flink; 
            } 
        } 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_PhysicalMemList), 
            IrqL_Original 
            ); 
 
        if (!bFound) 
        { 
            DebugPrintf(( 
                "ERROR - Physical memory object not found in list, unable to map\n" 
                )); 
 
            return ApiInvalidAddress; 
        } 
    } 
 
    // Verify an MDL for the memory 
    if (pMemObject->pMdl == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - MDL does not exist for this memory, cannot map to user space\n" 
            )); 
 
        return ApiInsufficientResources; 
    } 
 
    // Set cache mode 
    if (Gbl_PhysicalMemoryCacheable) 
    { 
        CacheMode = MmCached; 
    } 
    else 
    { 
        CacheMode = MmNonCached; 
    } 
 
    // Attempt to map the region 
    pUserVa = 
        MmMapLockedPagesSpecifyCache( 
            pMemObject->pMdl, 
            UserMode, 
            CacheMode, 
            NULL, 
            FALSE, 
            NormalPagePriority 
            ); 
 
    if (pUserVa == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - Unable to map Physical address (0x%08lx) ==> User Space\n", 
            pMemObject->CpuPhysical 
            )); 
 
        return ApiInsufficientResources; 
    } 
 
    DebugPrintf(( 
        "Mapped Physical address (0x%08lx) ==> Virtual address (0x%p) (owner=%p)\n", 
        pMemObject->CpuPhysical, pUserVa, pOwner 
        )); 
 
    // Return virtual address 
    pPciMem->UserAddr = (PLX_UINT_PTR)pUserVa; 
 
    // Save mapping to list for this physical memory 
    pMapObject = 
        ExAllocatePool( 
            NonPagedPool, 
            sizeof(PLX_USER_MAPPING) 
            ); 
 
    if (pMapObject == NULL) 
    { 
        DebugPrintf(( 
            "ERROR: Unable to save mapping, insufficient memory\n" 
            )); 
    } 
    else 
    { 
        // Clear the virtual addresses 
        RtlZeroMemory( 
            pMapObject->pUserVa, 
            MAX_VIRTUAL_ADDR * sizeof(VOID*) 
            ); 
 
        // Record the IRP owner 
        pMapObject->pOwner = pOwner; 
 
        // Record the virtual address 
        pMapObject->pUserVa[0] = pUserVa; 
 
        // Save the mapping for later cleanup 
        ExInterlockedInsertTailList( 
            &(pMemObject->List_Mappings), 
            &(pMapObject->ListEntry), 
            &(pMemObject->Lock_MappingsList) 
            ); 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryUnmap 
 * 
 * Description:  Unmap physical memory from User virtual address space 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPhysicalMemoryUnmap( 
    DEVICE_EXTENSION *pdx, 
    PLX_PHYSICAL_MEM *pPciMem, 
    VOID             *pOwner 
    ) 
{ 
    VOID                *pUserVa; 
    KIRQL                IrqL_Original; 
    BOOLEAN              bFound; 
    PLIST_ENTRY          pEntry; 
    PHYSICAL_ADDRESS     AddrPhysical; 
    PLX_USER_MAPPING    *pMapObject; 
    PLX_PHYS_MEM_OBJECT *pMemObject; 
 
 
    pMemObject = NULL; 
 
    // Check if memory object is common buffer 
    if (pGbl_CommonBuffer != NULL) 
    { 
        if (pPciMem->PhysicalAddr == pGbl_CommonBuffer->BusPhysical) 
        { 
            pMemObject = pGbl_CommonBuffer; 
        } 
    } 
 
    // Find the memory object to unmap 
    if (pMemObject == NULL) 
    { 
        // Find the object in the list 
        KeAcquireSpinLock( 
            &(pdx->Lock_PhysicalMemList), 
            &IrqL_Original 
            ); 
 
        pEntry = pdx->List_PhysicalMem.Flink; 
 
        bFound = FALSE; 
 
        // Traverse list to find the desired list object 
        while (!bFound && (pEntry != &(pdx->List_PhysicalMem))) 
        { 
            // Get the object 
            pMemObject = 
                CONTAINING_RECORD( 
                    pEntry, 
                    PLX_PHYS_MEM_OBJECT, 
                    ListEntry 
                    ); 
 
            // Check if the physical addresses matches 
            if (pMemObject->BusPhysical == pPciMem->PhysicalAddr) 
            { 
                bFound = TRUE; 
            } 
            else 
            { 
                // Jump to next item in the list 
                pEntry = pEntry->Flink; 
            } 
        } 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_PhysicalMemList), 
            IrqL_Original 
            ); 
 
        if (!bFound) 
        { 
            DebugPrintf(( 
                "ERROR - Physical memory object not found in list, unable to unmap\n" 
                )); 
 
            return ApiInvalidAddress; 
        } 
    } 
 
    // Find the map object to unmap 
    KeAcquireSpinLock( 
        &(pMemObject->Lock_MappingsList), 
        &IrqL_Original 
        ); 
 
    pEntry = pMemObject->List_Mappings.Flink; 
 
    bFound = FALSE; 
 
    // Traverse list to find the desired list object 
    while (!bFound && (pEntry != &(pMemObject->List_Mappings))) 
    { 
        // Get the object 
        pMapObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_USER_MAPPING, 
                ListEntry 
                ); 
 
        // Compare owner & virtual address 
        if ((pOwner == pMapObject->pOwner) && 
            ((PLX_UINT_PTR)pMapObject->pUserVa[0] == pPciMem->UserAddr)) 
        { 
            // Remove entry from the list 
            RemoveEntryList( 
                pEntry 
                ); 
 
            bFound = TRUE; 
        } 
        else 
        { 
            // Jump to next item in the list 
            pEntry = pEntry->Flink; 
        } 
    } 
 
    KeReleaseSpinLock( 
        &(pMemObject->Lock_MappingsList), 
        IrqL_Original 
        ); 
 
    if (!bFound) 
    { 
        DebugPrintf(( 
            "ERROR - Mapping object not found in list, unable to unmap\n" 
            )); 
 
        return ApiInvalidAddress; 
    } 
 
    // Unmap the memory 
    if (pMapObject->pUserVa[0] != NULL) 
    { 
        DebugPrintf(( 
            "Unmapping Virtual address (0x%p) from User Space (owner=%p)...\n", 
            pMapObject->pUserVa[0], pOwner 
            )); 
 
        MmUnmapLockedPages( 
            pMapObject->pUserVa[0], 
            pMemObject->pMdl 
            ); 
 
        // Clear virtual address 
        pPciMem->UserAddr = 0; 
    } 
    else 
    { 
        DebugPrintf(( 
            "ERROR - Invalid virtual address (0x%p), cannot unmap\n", 
            pMapObject->pUserVa[0] 
            )); 
 
        return ApiInvalidAddress; 
    } 
 
    // Release memory for map object 
    ExFreePool( 
        pMapObject 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBarMap 
 * 
 * Description:  Map a PCI BAR Space into User virtual space 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciBarMap( 
    DEVICE_EXTENSION *pdx, 
    U8                BarIndex, 
    VOID             *pUserVa, 
    VOID             *pOwner 
    ) 
{ 
    U8                i; 
    PMDL              pMdl[MAX_VIRTUAL_ADDR]; 
    VOID             *Va[MAX_VIRTUAL_ADDR]; 
    VOID             *VaBase; 
    BOOLEAN           bMapError; 
    PLX_USER_MAPPING *pMapObject; 
 
 
    // Set default address 
    *(PLX_UINT_PTR*)pUserVa = 0; 
 
    // Verify BAR is of type memory 
    if (pdx->PciBar[BarIndex].bIsIoSpace) 
    { 
        DebugPrintf(( 
            "ERROR - BAR %d is an I/O space, cannot map to user space\n", 
            BarIndex 
            )); 
 
        return ApiInvalidPciSpace; 
    } 
 
    // Check if the space is valid 
    if (pdx->PciBar[BarIndex].Physical.QuadPart == 0) 
    { 
        DebugPrintf(( 
            "ERROR - BAR %d address (0x%08lx) is invalid\n", 
            BarIndex, pdx->PciBar[BarIndex].Physical 
            )); 
 
        return ApiInvalidAddress; 
    } 
 
    // Verify Kernel virtual address 
    if (pdx->PciBar[BarIndex].pVa == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - Kernel Virtual address for BAR %d is not valid\n", 
            BarIndex 
            )); 
 
        return ApiInvalidAddress; 
    } 
 
    // Make sure memory size is valid 
    if (pdx->PciBar[BarIndex].Size == 0) 
    { 
        DebugPrintf(( 
            "ERROR - Size of BAR %d is 0\n", 
            BarIndex 
            )); 
 
        return ApiInvalidSize; 
    } 
 
    // Verify the MDL 
    if (pdx->PciBar[BarIndex].pMdl == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - MDL does not exist for BAR %d\n", 
            BarIndex 
            )); 
 
        return ApiInsufficientResources; 
    } 
 
    // Make sure there is enough space to store all virtual addresses 
    if (pdx->PciBar[BarIndex].Size >  (MAX_VIRTUAL_ADDR * MAX_MDL_SIZE)) 
    { 
        DebugPrintf(( 
            "ERROR - Not enough virtual addresses for space of size %dMB\n", 
            (pdx->PciBar[BarIndex].Size >> 20) 
            )); 
 
        return ApiInsufficientResources; 
    } 
 
    // Set initial values 
    i         = 0; 
    pMdl[0]   = pdx->PciBar[BarIndex].pMdl; 
    VaBase    = NULL; 
    bMapError = FALSE; 
 
    // Clear the virtual addresses 
    RtlZeroMemory( 
        Va, 
        MAX_VIRTUAL_ADDR * sizeof(VOID*) 
        ); 
 
    // Attempt to map the BAR into user space 
    while ((i < MAX_VIRTUAL_ADDR) && (pMdl[i] != NULL)) 
    { 
        Va[i] = 
            MmMapLockedPagesSpecifyCache( 
                pMdl[i], 
                UserMode, 
                MmNonCached,                    // Do not cache PCI BAR space 
                VaBase,                         // User address to use 
                FALSE, 
                LowPagePriority 
                ); 
 
        // Check if the mapping succeeded 
        if (Va[i] == NULL) 
        { 
            DebugPrintf(( 
                "ERROR - Unable to map PCI BAR %d (0x%08lx) ==> User space\n", 
                BarIndex, pdx->PciBar[BarIndex].Physical 
                )); 
 
            bMapError = TRUE; 
        } 
        else if (VaBase != NULL) 
        { 
            // Make sure VA address is consecutive 
            if (VaBase != Va[i]) 
            { 
                DebugPrintf(( 
                    "ERROR - Unable to assign contiguous virtual addresses for PCI BAR %d (0x%08lx) ==> User space\n", 
                    BarIndex, pdx->PciBar[BarIndex].Physical 
                    )); 
 
                bMapError = TRUE; 
            } 
        } 
 
        if (bMapError) 
        { 
            // Release any previous mappings 
            while (i != 0) 
            { 
                i--; 
 
                MmUnmapLockedPages( 
                    Va[i], 
                    pMdl[i] 
                    ); 
            } 
 
            return ApiInsufficientResources; 
        } 
 
        // Adjust to next consecutive base address 
        VaBase = ((U8*)Va[i]) + MmGetMdlByteCount(pMdl[i]); 
 
        // Go to next entry 
        i++; 
        pMdl[i] = pMdl[i-1]->Next; 
    } 
 
    // Return initial base address 
    *(PLX_UINT_PTR*)pUserVa = (PLX_UINT_PTR)Va[0]; 
 
    DebugPrintf(( 
        "Mapped PCI BAR %d (0x%08lx) ==> User VA (0x%08lx) (owner=%p)\n", 
        BarIndex, pdx->PciBar[BarIndex].Physical, *(PLX_UINT_PTR*)pUserVa, pOwner 
        )); 
 
 
    // Add mapping to list for later unmapping 
    pMapObject = 
        ExAllocatePool( 
            NonPagedPool, 
            sizeof(PLX_USER_MAPPING) 
            ); 
 
    if (pMapObject == NULL) 
    { 
        DebugPrintf(("ERROR: Unable to save mapping, insufficient memory\n")); 
    } 
    else 
    { 
        // Record mapping properties 
        pMapObject->pOwner   = pOwner; 
        pMapObject->BarIndex = BarIndex; 
 
        RtlCopyMemory( 
            pMapObject->pUserVa, 
            Va, 
            MAX_VIRTUAL_ADDR * sizeof(VOID*) 
            ); 
 
        // Save the mapping for later cleanup 
        ExInterlockedInsertTailList( 
            &(pdx->List_BarMappings), 
            &(pMapObject->ListEntry), 
            &(pdx->Lock_BarMappingsList) 
            ); 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBarUnmap 
 * 
 * Description:  Unmap a previously mapped PCI BAR Space from User virtual space 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciBarUnmap( 
    DEVICE_EXTENSION *pdx, 
    VOID             *UserVa, 
    VOID             *pOwner 
    ) 
{ 
    U8                i; 
    PMDL              pMdl; 
    KIRQL             IrqL_Original; 
    PLIST_ENTRY       pEntry; 
    PLX_USER_MAPPING *pMapObject; 
 
 
    // Find the previous mapping object 
    KeAcquireSpinLock( 
        &(pdx->Lock_BarMappingsList), 
        &IrqL_Original 
        ); 
 
    pEntry = pdx->List_BarMappings.Flink; 
 
    while (pEntry != &(pdx->List_BarMappings)) 
    { 
        pMapObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_USER_MAPPING, 
                ListEntry 
                ); 
 
        if ((pMapObject->pOwner == pOwner) && 
            (pMapObject->pUserVa[0] == UserVa)) 
        { 
            // Remove map object from list 
            RemoveEntryList( 
                pEntry 
                ); 
 
            KeReleaseSpinLock( 
                &(pdx->Lock_BarMappingsList), 
                IrqL_Original 
                ); 
 
            DebugPrintf(( 
                "Unmapping Virtual address (0x%p) for BAR %d from User Space (owner=%p)...\n", 
                pMapObject->pUserVa[0], pMapObject->BarIndex, pOwner 
                )); 
 
            // Unmap the space 
            i    = 0; 
            pMdl = pdx->PciBar[pMapObject->BarIndex].pMdl; 
 
            while ((i < MAX_VIRTUAL_ADDR) && (pMapObject->pUserVa[i] != NULL)) 
            { 
                MmUnmapLockedPages( 
                    pMapObject->pUserVa[i], 
                    pMdl 
                    ); 
 
                i++; 
                pMdl = pMdl->Next; 
            } 
 
            // Release the list object 
            ExFreePool( 
                pMapObject 
                ); 
 
            return ApiSuccess; 
        } 
 
        // Jump to next item 
        pEntry = pEntry->Flink; 
    } 
 
    DebugPrintf(( 
        "ERROR - Map object not found in list...\n" 
        )); 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_BarMappingsList), 
        IrqL_Original 
        ); 
 
    return ApiInvalidAddress; 
}