/*******************************************************************************
 * 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:
 *
 *      Driver.c
 *
 * Description:
 *
 *      Initializes the driver and claims system resources for the device
 *
 * Revision History:
 *
 *      12-01-12 : PLX SDK v7.00
 *
 *****************************************************************************/


#include <Wdm.h>             // Required before including Ntstrsafe.h
#include <Ntstrsafe.h>       // For Rtlxxx string functions
#include "ApiFunc.h"
#include "Dispatch.h"
#include "Driver.h"
#include "GlobalVars.h"
#include "PciFunc.h"
#include "PlugPlay.h"
#include "PlxChipFn.h"
#include "PlxInterrupt.h"
#include "Power.h"
#include "SuppFunc.h"


#if defined(ALLOC_PRAGMA) && !defined(PLX_DEBUG)
    #pragma alloc_text(INIT, DriverEntry)
#endif




/*******************************************************************************
 *
 * Function   :  DriverEntry
 *
 * Description:  Entry point for the driver
 *
 ******************************************************************************/
NTSTATUS
DriverEntry(
    DRIVER_OBJECT  *pDriverObject,
    UNICODE_STRING *pRegistryPath
    )
{
    PLX_REGISTRY_INFO RegistryInfo;


    DebugPrintf_Cont(("\n"));
    DebugPrintf(("<========================================================>\n"));
    DebugPrintf((
        "PLX driver v%d.%02d (%d-bit) - built on %s %s\n",
        PLX_SDK_VERSION_MAJOR, PLX_SDK_VERSION_MINOR,
        (U32)(sizeof(PLX_UINT_PTR) * 8),
        __DATE__, __TIME__
        ));

    DebugPrintf((
        "Driver supports WDM v%x.%x\n",
        WDM_MAJORVERSION, WDM_MINORVERSION
        ));

    // Default OS to Server 2003 or lower
    Gbl_WindowsVistaOrHigher = FALSE;

    // Check which OS version is running
    if (RtlIsNtDdiVersionAvailable( NTDDI_WIN8 ))
    {
        DebugPrintf(("OS is Windows 8 (WDM v6.2) or higher\n"));
        Gbl_WindowsVistaOrHigher = TRUE;
    }
    else if (RtlIsNtDdiVersionAvailable( NTDDI_WIN7 ))
    {
        DebugPrintf(("OS is Windows 7 (WDM v6.1) or higher\n"));
        Gbl_WindowsVistaOrHigher = TRUE;
    }
    else if (RtlIsNtDdiVersionAvailable( NTDDI_VISTA ) ||
             RtlIsNtDdiVersionAvailable( NTDDI_WS08 ))
    {
        DebugPrintf(("OS is Windows Vista or Server 2008 (WDM v6.0)\n"));
        Gbl_WindowsVistaOrHigher = TRUE;
    }
    else if (RtlIsNtDdiVersionAvailable( NTDDI_WS03 ))
    {
        DebugPrintf(("OS is Windows Server 2003 (WDM v1.30)\n"));
    }
    else if (RtlIsNtDdiVersionAvailable( NTDDI_WINXP ))
    {
        DebugPrintf(("OS is Windows XP (WDM v1.20)\n"));
    }
    else if (RtlIsNtDdiVersionAvailable( NTDDI_WIN2K ))
    {
        DebugPrintf(("OS is Windows 2000 (WDM v1.10)\n"));
    }
    else
    {
        DebugPrintf(("OS is <Unknown> (WDM v?)\n"));
    }

    // Fill in the appropriate dispatch handlers
    pDriverObject->DriverUnload                         = DriverUnload;
    pDriverObject->DriverExtension->AddDevice           = DriverAddDevice;
    pDriverObject->MajorFunction[IRP_MJ_CREATE]         = Dispatch_Create;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE]          = Dispatch_Close;
    pDriverObject->MajorFunction[IRP_MJ_CLEANUP]        = Dispatch_Cleanup;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Dispatch_IoControl;
    pDriverObject->MajorFunction[IRP_MJ_PNP]            = Dispatch_Pnp;
    pDriverObject->MajorFunction[IRP_MJ_POWER]          = Dispatch_Power;
    pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Dispatch_SystemControl;

    DebugPrintf((
        "Registry path = \"%ws\"\n",
        pRegistryPath->Buffer
        ));

    // Get configuration information from registry
    PlxRegistryInformationGet(
        pRegistryPath,
        &RegistryInfo
        );

    // Initialize device count
    Gbl_DeviceCount = 0;

    // Initialize Common buffer pointer
    pGbl_CommonBuffer = NULL;

    // Set common buffer requested size
    Gbl_CommonBufferSize = RegistryInfo.Size_CommonBuffer;

    DebugPrintf(("...driver loaded\n"));

    return STATUS_SUCCESS;
}




/*******************************************************************************
 *
 * Function   :  DriverUnload
 *
 * Description:  Unload the driver
 *
 ******************************************************************************/
VOID
DriverUnload(
    DRIVER_OBJECT *pDriverObject
    )
{
    DebugPrintf_Cont(("\n"));
    DebugPrintf(("Unload driver...\n"));

    DebugPrintf(("...driver unloaded\n"));
}




/*******************************************************************************
 *
 * Function   :  DriverAddDevice
 *
 * Description:  Add a new device object to the driver
 *
 ******************************************************************************/
NTSTATUS
DriverAddDevice(
    DRIVER_OBJECT *pDriverObject,
    DEVICE_OBJECT *pdo
    )
{
    U8                i;
    WCHAR             DeviceName[PLX_MAX_NAME_LENGTH];
    WCHAR             DeviceLinkName[PLX_MAX_NAME_LENGTH];
    NTSTATUS          status;
    POWER_STATE       PowerState;
    UNICODE_STRING    DeviceName_Unicode;
    UNICODE_STRING    DeviceLinkName_Unicode;
    DEVICE_OBJECT    *fdo;
    DEVICE_EXTENSION *pdx;


    // Build a device name and attempt to create it
    for (i=0; i < 64; i++)
    {
        RtlStringCchPrintfW(
            DeviceName,
            PLX_MAX_NAME_LENGTH,
            L"\\Device\\" PLX_DRIVER_NAME_UNICODE L"_v%d%02d-%d",
            PLX_SDK_VERSION_MAJOR,
            PLX_SDK_VERSION_MINOR,
            i
            );

        RtlInitUnicodeString(
            &DeviceName_Unicode,
            DeviceName
            );

        // Create the device object
        status =
            IoCreateDevice(
                pDriverObject,              // Driver object
                sizeof(DEVICE_EXTENSION),   // Device extension size
                &DeviceName_Unicode,        // Device name
                FILE_DEVICE_UNKNOWN,        // Device type
                FILE_DEVICE_SECURE_OPEN,    // Device characteristics
                FALSE,                      // Exclusive? (must be FALSE)
                &fdo                        // Returned device object
                );

        // IoCreateDevice() will fail if the same object already exists
        if (NT_SUCCESS(status))
        {
            break;
        }
    }

    // Check if the creation succeeded
    if ( !NT_SUCCESS(status) )
    {
        DebugPrintf(("ERROR - Unable to create Device\n"));
        return status;
    }

    DebugPrintf_Cont(("\n"));
    DebugPrintf((
        "Created Device (%ws)...\n",
        DeviceName
        ));

    // Link a Win32 name for user applications
    RtlStringCchPrintfW(
        DeviceLinkName,
        PLX_MAX_NAME_LENGTH,
        L"\\DosDevices\\" PLX_DRIVER_NAME_UNICODE L"_v%d%02d-%d",
        PLX_SDK_VERSION_MAJOR,
        PLX_SDK_VERSION_MINOR,
        i
        );

    RtlInitUnicodeString(
        &DeviceLinkName_Unicode,
        DeviceLinkName
        );

    DebugPrintf((
        "Create Win32 symbolic link (%ws)...\n",
        DeviceLinkName
        ));

    status =
        IoCreateSymbolicLink(
            &DeviceLinkName_Unicode,
            &DeviceName_Unicode
            );

    if ( !NT_SUCCESS(status) )
    {
        DebugPrintf(("WARNING - Unable to create symbolic link for Win32 Apps\n"));
        RtlStringCchPrintfW( DeviceLinkName, PLX_MAX_NAME_LENGTH, L"" );
    }

    //
    // Initialize the device extension
    //

    pdx = fdo->DeviceExtension;

    // Clear device extension
    RtlZeroMemory(
        pdx,
        sizeof(DEVICE_EXTENSION)
        );

    // Store parent device object
    pdx->pDeviceObject         = fdo;
    pdx->pPhysicalDeviceObject = pdo;
    pdx->Key.DeviceNumber      = i;             // Store device number of device

    // Set API access mode
    pdx->Key.ApiMode = PLX_API_MODE_PCI;

    RtlStringCchCopyW(
        pdx->LinkName,
        PLX_MAX_NAME_LENGTH,
        DeviceLinkName
        );

    // Initialize interrupt wait list
    InitializeListHead(
        &(pdx->List_WaitObjects)
        );

    KeInitializeSpinLock(
        &(pdx->Lock_WaitObjectsList)
        );

    // Initialize physical memories list
    InitializeListHead(
        &(pdx->List_PhysicalMem)
        );

    KeInitializeSpinLock(
        &(pdx->Lock_PhysicalMemList)
        );

    // Initialize BAR mappings list
    InitializeListHead(
        &(pdx->List_BarMappings)
        );

    KeInitializeSpinLock(
        &(pdx->Lock_BarMappingsList)
        );

    // Initialize the interrupt DPC
    KeInitializeDpc(
        &(pdx->DpcForIsr),
        DpcForIsr,
        pdx
        );

    // Attach to device stack and store lower object for passing down IRPs later
    pdx->pLowerDeviceObject =
        IoAttachDeviceToDeviceStack(
            fdo,
            pdo
            );

    DebugPrintf((
        "Attached device object to stack\n"
        "            Functional: %p\n"
        "            Physical  : %p\n"
        "            Lower     : %p\n",
        fdo, pdo, pdx->pLowerDeviceObject
        ));

    // Notify the power manager of the initial power state
    pdx->PowerState        = PowerDeviceD0;      // Start device in full power state
    PowerState.DeviceState = PowerDeviceD0;
    PoSetPowerState(
        fdo,
        DevicePowerState,
        PowerState
        );

    // Indicate the I/O Manager buffer management method
    fdo->Flags |= DO_BUFFERED_IO;

    // Manually clear the Device Initialzing flag
    fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    return STATUS_SUCCESS;
}




/*******************************************************************************
 *
 * Function   :  RemoveDevice
 *
 * Description:  Remove a functional device object
 *
 ******************************************************************************/
VOID
RemoveDevice(
    DEVICE_OBJECT *fdo
    )
{
    NTSTATUS           status;
    UNICODE_STRING     DeviceLinkName_Unicode;
    DEVICE_EXTENSION  *pdx;


    pdx = fdo->DeviceExtension;

    // Remove Win32 link name
    if (wcslen(pdx->LinkName) != 0)
    {
        DebugPrintf((
            "Remove Win32 link (%ws)\n",
            pdx->LinkName
            ));

        RtlInitUnicodeString(
            &DeviceLinkName_Unicode,
            pdx->LinkName
            );

        status =
            IoDeleteSymbolicLink(
                &DeviceLinkName_Unicode
                );

        if ( !NT_SUCCESS(status) )
            DebugPrintf(("WARNING - Unable to remove Win32 link\n"));
    }

    // Detach device from the device object stack
    if (pdx->pLowerDeviceObject)
    {
        IoDetachDevice(
            pdx->pLowerDeviceObject
            );
    }

    DebugPrintf(("Delete device object...\n"));

    // Delete the device object
    IoDeleteDevice(
        fdo
        );
}




/*******************************************************************************
 *
 * Function   :  StartDevice
 *
 * Description:  Start a device
 *
 ******************************************************************************/
NTSTATUS
StartDevice(
    DEVICE_OBJECT            *fdo,
    CM_PARTIAL_RESOURCE_LIST *ResourceListRaw,
    CM_PARTIAL_RESOURCE_LIST *ResourceList
    )
{
    U8                              i;
    U8                              BarIndex;
    U32                             vector;
    U32                             BarSize;
    U32                             RegValue;
    U32                             PciBars[PCI_NUM_BARS_TYPE_00];
    U64                             BarBusAddr;
    U64                             BarCpuAddr;
    KIRQL                           IrqL;
    BOOLEAN                         bIntPresent;
    NTSTATUS                        status;
    KAFFINITY                       affinity;
    KINTERRUPT_MODE                 mode;
    PLX_PHYSICAL_MEM                PciMem;
    DEVICE_EXTENSION               *pdx;
    CM_PARTIAL_RESOURCE_DESCRIPTOR *ResourceRaw;
    CM_PARTIAL_RESOURCE_DESCRIPTOR *Resource;


    pdx         = fdo->DeviceExtension;
    bIntPresent = FALSE;
    ResourceRaw = ResourceListRaw->PartialDescriptors;
    Resource    = ResourceList->PartialDescriptors;

    // Read the PCI base addresses from the device
    for (i=0; i < PCI_NUM_BARS_TYPE_00; i++)
    {
        PlxPciRegisterRead_UseOS(
            pdx,
            0x10 + (i * sizeof(U32)),
            &PciBars[i]
            );
    }

    // Get bus and slot numbers of the device
    status =
        GetBusSlotNumber(
            pdx->pPhysicalDeviceObject,
            pdx,
            PciBars[0]
            );

    if (!NT_SUCCESS(status))
    {
        DebugPrintf(("WARNING - Unable to get bus and slot number\n"));
    }

    DebugPrintf((
        "Resource list contains %d descriptors\n",
        ResourceListRaw->Count
        ));

    for (i = 0; i < ResourceListRaw->Count; ++i, ++Resource, ++ResourceRaw)
    {
        DebugPrintf_Cont(("            Resource %02d\n", i));

        switch (ResourceRaw->Type)
        {
            case CmResourceTypeInterrupt:
                bIntPresent = TRUE;
                IrqL        = (KIRQL) Resource->u.Interrupt.Level;
                vector      = Resource->u.Interrupt.Vector;
                affinity    = Resource->u.Interrupt.Affinity;

                DebugPrintf_Cont((
                    "              Type     : Interrupt\n"
                    "              Vector   : %02x        (Translated = %02x)\n"
                    "              IRQL     : %02x        (Translated = %02x)\n"
                    "              Affinity : %08x\n",
                    ResourceRaw->u.Interrupt.Vector, vector,
                    ResourceRaw->u.Interrupt.Level, IrqL,
                    ResourceRaw->u.Interrupt.Affinity
                    ));

                if (ResourceRaw->Flags == CM_RESOURCE_INTERRUPT_LATCHED)
                {
                    mode = Latched;
                    DebugPrintf_Cont(("              Mode     : Latched\n"));
                }
                else
                {
                    mode = LevelSensitive;
                    DebugPrintf_Cont(("              Mode     : Level Sensitive\n"));
                }
                break;

            case CmResourceTypePort:
            case CmResourceTypeMemory:
                DebugPrintf_Cont((
                    "              Type     : %s\n",
                    (Resource->Type == CmResourceTypePort) ? "I/O" : "Memory"
                    ));

                // Get BAR address & size
                if (Resource->Type == CmResourceTypePort)
                {
                    BarBusAddr = ResourceRaw->u.Port.Start.QuadPart;
                    BarCpuAddr = Resource->u.Port.Start.QuadPart;
                    BarSize    = Resource->u.Port.Length;
                }
                else
                {
                    BarBusAddr = ResourceRaw->u.Memory.Start.QuadPart;
                    BarCpuAddr = Resource->u.Memory.Start.QuadPart;
                    BarSize    = Resource->u.Memory.Length;
                }

                BarIndex =
                    GetBarIndex(
                        pdx,
                        (U32)BarBusAddr,
                        PciBars
                        );

                if (BarIndex == (U8)-1)
                {
                    // Unable to find the BAR index
                    DebugPrintf_Cont((
                        "              PCI BAR ?: ERROR - unable to detect index for BAR (%08lX)\n",
                        (PLX_UINT_PTR)BarBusAddr
                        ));
                }
                else
                {
                    // Record resources
                    pdx->PciBar[BarIndex].Properties.BarValue = (U32)BarBusAddr;
                    pdx->PciBar[BarIndex].Properties.Physical = BarCpuAddr;
                    pdx->PciBar[BarIndex].Properties.Size     = BarSize;

                    if (Resource->Type == CmResourceTypePort)
                        pdx->PciBar[BarIndex].Properties.Flags = PLX_BAR_FLAG_IO;
                    else
                        pdx->PciBar[BarIndex].Properties.Flags = PLX_BAR_FLAG_MEM;

                    DebugPrintf_Cont((
                        "              PCI BAR %d: %08lX\n",
                        BarIndex, (PLX_UINT_PTR)BarBusAddr
                        ));
                }

                DebugPrintf_Cont((
                    "              Phys Addr: %08lX\n"
                    "              Size     : %08X (%ld %s)\n",
                    (PLX_UINT_PTR)BarCpuAddr, BarSize,
                    BarSize < (1 << 10) ? BarSize : BarSize >> 10,
                    BarSize < (1 << 10) ? "Bytes" : "KB"
                    ));

                // For memory spaces, map to kernel space
                if ((Resource->Type == CmResourceTypeMemory) && (BarIndex != (U8)-1))
                {
                    // Set BAR flags
                    pdx->PciBar[BarIndex].Properties.Flags |= PLX_BAR_FLAG_32_BIT;
                    if (Resource->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE)
                        pdx->PciBar[BarIndex].Properties.Flags |= PLX_BAR_FLAG_PREFETCHABLE;

                    DebugPrintf_Cont((
                        "              Property : %sPrefetchable %d-bit\n",
                        (pdx->PciBar[BarIndex].Properties.Flags & PLX_BAR_FLAG_PREFETCHABLE) ? "" : "Non-",
                        (pdx->PciBar[BarIndex].Properties.Flags & PLX_BAR_FLAG_64_BIT) ? 64 : 32
                        ));

                    // Map to kernel space
                    DebugPrintf_Cont(("              Kernel VA: "));

                    status =
                        PlxPciBarResourceMap(
                            pdx,
                            BarIndex
                            );

                    if ( NT_SUCCESS(status) )
                    {
                        DebugPrintf_Cont((
                            "%p\n",
                            pdx->PciBar[BarIndex].pVa
                            ));
                    }
                    else
                    {
                        DebugPrintf_Cont((
                            "ERROR - Unable to map %08lX ==> Kernel VA\n",
                            (PLX_UINT_PTR)BarCpuAddr
                            ));
                    }
                }
                break;

            case CmResourceTypeNull:
                DebugPrintf_Cont(("              Type: Null (unsupported)\n"));
                break;

            case CmResourceTypeDma:
                DebugPrintf_Cont(("              Type: DMA (unsupported)\n"));
                break;

            case CmResourceTypeDeviceSpecific:
                DebugPrintf_Cont(("              Type: Device Specific (unsupported)\n"));
                break;

            case CmResourceTypeBusNumber:
                DebugPrintf_Cont(("              Type: Bus Number (unsupported)\n"));
                break;

            case CmResourceTypeDevicePrivate:
                DebugPrintf_Cont(("              Type: Device Private Data (ignoring - reserved for system use)\n"));
                break;

            case CmResourceTypePcCardConfig:
                DebugPrintf_Cont(("              Type: PC Card Configuration (unsupported)\n"));
                break;

            case CmResourceTypeMfCardConfig:
                DebugPrintf_Cont(("              Type: Multi-function Card Configuration (unsupported)\n"));
                break;

            default:
                DebugPrintf_Cont(("              Type: ?Unknown Resource Type? (%d)\n", Resource->Type));
                break;
        }
    }

    // Fill in remaining device key information
    PlxUpdateDeviceKey(
        pdx
        );

    // Determine & store the PLX chip type
    PlxChipTypeDetect(
        pdx
        );

    // Determine which side of NT port we are located on
    PlxDetermineNtPortSide(
        pdx
        );

    // Disable all interrupts
    PlxChipInterruptsDisable(
        pdx
        );

    // Install the ISR if available
    if (bIntPresent)
    {
        status =
            IoConnectInterrupt(
                &pdx->pInterruptObject,
                OnInterrupt,
                pdx,
                NULL,
                vector,
                IrqL,
                IrqL,
                mode,
                TRUE,
                affinity,
                FALSE
                );

        if ( !NT_SUCCESS(status) )
        {
            ErrorPrintf(("ERROR - Unable to install ISR\n"));
        }
        else
        {
            DebugPrintf(("Installed ISR for interrupt\n"));

            // Re-enable interrupts
            KeSynchronizeExecution(
                pdx->pInterruptObject,
                PlxChipInterruptsEnable,
                pdx
                );
        }
    }
    else
    {
        DebugPrintf(("Device is not using a PCI interrupt resource\n"));
    }

    // Allocate a DMA adapter object for physical memory allocations
    PlxDmaAdapterAllocate(
        pdx
        );

    // Increment our device count & if first device, allocate buffers
    if (InterlockedIncrement( &Gbl_DeviceCount ) == 1)
    {
        if (Gbl_CommonBufferSize != 0)
        {
            DebugPrintf(("Allocate common buffer...\n"));

            // Set requested size
            PciMem.Size = Gbl_CommonBufferSize;

            // Allocate common buffer
            PlxPciPhysicalMemoryAllocate(
                pdx,
                &PciMem,
                TRUE,                   // Smaller buffer is ok
                pdx                     // Assign owner as the device
                );
        }

        /********************************************************
         * Since this is the first device, make sure to invalidate
         * its removal relations.  This forces the PnP Manager
         * to eventually query for removal relations before
         * attempting to remove the device.  Devices added after
         * this one rely on it for the Common DMA and SGL buffers;
         * therefore, it can only be removed last.
         *******************************************************/
        IoInvalidateDeviceRelations(
            pdx->pPhysicalDeviceObject,
            RemovalRelations
            );
    }

    return STATUS_SUCCESS;
}




/*******************************************************************************
 *
 * Function   :  StopDevice
 *
 * Description:  Stop a device
 *
 ******************************************************************************/
VOID
StopDevice(
    DEVICE_OBJECT *fdo
    )
{
    PLX_PHYSICAL_MEM  PciMem;
    DEVICE_EXTENSION *pdx;


    pdx = fdo->DeviceExtension;

    // Free all interrupt resources
    if (pdx->pInterruptObject != NULL)
    {
        DebugPrintf(("Remove ISR...\n"));

        // Disable all interrupts
        KeSynchronizeExecution(
            pdx->pInterruptObject,
            (PKSYNCHRONIZE_ROUTINE)PlxChipInterruptsDisable,
            (PVOID)pdx
            );

        // Remove the ISR
        IoDisconnectInterrupt(
            pdx->pInterruptObject
            );

        pdx->pInterruptObject = NULL;
    }

    // Release the common buffer if this device owns it
    if (pGbl_CommonBuffer != NULL)
    {
        if (pGbl_CommonBuffer->pOwner == pdx)
        {
            DebugPrintf(("De-allocate Common Buffer...\n"));

            // Prepare buffer properties for parameter
            PciMem.PhysicalAddr = pGbl_CommonBuffer->BusPhysical;
            PciMem.Size         = pGbl_CommonBuffer->Size;

            // Release the buffer
            PlxPciPhysicalMemoryFree(
                pdx,
                &PciMem
                );

            // Mark buffer as released
            pGbl_CommonBuffer = NULL;
        }
    }

    // Release the DMA adapter object
    PlxDmaAdapterFree(
        pdx
        );

    // Unmap I/O regions from kernel space (No local register access after this)
    PlxPciBarResourcesUnmap(
        pdx
        );

    // Decrement our device count
    InterlockedDecrement(
        &Gbl_DeviceCount
        );
}




/*******************************************************************************
 *
 * Function   :  PlxDmaAdapterAllocate
 *
 * Description:  Allocate a DMA adapter object which will provide support for
 *               later allocation of physically contiguous page-locked memory.
 *
 ******************************************************************************/
NTSTATUS
PlxDmaAdapterAllocate(
    DEVICE_EXTENSION *pdx
    )
{
    ULONG               NumMapRegisters;
    DEVICE_DESCRIPTION  DeviceDescription;


    DebugPrintf((
        "Allocate DMA Adapter object...\n"
        ));

    // Verify object not already created
    if (pdx->pDmaAdapter != NULL)
    {
        DebugPrintf(("ERROR - DMA Adapter object already exist, unable to allocate\n"));
        return STATUS_OBJECT_NAME_EXISTS;
    }

    // Clear device description
    RtlZeroMemory(
        &DeviceDescription,
        sizeof(DEVICE_DESCRIPTION)
        );

    // Set device DMA properties
    DeviceDescription.Version            = DEVICE_DESCRIPTION_VERSION;
    DeviceDescription.Master             = TRUE;                    // Device is bus master
    DeviceDescription.ScatterGather      = TRUE;                    // Device supports SGL
    DeviceDescription.Dma32BitAddresses  = TRUE;                    // Device supports 32-bit addressing
    DeviceDescription.Dma64BitAddresses  = FALSE;                   // Don't use 64-bit addressing
    DeviceDescription.InterfaceType      = PCIBus;                  // Device is PCI
    DeviceDescription.MaximumLength      = PHYS_MEM_MAX_SIZE_ALL;   // Max bytes per DMA xfer

    // OS will assign map register count
    NumMapRegisters = 0;

    // Allocate a DMA adapter object
    pdx->pDmaAdapter =
        IoGetDmaAdapter(
            pdx->pPhysicalDeviceObject,
            &DeviceDescription,
            &NumMapRegisters
            );

    if (pdx->pDmaAdapter == NULL)
    {
        DebugPrintf(("ERROR - DMA Adapter allocation failed\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // Added to avoid OACR warning if driver doesn't check return value
    if (NumMapRegisters == 0)
    {
    }

    return STATUS_SUCCESS;
}




/*******************************************************************************
 *
 * Function   :  PlxDmaAdapterFree
 *
 * Description:  Frees the device DMA adapter object
 *
 ******************************************************************************/
NTSTATUS
PlxDmaAdapterFree(
    DEVICE_EXTENSION *pdx
    )
{
    DebugPrintf((
        "Release DMA Adapter object...\n"
        ));

    // Verify DMA Adapter was created
    if (pdx->pDmaAdapter == NULL)
    {
        DebugPrintf(("ERROR - DMA Adapter object doesn't exist, unable to free object\n"));
        return STATUS_RESOURCE_TYPE_NOT_FOUND;
    }

    // Delete the DMA Adapter
    pdx->pDmaAdapter->DmaOperations->PutDmaAdapter(
        pdx->pDmaAdapter
        );

    // DMA adapter object deleted, no longer available
    pdx->pDmaAdapter = NULL;

    return STATUS_SUCCESS;
}
