/*******************************************************************************
 * Copyright (c) PLX Technology, Inc.
 *
 * PLX Technology Inc. licenses this source file under the GNU Lesser General Public
 * License (LGPL) version 2.  This source file may be modified or redistributed
 * under the terms of the LGPL and without express permission from PLX Technology.
 *
 * 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.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * File Name:
 *
 *      SuppFunc.c
 *
 * Description:
 *
 *      Additional support functions
 *
 * Revision History:
 *
 *      09-01-10 : PLX SDK v6.40
 *
 ******************************************************************************/


#include "ApiFunc.h"
#include "GlobalVars.h"
#include "PciFunc.h"
#include "PciRegs.h"
#include "PlxInterrupt.h"
#include "SuppFunc.h"

#if defined(PLX_DBG_DEST_FILE)
    #include <stdio.h>   // For PlxLogPrintf
    #include <stdarg.h>
#endif




#if defined(PLX_DBG_DEST_FILE)
/*******************************************************************************
 *
 * Function   :  PlxLogPrintf
 *
 * Description:  Logs printf() style data to a file for debug purposes
 *
 ******************************************************************************/
VOID
PlxLogPrintf(
    const char *format,
    ...
    )
{
    int               i;
    int               j;
    int               StringLength;
    char              pOut[300];
    char              ConvFormat[300];
    HANDLE            hFile;
    va_list           pArgs;
    NTSTATUS          status;
    LARGE_INTEGER     ByteOffset;
    UNICODE_STRING    ObjectName;
    IO_STATUS_BLOCK   IoStatus;
    OBJECT_ATTRIBUTES ObjAttributes;


    // File functions are only supported at PASSIVE_LEVEL
    if (KeGetCurrentIrql() != PASSIVE_LEVEL)
        return;

    // Convert '\n' to '\r\n' to make MS-DOS text file
    i = 0;
    j = 0;

    while (format[i] != '\0')
    {
        if (format[i] == '\n')
        {
            ConvFormat[j] = '\r';
            j++;
        }

        // Copy character
        ConvFormat[j] = format[i];

        // Jump to next character
        i++;
        j++;
    }

    // Terminate string
    ConvFormat[j] = '\0';

    // Initialize the optional arguments pointer
    va_start(pArgs, format);

    // Build string to write
    StringLength = vsprintf(pOut, ConvFormat, pArgs);

    // Terminate arguments pointer
    va_end(pArgs);

    // Create the unicode file name
    RtlInitUnicodeString(
        &ObjectName,
        PLX_LOG_FILE_UNICODE
        );

    // Initialize file object
    InitializeObjectAttributes(
        &ObjAttributes,                // Attributes object
        &ObjectName,                   // Unicode object name
        OBJ_CASE_INSENSITIVE           // Attributes
         | OBJ_KERNEL_HANDLE,
        NULL,                          // Root directory handle
        NULL                           // Security Descriptor
        );

    // Open the file for appending output
    status =
        ZwCreateFile(
            &hFile,                    // File handle
            FILE_APPEND_DATA,          // Desired Access
            &ObjAttributes,            // Object attributes
            &IoStatus,                 // I/O status information
            NULL,                      // Allocation Size
            FILE_ATTRIBUTE_NORMAL,     // File Attributes
            0,                         // Share access
            FILE_OPEN_IF,              // Create disposition
            FILE_NON_DIRECTORY_FILE,   // Create options
            NULL,                      // EaBuffer
            0                          // EaLength
            );

    if (status != STATUS_SUCCESS)
    {
        DbgPrint(PLX_DRIVER_NAME ": ERROR: Unable to open/create log file (status=%X)\n", status);
        return;
    }

    // Setup to write at end-of-file
    ByteOffset.HighPart = -1;
    ByteOffset.LowPart  = FILE_WRITE_TO_END_OF_FILE;

    // Write string to file
    status =
        ZwWriteFile(
            hFile,                     // File handle
            NULL,                      // Event handle
            NULL,                      // APC Routine
            NULL,                      // APC Context
            &IoStatus,                 // I/O status information
            pOut,                      // Buffer to write
            StringLength,              // Buffer length
            &ByteOffset,               // Byte offset
            NULL                       // Key
            );

    if ((status != STATUS_SUCCESS) && (status != STATUS_PENDING))
    {
        DbgPrint(PLX_DRIVER_NAME ": ERROR: Unable to write to log file (status=%X)\n", status);
    }

    // Close the file
    ZwClose( hFile );
}
#endif  // PLX_DBG_DEST_FILE




/*******************************************************************************
 *
 * Function   :  Plx_sleep
 *
 * Description:  Function as a normal sleep. Parameter is in millisecond
 *
 ******************************************************************************/
VOID
Plx_sleep(
    U32 delay
    )
{
    LARGE_INTEGER liTime;


    /* Convert milliseconds to 100-nanosecond increments using:
     *
     *     1 ns  = 10 ^ -9 sec
     *   100 ns  = 10 ^ -7 sec (1 timer interval)
     *     1 ms  = 10 ^ -3 sec
     *     1 ms  = (1 timer interval) * 10^4
     */
    delay = delay * 10000;

    // Negative value means relative time, not absolute
    liTime.QuadPart = (S64)(-(LONG)delay);

    KeDelayExecutionThread(
        KernelMode,
        TRUE,
        &liTime
        );
}




/*******************************************************************************
 *
 * Function   :  PlxSynchronizedRegisterModify
 *
 * Description:  Synchronized function with ISR to modify a PLX register
 *
 ******************************************************************************/
BOOLEAN
PlxSynchronizedRegisterModify(
    PLX_REG_DATA *pRegData
    )
{
    U32 RegValue;


    PLX_PCI_REG_READ(
        pRegData->pdx,
        (U16)pRegData->offset,
        &RegValue
        );

    RegValue |= pRegData->BitsToSet;
    RegValue &= ~(pRegData->BitsToClear);

    PLX_PCI_REG_WRITE(
        pRegData->pdx,
        (U16)pRegData->offset,
        RegValue
        );

    return TRUE;
}




/*******************************************************************************
 *
 * Function   :  PlxSynchronizedGetInterruptSource
 *
 * Description:  Synchronized function with ISR to get interrupt source
 *
 ******************************************************************************/
BOOLEAN
PlxSynchronizedGetInterruptSource(
    PLX_INTERRUPT_DATA *pIntData
    )
{
    // Get active interrupt flags
    pIntData->Source_Ints     = pIntData->pdx->Source_Ints;
    pIntData->Source_Doorbell = pIntData->pdx->Source_Doorbell;

    // Clear interrupt flags
    pIntData->pdx->Source_Ints     = INTR_TYPE_NONE;
    pIntData->pdx->Source_Doorbell = 0;

    return TRUE;
}




/*******************************************************************************
 *
 * Function   :  PlxSignalNotifications
 *
 * Description:  Called by the DPC to signal any notification events
 *
 * Note       :  This is expected to be called at DPC level
 *
 ******************************************************************************/
VOID
PlxSignalNotifications(
    DEVICE_EXTENSION   *pdx,
    PLX_INTERRUPT_DATA *pIntData
    )
{
    U32              SourceDB;
    U32              SourceInt;
    PLIST_ENTRY      pEntry;
    PLX_WAIT_OBJECT *pWaitObject;


    KeAcquireSpinLockAtDpcLevel(
        &(pdx->Lock_WaitObjectsList)
        );

    // Get the interrupt wait list
    pEntry = pdx->List_WaitObjects.Flink;

    // Traverse wait objects and wake-up processes
    while (pEntry != &(pdx->List_WaitObjects))
    {
        // Get the wait object
        pWaitObject =
            CONTAINING_RECORD(
                pEntry,
                PLX_WAIT_OBJECT,
                ListEntry
                );

        // Set active notifications
        SourceInt = pWaitObject->Notify_Flags & pIntData->Source_Ints;
        SourceDB  = pWaitObject->Notify_Doorbell & pIntData->Source_Doorbell;

        // Check if waiting for active interrupt
        if (SourceInt || SourceDB)
        {
            DebugPrintf((
                "DPC signaling wait object (%p)\n",
                pWaitObject
                ));

            // Save new interrupt sources in case later requested
            pWaitObject->Source_Ints     |= SourceInt;
            pWaitObject->Source_Doorbell |= SourceDB;

            // Signal wait object
            KeSetEvent(
                pWaitObject->pKEvent,
                IO_NO_INCREMENT,
                FALSE
                );
        }

        // Jump to next item in the list
        pEntry = pEntry->Flink;
    }

    KeReleaseSpinLockFromDpcLevel(
        &(pdx->Lock_WaitObjectsList)
        );
}




/*******************************************************************************
 *
 * Function   :  PlxRegistryInformationGet
 *
 * Description:  Gets driver configuration information from the registry
 *
 ******************************************************************************/
VOID
PlxRegistryInformationGet(
    UNICODE_STRING    *pRegistryPath,
    PLX_REGISTRY_INFO *pRegistryInfo
    )
{
    NTSTATUS                  status;
    RTL_QUERY_REGISTRY_TABLE  RegTable[2];


    RtlZeroMemory(
        RegTable,
        sizeof(RegTable)
        );

    RegTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT;

    // Get Common buffer size
    RegTable[0].Name         = L"CommonBufferSize";
    RegTable[0].EntryContext = &(pRegistryInfo->Size_CommonBuffer);

    status =
        RtlQueryRegistryValues(
            RTL_REGISTRY_ABSOLUTE,
            pRegistryPath->Buffer,
            RegTable,
            NULL,
            NULL
            );

    if ( !NT_SUCCESS(status) )
    {
        pRegistryInfo->Size_CommonBuffer = DEFAULT_SIZE_COMMON_BUFFER;
    }
}




/*******************************************************************************
 *
 * Function   :  GetBarIndex
 *
 * Description:  Determine which PCI BAR contains the specified address
 *
 ******************************************************************************/
U8
GetBarIndex(
    DEVICE_EXTENSION *pdx,
    U32               BarValue,
    U32              *pPciBars
    )
{
    U8  i;
    U32 CompareAddress;


    // Compare the physical address with each BAR
    for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++)
    {
        if (pPciBars[i] & (1 << 0))
            CompareAddress = pPciBars[i] & ~(0x3);
        else
            CompareAddress = pPciBars[i] & ~(0xF);

        if (BarValue == CompareAddress)
            return i;
    }

    // Unable to find the BAR index
    DebugPrintf((
        "ERROR - GetBarIndex() unable to match BAR value (%08lX)\n",
        BarValue
        ));

    return (U8)-1;
}




/*******************************************************************************
 *
 * Function   :  PlxCompleteIrp
 *
 * Description:  Complete an IRP
 *
 ******************************************************************************/
NTSTATUS
PlxCompleteIrp(
    PIRP     pIrp,
    NTSTATUS status
    )
{
    if (status == STATUS_PENDING)
    {
        DebugPrintf(("...IRP (%p) is pending\n", pIrp));
        return STATUS_PENDING;
    }

    DebugPrintf((
        "...%s IRP %p (status=%08X)\n",
        (status == STATUS_CANCELLED) ? "Cancelled" : "Completed",
        pIrp, status
        ));

    pIrp->IoStatus.Status = status;

    IoCompleteRequest(
        pIrp,
        IO_NO_INCREMENT
        );

    return status;
}




/*******************************************************************************
 *
 * Function   :  PlxCompleteIrpWithInformation
 *
 * Description:  Complete an IRP including modification of the Information field
 *
 ******************************************************************************/
NTSTATUS
PlxCompleteIrpWithInformation(
    PIRP         pIrp,
    NTSTATUS     status,
    PLX_UINT_PTR Info
    )
{
    DebugPrintf((
        "...%s IRP %p (status=%08X info=%d)\n",
        (status == STATUS_CANCELLED) ? "Cancelled" : "Completed",
        pIrp, status, (U32)Info
        ));

    pIrp->IoStatus.Status      = status;
    pIrp->IoStatus.Information = Info;

    IoCompleteRequest(
        pIrp,
        IO_NO_INCREMENT
        );

    return status;
}




/*******************************************************************************
 *
 * Function   :  PlxGetExtendedCapabilityOffset
 *
 * Description:  Scans the capability list to search for a specific capability
 *
 ******************************************************************************/
U16
PlxGetExtendedCapabilityOffset(
    DEVICE_EXTENSION *pdx,
    U16               CapabilityId
    )
{
    U16 Offset_Cap;
    U32 RegValue;


    // Get offset of first capability
    PLX_PCI_REG_READ(
        pdx,
        0x34,           // PCI capabilities pointer
        &RegValue
        );

    // If link is down, PCI reg accesses will fail
    if (RegValue == (U32)-1)
        return 0;

    // Set first capability
    Offset_Cap = (U16)RegValue;

    // Traverse capability list searching for desired ID
    while ((Offset_Cap != 0) && (RegValue != (U32)-1))
    {
        // Get next capability
        PLX_PCI_REG_READ(
            pdx,
            Offset_Cap,
            &RegValue
            );

        if ((U8)RegValue == (U8)CapabilityId)
        {
            // Capability found, return base offset
            return Offset_Cap;
        }

        // Jump to next capability
        Offset_Cap = (U16)((RegValue >> 8) & 0xFF);
    }

    // Capability not found
    return 0;
}




/*******************************************************************************
 *
 * Function   :  PlxUpdateDeviceKey
 *
 * Description:  Updates device key information
 *
 ******************************************************************************/
VOID
PlxUpdateDeviceKey(
    DEVICE_EXTENSION *pdx
    )
{
    U32 RegValue;


    // Read the Device/Vendor ID
    PLX_PCI_REG_READ(
        pdx,
        0,
        &RegValue
        );

    // Store device/vendor IDs
    pdx->Key.VendorId = (U16)RegValue;
    pdx->Key.DeviceId = (U16)(RegValue >> 16);

    // Update Subsystem Device/Vendor ID
    PLX_PCI_REG_READ(
        pdx,
        0x2c,        // PCI Subsystem ID
        &RegValue
        );

    pdx->Key.SubVendorId = (U16)(RegValue & 0xffff);
    pdx->Key.SubDeviceId = (U16)(RegValue >> 16);

    // Update Revision ID
    PLX_PCI_REG_READ(
        pdx,
        0x08,        // PCI Revision ID
        &RegValue
        );

    pdx->Key.Revision = (U8)(RegValue & 0xFF);
}




/*******************************************************************************
 *
 * Function   :  PlxPowerStateIsSupported
 *
 * Description:  Verify whether the device supports a power state
 *
 ******************************************************************************/
BOOLEAN
PlxPowerStateIsSupported(
    DEVICE_EXTENSION   *pdx,
    DEVICE_POWER_STATE  PowerState
    )
{
    U16 Offset_PM;
    U32 RegValue;


    // Verify that the Power State is supported
    switch (PowerState)
    {
        case PowerDeviceD0:
        case PowerDeviceD3:
            // These states supported by all devices
            return TRUE;

        case PowerDeviceD1:
        case PowerDeviceD2:
            // Check if device supports Power Management capability
            Offset_PM =
                PlxGetExtendedCapabilityOffset(
                    pdx,
                    CAP_ID_POWER_MAN
                    );

            if (Offset_PM == 0)
            {
                return FALSE;
            }

            // Get the PM Capabilities
            PLX_PCI_REG_READ(
                pdx,
                Offset_PM,
                &RegValue
                );

            // Check supported power states as reported by device
            if  (((PowerState == PowerDeviceD1) && (RegValue & (1 << 25))) ||
                 ((PowerState == PowerDeviceD2) && (RegValue & (1 << 26))))
            {
                return TRUE;
            }
            break;

        case PowerDeviceUnspecified:
        default:
            break;
    }

    return FALSE;
}




/*******************************************************************************
 *
 * Function   :  PlxPowerStateSet
 *
 * Description:  Set the device power state
 *
 ******************************************************************************/
PLX_STATUS
PlxPowerStateSet(
    DEVICE_EXTENSION   *pdx,
    DEVICE_POWER_STATE  PowerState
    )
{
    U16 Offset_PM;
    U32 RegValue;


    // Check if New capabilities are enabled
    Offset_PM =
        PlxGetExtendedCapabilityOffset(
            pdx,
            CAP_ID_POWER_MAN
            );

    if (Offset_PM == 0)
    {
        return ApiUnsupportedFunction;
    }

    // Check if we support the power state
    if (PlxPowerStateIsSupported(
            pdx,
            PowerState
            ) == FALSE)
    {
        return ApiInvalidPowerState;
    }

    // Adjust offset to PM Control/Status register
    Offset_PM += sizeof(U32);

    DebugPrintf(("Putting device into state ==> "));

    // Get the power state
    PLX_PCI_REG_READ(
        pdx,
        Offset_PM,
        &RegValue
        );

    // Clear power state (bits [1:0])
    RegValue = (RegValue & ~0x3);

    // Set new power state (bits [1:0])
    if (PowerState == PowerDeviceD0)
    {
        DebugPrintf_Cont(("D0\n"));
        RegValue |= 0;
    }
    else if (PowerState == PowerDeviceD1)
    {
        DebugPrintf_Cont(("D1\n"));
        RegValue |= 1;
    }
    else if (PowerState == PowerDeviceD2)
    {
        DebugPrintf_Cont(("D2\n"));
        RegValue |= 2;
    }
    else if (PowerState == PowerDeviceD3)
    {
        DebugPrintf_Cont(("D3\n"));
        RegValue |= 3;
    }

    // Set the new power state
    PLX_PCI_REG_WRITE(
        pdx,
        Offset_PM,
        RegValue
        );

    return ApiSuccess;
}




/*******************************************************************************
 *
 * Function   :  PlxPciBarResourceMap
 *
 * Description:  Maps a PCI BAR resource into kernel space
 *
 ******************************************************************************/
NTSTATUS
PlxPciBarResourceMap(
    DEVICE_EXTENSION *pdx,
    U8                BarIndex
    )
{
    U32                  SizeToMap;
    U32                  SizeRemain;
    VOID                *pVa;
    PMDL                 pMdl;
    PMDL                 pMdl_Previous;
    PHYSICAL_ADDRESS     PhysAddr;
    MEMORY_CACHING_TYPE  CacheMode;


    /***********************************************************
     * For pre-fetchable region, enable write-combining
     * Cached mode seems to have no effect, but write-combining
     * results in much better CPU write performance.
     **********************************************************/
    if (pdx->PciBar[BarIndex].Properties.Flags & PLX_BAR_FLAG_PREFETCHABLE)
        CacheMode = MmWriteCombined;
    else
        CacheMode = MmNonCached;

    // Setup physical address for mapping
    PhysAddr.QuadPart = pdx->PciBar[BarIndex].Properties.Physical;

    // Map into kernel virtual space
    pdx->PciBar[BarIndex].pVa =
        MmMapIoSpace(
            PhysAddr,
            (ULONG)pdx->PciBar[BarIndex].Properties.Size,
            CacheMode
            );

    if (pdx->PciBar[BarIndex].pVa == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /******************************************************************
     * A future mapping into user space will require an MDL. Due to
     * the limited region a single MDL can describe, multiple MDL's
     * must be allocated and chained together via the 'Next' field.
     *****************************************************************/

    // Set initial values
    pVa        = pdx->PciBar[BarIndex].pVa;
    SizeRemain = (U32)pdx->PciBar[BarIndex].Properties.Size;

    while (SizeRemain != 0)
    {
        // Determine size for MDL
        if (SizeRemain <= MAX_MDL_SIZE)
        {
            SizeToMap = SizeRemain;
        }
        else
        {
            SizeToMap = MAX_MDL_SIZE;
        }

        // Get an MDL for future mapping into user space
        pMdl =
            IoAllocateMdl(
                pVa,
                SizeToMap,
                FALSE,          // Is this a secondary buffer?
                FALSE,          // Charge quota?
                NULL            // No IRP associated with MDL
                );

        // Check if the MDL allocation succeeded
        if (pMdl == NULL)
        {
            DebugPrintf(("ERROR - MDL allocation for space failed\n"));

            // Release any created MDLs
            while (pdx->PciBar[BarIndex].pMdl != NULL)
            {
                pMdl = pdx->PciBar[BarIndex].pMdl;

                pdx->PciBar[BarIndex].pMdl = pMdl->Next;

                IoFreeMdl( pMdl );
            }

            return STATUS_SUCCESS;
        }

        // Build the MDL
        MmBuildMdlForNonPagedPool( pMdl );

        // Clear next MDL pointer
        pMdl->Next = NULL;

        // Store MDL
        if (pdx->PciBar[BarIndex].pMdl == NULL)
        {
            // Insert first MDL
            pdx->PciBar[BarIndex].pMdl = pMdl;
        }
        else
        {
            // Add new MDL to list
            pMdl_Previous->Next = pMdl;
        }

        // Update previous pointer
        pMdl_Previous = pMdl;

        // Adjust for next virtual address
        (U8*)pVa += SizeToMap;

        // Adjust remaining count
        SizeRemain -= SizeToMap;
    }

    return STATUS_SUCCESS;
}




/*******************************************************************************
 *
 * Function   :  PlxPciBarResourcesUnmap
 *
 * Description:  Unmap all mapped PCI BAR memory for a device
 *
 ******************************************************************************/
VOID
PlxPciBarResourcesUnmap(
    DEVICE_EXTENSION *pdx
    )
{
    U8   i;
    PMDL pMdl;


    // Go through all the BARS
    for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++)
    {
        // Unmap the space from Kernel space if previously mapped
        if ((pdx->PciBar[i].pVa != NULL) || (pdx->PciBar[i].pMdl != NULL))
        {
            DebugPrintf((
                "Unmap BAR %d from kernel space (VA=%p)\n",
                i, pdx->PciBar[i].pVa
                ));

            // Release the MDLs describing the BAR space
            while (pdx->PciBar[i].pMdl != NULL)
            {
                pMdl = pdx->PciBar[i].pMdl;

                pdx->PciBar[i].pMdl = pMdl->Next;

                IoFreeMdl( pMdl );
            }

            // Unmap from kernel space
            if (pdx->PciBar[i].pVa != NULL)
            {
                MmUnmapIoSpace(
                    pdx->PciBar[i].pVa,
                    (ULONG)pdx->PciBar[i].Properties.Size
                    );
                pdx->PciBar[i].pVa = NULL;
            }
        }
    }
}




/*******************************************************************************
 *
 * Function   :  PlxPciBarSpaceUnmapAll_ByOwner
 *
 * Description:  Unmap any PCI BAR spaces assigned to the specified owner
 *
 ******************************************************************************/
VOID
PlxPciBarSpaceUnmapAll_ByOwner(
    DEVICE_EXTENSION *pdx,
    VOID             *pOwner
    )
{
    VOID             *UserVa;
    KIRQL             IrqL_Original;
    PLIST_ENTRY       pEntry;
    PLX_USER_MAPPING *pMapObject;


    KeAcquireSpinLock(
        &(pdx->Lock_BarMappingsList),
        &IrqL_Original
        );

    pEntry = pdx->List_BarMappings.Flink;

    // Traverse list to find the desired list objects
    while (pEntry != &(pdx->List_BarMappings))
    {
        // Get the object
        pMapObject =
            CONTAINING_RECORD(
                pEntry,
                PLX_USER_MAPPING,
                ListEntry
                );

        // Check if owner matches
        if (pMapObject->pOwner == pOwner)
        {
            // Copy address
            UserVa = pMapObject->pUserVa[0];

            // Release list lock
            KeReleaseSpinLock(
                &(pdx->Lock_BarMappingsList),
                IrqL_Original
                );

            // Unmap BAR space
            PlxPciBarUnmap(
                pdx,
                UserVa,
                pOwner
                ); 

            KeAcquireSpinLock(
                &(pdx->Lock_BarMappingsList),
                &IrqL_Original
                );

            // Restart parsing the list from the beginning
            pEntry = pdx->List_BarMappings.Flink;
        }
        else
        {
            // Jump to next item
            pEntry = pEntry->Flink;
        }
    }

    KeReleaseSpinLock(
        &(pdx->Lock_BarMappingsList),
        IrqL_Original
        );
}




/*******************************************************************************
 *
 * Function   :  PlxPciPhysicalMemoryFreeAll_ByOwner
 *
 * Description:  Unmap & release all physical memory assigned to the specified owner
 *
 ******************************************************************************/
VOID
PlxPciPhysicalMemoryFreeAll_ByOwner(
    DEVICE_EXTENSION *pdx,
    VOID             *pOwner
    )
{
    KIRQL                IrqL_Original;
    PLIST_ENTRY          pEntry;
    PLX_PHYSICAL_MEM     PciMem;
    PLX_PHYS_MEM_OBJECT *pMemObject;


    KeAcquireSpinLock(
        &(pdx->Lock_PhysicalMemList),
        &IrqL_Original
        );

    pEntry = pdx->List_PhysicalMem.Flink;

    // Traverse list to find the desired list objects
    while (pEntry != &(pdx->List_PhysicalMem))
    {
        // Get the object
        pMemObject =
            CONTAINING_RECORD(
                pEntry,
                PLX_PHYS_MEM_OBJECT,
                ListEntry
                );

        // Check if owner matches
        if (pMemObject->pOwner == pOwner)
        {
            // Copy memory information
            PciMem.PhysicalAddr = pMemObject->BusPhysical;
            PciMem.Size         = pMemObject->Size;

            // Release list lock
            KeReleaseSpinLock(
                &(pdx->Lock_PhysicalMemList),
                IrqL_Original
                );

            // Release the memory & remove from list
            PlxPciPhysicalMemoryFree(
                pdx,
                &PciMem
                ); 

            KeAcquireSpinLock(
                &(pdx->Lock_PhysicalMemList),
                &IrqL_Original
                );

            // Restart parsing the list from the beginning
            pEntry = pdx->List_PhysicalMem.Flink;
        }
        else
        {
            // Jump to next item
            pEntry = pEntry->Flink;
        }
    }

    KeReleaseSpinLock(
        &(pdx->Lock_PhysicalMemList),
        IrqL_Original
        );
}




/*******************************************************************************
 *
 * Function   :  PlxPciPhysicalMemoryUnmapAll_ByOwner
 *
 * Description:  Unmap any physical memory assigned to the specified owner
 *
 ******************************************************************************/
VOID
PlxPciPhysicalMemoryUnmapAll_ByOwner(
    DEVICE_EXTENSION    *pdx,
    PLX_PHYS_MEM_OBJECT *pMemObject,
    VOID                *pOwner
    )
{
    VOID             *pUserVa;
    KIRQL             IrqL_Original;
    PLIST_ENTRY       pEntry;
    PLX_PHYSICAL_MEM  PciMem;
    PHYSICAL_ADDRESS  AddrPhysical;
    PLX_USER_MAPPING *pMapObject;


    // Verify Memory Object
    if (pMemObject == NULL)
    {
        return;
    }

    // Verify size
    if (pMemObject->Size == 0)
    {
        return;
    }

    // Setup memory properties
    PciMem.PhysicalAddr = pMemObject->BusPhysical;
    PciMem.Size         = pMemObject->Size;

    // Find the map object to unmap
    KeAcquireSpinLock(
        &(pMemObject->Lock_MappingsList),
        &IrqL_Original
        );

    pEntry = pMemObject->List_Mappings.Flink;

    // Traverse list to find the desired list object
    while (pEntry != &(pMemObject->List_Mappings))
    {
        // Get the object
        pMapObject =
            CONTAINING_RECORD(
                pEntry,
                PLX_USER_MAPPING,
                ListEntry
                );

        // Check if the owner matches
        if ((pOwner == NULL) || (pMapObject->pOwner == pOwner))
        {
            // Copy virtual address
            PciMem.UserAddr = (PLX_UINT_PTR)pMapObject->pUserVa[0];

            // Release list lock
            KeReleaseSpinLock(
                &(pMemObject->Lock_MappingsList),
                IrqL_Original
                );

            // Unmap the memory & remove from list
            PlxPciPhysicalMemoryUnmap(
                pdx,
                &PciMem,
                pMapObject->pOwner
                ); 

            KeAcquireSpinLock(
                &(pMemObject->Lock_MappingsList),
                &IrqL_Original
                );

            // Restart parsing the list from the beginning
            pEntry = pMemObject->List_Mappings.Flink;
        }
        else
        {
            // Jump to next item
            pEntry = pEntry->Flink;
        }
    }

    KeReleaseSpinLock(
        &(pMemObject->Lock_MappingsList),
        IrqL_Original
        );
}




/*******************************************************************************
 *
 * Function   :  Plx_dma_buffer_alloc
 *
 * Description:  Allocates physically contiguous non-paged memory
 *
 ******************************************************************************/
VOID*
Plx_dma_buffer_alloc(
    DEVICE_EXTENSION    *pdx,
    PLX_PHYS_MEM_OBJECT *pMemObject
    )
{
    PHYSICAL_ADDRESS BufferAddress;


    // Verify the DMA adapter object
    if (pdx->pDmaAdapter == NULL)
    {
        DebugPrintf(("ERROR - DMA Adapter object does not exist, cannot allocate physical memory\n"));
        return NULL;
    }

    // Attempt to allocate contiguous memory
    pMemObject->pKernelVa =
        pdx->pDmaAdapter->DmaOperations->AllocateCommonBuffer(
            pdx->pDmaAdapter,
            pMemObject->Size,
            &BufferAddress,
            pMemObject->bCacheable      // Enable Caching for buffer?
            );

    if (pMemObject->pKernelVa == NULL)
    {
        return NULL;
    }

    // Store the buffer logical/bus physical address
    pMemObject->BusPhysical = BufferAddress.QuadPart;

    // CPU Address is the same as bus on this architecture
    pMemObject->CpuPhysical = BufferAddress.QuadPart;

    // Clear the buffer
    RtlZeroMemory(
        pMemObject->pKernelVa,
        pMemObject->Size
        );

    DebugPrintf(("Allocated physical memory...\n"));

    DebugPrintf((
        "    CPU Phys Addr: %08lx\n",
        (PLX_UINT_PTR)pMemObject->CpuPhysical
        ));

    DebugPrintf((
        "    Bus Phys Addr: %08lx\n",
        (PLX_UINT_PTR)pMemObject->BusPhysical
        ));

    DebugPrintf((
        "    Kernel VA    : %p\n",
        pMemObject->pKernelVa
        ));

    DebugPrintf((
        "    Size         : %8X (%d %s)\n",
        pMemObject->Size,
        (pMemObject->Size < (10 << 10)) ? pMemObject->Size : (pMemObject->Size >> 10),
        (pMemObject->Size < (10 << 10)) ? "bytes" : "KB"
        ));

    DebugPrintf((
        "    Cacheable?   : %s\n",
        (pMemObject->bCacheable == FALSE) ? "No" : "Yes"
        ));

    // Return the kernel virtual address of the allocated block
    return pMemObject->pKernelVa;
}




/*******************************************************************************
 *
 * Function   :  Plx_dma_buffer_free
 *
 * Description:  Frees previously allocated physical memory
 *
 ******************************************************************************/
VOID
Plx_dma_buffer_free(
    DEVICE_EXTENSION    *pdx,
    PLX_PHYS_MEM_OBJECT *pMemObject
    )
{
    PHYSICAL_ADDRESS BufferAddress;


    // Verify the DMA adapter object
    if (pdx->pDmaAdapter == NULL)
    {
        DebugPrintf(("ERROR - DMA Adapter object does not exist, cannot free physical memory\n"));
        return;
    }

    // Set logical address
    BufferAddress.QuadPart = pMemObject->BusPhysical;

    // Release the buffer
    pdx->pDmaAdapter->DmaOperations->FreeCommonBuffer(
        pdx->pDmaAdapter,
        pMemObject->Size,
        BufferAddress,
        pMemObject->pKernelVa,
        pMemObject->bCacheable      // Enable Caching for buffer?
        );

    DebugPrintf((
        "Released physical memory at %08I64X (%d %s)\n",
        pMemObject->CpuPhysical,
        (pMemObject->Size < (10 << 10)) ? pMemObject->Size : (pMemObject->Size >> 10),
        (pMemObject->Size < (10 << 10)) ? "bytes" : "KB"
        ));

    // Clear memory object properties
    RtlZeroMemory(
        pMemObject,
        sizeof(PLX_PHYS_MEM_OBJECT)
        );
}
