/*++


Copyright (c) 1989  Microsoft Corporation

Module Name:

    ixpcidat.c

Abstract:

    Get/Set bus data routines for the PCI bus

Author:

Environment:

    Kernel mode

Revision History:


--*/

#include "bootia64.h"
#include "arc.h"
#include "ixfwhal.h"
#include "ntconfig.h"

#include "pci.h"

//extern WCHAR rgzMultiFunctionAdapter[];
//extern WCHAR rgzConfigurationData[];
//extern WCHAR rgzIdentifier[];
//extern WCHAR rgzPCIIdentifier[];

//
// Hal specific PCI bus structures
//

typedef struct tagPCIPBUSDATA {
    union {
        struct {
            PULONG      Address;
            ULONG       Data;
        } Type1;
        struct {
            PUCHAR      CSE;
            PUCHAR      Forward;
            ULONG       Base;
        } Type2;
    } Config;

} PCIPBUSDATA, *PPCIPBUSDATA;

#define PciBitIndex(Dev,Fnc)    (Fnc*32 + Dev);
#define PCI_CONFIG_TYPE(PciData)    ((PciData)->HeaderType & ~PCI_MULTIFUNCTION)

#define BUSHANDLER  _BUSHANDLER
#define PBUSHANDLER _PBUSHANDLER

// thunk for NtLdr
typedef struct {
    ULONG           NoBuses;
    ULONG           BusNumber;
    PVOID           BusData;
    PCIPBUSDATA     theBusData;
} BUSHANDLER, *PBUSHANDLER;

#define HalpPCIPin2Line(bus,rbus,slot,pcidata)
#define HalpPCILine2Pin(bus,rbus,slot,pcidata,pcidata2)
#define ExAllocatePool(a,l) FwAllocatePool(l)
// thunk for NtLdr


typedef ULONG (*FncConfigIO) (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

typedef VOID (*FncSync) (
    IN PBUSHANDLER      BusHandler,
    IN PCI_SLOT_NUMBER  Slot,
    IN PKIRQL           Irql,
    IN PVOID            State
    );

typedef VOID (*FncReleaseSync) (
    IN PBUSHANDLER      BusHandler,
    IN KIRQL            Irql
    );

typedef struct {
    FncSync         Synchronize;
    FncReleaseSync  ReleaseSynchronzation;
    FncConfigIO     ConfigRead[3];
    FncConfigIO     ConfigWrite[3];
} CONFIG_HANDLER, *PCONFIG_HANDLER;


//
// Prototypes
//

ULONG
HalpGetPCIData (
    IN ULONG BusNumber,
    IN PCI_SLOT_NUMBER SlotNumber,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    );

ULONG
HalpSetPCIData (
    IN ULONG BusNumber,
    IN PCI_SLOT_NUMBER SlotNumber,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    );

extern ULONG
HalpGetPCIInterruptVector (
    IN PBUSHANDLER BusHandler,
    IN PBUSHANDLER RootHandler,
    IN ULONG BusInterruptLevel,
    IN ULONG BusInterruptVector,
    OUT PKIRQL Irql,
    OUT PKAFFINITY Affinity
    );

NTSTATUS
HalpAdjustPCIResourceList (
    IN PBUSHANDLER BusHandler,
    IN PBUSHANDLER RootHandler,
    IN OUT PIO_RESOURCE_REQUIREMENTS_LIST   *pResourceList
    );

NTSTATUS
HalpAssignPCISlotResources (
    IN ULONG                    BusNumber,
    IN ULONG                    SlotNumber,
    IN OUT PCM_RESOURCE_LIST   *AllocatedResources
    );

VOID
HalpInitializePciBuses (
    VOID
    );

//VOID
//HalpPCIPin2Line (
//    IN PBUSHANDLER         BusHandler,
//    IN PBUSHANDLER         RootHandler,
//    IN PCI_SLOT_NUMBER     Slot,
//    IN PPCI_COMMON_CONFIG  PciData
//    );
//
//VOID
//HalpPCILine2Pin (
//    IN PBUSHANDLER          BusHandler,
//    IN PBUSHANDLER          RootHandler,
//    IN PCI_SLOT_NUMBER      SlotNumber,
//    IN PPCI_COMMON_CONFIG   PciNewData,
//    IN PPCI_COMMON_CONFIG   PciOldData
//    );

BOOLEAN
HalpValidPCISlot (
    IN PBUSHANDLER     BusHandler,
    IN PCI_SLOT_NUMBER Slot
    );

VOID
HalpReadPCIConfig (
    IN PBUSHANDLER BusHandler,
    IN PCI_SLOT_NUMBER Slot,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    );


VOID
HalpWritePCIConfig (
    IN PBUSHANDLER BusHandler,
    IN PCI_SLOT_NUMBER Slot,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    );

PBUSHANDLER
HalpGetPciBusHandler (
    IN ULONG BusNumber
    );

//-------------------------------------------------

VOID HalpPCISynchronizeType1 (
    IN PBUSHANDLER      BusHandler,
    IN PCI_SLOT_NUMBER  Slot,
    IN PKIRQL           Irql,
    IN PVOID            State
    );

VOID HalpPCIReleaseSynchronzationType1 (
    IN PBUSHANDLER      BusHandler,
    IN KIRQL            Irql
    );

ULONG HalpPCIReadUlongType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIReadUcharType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIReadUshortType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUlongType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUcharType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUshortType1 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

VOID HalpPCISynchronizeType2 (
    IN PBUSHANDLER      BusHandler,
    IN PCI_SLOT_NUMBER  Slot,
    IN PKIRQL           Irql,
    IN PVOID            State
    );

VOID HalpPCIReleaseSynchronzationType2 (
    IN PBUSHANDLER      BusHandler,
    IN KIRQL            Irql
    );

ULONG HalpPCIReadUlongType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIReadUcharType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIReadUshortType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUlongType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUcharType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );

ULONG HalpPCIWriteUshortType2 (
    IN PPCIPBUSDATA     BusData,
    IN PVOID            State,
    IN PUCHAR           Buffer,
    IN ULONG            Offset
    );


//#define DISABLE_INTERRUPTS() //_asm { cli }
//#define ENABLE_INTERRUPTS()  //_asm { sti }


//
// Globals
//

ULONG               PCIMaxDevice;
BUSHANDLER          PCIBusHandler;

CONFIG_HANDLER      PCIConfigHandlers = {
    HalpPCISynchronizeType1,
    HalpPCIReleaseSynchronzationType1,
    {
        HalpPCIReadUlongType1,          // 0
        HalpPCIReadUcharType1,          // 1
        HalpPCIReadUshortType1          // 2
    },
    {
        HalpPCIWriteUlongType1,         // 0
        HalpPCIWriteUcharType1,         // 1
        HalpPCIWriteUshortType1         // 2
    }
};

CONFIG_HANDLER      PCIConfigHandlersType2 = {
    HalpPCISynchronizeType2,
    HalpPCIReleaseSynchronzationType2,
    {
        HalpPCIReadUlongType2,          // 0
        HalpPCIReadUcharType2,          // 1
        HalpPCIReadUshortType2          // 2
    },
    {
        HalpPCIWriteUlongType2,         // 0
        HalpPCIWriteUcharType2,         // 1
        HalpPCIWriteUshortType2         // 2
    }
};

UCHAR PCIDeref[4][4] = { {0,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} };


VOID
HalpPCIConfig (
    IN PBUSHANDLER      BusHandler,
    IN PCI_SLOT_NUMBER  Slot,
    IN PUCHAR           Buffer,
    IN ULONG            Offset,
    IN ULONG            Length,
    IN FncConfigIO      *ConfigIO
    );


VOID
HalpInitializePciBus (
    VOID
    )
{
    PPCI_REGISTRY_INFO  PCIRegInfo;
    PPCIPBUSDATA        BusData;
    PBUSHANDLER         Bus;
    PCONFIGURATION_COMPONENT_DATA   ConfigData;
    PCM_PARTIAL_RESOURCE_LIST       Desc;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
    ULONG               i;
    ULONG               HwType;

    Bus = &PCIBusHandler;
    PCIBusHandler.BusData = &PCIBusHandler.theBusData;

    PCIRegInfo = NULL;      // not found
    ConfigData = NULL;      // start at begining
    do {
        ConfigData = KeFindConfigurationNextEntry (
            FwConfigurationTree,
            AdapterClass,
            MultiFunctionAdapter,
            NULL,
            &ConfigData
            );

        if (ConfigData == NULL) {
            // PCI info not found
            return ;
        }

        if (ConfigData->ComponentEntry.Identifier == NULL  ||
            _stricmp (ConfigData->ComponentEntry.Identifier, "PCI") != 0) {
            continue;
        }

        PCIRegInfo = NULL;
        Desc  = ConfigData->ConfigurationData;
        PDesc = Desc->PartialDescriptors;
        for (i = 0; i < Desc->Count; i++) {
            if (PDesc->Type == CmResourceTypeDeviceSpecific) {
                PCIRegInfo = (PPCI_REGISTRY_INFO) (PDesc+1);
                break;
            }
            PDesc++;
        }
    } while (!PCIRegInfo) ;

    //
    // PCIRegInfo describes the system's PCI support as indicated
    // by the BIOS.
    //

    HwType = PCIRegInfo->HardwareMechanism & 0xf;

    switch (HwType) {
        case 1:
            // this is the default case
            PCIMaxDevice = PCI_MAX_DEVICES;
            break;

        //
        // Type2 does not work MP, nor does the default type2
        // support more the 0xf device slots
        //

        case 2:
            RtlMoveMemory (&PCIConfigHandlers,
                           &PCIConfigHandlersType2,
                           sizeof (PCIConfigHandlersType2));
            PCIMaxDevice = 0x10;
            break;

        default:
            // unsupport type
            PCIRegInfo->NoBuses = 0;
    }

    PCIBusHandler.NoBuses = PCIRegInfo->NoBuses;
    if (PCIRegInfo->NoBuses) {

        BusData = (PPCIPBUSDATA) Bus->BusData;
        switch (HwType) {
            case 1:
                BusData->Config.Type1.Address = PCI_TYPE1_ADDR_PORT;
                BusData->Config.Type1.Data    = PCI_TYPE1_DATA_PORT;
                break;

            case 2:
                BusData->Config.Type2.CSE     = PCI_TYPE2_CSE_PORT;
                BusData->Config.Type2.Forward = PCI_TYPE2_FORWARD_PORT;
                BusData->Config.Type2.Base    = PCI_TYPE2_ADDRESS_BASE;
                break;
        }
    }
}


PBUSHANDLER
HalpGetPciBusHandler (
    IN ULONG BusNumber
    )
{
    if (PCIBusHandler.BusData == NULL) {
        HalpInitializePciBus ();
    }

    if (BusNumber > PCIBusHandler.NoBuses) {
        return NULL;
    }

    PCIBusHandler.BusNumber = BusNumber;
    return &PCIBusHandler;
}


ULONG
HalpGetPCIData (
    IN ULONG BusNumber,
    IN PCI_SLOT_NUMBER Slot,
    IN PUCHAR Buffer,
    IN ULONG Offset,
    IN ULONG Length
    )
/*++

Routine Description:

    The function returns the Pci bus data for a device.

Arguments:

    BusNumber - Indicates which bus.

    VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word)

    Buffer - Supplies the space to store the data.

    Length - Supplies a count in bytes of the maximum amount to return.

Return Value:

    Returns the amount of data stored into the buffer.

    If this PCI slot has never been set, then the configuration information
    returned is zeroed.


--*/
{
    PBUSHANDLER         BusHandler;
    PPCI_COMMON_CONFIG  PciData;
    UCHAR               iBuffer[PCI_COMMON_HDR_LENGTH];
    PPCIPBUSDATA        BusData;
    ULONG               Len;

    BusHandler = HalpGetPciBusHandler (BusNumber);
    if (!BusHandler) {
        return 0;
    }

    if (Length > sizeof (PCI_COMMON_CONFIG)) {
        Length = sizeof (PCI_COMMON_CONFIG);
    }

    Len = 0;
    PciData = (PPCI_COMMON_CONFIG) iBuffer;

    if (Offset >= PCI_COMMON_HDR_LENGTH) {
        //
        // The user did not request any data from the common
        // header.  Verify the PCI device exists, then continue
        // in the device specific area.
        //

        HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG));

        if (PciData->VendorID == PCI_INVALID_VENDORID) {
            return 0;
        }

    } else {

        //
        // Caller requested at least some data within the
        // common header.  Read the whole header, effect the
        // fields we need to and then copy the user's requested
        // bytes from the header
        //

        //
        // Read this PCI devices slot data
        //

        Len = PCI_COMMON_HDR_LENGTH;
        HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len);

        if (PciData->VendorID == PCI_INVALID_VENDORID) {
            Len = 2;       // only return invalid id
        }

        //
        // Has this PCI device been configured?
        //

        BusData = (PPCIPBUSDATA) BusHandler->BusData;
        HalpPCIPin2Line (BusHandler, RootHandler, Slot, PciData);

        //
        // Copy whatever data overlaps into the callers buffer
        //

        if (Len < Offset) {
            // no data at caller's buffer
            return 0;
        }

        Len -= Offset;
        if (Len > Length) {
            Len = Length;
        }

        RtlMoveMemory(Buffer, iBuffer + Offset, Len);

        Offset += Len;
        Buffer += Len;
        Length -= Len;
    }

    if (Length) {
        if (Offset >= PCI_COMMON_HDR_LENGTH) {
            //
            // The remaining Buffer comes from the Device Specific
            // area - put on the kitten gloves and read from it.
            //
            // Specific read/writes to the PCI device specific area
            // are guarenteed:
            //
            //    Not to read/write any byte outside the area specified
            //    by the caller.  (this may cause WORD or BYTE references
            //    to the area in order to read the non-dword aligned
            //    ends of the request)
            //
            //    To use a WORD access if the requested length is exactly
            //    a WORD long & WORD aligned.
            //
            //    To use a BYTE access if the requested length is exactly
            //    a BYTE long.
            //

            HalpReadPCIConfig (BusHandler, Slot, Buffer, Offset, Length);
            Len += Length;
        }
    }

    return Len;
}

ULONG
HalpSetPCIData (
    IN ULONG BusNumber,
    IN PCI_SLOT_NUMBER Slot,
    IN PUCHAR Buffer,
    IN ULONG Offset,
    IN ULONG Length
    )
/*++

Routine Description:

    The function returns the Pci bus data for a device.

Arguments:


    VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word)

    Buffer - Supplies the space to store the data.

    Length - Supplies a count in bytes of the maximum amount to return.

Return Value:

    Returns the amount of data stored into the buffer.

--*/
{
    PBUSHANDLER         BusHandler;
    PPCI_COMMON_CONFIG  PciData, PciData2;
    UCHAR               iBuffer[PCI_COMMON_HDR_LENGTH];
    UCHAR               iBuffer2[PCI_COMMON_HDR_LENGTH];
    PPCIPBUSDATA        BusData;
    ULONG               Len;

    BusHandler = HalpGetPciBusHandler (BusNumber);
    if (!BusHandler) {
        return 0;
    }

    if (Length > sizeof (PCI_COMMON_CONFIG)) {
        Length = sizeof (PCI_COMMON_CONFIG);
    }

    Len = 0;
    PciData = (PPCI_COMMON_CONFIG) iBuffer;
    PciData2 = (PPCI_COMMON_CONFIG) iBuffer2;

    if (Offset >= PCI_COMMON_HDR_LENGTH) {
        //
        // The user did not request any data from the common
        // header.  Verify the PCI device exists, then continue in
        // the device specific area.
        //

        HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG));

        if (PciData->VendorID == PCI_INVALID_VENDORID) {
            return 0;
        }

    } else {

        //
        // Caller requested to set at least some data within the
        // common header.
        //

        Len = PCI_COMMON_HDR_LENGTH;
        HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len);
        if (PciData->VendorID == PCI_INVALID_VENDORID  ||
            PCI_CONFIG_TYPE (PciData) != 0) {

            // no device, or header type unkown
            return 0;
        }

        //
        // Set this device as configured
        //

        BusData = (PPCIPBUSDATA) BusHandler->BusData;
        //
        // Copy COMMON_HDR values to buffer2, then overlay callers changes.
        //

        RtlMoveMemory (iBuffer2, iBuffer, Len);

        Len -= Offset;
        if (Len > Length) {
            Len = Length;
        }

        RtlMoveMemory (iBuffer2+Offset, Buffer, Len);

        // in case interrupt line or pin was editted
        HalpPCILine2Pin (BusHandler, RootHandler, Slot, PciData2, PciData);

        //
        // Set new PCI configuration
        //

        HalpWritePCIConfig (BusHandler, Slot, iBuffer2+Offset, Offset, Len);

        Offset += Len;
        Buffer += Len;
        Length -= Len;
    }

    if (Length) {
        if (Offset >= PCI_COMMON_HDR_LENGTH) {
            //
            // The remaining Buffer comes from the Device Specific
            // area - put on the kitten gloves and write it
            //
            // Specific read/writes to the PCI device specific area
            // are guarenteed:
            //
            //    Not to read/write any byte outside the area specified
            //    by the caller.  (this may cause WORD or BYTE references
            //    to the area in order to read the non-dword aligned
            //    ends of the request)
            //
            //    To use a WORD access if the requested length is exactly
            //    a WORD long & WORD aligned.
            //
            //    To use a BYTE access if the requested length is exactly
            //    a BYTE long.
            //

            HalpWritePCIConfig (BusHandler, Slot, Buffer, Offset, Length);
            Len += Length;
        }
    }

    return Len;
}

VOID
HalpReadPCIConfig (
    IN PBUSHANDLER BusHandler,
    IN PCI_SLOT_NUMBER Slot,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    )
{
    if (!HalpValidPCISlot (BusHandler, Slot)) {
        //
        // Invalid SlotID return no data
        //

        RtlFillMemory (Buffer, Length, (UCHAR) -1);
        return ;
    }

    HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length,
                   PCIConfigHandlers.ConfigRead);
}

VOID
HalpWritePCIConfig (
    IN PBUSHANDLER BusHandler,
    IN PCI_SLOT_NUMBER Slot,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
    )
{
    if (!HalpValidPCISlot (BusHandler, Slot)) {
        //
        // Invalid SlotID do nothing
        //
        return ;
    }

    HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length,
                   PCIConfigHandlers.ConfigWrite);
}

BOOLEAN
HalpValidPCISlot (
    IN PBUSHANDLER BusHandler,
    IN PCI_SLOT_NUMBER Slot
    )
{
    PCI_SLOT_NUMBER                 Slot2;
    UCHAR                           HeaderType;
    ULONG                           i;

    if (Slot.u.bits.Reserved != 0) {
        return FALSE;
    }

    if (Slot.u.bits.DeviceNumber >= PCIMaxDevice) {
        return FALSE;
    }

    if (Slot.u.bits.FunctionNumber == 0) {
        return TRUE;
    }

    //
    // Non zero function numbers are only supported if the
    // device has the PCI_MULTIFUNCTION bit set in it's header
    //

    i = Slot.u.bits.DeviceNumber;

    //
    // Read DeviceNumber, Function zero, to determine if the
    // PCI supports multifunction devices
    //

    Slot2 = Slot;
    Slot2.u.bits.FunctionNumber = 0;

    HalpReadPCIConfig (
        BusHandler,
        Slot2,
        &HeaderType,
        FIELD_OFFSET (PCI_COMMON_CONFIG, HeaderType),
        sizeof (UCHAR)
        );

    if (!(HeaderType & PCI_MULTIFUNCTION) || HeaderType == 0xFF) {
        // this device doesn't exists or doesn't support MULTIFUNCTION types
        return FALSE;
    }

    return TRUE;
}


VOID
HalpPCIConfig (
    IN PBUSHANDLER      BusHandler,
    IN PCI_SLOT_NUMBER  Slot,
    IN PUCHAR           Buffer,
    IN ULONG            Offset,
    IN ULONG            Length,
    IN FncConfigIO      *ConfigIO
    )
{
    KIRQL               OldIrql;
    ULONG               i;
    UCHAR               State[20];
    PPCIPBUSDATA        BusData;

    BusData = (PPCIPBUSDATA) BusHandler->BusData;
    PCIConfigHandlers.Synchronize (BusHandler, Slot, &OldIrql, State);

    while (Length) {
        i = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
        i = ConfigIO[i] (BusData, State, Buffer, Offset);

        Offset += i;
        Buffer += i;
        Length -= i;
    }

    PCIConfigHandlers.ReleaseSynchronzation (BusHandler, OldIrql);
}

VOID HalpPCISynchronizeType1 (
    IN PBUSHANDLER          BusHandler,
    IN PCI_SLOT_NUMBER      Slot,
    IN PKIRQL               Irql,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1
    )
{
    //
    // Initialize PciCfg1
    //

    PciCfg1->u.AsULONG = 0;
    PciCfg1->u.bits.BusNumber = BusHandler->BusNumber;
    PciCfg1->u.bits.DeviceNumber = Slot.u.bits.DeviceNumber;
    PciCfg1->u.bits.FunctionNumber = Slot.u.bits.FunctionNumber;
    PciCfg1->u.bits.Enable = TRUE;

    //
    // Synchronize with PCI type1 config space
    //

    //KeAcquireSpinLock (&HalpPCIConfigLock, Irql);
}

VOID HalpPCIReleaseSynchronzationType1 (
    IN PBUSHANDLER      BusHandler,
    IN KIRQL            Irql
    )
{
    PCI_TYPE1_CFG_BITS  PciCfg1;
    PPCIPBUSDATA        BusData;

    //
    // Disable PCI configuration space
    //

    PciCfg1.u.AsULONG = 0;
    BusData = (PPCIPBUSDATA) BusHandler->BusData;
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1.u.AsULONG);

    //
    // Release spinlock
    //

    //KeReleaseSpinLock (&HalpPCIConfigLock, Irql);
}


ULONG
HalpPCIReadUcharType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    ULONG               i;

    i = Offset % sizeof(ULONG);
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    *Buffer = READ_PORT_UCHAR ((PUCHAR)UlongToPtr((BusData->Config.Type1.Data + i)));
    return sizeof (UCHAR);
}

ULONG
HalpPCIReadUshortType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    ULONG               i;

    i = Offset % sizeof(ULONG);
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT)ULongToPtr(BusData->Config.Type1.Data + i));
    return sizeof (USHORT);
}

ULONG
HalpPCIReadUlongType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG)ULongToPtr(BusData->Config.Type1.Data));
    return sizeof (ULONG);
}


ULONG
HalpPCIWriteUcharType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    ULONG               i;

    i = Offset % sizeof(ULONG);
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    WRITE_PORT_UCHAR ((PUCHAR)ULongToPtr(BusData->Config.Type1.Data + i), *Buffer);
    return sizeof (UCHAR);
}

ULONG
HalpPCIWriteUshortType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    ULONG               i;

    i = Offset % sizeof(ULONG);
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    WRITE_PORT_USHORT ((PUSHORT)ULongToPtr(BusData->Config.Type1.Data + i), *((PUSHORT) Buffer));
    return sizeof (USHORT);
}

ULONG
HalpPCIWriteUlongType1 (
    IN PPCIPBUSDATA         BusData,
    IN PPCI_TYPE1_CFG_BITS  PciCfg1,
    IN PUCHAR               Buffer,
    IN ULONG                Offset
    )
{
    PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
    WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG);
    WRITE_PORT_ULONG ((PULONG)ULongToPtr(BusData->Config.Type1.Data), *((PULONG) Buffer));
    return sizeof (ULONG);
}


VOID HalpPCISynchronizeType2 (
    IN PBUSHANDLER              BusHandler,
    IN PCI_SLOT_NUMBER          Slot,
    IN PKIRQL                   Irql,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr
    )
{
    PCI_TYPE2_CSE_BITS      PciCfg2Cse;
    PPCIPBUSDATA            BusData;

    BusData = (PPCIPBUSDATA) BusHandler->BusData;

    //
    // Initialize Cfg2Addr
    //

    PciCfg2Addr->u.AsUSHORT = 0;
    PciCfg2Addr->u.bits.Agent = (USHORT) Slot.u.bits.DeviceNumber;
    PciCfg2Addr->u.bits.AddressBase = (USHORT) BusData->Config.Type2.Base;

    //
    // Synchronize with type2 config space - type2 config space
    // remaps 4K of IO space, so we can not allow other I/Os to occur
    // while using type2 config space, hence the disable_interrupts.
    //

    //KeAcquireSpinLock (&HalpPCIConfigLock, Irql);
    //DISABLE_INTERRUPTS ();                      // is not MP safe

    PciCfg2Cse.u.AsUCHAR = 0;
    PciCfg2Cse.u.bits.Enable = TRUE;
    PciCfg2Cse.u.bits.FunctionNumber = (UCHAR) Slot.u.bits.FunctionNumber;
    PciCfg2Cse.u.bits.Key = 0xff;

    //
    // Select bus & enable type 2 configuration space
    //

    WRITE_PORT_UCHAR (BusData->Config.Type2.Forward, (UCHAR) BusHandler->BusNumber);
    WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR);
}


VOID HalpPCIReleaseSynchronzationType2 (
    IN PBUSHANDLER          BusHandler,
    IN KIRQL                Irql
    )
{
    PCI_TYPE2_CSE_BITS      PciCfg2Cse;
    PPCIPBUSDATA            BusData;

    //
    // disable PCI configuration space
    //

    BusData = (PPCIPBUSDATA) BusHandler->BusData;

    PciCfg2Cse.u.AsUCHAR = 0;
    WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR);

    //
    // Restore interrupts, release spinlock
    //

    //ENABLE_INTERRUPTS ();
    //KeReleaseSpinLock (&HalpPCIConfigLock, Irql);
}


ULONG
HalpPCIReadUcharType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    *Buffer = READ_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT);
    return sizeof (UCHAR);
}

ULONG
HalpPCIReadUshortType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT);
    return sizeof (USHORT);
}

ULONG
HalpPCIReadUlongType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT);
    return sizeof(ULONG);
}


ULONG
HalpPCIWriteUcharType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    WRITE_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT, *Buffer);
    return sizeof (UCHAR);
}

ULONG
HalpPCIWriteUshortType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    WRITE_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT, *((PUSHORT) Buffer));
    return sizeof (USHORT);
}

ULONG
HalpPCIWriteUlongType2 (
    IN PPCIPBUSDATA             BusData,
    IN PPCI_TYPE2_ADDRESS_BITS  PciCfg2Addr,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset
    )
{
    PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset;
    WRITE_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT, *((PULONG) Buffer));
    return sizeof(ULONG);
}

NTSTATUS
HalpAssignPCISlotResources (
    IN ULONG                    BusNumber,
    IN ULONG                    Slot,
    IN OUT PCM_RESOURCE_LIST   *pAllocatedResources
    )
/*++

Routine Description:

    Reads the targeted device to determine it's required resources.
    Calls IoAssignResources to allocate them.
    Sets the targeted device with it's assigned resoruces
    and returns the assignments to the caller.

Arguments:

Return Value:

    STATUS_SUCCESS or error

--*/
{
    PBUSHANDLER                     BusHandler;
    UCHAR                           buffer[PCI_COMMON_HDR_LENGTH];
    UCHAR                           buffer2[PCI_COMMON_HDR_LENGTH];
    PPCI_COMMON_CONFIG              PciData, PciData2;
    PCI_SLOT_NUMBER                 PciSlot;
    ULONG                           i, j, length, type;
    PHYSICAL_ADDRESS                Address;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor;
    static PCM_RESOURCE_LIST        CmResList;

    BusHandler = HalpGetPciBusHandler (BusNumber);
    if (!BusHandler) {
        return 0;
    }

    *pAllocatedResources = NULL;

    PciData  = (PPCI_COMMON_CONFIG) buffer;
    PciData2 = (PPCI_COMMON_CONFIG) buffer2;
    PciSlot  = *((PPCI_SLOT_NUMBER) &Slot);
    BusNumber = BusHandler->BusNumber;

    //
    // Read the PCI device's configuration
    //

    HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
    if (PciData->VendorID == PCI_INVALID_VENDORID) {
        return STATUS_NO_SUCH_DEVICE;
    }

    //
    // Make a copy of the device's current settings
    //

    RtlMoveMemory (buffer2, buffer, PCI_COMMON_HDR_LENGTH);

    //
    // Set resources to all bits on to see what type of resources
    // are required.
    //

    for (j=0; j < PCI_TYPE0_ADDRESSES; j++) {
        PciData->u.type0.BaseAddresses[j] = 0xFFFFFFFF;
    }
    PciData->u.type0.ROMBaseAddress = 0xFFFFFFFF;

    PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE);
    PciData->u.type0.ROMBaseAddress &= ~PCI_ROMADDRESS_ENABLED;
    HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
    HalpReadPCIConfig  (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
    HalpPCIPin2Line (BusHandler, RootHandler, PciSlot, PciData);

    //
    // Restore the device's settings in case we don't complete
    //

    HalpWritePCIConfig (BusHandler, PciSlot, buffer2, 0, PCI_COMMON_HDR_LENGTH);

    //
    // Build a CmResource descriptor list for the device
    //

    if (!CmResList) {
        // NtLdr pool is only allocated and never freed.  Allocate the
        // buffer once, and from then on just use the buffer over

        CmResList = ExAllocatePool (PagedPool,
            sizeof (CM_RESOURCE_LIST) +
            sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR) * (PCI_TYPE0_ADDRESSES + 2)
            );
    }

    if (!CmResList) {
        return STATUS_NO_MEMORY;
    }

    RtlZeroMemory (CmResList,
        sizeof (CM_RESOURCE_LIST) +
        sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR) * (PCI_TYPE0_ADDRESSES + 2)
        );

    *pAllocatedResources = CmResList;
    CmResList->List[0].InterfaceType = PCIBus;
    CmResList->List[0].BusNumber = BusNumber;

    CmDescriptor = CmResList->List[0].PartialResourceList.PartialDescriptors;
    if (PciData->u.type0.InterruptPin) {

        CmDescriptor->Type = CmResourceTypeInterrupt;
        CmDescriptor->ShareDisposition = CmResourceShareShared;
        CmDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;

        // in the loader interrupts aren't actually enabled, so just
        // pass back the untranslated values
        CmDescriptor->u.Interrupt.Level = PciData->u.type0.InterruptLine;
        CmDescriptor->u.Interrupt.Vector =  PciData->u.type0.InterruptLine;
        CmDescriptor->u.Interrupt.Affinity = 1;

        CmResList->List[0].PartialResourceList.Count++;
        CmDescriptor++;
    }

    // clear last address index + 1
    PciData->u.type0.BaseAddresses[PCI_TYPE0_ADDRESSES] = 0;
    if (PciData2->u.type0.ROMBaseAddress & PCI_ROMADDRESS_ENABLED) {

        // put rom address in last index+1
        PciData->u.type0.BaseAddresses[PCI_TYPE0_ADDRESSES] =
            PciData->u.type0.ROMBaseAddress & ~PCI_ADDRESS_IO_SPACE;

        PciData2->u.type0.BaseAddresses[PCI_TYPE0_ADDRESSES] =
            PciData2->u.type0.ROMBaseAddress & ~PCI_ADDRESS_IO_SPACE;
    }

    for (j=0; j < PCI_TYPE0_ADDRESSES + 1; j++) {
        if (PciData->u.type0.BaseAddresses[j]) {
            i = PciData->u.type0.BaseAddresses[j];

            // scan for first set bit, that's the length & alignment
            length = 1 << (i & PCI_ADDRESS_IO_SPACE ? 2 : 4);
            Address.HighPart = 0;
            Address.LowPart = PciData2->u.type0.BaseAddresses[j] & ~(length-1);
            while (!(i & length)  &&  length) {
                length <<= 1;
            }

            // translate bus specific address
            type = (i & PCI_ADDRESS_IO_SPACE) ? 0 : 1;
            if (!HalTranslateBusAddress (
                    PCIBus,
                    BusNumber,
                    Address,
                    &type,
                    &Address )) {
                // translation failed, skip it
                continue;
            }

            // fill in CmDescriptor to return
            if (type == 0) {
                CmDescriptor->Type = CmResourceTypePort;
                CmDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
                CmDescriptor->Flags = CM_RESOURCE_PORT_IO;
                CmDescriptor->u.Port.Length = length;
                CmDescriptor->u.Port.Start = Address;
            } else {
                CmDescriptor->Type = CmResourceTypeMemory;
                CmDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
                CmDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
                CmDescriptor->u.Memory.Length = length;
                CmDescriptor->u.Memory.Start = Address;

                if (j == PCI_TYPE0_ADDRESSES) {
                    // this is a ROM address
                    CmDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
                }
            }

            CmResList->List[0].PartialResourceList.Count++;
            CmDescriptor++;

            if (i & PCI_TYPE_64BIT) {
                // skip upper half of 64 bit address.
                j++;
            }
        }
    }

    return STATUS_SUCCESS;
}