//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

//
// Routines to configure and initialize the PCIbus
//
#include <windows.h>
#include <ceddk.h>
#include <devload.h>
#include <PCIbus.h>
#include "PCIcfg.h"
#include "PCIproto.h"
#include "PCIdbg.h"

#pragma warning (disable: 4002)

//
// Resource lists for Memory and I/O
//
PPCI_RSRC g_MemHead, g_IoHead,g_PrefetchMemHead;
DWORD       g_dwBusNumberBase,g_dwBusNumberLength;

//   
// Function prototypes
//
static BOOL
PCICfgBus(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    );

static BOOL
PCICfgDevice(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    );

static BOOL
PCICfgBridge(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    );

static BOOL
PCICfgCardBusBridge(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize
    );

static BOOL
PCICfgCheckBARs(
    PPCI_DEV_INFO pInfo,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    );

static BOOL
PCICfgAllocateIoSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    );

static BOOL
PCICfgAllocateMemSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    );

static BOOL
PCICfgAllocatePrefetchMemSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    );

static BOOL
PCICfgFindMatch(
    PPCI_DEV_INFO pInfo
    );

static BOOL
RequestIrq(
    PDEVICE_LOCATION pDevLoc,
    PDWORD pIrq
    );
//    
// Inline functions
//

//
// Read a DWORD from a device's configuration space
//
__inline static DWORD
PCIConfig_Read(
    ULONG BusNumber,
    ULONG Device,
    ULONG Function,
    ULONG Offset
    )
{
    ULONG RetVal = FALSE;
    PCI_SLOT_NUMBER SlotNumber;

    SlotNumber.u.AsULONG = 0;
    SlotNumber.u.bits.DeviceNumber = Device;
    SlotNumber.u.bits.FunctionNumber = Function;
    
    HalGetBusDataByOffset(PCIConfiguration, BusNumber, SlotNumber.u.AsULONG, &RetVal, Offset, sizeof(RetVal));

    return RetVal;
}


//
// Write a DWORD to a device's configuration space
//
__inline static void
PCIConfig_Write(
    ULONG BusNumber,
    ULONG Device,
    ULONG Function,
    ULONG Offset,
    ULONG Value
    )
{
    PCI_SLOT_NUMBER SlotNumber;

    SlotNumber.u.AsULONG = 0;
    SlotNumber.u.bits.DeviceNumber = Device;
    SlotNumber.u.bits.FunctionNumber = Function;

    HalSetBusDataByOffset(PCIConfiguration, BusNumber, SlotNumber.u.AsULONG, &Value, Offset, sizeof(Value));
}


//
// Top-level PCIbus initialization and configuration routine
//
BOOL
PCICfg(
    PPCI_DEV_INFO_EX pBusInfoEx
    )
{
    ULONG SubordinateBus = 0;
    ULONG MemSize = 0;
    ULONG IoSize = 0;
    ULONG PrefetchMem = 0;
    BOOL Status = TRUE;
    PPCI_DEV_INFO pBusInfo = &(pBusInfoEx->pciDevice);
        
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfg+(%s, BusNumberBase 0x%X, BusNumberLength 0x%X MemBase 0x%X, MemLen 0x%X, IoBase 0x%X, IoLen 0x%X)\r\n",
        pBusInfo->RegPath, pBusInfoEx->dwBusNumberBase, pBusInfoEx->dwBusNumberLength, pBusInfo->MemBase.Reg[0], pBusInfo->MemLen.Reg[0], pBusInfo->IoBase.Reg[0], pBusInfo->IoLen.Reg[0]));

    if (pBusInfoEx->PrefetchMemBase.Num!=0 && pBusInfoEx->PrefetchMemLen.Num!=0 ) {
        DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfg(%s, PrefetchMemBase 0x%X, PrefetchMemLen 0x%X)\r\n",
            pBusInfo->RegPath, pBusInfoEx->PrefetchMemBase.Reg[0], pBusInfoEx->PrefetchMemLen.Reg[0]));
    }
    else {
        DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfg : No Prefetchable Memory Support\r\n"));
    }
    g_MemHead = g_PrefetchMemHead = g_IoHead = NULL; 
    g_dwBusNumberBase = pBusInfoEx->dwBusNumberBase;
    g_dwBusNumberLength = pBusInfoEx->dwBusNumberLength;
    SubordinateBus = pBusInfoEx->dwBusNumberBase;
    
    // Initialize resource lists
    if (!(g_MemHead = PCIRsrc_New(g_dwBusNumberBase, 0, 0, 0, pBusInfo->MemBase.Reg[0], pBusInfo->MemLen.Reg[0], TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfg: Failed local alloc of g_MemHead)\r\n"));
        return FALSE;
    }
    
    if (!(g_IoHead = PCIRsrc_New(g_dwBusNumberBase, 0, 0, 0, pBusInfo->IoBase.Reg[0], pBusInfo->IoLen.Reg[0], TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfg: Failed local alloc of g_IoHead)\r\n"));
        PCIRsrc_DelList(g_MemHead);
        return FALSE;
    }
    if ( pBusInfoEx->PrefetchMemBase.Num!=0 && pBusInfoEx->PrefetchMemLen.Num!=0 && 
            !(g_PrefetchMemHead = PCIRsrc_New(g_dwBusNumberBase, 0, 0, 0, pBusInfoEx->PrefetchMemBase.Reg[0], pBusInfoEx->PrefetchMemLen.Reg[0], TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfg: No Prefetchable Memory Support!)\r\n"));
    }
    if (!PCICfgBus(pBusInfo, g_dwBusNumberBase, &SubordinateBus, &MemSize, &IoSize,g_PrefetchMemHead!=NULL?&PrefetchMem:NULL)) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfg: Failed PCICfgBus( ) call. At least one device does not configure properly\r\n"));
    }

    // Update the number of Sub-Bus found.
    if (SubordinateBus >= pBusInfoEx->dwBusNumberBase) {
        pBusInfoEx->dwBusNumberLength = SubordinateBus + 1 - pBusInfoEx->dwBusNumberBase ;
    }
    else
        ASSERT(FALSE);

#ifdef DEBUG
    DEBUGMSG(ZONE_RSRC, (L"Mem List:\r\n"));
    if (ZONE_RSRC) PCIRsrc_PrintList(g_MemHead);
    DEBUGMSG(ZONE_RSRC, (L"I/O List:\r\n"));
    if (ZONE_RSRC) PCIRsrc_PrintList(g_IoHead);
    DEBUGMSG(ZONE_RSRC, (L"Prefetch Mem List:\r\n"));
    if (ZONE_RSRC && g_PrefetchMemHead) PCIRsrc_PrintList(g_PrefetchMemHead);
#endif
    
    if (MemSize > pBusInfo->MemLen.Reg[0]) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Not enough memory space allocated for PCI Bus, PCIbus driver terminating.\r\n"));
        RETAILMSG(1, (L"PCIBUS!PCICfg: Host Bridge window size: 0x%X, requested: 0x%X\r\n", pBusInfo->MemLen.Reg[0], MemSize));
        RETAILMSG(1, (L"PCIBUS!PCICfg: List of requested memory resources:\r\n"));
        PCIRsrc_PrintList(g_MemHead);
        Status = FALSE;
        goto PCICfg_Exit;
    }
     
    if (IoSize > pBusInfo->IoLen.Reg[0]) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Not enough I/O space allocated for PCI Bus, PCIbus driver terminating.\r\n"));
        RETAILMSG(1, (L"PCIBUS!PCICfg: Host Bridge window size: 0x%X, requested: 0x%X\r\n", pBusInfo->IoLen.Reg[0], IoSize));
        RETAILMSG(1, (L"PCIBUS!PCICfg: List of requested I/O resources:\r\n"));
        PCIRsrc_PrintList(g_IoHead);
        Status = FALSE;
        goto PCICfg_Exit;
    }

    if (g_PrefetchMemHead && PrefetchMem > pBusInfoEx->PrefetchMemLen.Reg[0]) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Not enough Prefetch space allocated for PCI Bus, PCIbus driver terminating.\r\n"));
        RETAILMSG(1, (L"PCIBUS!PCICfg: Host Bridge window size: 0x%X, requested: 0x%X\r\n", pBusInfoEx->PrefetchMemLen.Reg[0], PrefetchMem));
        RETAILMSG(1, (L"PCIBUS!PCICfg: List of requested prefetch memory resources:\r\n"));
        PCIRsrc_PrintList(g_PrefetchMemHead);
        Status = FALSE;
        goto PCICfg_Exit;
    }
        
    if (!PCICfgAllocateMemSpace(g_dwBusNumberBase, pBusInfo->MemBase.Reg[0], pBusInfo->MemLen.Reg[0])) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Failure allocating PCI memory space, PCIbus driver terminating.\r\n"));
        Status = FALSE;
        goto PCICfg_Exit;
    }
    
    if (!PCICfgAllocateIoSpace(g_dwBusNumberBase, pBusInfo->IoBase.Reg[0], pBusInfo->IoLen.Reg[0])) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Failure allocating PCI I/O space, PCIbus driver terminating.\r\n"));
        Status = FALSE;
        goto PCICfg_Exit;
    }

    if (g_PrefetchMemHead && !PCICfgAllocatePrefetchMemSpace(g_dwBusNumberBase, pBusInfoEx->PrefetchMemBase.Reg[0], pBusInfoEx->PrefetchMemLen.Reg[0])) {
        RETAILMSG(1, (L"PCIBUS!PCICfg: Failure allocating Prefetchable Memory space, PCIbus driver terminating.\r\n"));
        Status = FALSE;
        goto PCICfg_Exit;
    }

PCICfg_Exit:
    UnloadConfigEntrys();           // Unload any configuration DLLs that were loaded
    PCIRsrc_DelList(g_MemHead);
    PCIRsrc_DelList(g_IoHead);
    if (g_PrefetchMemHead)
        PCIRsrc_DelList(g_PrefetchMemHead);
    g_MemHead = g_IoHead = g_PrefetchMemHead = NULL;
        
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfg-\r\n"));
    
    return Status;
}


//
// Initialize and configure PCIbus
//
static BOOL
PCICfgBus(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize ,
    PULONG pPrefetchMemSize
    )
{
    DWORD Device;
    DWORD Function;
    DWORD Length;

    PCI_SLOT_NUMBER SlotNumber;
    PCI_COMMON_CONFIG Cfg;

    BOOL RetVal = TRUE;

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgBus+(Bus %d)\r\n", Bus));
    if (Bus<g_dwBusNumberBase || Bus>=g_dwBusNumberBase + g_dwBusNumberLength) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBus: Invalid BusNumber 0x%X, BusNumberBase 0x%X, BusNumberLength 0x%X\r\n",
            Bus, g_dwBusNumberBase, g_dwBusNumberLength));
        return FALSE;
    }

    SlotNumber.u.AsULONG = 0;
    
    // Loop through all device numbers
    for (Device = 0; Device < PCI_MAX_DEVICES; Device++) {
        SlotNumber.u.bits.DeviceNumber = Device;
        
        // Loop through all functions  
        for (Function = 0; Function < PCI_MAX_FUNCTION; Function++) {
            Cfg.VendorID = PCI_INVALID_VENDORID;
            Cfg.DeviceID = PCI_INVALID_DEVICEID;
            Cfg.HeaderType = PCI_DEVICE_TYPE;
            
            SlotNumber.u.bits.FunctionNumber = Function;

            // Read device's configuration header (except for device-specific information) 
            Length = HalGetBusData(PCIConfiguration, Bus, SlotNumber.u.AsULONG, &Cfg, sizeof(Cfg) - sizeof(Cfg.DeviceSpecific));

            if (Length != (sizeof(Cfg) - sizeof(Cfg.DeviceSpecific)) ||
                (Cfg.DeviceID == PCI_INVALID_DEVICEID) || (Cfg.VendorID == PCI_INVALID_VENDORID) || (Cfg.VendorID == 0)) {
                // No function found
                if (Function != 0) {
                    // If a multi-function device, continue to next function
                    continue;
                } else {
                    // If not a multi-function device, continue to next device
                    break;
                }
            }

            switch (Cfg.HeaderType & ~PCI_MULTIFUNCTION) {
            case PCI_DEVICE_TYPE:  // Device   
                if (!PCICfgDevice(pBusInfo, Bus, Device, Function, &Cfg, pMemSize, pIoSize, pPrefetchMemSize)) {
                    RetVal = FALSE;
                }

                break;

            case PCI_BRIDGE_TYPE:  // PCI-PCI bridge
                if (!PCICfgBridge(pBusInfo, Bus, Device, Function, &Cfg, pSubordinateBus, pMemSize, pIoSize, pPrefetchMemSize)) {
                    RetVal = FALSE;
                }

                break;

            case PCI_CARDBUS_TYPE:  // PCI-Cardbus bridge
                if (!PCICfgCardBusBridge(pBusInfo, Bus, Device, Function, &Cfg,pSubordinateBus, pMemSize, pIoSize)) {
                    RetVal = FALSE;
                }

                break;

            default:
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBus: Invalid HeaderType, 0x%X, found for Device %d/%d/%d\r\n",
                    Cfg.HeaderType, Bus, Device, Function));
            }

            if (Function == 0 && !(Cfg.HeaderType & PCI_MULTIFUNCTION)) {
                // Not a multifunction device, continue on to next device
                break;
            }
        }
    }

    DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBus-(Bus %d, SubBus %d, MemSize 0x%X, IoSize 0x%X, PrefetchMemSize=0x%X)\r\n", Bus, *pSubordinateBus, *pMemSize, *pIoSize, 
        pPrefetchMemSize!=NULL?*pPrefetchMemSize:0));
    
    return RetVal;
}


//
// Initialize and configure device
//
static BOOL
PCICfgDevice(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    )
{
    DWORD Reg;
    PCI_DEV_INFO Info;
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgDevice+(Bus %d, Device %d, Function %d, Vendor ID 0x%X, Device ID 0x%X)\r\n",
        Bus, Device, Function, pCfg->VendorID, pCfg->DeviceID));

    // Initialize Info structure
    PCIInitInfo(pBusInfo->RegPath, Bus, Device, Function, pCfg, &Info);

    // Attempt to find match for this device in the registry and get info
    if (PCICfgFindMatch(&Info)) {
        Info.Matched = TRUE;

        if (!RegGetInfo(&Info)) {
            return FALSE;
        }
        if ( (Info.DevFlags & DEVFLAGS_BOOTPHASE_1 ) && !IsSystemPhase1()) {
            DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgDevice: Registry says only configure this device in phase 1\r\n"));
            // Do not load driver that set only loaded on phase 1.
            return TRUE;
        }

        if (!Info.Configure) {
            // If instructed not to configure, disable device and return
            DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgDevice: Registry says don't configure this device\r\n"));

            if (pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE | PCI_ENABLE_BUS_MASTER)) {
                PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS, 0xFFFF0000);
            }
            
            return TRUE;
        }

        // Make sure the sum of Base entries is less than or equal to 6
        if (Info.MemBase.Num + Info.IoBase.Num > PCI_TYPE0_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMBASE_VALNAME, PCIBUS_IOBASE_VALNAME, Info.RegPath, PCI_TYPE0_ADDRESSES));

            return FALSE;
        }

        // Make sure the sum of Len entries is less than or equal to 6
        if (Info.MemLen.Num + Info.IoLen.Num > PCI_TYPE0_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMLEN_VALNAME, PCIBUS_IOLEN_VALNAME, Info.RegPath, PCI_TYPE0_ADDRESSES));

            return FALSE;
        }
    }
    else {
#ifdef DEBUG
        if (ZONE_WARNING) {
            RETAILMSG(1,(TEXT("Warning: No Match template is found for following PCI Device\r\n")));
            DumpPciConfig(&Info);
        }
#endif    
    }
    // If ConfigEntry defined, call it instead of CheckBARs
    if (Info.ConfigEntry) {
        DWORD Status;
        
        // Load driver Dll and obtain ConfigEntry function address
        if (!LoadConfigEntry(&Info)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: Failed LoadConfigEntry call\r\n"));

            return FALSE;
        }

        Status = ((PFN_CONFIGENTRY)(Info.ConfigInfo->ConfigEntryFn))(PCIBUS_CONFIG_RSRC, &Info, g_MemHead, g_IoHead, pMemSize, pIoSize);
        
        if (Status != ERROR_SUCCESS) {
            if (Status == ERROR_NOT_SUPPORTED) {
                // ConfigEntry point doesn't support this type of call, so do it here
                // Read the base address registers to determine space to allocate
                if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize,pPrefetchMemSize)) {
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: Failed PCICfgCheckBARs call\r\n"));
                    
                    return FALSE;
                }
            } else {
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: Failed %s:%s call\r\n", Info.DllName, Info.ConfigEntryName));

                return FALSE;
            }
        }
    } else {
        // Read the base address registers to determine space to allocate
        if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize,pPrefetchMemSize)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgDevice: Failed PCICfgCheckBARs call\r\n"));
            
            return FALSE;
        }
    }

    // If device not already placed, configure interrupt
    if (!(pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) {
        DEVICE_LOCATION DevLoc;
        DWORD Irq = 0xFF;

        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_INTERRUPT);
        
        // Fill in device location structure
        DevLoc.IfcType = PCIBus;
        DevLoc.BusNumber = Bus;
        DevLoc.LogicalLoc = (Bus << 16) | (Device << 8) | Function;
        DevLoc.Pin = ((PPCI_INT)(&Reg))->InterruptPin;

        if (DevLoc.Pin) {
            // Interrupts used, get IRQ from OAL
            if (!RequestIrq(&DevLoc, &Irq)) {
                DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgDevice: Get IRQ call failed.\r\n"));

                // No interrupts
                Irq = 0xFF;
            } else if (Irq > 254) {
                DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgDevice: IRQ returned from OAL is outside of range (0-254).  Ignoring.\r\n"));

                // No interrupts
                Irq = 0xFF;
            }
        }

        DEBUGMSG(ZONE_INIT, (L"RequestIrq(%d/%d/%d) returns %d\r\n", Bus, Device, Function, Irq));
        
        ((PPCI_INT)(&Reg))->InterruptLine = (BYTE)Irq;
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_INTERRUPT, Reg);      

        // Disable expansion ROM
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_ROM, 0);
    }

    // Set Latency register
    Info.Latency = (Info.Latency > 0) ? Info.Latency : pBusInfo->Latency;   // use global bus value if not defined for device
    if (Info.Latency) {
        // Reprogram only if non-zero
        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_HEAD);
        Reg = (Reg & 0xFFFF00FF) | ((Info.Latency & 0xFF) << 8);
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_HEAD, Reg);
    }

    // Write command value and reset device status
    if (!(pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) {
        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS);
        Reg &= 0x7;
        Reg |= 0xFFFF0000 | (Info.Command & 0x000003FF);
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS, Reg);
    }
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgDevice-\r\n"));
        
    return TRUE;
}


//
// Initialize and configure PCI-PCI bridge
//
static BOOL
PCICfgBridge(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize
    )
{
    ULONG BusReg;
    ULONG SecondaryMemSize;
    ULONG SecondaryIoSize;
    ULONG SecondaryPrefetchMemSize;
    ULONG i;
    ULONG Reg;

    PCI_DEV_INFO Info;

    BOOL MemPlaced = FALSE;
    BOOL IOPlaced = FALSE;
    DWORD SavedMemBase = 0xFFF00000, SavedMemSize = 0;
    DWORD SavedIOBase = 0xF000, SavedIOSize = 0;
    ULONGLONG SavedPrefetchMemBase = (ULONGLONG)0xfffffffffff00000,SavedPrefetchMemSize = 0;
    BOOL prefetchMemory=(pPrefetchMemSize!=NULL && g_PrefetchMemHead!=NULL);

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgBridge+(Bus %d, Device %d, Function %d, Device ID/Vendor ID 0x%X)\r\n",
        Bus, Device, Function, PCIConfig_Read(Bus, Device, Function, 0)));
    
    // Check to see if bridge memory space already enabled
    Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS);
    if (Reg & PCI_ENABLE_MEMORY_SPACE) {
        DWORD MemLimit;
        ULONGLONG PrefetchMemLimit ;
        
        // Memory access enabled
        MemPlaced = TRUE;

        // Save off current values of memory and I/O bases and sizes
        Reg = PCIConfig_Read(Bus, Device, Function, PCIBRIDGE_MEMORY);
        SavedMemBase = (Reg & 0xFFF0) << 16;
        MemLimit = (Reg & 0xFFF00000);

        if (MemLimit < SavedMemBase) {
            // Invalid memory base/limit pair
            SavedMemSize = 0;
        } else {
            SavedMemSize = (MemLimit + 0x000FFFFF + 1) - SavedMemBase;
        }
        Reg = PCIConfig_Read(Bus,Device,Function,PCIBRIDGE_PREFETCHABLE);
        SavedPrefetchMemBase = ((Reg<<16) & 0xfff00000);
        PrefetchMemLimit = (Reg & 0xFFF00000);
        Reg = PCIConfig_Read(Bus,Device,Function,PCIBRIDGE_BASE_UPPER32);
        SavedPrefetchMemBase += (((ULONGLONG)Reg)<<32);
        Reg = PCIConfig_Read(Bus,Device,Function,PCIBRIDGE_LIMIT_UPPER32);
        PrefetchMemLimit += (((ULONGLONG)Reg)<<32);
        if (PrefetchMemLimit<SavedPrefetchMemBase) {
            SavedPrefetchMemSize =0;
        }
        else  {
            SavedPrefetchMemSize =  (PrefetchMemLimit + 0x100000) - SavedPrefetchMemBase;
        }
        if (SavedPrefetchMemBase == 0 && PrefetchMemLimit == 0 ) {
            //Write FFFFFFFF to verify it is read only or not.
            PCIConfig_Write(Bus, Device, Function,PCIBRIDGE_PREFETCHABLE, (ULONG)-1);
            Reg = PCIConfig_Read(Bus, Device, Function, PCIBRIDGE_PREFETCHABLE);
            if ((Reg & 0xfff00000)==0) {
                prefetchMemory=FALSE;
            }
            // Recover here.
            PCIConfig_Write(Bus, Device, Function,PCIBRIDGE_PREFETCHABLE, (ULONG)0);            
        }
            
    }

    // Check to see if bridge I/O space already enabled
    Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS);
    if (Reg & PCI_ENABLE_IO_SPACE) {
        DWORD IOLimit;
        
        // I/O access enabled
        IOPlaced = TRUE;
        
        // Save off current values of I/O base and size
        Reg = PCIConfig_Read(Bus, Device, Function, PCIBRIDGE_IO);

        SavedIOBase = (Reg & 0xF0) << 8;
        IOLimit = (Reg & 0xF000);

        if ((Reg & 0xF) == PCI_SECONDARY_IO32) {
            // 32-bit I/O
            Reg = PCIConfig_Read(Bus, Device, Function, PCIBRIDGE_IO_UPPER16);

            SavedIOBase |= (Reg & 0x0000FFFF) << 16;
            IOLimit |= (Reg & 0xFFFF0000);
        }

        if (IOLimit < SavedIOBase) {
            // Invalid I/O base/limit combination
            SavedIOSize = 0;
        } else {
            SavedIOSize = (IOLimit + 0x0FFF + 1) - SavedIOBase;
        }
    }

    // Initialize Info structure
    PCIInitInfo(pBusInfo->RegPath, Bus, Device, Function, pCfg, &Info);

    // Attempt to find match for this device in the registry and get info
    if (PCICfgFindMatch(&Info)) {
        Info.Matched = TRUE;

        if (!RegGetInfo(&Info)) {
            return FALSE;
        }

        // Make sure the sum of Base entries is less than or equal to 2
        if (Info.MemBase.Num + Info.IoBase.Num > PCI_TYPE1_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMBASE_VALNAME, PCIBUS_IOBASE_VALNAME, Info.RegPath, PCI_TYPE1_ADDRESSES));

            return FALSE;
        }

        // Make sure the sum of Len entries is less than or equal to 2
        if (Info.MemLen.Num + Info.IoLen.Num > PCI_TYPE1_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMLEN_VALNAME, PCIBUS_IOLEN_VALNAME, Info.RegPath, PCI_TYPE1_ADDRESSES));

            return FALSE;
        }
    }

    // If ConfigEntry defined, call it instead of CheckBARs
    if (Info.ConfigEntry) {
        DWORD Status;
        
        // Load driver Dll and obtain ConfigEntry function address
        if (!LoadConfigEntry(&Info)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed LoadConfigEntry call\r\n"));

            return FALSE;
        }

        Status = ((PFN_CONFIGENTRY)(Info.ConfigInfo->ConfigEntryFn))(PCIBUS_CONFIG_RSRC, &Info, g_MemHead, g_IoHead, pMemSize, pIoSize);
        
        if (Status != ERROR_SUCCESS) {
            if (Status == ERROR_NOT_SUPPORTED) {
                // ConfigEntry point doesn't support this type of call, so do it here
                // Read the base address registers to determine space to allocate
                if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize, pPrefetchMemSize)) {
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed PCICfgCheckBARs call\r\n"));
                    
                    return FALSE;
                }
            } else {
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed %s:%s call\r\n", Info.DllName, Info.ConfigEntryName));

                return FALSE;
            }
        }
    } else {
        // Read the base address registers to determine space to allocate
        if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize, pPrefetchMemSize)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed PCICfgCheckBARs call\r\n"));
            
            return FALSE;
        }
    }

    // If bridge not already placed, configure interrupt
    if (!(pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) {
        DEVICE_LOCATION DevLoc;
        DWORD Irq = 0xFF;

        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_INTERRUPT);
        
        // Fill in device location structure
        DevLoc.IfcType = PCIBus;
        DevLoc.BusNumber = Bus;
        DevLoc.LogicalLoc = (Bus << 16) | (Device << 8) | Function;
        DevLoc.Pin = ((PPCI_INT)(&Reg))->InterruptPin;

        if (DevLoc.Pin) {
            // Interrupts used, get IRQ from OAL
            if (!RequestIrq(&DevLoc, &Irq)) {
                DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBridge: Get IRQ call failed.\r\n"));

                // No interrupts
                Irq = 0xFF;
            } else if (Irq > 254) {
                DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBridge: IRQ returned from OAL is outside of range (0-254).  Ignoring.\r\n"));

                // No interrupts
                Irq = 0xFF;
            }
        }

        DEBUGMSG(ZONE_INIT, (L"RequestIrq(%d/%d/%d) returns %d\r\n", Bus, Device, Function, Irq));
        
        ((PPCI_INT)(&Reg))->InterruptLine = (BYTE)Irq;
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_INTERRUPT, Reg);       
    
        // Disable expansion ROM, prefetchable memory space, upper memory window
        PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_ROM, 0x0);
        PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_PREFETCHABLE, 0x0000FFF0);
        PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_BASE_UPPER32, 0x0);
        PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_LIMIT_UPPER32, 0x0);
        
        // Set bridge control register
        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_INTERRUPT);
        Reg = (Reg & 0x0000FFFF) | Info.BridgeControl << 16;
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_INTERRUPT, Reg);
    }   
    
    // Set Latency register
    Info.Latency = (Info.Latency > 0) ? Info.Latency : pBusInfo->Latency;   // use global bus value if not defined for bridge
    if (Info.Latency) {
        // Reprogram only if non-zero
        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_HEAD);
        Reg = (Reg & 0xFFFF00FF) | ((Info.Latency & 0xFF) << 8);
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_HEAD, Reg);
    }
    
    // Set the bus numbers & secondary latency
    // Need to set the subordinate bus as max for now, then write
    // actual number after found all downstream busses
    (*pSubordinateBus)++;
    Info.SecondaryLatency = (Info.SecondaryLatency > 0) ? Info.SecondaryLatency : pBusInfo->SecondaryLatency;   // use global bus value if not defined for bridge
    BusReg = PCIConfig_Read(Bus, Device, Function, PCIBRIDGE_BUS_NUMBER);
    ((PBRIDGE_BUS)(&BusReg))->PrimaryBusNumber = (BYTE)Bus;
    ((PBRIDGE_BUS)(&BusReg))->SecondaryBusNumber = (BYTE)(*pSubordinateBus);
    ((PBRIDGE_BUS)(&BusReg))->SubordinateBusNumber = 0xFF;
    if (Info.SecondaryLatency) ((PBRIDGE_BUS)(&BusReg))->SecondaryLatencyTimer = (BYTE)Info.SecondaryLatency;
    PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_BUS_NUMBER, BusReg);
      
    SecondaryMemSize = 0;
    SecondaryIoSize = 0;
    SecondaryPrefetchMemSize=0;
    
    // Call PCICfgBus recursively
    if (!PCICfgBus(pBusInfo, *pSubordinateBus, pSubordinateBus, &SecondaryMemSize, &SecondaryIoSize,
             prefetchMemory?&SecondaryPrefetchMemSize:NULL)) {
        DEBUGMSG(ZONE_ERROR,(TEXT("Some Device(s) on sub bus %d doesn't configure properly"),*pSubordinateBus));
    }
     
    // Set the proper value for subordinate bus
    ((PBRIDGE_BUS)(&BusReg))->SubordinateBusNumber = (BYTE)(*pSubordinateBus);
    PCIConfig_Write(Bus, Device, Function, PCIBRIDGE_BUS_NUMBER, BusReg);

 
    // Round up space required by secondary bus to 1MB boundary and then
    // align to proper boundary after that
    SecondaryMemSize = (SecondaryMemSize + 0x000FFFFF) & 0xFFF00000;
    if (((SecondaryMemSize - 1) & (SecondaryMemSize)) != 0) {
        for (i = 31; i > 1; i--) {
            if (SecondaryMemSize & (1 << i)) {
                SecondaryMemSize = (1 << (i+1));
                break;
            }
        }
    }
    SecondaryIoSize = (SecondaryIoSize + 0x00000FFF) & 0xFFFFF000;
    if (((SecondaryIoSize - 1) & (SecondaryIoSize)) != 0) {
        for (i = 31; i > 1; i--) {
            if (SecondaryIoSize & (1 << i)) {
                SecondaryIoSize = (1 << (i+1));
                break;
            }
        }
    }
    if (prefetchMemory) {
        SecondaryPrefetchMemSize = (SecondaryPrefetchMemSize + 0x000FFFFF) & 0xFFF00000;
        if (((SecondaryPrefetchMemSize - 1) & (SecondaryPrefetchMemSize)) != 0) {
            for (i = 31; i > 1; i--) {
                if (SecondaryPrefetchMemSize & (1 << i)) {
                    SecondaryPrefetchMemSize = (1 << (i+1));
                    break;
                }
            }
        }
        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBridge(Bus %d, Device %d, Function %d): Saved Prefetch MemSize: 0x%X, Calculated PrefetchMemSize: 0x%X\r\n",
            Bus, Device, Function,(ULONG)SavedPrefetchMemSize,(ULONG)SecondaryPrefetchMemSize));
    }

    DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBridge(Bus %d, Device %d, Function %d): Saved MemSize: 0x%X, Calculated MemSize: 0x%X, Saved IoSize: 0x%X, Calculated IoSize: 0x%X\r\n",
        Bus, Device, Function, SavedMemSize, SecondaryMemSize, SavedIOSize, SecondaryIoSize));
        
    // Take maximum of saved resource size (if any) and calculated resource size
    // For CEPC platform, the registry could set NOCONFIG =1 because the BIOS configure the PCI-PCI bridge
    // and it is different from how we configured. So we can not touch the window if this flag is set.
    if (Info.Matched && !Info.Configure) { 
        SecondaryMemSize =  SavedMemSize;
        SecondaryIoSize =  SavedIOSize;
        if (prefetchMemory)
            SecondaryPrefetchMemSize=(ULONG)SavedPrefetchMemSize;
    }
    else {
        SecondaryMemSize = (SecondaryMemSize > SavedMemSize) ? SecondaryMemSize : SavedMemSize;
        SecondaryIoSize = (SecondaryIoSize > SavedIOSize) ? SecondaryIoSize : SavedIOSize;
        if (prefetchMemory)
            SecondaryPrefetchMemSize= (ULONG)((SecondaryPrefetchMemSize>SavedPrefetchMemSize)?SecondaryPrefetchMemSize:SavedPrefetchMemSize);
    }
    
    DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgBridge-(Bus %d, Device %d, Function %d, SecBus %d, SubBus %d, MemSize 0x%X, IoSize 0x%X)\r\n",
        Bus, Device, Function, ((PBRIDGE_BUS)(&BusReg))->SecondaryBusNumber, *pSubordinateBus, SecondaryMemSize, SecondaryIoSize));

    // Add bridge resource requirements
    if (SecondaryMemSize) {
        PPCI_RSRC Rsrc = PCIRsrc_New(Bus, Device, Function, 0, SavedMemBase, SecondaryMemSize, TRUE, ((PBRIDGE_BUS)(&BusReg))->SecondaryBusNumber, MemPlaced, NULL);
        
        if (!Rsrc) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed local alloc of Rsrc\r\n"));
            return FALSE;
        }
        
        PCIRsrc_Add(g_MemHead, Rsrc);
    }

    if (SecondaryIoSize) {
        PPCI_RSRC Rsrc = PCIRsrc_New(Bus, Device, Function, 0, SavedIOBase, SecondaryIoSize, TRUE, ((PBRIDGE_BUS)(&BusReg))->SecondaryBusNumber, IOPlaced, NULL);
        
        if (!Rsrc) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed local alloc of Rsrc\r\n"));
            return FALSE;
        }
        
        PCIRsrc_Add(g_IoHead, Rsrc);
    }
    if (prefetchMemory && SecondaryPrefetchMemSize!=0) {
        
        PPCI_RSRC Rsrc = PCIRsrc_New(Bus, Device, Function, 0, SavedMemBase, SecondaryPrefetchMemSize, TRUE, ((PBRIDGE_BUS)(&BusReg))->SecondaryBusNumber, MemPlaced, NULL);
        if (!Rsrc) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgBridge: Failed local alloc of Rsrc\r\n"));
            return FALSE;
        }
        
        PCIRsrc_Add(g_PrefetchMemHead, Rsrc);
    }
    *pMemSize += SecondaryMemSize;
    *pIoSize += SecondaryIoSize;
    if (prefetchMemory)
        *pPrefetchMemSize += SecondaryPrefetchMemSize;
    
    // Write Command value and reset bridge status
    Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS);
    Reg &= 0x7;
    Reg |= 0xFFFF0000 | (Info.Command & 0x000003FF);
    PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS, Reg);

    return TRUE;

}


//
// Initialize and configure CardBus Bridge
//
// NOTE: We do not support CardBus, only the PCMCIA part of CardBus.  The memory and I/O base registers are programmed
// to not decode addresses.
//
static BOOL
PCICfgCardBusBridge(
    PPCI_DEV_INFO pBusInfo,
    ULONG Bus,
    ULONG Device,
    ULONG Function,
    PPCI_COMMON_CONFIG pCfg,
    PULONG pSubordinateBus,
    PULONG pMemSize,
    PULONG pIoSize
    )
{
    DWORD Reg;
    PCI_DEV_INFO Info;

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgCardBusBridge+(Bus %d, Device %d, Function %d, Vendor ID 0x%X, Device ID 0x%X)\r\n",
        Bus, Device, Function, pCfg->VendorID, pCfg->DeviceID));

    // Initialize Info structure
    PCIInitInfo(pBusInfo->RegPath, Bus, Device, Function, pCfg, &Info);

    // Attempt to find match for this device in the registry and get info
    if (PCICfgFindMatch(&Info)) {
        Info.Matched = TRUE;

        if (!RegGetInfo(&Info)) {
            return FALSE;
        }

        if (!Info.Configure) {
            // If instructed not to configure, disable device and return
            DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgCardBusBridge: Registry says don't configure this device\r\n"));

            if (pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE | PCI_ENABLE_BUS_MASTER)) {
                PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS, 0xFFFF0000);
            }
            
            return TRUE;
        }

        // Make sure the sum of Base entries is less than or equal to 1
        if (Info.MemBase.Num + Info.IoBase.Num > PCI_TYPE2_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMBASE_VALNAME, PCIBUS_IOBASE_VALNAME, Info.RegPath, PCI_TYPE2_ADDRESSES));

            return FALSE;
        }

        // Make sure the sum of Len entries is less than or equal to 1
        if (Info.MemLen.Num + Info.IoLen.Num > PCI_TYPE2_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: The number of registry values '%s' and '%s' under '%s' must sum to be less than %d.\r\n",
                PCIBUS_MEMLEN_VALNAME, PCIBUS_IOLEN_VALNAME, Info.RegPath, PCI_TYPE2_ADDRESSES));

            return FALSE;
        }
    }

    // If ConfigEntry defined, call it instead of CheckBARs
    if (Info.ConfigEntry) {
        DWORD Status;
        
        // Load driver Dll and obtain ConfigEntry function address
        if (!LoadConfigEntry(&Info)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: Failed LoadConfigEntry call\r\n"));

            return FALSE;
        }

        Status = ((PFN_CONFIGENTRY)(Info.ConfigInfo->ConfigEntryFn))(PCIBUS_CONFIG_RSRC, &Info, g_MemHead, g_IoHead, pMemSize, pIoSize);
        
        if (Status != ERROR_SUCCESS) {
            if (Status == ERROR_NOT_SUPPORTED) {
                // ConfigEntry point doesn't support this type of call, so do it here
                // Read the base address registers to determine space to allocate
                if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize, NULL)) {
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: Failed PCICfgCheckBARs call\r\n"));
                    
                    return FALSE;
                }
            } else {
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: Failed %s:%s call\r\n", Info.DllName, Info.ConfigEntryName));

                return FALSE;
            }
        }
    } else {
        // Read the base address registers to determine space to allocate
        if (!PCICfgCheckBARs(&Info, pMemSize, pIoSize, NULL)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCardBusBridge: Failed PCICfgCheckBARs call\r\n"));
            
            return FALSE;
        }
    }

    // If device not already placed, configure interrupt
    if (!(pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) {
        DEVICE_LOCATION DevLoc;
        DWORD Irq = 0xFF;

        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_INTERRUPT);
        
        if (((PPCI_INT)(&Reg))->InterruptLine==0xff || ((PPCI_INT)(&Reg))->InterruptLine==0) { // Not be configured
            // Fill in device location structure
            DevLoc.IfcType = PCIBus;
            DevLoc.BusNumber = Bus;
            DevLoc.LogicalLoc = (Bus << 16) | (Device << 8) | Function;
            DevLoc.Pin = ((PPCI_INT)(&Reg))->InterruptPin;
            
            if (DevLoc.Pin) {
                // Interrupts used, get IRQ from OAL
                if (!RequestIrq(&DevLoc, &Irq)) {
                    DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgCardBusBridge: Get IRQ call failed.\r\n"));
                    // No Interrupt
                    Irq = 0xff;
                } 
                else if (Irq > 254) {
                    DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgCardBusBridge: IRQ returned from OAL is outside of range (0-254).  Ignoring.\r\n"));
                    
                    // No interrupts
                    Irq = 0xFF;
                }
            }
            
            DEBUGMSG(ZONE_INIT, (L"RequestIrq(%d/%d/%d) returns %d\r\n", Bus, Device, Function, Irq));
            
            ((PPCI_INT)(&Reg))->InterruptLine = (BYTE)Irq;
            PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_INTERRUPT, Reg);   
        }
    }
    
    // Disable memory and I/O windows
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_MEMBASE0, 0xFFFFF000);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_MEMLIMIT0, 0x0);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_MEMBASE1, 0xFFFFF000);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_MEMLIMIT1, 0x0);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_IOBASE0, 0xFFFFFFFC);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_IOLIMIT0, 0x0);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_IOBASE1, 0xFFFFFFFC);
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_IOLIMIT1, 0x0);
    
    // Set bridge control register
    Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_INTERRUPT);
    Reg = (Reg & 0x0000FFFF) | Info.BridgeControl << 16;
    PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_INTERRUPT, Reg);

    // Set Latency register
    Info.Latency = (Info.Latency > 0) ? Info.Latency : pBusInfo->Latency;   // use global bus value if not defined for bridge
    if (Info.Latency) {
        // Reprogram only if non-zero
        Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_HEAD);
        Reg = (Reg & 0xFFFF00FF) | ((Info.Latency & 0xFF) << 8);
        PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_HEAD, Reg);
    }
    
    // Set the bus numbers & secondary (CardBus) latency
    // Assume There is no bridge type device attached the CardBus Bridge
    (*pSubordinateBus)++;
    Info.SecondaryLatency = (Info.SecondaryLatency > 0) ? Info.SecondaryLatency : pBusInfo->SecondaryLatency;   // use global bus value if not defined for bridge
    Reg = PCIConfig_Read(Bus, Device, Function, PCICARDBUS_BUS_NUMBER);
    ((PBRIDGE_BUS)(&Reg))->PrimaryBusNumber = (BYTE)Bus;
    ((PBRIDGE_BUS)(&Reg))->SecondaryBusNumber = (BYTE)(*pSubordinateBus);
    ((PBRIDGE_BUS)(&Reg))->SubordinateBusNumber = (BYTE)(*pSubordinateBus);
    if (Info.SecondaryLatency) ((PBRIDGE_BUS)(&Reg))->SecondaryLatencyTimer = (BYTE)Info.SecondaryLatency;
    PCIConfig_Write(Bus, Device, Function, PCICARDBUS_BUS_NUMBER, Reg);
    
    // Write command value and reset device status
    Reg = PCIConfig_Read(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS);
    Reg &= 0x7;
    Reg |= 0xFFFF0000 | (Info.Command & 0x000003FF);
    PCIConfig_Write(Bus, Device, Function, PCI_CONFIG_COMMAND_STATUS, Reg);

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgCardBusBridge-\r\n"));
        
    return TRUE;
}


//
// Read and check Base Address Registers, calculate size and allocate resources
//
static BOOL
PCICfgCheckBARs(
    PPCI_DEV_INFO pInfo,
    PULONG pMemSize,
    PULONG pIoSize,
    PULONG pPrefetchMemSize    
    )
{
    DWORD NumberOfRegs;
    ULONG Offset;
    ULONG i;
    ULONG BaseAddress;
    ULONG Size;
    ULONG Type;
    BOOL  Prefetchable;
    DWORD Reg;
    BOOL Placed = FALSE;
    BOOL SizeFound;
    DWORD IoIndex = 0;
    DWORD MemIndex = 0;
    DWORD Bus = pInfo->Bus;
    DWORD Device = pInfo->Device;
    DWORD Function = pInfo->Function;
    PPCI_COMMON_CONFIG pCfg = pInfo->Cfg;

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgCheckBARs+(Bus %d, Device %d, Function %d)\r\n",
        Bus, Device, Function));
    
    // Check to see if device/function already enabled
    if (pCfg->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE)) {
        // Either Memory or I/O access enabled
        Placed = TRUE;

#ifdef DEBUG
        if (!pInfo->Matched) {
            DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgCheckBARs: WARNING: No matching registry key was found for placed device %d/%d/%d.\r\n",
                Bus, Device, Function));
            DEBUGMSG(ZONE_WARNING, (L"PCIBUS!PCICfgCheckBARs:          Problems can result if the device is being used while configuring PCIbus.\r\n"));
        }
#endif
    }

    // Determine number of BARs to examine from header type
    switch (pCfg->HeaderType & ~PCI_MULTIFUNCTION) {
    case PCI_DEVICE_TYPE:
        NumberOfRegs = PCI_TYPE0_ADDRESSES;
        break;

    case PCI_BRIDGE_TYPE:
        NumberOfRegs = PCI_TYPE1_ADDRESSES;
        break;

    case PCI_CARDBUS_TYPE:
        NumberOfRegs = PCI_TYPE2_ADDRESSES;
        break;

    default:
        return FALSE;
    }
    
    for (i = 0, Offset = 0x10; i < NumberOfRegs; i++, Offset += 4) {
        // Get base address register value
        Reg = pCfg->u.type0.BaseAddresses[i];
        Type = Reg & PCI_ADDRESS_IO_SPACE;
        Prefetchable = ( Type==0 && (Reg & PCI_ADDRESS_MEMORY_PREFETCHABLE)!=0 && pPrefetchMemSize!=NULL) ;
        
        if (Placed && pInfo->Matched && (pInfo->IoLen.Num + pInfo->MemLen.Num) != 0) {
            // Device already placed and has matching registry entry
            if (Type) {
                // I/O space
                Size = pInfo->IoLen.Reg[IoIndex++];
                Reg &= PCI_ADDRESS_IO_ADDRESS_MASK;

                SizeFound = (IoIndex <= pInfo->IoLen.Num);
            } else {
                // Memory space
                Size = pInfo->MemLen.Reg[MemIndex++];
                Reg &= PCI_ADDRESS_MEMORY_ADDRESS_MASK;

                SizeFound = (MemIndex <= pInfo->MemLen.Num);
            }
        } else {
            // Device not placed or is placed and has no matching registry entry => Probe hardware for size
            PCIConfig_Write(Bus,Device,Function,Offset,0xFFFFFFFF);
            BaseAddress = PCIConfig_Read(Bus,Device,Function,Offset);
            PCIConfig_Write(Bus, Device, Function, Offset, Reg);

            if (Type) {
                // IO space
                // Re-adjust BaseAddress if upper 16-bits are 0 (allowable in PCI 2.2 spec)
                if (((BaseAddress & PCI_ADDRESS_IO_ADDRESS_MASK) != 0) && ((BaseAddress & 0xFFFF0000) == 0)) {
                    BaseAddress |= 0xFFFF0000;
                }
                
                Size = ~(BaseAddress & PCI_ADDRESS_IO_ADDRESS_MASK);
                Reg &= PCI_ADDRESS_IO_ADDRESS_MASK;
            } else {
                // Memory space
                if ((Reg & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
                    // PCI 2.2 spec no longer supports this type of memory addressing
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCheckBARs: 20-bit addressing not supported\r\n"));
                    return FALSE;
                }
                
                // Re-adjust BaseAddress if upper 16-bits are 0 (allowed by PCI 2.2 spec)
                if (((BaseAddress & PCI_ADDRESS_MEMORY_ADDRESS_MASK) != 0) && ((BaseAddress & 0xFFFF0000) == 0)) {
                    BaseAddress |= 0xFFFF0000;
                }
                
                Size = ~(BaseAddress & PCI_ADDRESS_MEMORY_ADDRESS_MASK);
                Reg &= PCI_ADDRESS_MEMORY_ADDRESS_MASK;
            }

            // Check that the register has a valid format; it should have consecutive high 1's and consecutive low 0's
            SizeFound = (BaseAddress != 0) && (BaseAddress != 0xFFFFFFFF) && (((Size + 1) & Size) == 0);

            Size +=1;
        }

        if (SizeFound) {
            PPCI_RSRC Rsrc = PCIRsrc_New(Bus, Device, Function, Offset, Reg, Size, FALSE, 0, Placed, NULL);
            
            if (!Rsrc) {
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgCheckBARs: Failed local alloc of Rsrc\r\n"));
                return FALSE;
            }

            if (Type == PCI_ADDRESS_IO_SPACE) {
                *pIoSize += Size;
                PCIRsrc_Add(g_IoHead, Rsrc);
            } else {
                if (Prefetchable && g_PrefetchMemHead!=NULL) {
                    *pPrefetchMemSize += Size;
                    PCIRsrc_Add(g_PrefetchMemHead, Rsrc);
                }
                else {
                    *pMemSize += Size;
                    PCIRsrc_Add(g_MemHead, Rsrc);
                }
            }

            DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgCheckBars: BAR(%d/%d/%d): Offset 0x%x, Type %s, Size 0x%X\r\n",
                Bus, Device, Function, Offset, (Type == PCI_ADDRESS_IO_SPACE) ? TEXT("I/O") : TEXT("Memory"), Size));
        } else {
            // Some devices have invalid BARs before valid ones (allowed by PCI 2.2 spec).  Skip invalid BARs.
            continue;
        }

        // check for 64 bit device (memory only)
        if ((Type == PCI_ADDRESS_MEMORY_SPACE) && ((Reg & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)) {
            // 64 bit device - BAR is twice as wide - zero out high part
            Offset += 4;
            i++;
            PCIConfig_Write(Bus, Device, Function, Offset, 0x0);
        }
    }

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgCheckBars-\r\n", Bus, Device, Function));

    return TRUE;
}


//
// For each I/O resource, allocate I/O space on PCIbus and assign address to BAR
//
static BOOL
PCICfgAllocateIoSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    )
{
    PPCI_RSRC Rsrc, BusHead;
    ULONG IoBase;
    DWORD Reg;
   
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocateIoSpace+(Bus %d, Base 0x%X, Size 0x%X)\r\n", Bus, Base, Size));
    
    // Create bus head node with available address range
    if (!(BusHead = PCIRsrc_New(Bus, 0, 0, 0, Base, Size, TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocateIoSpace: Failed local alloc of BusHead\r\n"));
        return FALSE;
    }

    while (Rsrc = PCIRsrc_GetNext(g_IoHead, Bus)) {
        // Place resource
        if (!PCIRsrc_Place(BusHead, Rsrc)) {
            DEBUGMSG(ZONE_ERROR,
                (L"PCIBUS!PCICfgAllocIoSpace: Failed to place %s resource: Bus %d, Device %d, Function %d\r\n",
                (Rsrc->Bridge) ? L"Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function));
                    
            continue;
        }

        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PciIinitAllocIoSpace (%s): Bus %d, Device %d, Function %d, Offset 0x%X, IoBase 0x%X, IoSize 0x%X\r\n",
            (Rsrc->Bridge) ? L"PCI-PCI Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base, Rsrc->Size));

        if (Rsrc->Bridge) {
            // PCI-PCI bridge
            BOOL Use32bitIo;
            
            // Allocate resources for bridge's secondary bus
            PCICfgAllocateIoSpace(Rsrc->SecBus, Rsrc->Base, Rsrc->Size);
            
            // Write I/O base and limit registers
            IoBase = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_IO);

            // Check to see if the bridge is using 16-bit I/O or 32-bit I/O
            Use32bitIo = ((IoBase & 0xF) == PCI_SECONDARY_IO32);
            
            // Set bridge's base and limit address registers
            ((PBRIDGE_IO)(&IoBase))->IoLimit.Address = (BYTE)(((Rsrc->Base + Rsrc->Size - 1) & 0xF000) >> 12);
            ((PBRIDGE_IO)(&IoBase))->IoBase.Address = (BYTE)(((Rsrc->Base) & 0xF000) >> 12);
            PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_IO, IoBase);

            // If 32-bit I/O, set the I/O limit & base upper address registers.
            if (Use32bitIo) {
                DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCI-PCI bridge %d/%d/%d supports 32-bit I/O\r\n", Rsrc->Bus, Rsrc->Device, Rsrc->Function));
                DEBUGMSG(1, (L"IoBase = 0x%X, IoLimit = 0x%X\r\n", Rsrc->Base, Rsrc->Base + Rsrc->Size - 1));
                IoBase = (Rsrc->Base + Rsrc->Size - 1) & 0xFFFF0000;
                IoBase |= (Rsrc->Base) >> 16;
                DEBUGMSG(1, (L"IoBase reg = 0x%X, offset 0x%X\r\n", IoBase, PCIBRIDGE_IO_UPPER16));
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_IO_UPPER16, IoBase);
            }

            // Enable device's I/O space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE)) != ( PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg);  
            }
        } else if (!Rsrc->Placed) {
            // Device
            if (Rsrc->ConfigInfo) {
                // This resource needs to be set by the device's ConfigEntry routine (if supported)
                DWORD Status = ((PFN_CONFIGENTRY)Rsrc->ConfigInfo->ConfigEntryFn)(PCIBUS_CONFIG_SET, NULL, Rsrc, NULL, NULL, NULL);
        
                if (Status != ERROR_SUCCESS) {
                    if (Status == ERROR_NOT_SUPPORTED) {
                        // ConfigEntry point doesn't support this type of call, so do it here
                        // Set device's base address register
                        PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
                    } else {
                        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocIoSpace: Failed ConfigEntry call\r\n"));

                        return FALSE;
                    }
                }
            } else {    
                // Set device's base address register
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
            }
            
            // Enable device's I/O space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE )) != (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_IO_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg);
            }
        } 
    }

    PCIRsrc_DelList(BusHead);
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocateIoSpace-(Bus %d)\r\n", Bus));
    
    return TRUE;
}


//
// For each memory resource, allocate memory space on PCIbus and assign address to BAR
//
static BOOL
PCICfgAllocateMemSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    )
{
    PPCI_RSRC Rsrc, BusHead;
    ULONG MemBase;
    DWORD Reg;
   
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocateMemSpace+(Bus %d, Base 0x%X, Size 0x%X)\r\n", Bus, Base, Size));
    
    // Create bus head node with available address range
    if (!(BusHead = PCIRsrc_New(Bus, 0, 0, 0, Base, Size, TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocateIoSpace: Failed local alloc of BusHead\r\n"));
        return FALSE;
    }

    while (Rsrc = PCIRsrc_GetNext(g_MemHead, Bus)) {
        // Place resource
        if (!PCIRsrc_Place(BusHead, Rsrc)) {
            DEBUGMSG(ZONE_ERROR,
                (L"PCIBUS!PCICfgAllocMemSpace: Failed to place %s resource: Bus %d, Device %d, Function %d\r\n",
                (Rsrc->Bridge) ? L"Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function));
                    
            continue;
        }

        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgAllocMemSpace (%s): Bus %d, Device %d, Function %d, Offset 0x%X, MemBase 0x%X, MemSize 0x%X\r\n",
            (Rsrc->Bridge) ? L"PCI-PCI Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base, Rsrc->Size));
                
        if (Rsrc->Bridge) {
            // Allocate resources for bridge's secondary bus
            PCICfgAllocateMemSpace(Rsrc->SecBus, Rsrc->Base, Rsrc->Size);

            // Set bridge's base and limit address registers
            ((PBRIDGE_MEM)(&MemBase))->MemoryLimit.Address = (WORD)(((Rsrc->Base + Rsrc->Size - 1) & 0xFFF00000) >> 20);
            ((PBRIDGE_MEM)(&MemBase))->MemoryBase.Address = (WORD)(((Rsrc->Base) & 0xFFF00000) >> 20);
            PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_MEMORY, MemBase);

            // Enable device's memory space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE )) != (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg); 
            }
        } else if (!Rsrc->Placed) {
            if (Rsrc->ConfigInfo) {
                // This resource needs to be set by the device's ConfigEntry routine (if supported)
                DWORD Status = ((PFN_CONFIGENTRY)Rsrc->ConfigInfo->ConfigEntryFn)(PCIBUS_CONFIG_SET, NULL, Rsrc, NULL, NULL, NULL);
        
                if (Status != ERROR_SUCCESS) {
                    if (Status == ERROR_NOT_SUPPORTED) {
                        // ConfigEntry point doesn't support this type of call, so do it here
                        // Set device's base address register
                        PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
                    } else {
                        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocMemSpace: Failed ConfigEntry call\r\n"));

                        return FALSE;
                    }
                }
            } else {    
                // Set device's base address register
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
            }

            // Enable device's memory space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) != (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg);
            }
        } 
    }

    PCIRsrc_DelList(BusHead);
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocateMemSpace-(Bus %d)\r\n", Bus));
    
    return TRUE;
}

//
// For each prefetch memory resource, allocate memory space on PCIbus and assign address to BAR
//
static BOOL
PCICfgAllocatePrefetchMemSpace(
    DWORD Bus,
    DWORD Base,
    DWORD Size
    )
{
    PPCI_RSRC Rsrc, BusHead;
    ULONG MemBase;
    DWORD Reg;
   
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocatePrefetchMemSpace+(Bus %d, Base 0x%X, Size 0x%X)\r\n", Bus, Base, Size));
    
    // Create bus head node with available address range
    if (!(BusHead = PCIRsrc_New(Bus, 0, 0, 0, Base, Size, TRUE, 0, TRUE, NULL))) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocatePrefetchMemSpace: Failed local alloc of BusHead\r\n"));
        return FALSE;
    }

    while (Rsrc = PCIRsrc_GetNext(g_PrefetchMemHead, Bus)) {
        // Place resource
        if (!PCIRsrc_Place(BusHead, Rsrc)) {
            DEBUGMSG(ZONE_ERROR,
                (L"PCIBUS!PCICfgAllocPrefetchMemSpace: Failed to place %s resource: Bus %d, Device %d, Function %d\r\n",
                (Rsrc->Bridge) ? L"Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function));
                    
            continue;
        }

        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgAllocPrefetchMemSpace (%s): Bus %d, Device %d, Function %d, Offset 0x%X, MemBase 0x%X, MemSize 0x%X\r\n",
            (Rsrc->Bridge) ? L"PCI-PCI Bridge" : L"Device", Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base, Rsrc->Size));
                
        if (Rsrc->Bridge) {
            // Allocate resources for bridge's secondary bus
            PCICfgAllocatePrefetchMemSpace(Rsrc->SecBus, Rsrc->Base, Rsrc->Size);

            // Set bridge's base and limit address registers
            
            ((PBRIDGE_MEM)(&MemBase))->MemoryLimit.Address = (WORD)(((Rsrc->Base + Rsrc->Size - 1) & 0xFFF00000) >> 20);
            ((PBRIDGE_MEM)(&MemBase))->MemoryBase.Address = (WORD)(((Rsrc->Base) & 0xFFF00000) >> 20);
            PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_PREFETCHABLE, MemBase);
            PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_BASE_UPPER32, 0);
            PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCIBRIDGE_LIMIT_UPPER32,0);

            // Enable device's memory space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE )) != (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg); 
            }
        } else if (!Rsrc->Placed) {
            if (Rsrc->ConfigInfo) {
                // This resource needs to be set by the device's ConfigEntry routine (if supported)
                DWORD Status = ((PFN_CONFIGENTRY)Rsrc->ConfigInfo->ConfigEntryFn)(PCIBUS_CONFIG_SET, NULL, Rsrc, NULL, NULL, NULL);
        
                if (Status != ERROR_SUCCESS) {
                    if (Status == ERROR_NOT_SUPPORTED) {
                        // ConfigEntry point doesn't support this type of call, so do it here
                        // Set device's base address register
                        PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
                    } else {
                        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCICfgAllocMemSpace: Failed ConfigEntry call\r\n"));

                        return FALSE;
                    }
                }
            } else {    
                // Set device's base address register
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, Rsrc->Offset, Rsrc->Base);
            }

            // Enable device's memory space (and bus mastering capability)
            Reg = PCIConfig_Read(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS);
            if ((Reg & (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) != (PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE)) {
                Reg &= 0x3FF;
                Reg |= PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE;
                PCIConfig_Write(Rsrc->Bus, Rsrc->Device, Rsrc->Function, PCI_CONFIG_COMMAND_STATUS, Reg);
            }
        } 
    }

    PCIRsrc_DelList(BusHead);
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCICfgAllocateMemSpace-(Bus %d)\r\n", Bus));
    
    return TRUE;
}

//
// Find match for device in registry
//
static BOOL
PCICfgFindMatch(
    PPCI_DEV_INFO pInfo
    )
{
    WCHAR SavePath[DEVKEY_LEN];

    // Save off registry path
    wcsncpy(SavePath, pInfo->RegPath, DEVKEY_LEN - 1);
    SavePath[DEVKEY_LEN - 1] = L'\0';    // make sure longest string is terminated

    // Fail if key name too long
    if (wcslen(SavePath) + wcslen(L"\\") + wcslen(PCIBUS_INSTANCE_KEYNAME) >= DEVKEY_LEN) {
        wcscpy(pInfo->RegPath, SavePath);
        return FALSE;
    }
    
    // Check for exact match under Instance sub-key
    wcscat(pInfo->RegPath, L"\\");
    wcscat(pInfo->RegPath, PCIBUS_INSTANCE_KEYNAME);
        
    if (RegMatch(pInfo) == PCIBUS_MATCH_ALL) {
        // Exact instance match found
        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgFindMatch: Matched device %d/%d/%d with registry key '%s'\r\n",
            pInfo->Bus, pInfo->Device, pInfo->Function, pInfo->RegPath));

        return TRUE;
    }

    // Fail if key name too long
    if (wcslen(SavePath) + wcslen(L"\\") + wcslen(PCIBUS_TEMPLATE_KEYNAME) >= DEVKEY_LEN) {
        wcscpy(pInfo->RegPath, SavePath);
        return FALSE;
    }
    
    // No exact instance match found, check for match under Template key
    wcscpy(pInfo->RegPath, SavePath);
    wcscat(pInfo->RegPath, L"\\");
    wcscat(pInfo->RegPath, PCIBUS_TEMPLATE_KEYNAME);
            
    if (RegMatch(pInfo) != PCIBUS_MATCH_NONE) {
        // Partial template match found
        DEBUGMSG(ZONE_INIT, (L"PCIBUS!PCICfgFindMatch: Matched device %d/%d/%d with registry key '%s'\r\n",
            pInfo->Bus, pInfo->Device, pInfo->Function, pInfo->RegPath));

        return TRUE;
    }
    // No match found
    wcscpy(pInfo->RegPath, SavePath);
    return FALSE;
}


//
// Request IRQ from OAL
//
static BOOL
RequestIrq(
    PDEVICE_LOCATION pDevLoc,
    PDWORD pIrq
    )
{
    return KernelIoControl(IOCTL_HAL_REQUEST_IRQ, pDevLoc, sizeof(*pDevLoc), pIrq, sizeof(*pIrq), NULL);
}


//
// Read Base Address Registers and size, populate Info struct with this information
//
static BOOL
PCIReadBARs(
    PPCI_DEV_INFO pInfo
    )
{   
    DWORD NumberOfRegs;
    DWORD Offset;
    DWORD i;
    DWORD BaseAddress;
    DWORD Size;
    DWORD Reg;
    DWORD IoIndex = 0;
    DWORD MemIndex = 0;

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIReadBARs+(Bus %d, Device %d, Function %d)\r\n",
        pInfo->Bus, pInfo->Device, pInfo->Function));

    // Determine number of BARs to examine from header type
    switch (pInfo->Cfg->HeaderType & ~PCI_MULTIFUNCTION) {
    case PCI_DEVICE_TYPE:
        NumberOfRegs = PCI_TYPE0_ADDRESSES;
        break;

    case PCI_BRIDGE_TYPE:
        NumberOfRegs = PCI_TYPE1_ADDRESSES;
        break;

    case PCI_CARDBUS_TYPE:
        NumberOfRegs = PCI_TYPE2_ADDRESSES;
        break;

    default:
        return FALSE;
    }
        
    for (i = 0, Offset = 0x10; i < NumberOfRegs; i++, Offset += 4) {
        // Get base address register value
        Reg = pInfo->Cfg->u.type0.BaseAddresses[i];

        // Get size info
        PCIConfig_Write(pInfo->Bus, pInfo->Device, pInfo->Function, Offset, 0xFFFFFFFF);
        BaseAddress = PCIConfig_Read(pInfo->Bus, pInfo->Device, pInfo->Function, Offset);
        PCIConfig_Write(pInfo->Bus, pInfo->Device, pInfo->Function, Offset, Reg);
       
        if (Reg & PCI_ADDRESS_IO_SPACE) {
            // IO space
            // Re-adjust BaseAddress if upper 16-bits are 0 (this is allowable in PCI 2.2 spec)
            if (((BaseAddress & PCI_ADDRESS_IO_ADDRESS_MASK) != 0) && ((BaseAddress & 0xFFFF0000) == 0)) {
                BaseAddress |= 0xFFFF0000;
            }
            
            Size = ~(BaseAddress & PCI_ADDRESS_IO_ADDRESS_MASK);

            if ((BaseAddress != 0) && (BaseAddress != 0xFFFFFFFF) && (((Size + 1) & Size) == 0)) {
                // BAR has valid format (consecutive high 1's and consecutive low 0's)
                pInfo->IoLen.Reg[IoIndex] = Size + 1;
                pInfo->IoLen.Num++;
                pInfo->IoBase.Reg[IoIndex++] = Reg & PCI_ADDRESS_IO_ADDRESS_MASK;
                pInfo->IoBase.Num++;
            } else {
                // BAR invalid => skip to next one
                continue;
            }
        } else {
            // Memory space
            if ((Reg & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
                // PCI 2.2 spec no longer supports this type of memory addressing
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIReadBARs: 20-bit addressing not supported\r\n"));
                return FALSE;
            }
            
            // Re-adjust BaseAddress if upper 16-bits are 0 (this is allowable in PCI 2.2 spec)
            if (((BaseAddress & PCI_ADDRESS_MEMORY_ADDRESS_MASK) != 0) && ((BaseAddress & 0xFFFF0000) == 0)) {
                BaseAddress |= 0xFFFF0000;
            }
            
            Size = ~(BaseAddress & PCI_ADDRESS_MEMORY_ADDRESS_MASK);

            if ((BaseAddress != 0) && (BaseAddress != 0xFFFFFFFF) && (((Size + 1) & Size) == 0)) {
                // BAR has valid format (consecutive high 1's and consecutive low 0's)
                pInfo->MemLen.Reg[MemIndex] = Size + 1;
                pInfo->MemLen.Num++;
                pInfo->MemBase.Reg[MemIndex++] = Reg & PCI_ADDRESS_MEMORY_ADDRESS_MASK;
                pInfo->MemBase.Num++;
                
                
            } else {
                // BAR invalid => skip to next one
                continue;
            }

            if ((Reg & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
                // 64 bit device - BAR is twice as wide, skip upper 32-bits
                Offset += 4;
                i++;
            }
        }     
    }

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIReadBARs-\r\n"));

    return TRUE;
}


//
// Retrieve Base Address Register information from device
//
BOOL
PCIGetBARs(
    PPCI_DEV_INFO pInfo
    )
{
    WCHAR SavePath[DEVKEY_LEN];
    BOOL RetVal = TRUE;
          
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIGetBARs+(Bus %d, Device %d, Function %d)\r\n",
        pInfo->Bus, pInfo->Device, pInfo->Function));

    // Save off registry path
    wcsncpy(SavePath, pInfo->RegPath, DEVKEY_LEN - 1);
    SavePath[DEVKEY_LEN - 1] = L'\0';    // make sure longest string is terminated
    
    // Attempt to find match for this device in the registry and get info
       if (PCICfgFindMatch(pInfo)) {
            if (!RegGetInfo(pInfo)) {
                RetVal = FALSE;
                goto PCIGetBARs_Exit;
           }    
            if (!pInfo->Configure) {
            goto PCIGetBARs_Exit;
           }
            // If no Config Entry Defined update BAR's from PCI config space as it might be updated if a new Card is inserted and The BIOS/Bootloader doesnot configure it.
            if (!pInfo->ConfigEntry) {  
                pInfo->MemBase.Num = 0;
                pInfo->MemLen.Num = 0;
            
                //  Read the base address registers to determine BAR lengths to update pInfo. This value will be updated in Registry in RegSetConfig.
                if (!PCIReadBARs(pInfo)) {
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: Failed PCIReadBARs call\r\n"));
                    RetVal = FALSE;
                    goto PCIGetBARs_Exit;
                }               
            } 
   
               
        // Make sure the sum of Base entries is less than or equal to 6
        if (pInfo->MemBase.Num + pInfo->IoBase.Num > PCI_TYPE0_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: The number of registry values '%s' and '%s' under '%s'  must sum to be less than %d.\r\n",
                pInfo->RegPath, PCIBUS_MEMBASE_VALNAME, PCIBUS_IOBASE_VALNAME, PCI_TYPE0_ADDRESSES));

            RetVal = FALSE;
            goto PCIGetBARs_Exit;
        }

        // Make sure the sum of Len entries is less than or equal to 6
        if (pInfo->MemLen.Num + pInfo->IoLen.Num > PCI_TYPE0_ADDRESSES) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: The number of registry values '%s' and '%s' under '%s'  must sum to be less than %d.\r\n",
                pInfo->RegPath, PCIBUS_MEMLEN_VALNAME, PCIBUS_IOLEN_VALNAME, PCI_TYPE0_ADDRESSES));

            RetVal = FALSE;
            goto PCIGetBARs_Exit;
        }

        if (pInfo->MemLen.Num + pInfo->IoLen.Num > 0) {
            // If there is any Length info, return
            goto PCIGetBARs_Exit;
        }
    }


    // If ConfigEntry point defined, call it to determine BAR lengths
    if (pInfo->ConfigEntry) {
        DWORD Status;
        
        // Load driver Dll and obtain ConfigEntry function address
        if (!LoadConfigEntry(pInfo)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: Failed LoadConfigEntry call\r\n"));
            RetVal = FALSE;
            goto PCIGetBARs_Exit;
        }
        
        Status = ((PFN_CONFIGENTRY)(pInfo->ConfigInfo->ConfigEntryFn))(PCIBUS_CONFIG_SIZE, pInfo, NULL, NULL, NULL, NULL);

        // Unload Dll
        UnloadConfigEntrys( );    
        
        if (Status != ERROR_SUCCESS) {
            if (Status == ERROR_NOT_SUPPORTED) {
                // ConfigEntry point doesn't support this type of call, so do it here
                // Read the base address registers to determine BAR lengths
                if (!PCIReadBARs(pInfo)) {
                    DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: Failed PCIReadBARs call\r\n"));
                    
                    RetVal = FALSE;
                }
            } else {
                DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: Failed ConfigEntry call\r\n"));

                RetVal = FALSE;
            }
        }
    } else {
        // Read the base address registers to determine BAR lengths
        if (!PCIReadBARs(pInfo)) {
            DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIGetBARs: Failed PCIReadBARs call\r\n"));
            
            RetVal = FALSE;
        }
    }

PCIGetBARs_Exit:
    // Restore path
    wcscpy(pInfo->RegPath, SavePath);
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIGetBARs-\r\n"));
        
    return RetVal;
}


//
// Call device configuration routine with INIT parameter, if it exists
//
BOOL
PCIInitDevice(
    PPCI_DEV_INFO pInfo
    )
{
    DWORD Status;
    
    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIInitDevice+(Bus %d, Device %d, Function %d)\r\n",
        pInfo->Bus, pInfo->Device, pInfo->Function));
    
    // If ConfigEntry point not defined, do nothing
    if (!pInfo->ConfigEntry) return TRUE;
    
    // Load driver Dll and obtain ConfigEntry function address
    if (!LoadConfigEntry(pInfo)) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIInitDevice: Failed LoadConfigEntry call\r\n"));
        return FALSE;
    }
        
    Status = ((PFN_CONFIGENTRY)(pInfo->ConfigInfo->ConfigEntryFn))(PCIBUS_CONFIG_INIT, pInfo, NULL, NULL, NULL, NULL);

    // Unload Dll
    UnloadConfigEntrys( );    
        
    if ((Status != ERROR_SUCCESS) && (Status != ERROR_NOT_SUPPORTED)) {
        DEBUGMSG(ZONE_ERROR, (L"PCIBUS!PCIInitDevice: Failed ConfigEntry(PCIBUS_CONFIG_INIT) call\r\n"));
        return FALSE;
    }

    DEBUGMSG(ZONE_FUNCTION, (L"PCIBUS!PCIInitDevice-\r\n"));
    
    return TRUE;
}


