//
// 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.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

PciBus.cpp

Abstract:

This file implements the PCMCIA model device driver initialization functions
This is provided as a sample to platform writers and is
expected to be able to be used without modification on most (if not
all) hardware platforms.

Functions:


Notes:


--*/
#include <windows.h>
#include <types.h>
#include <ceddk.h>
#include <resmgr.h>
#include "pcib.h"
#include "debug.h"
//
// Request SysIntr from OAL given an IRQ.
//
static BOOL RequestSysIntr( DWORD Irq, DWORD* pSysIntr )
{
    BOOL RetVal = KernelIoControl( IOCTL_HAL_REQUEST_SYSINTR,
                                   &Irq,
                                   sizeof( Irq ),
                                   pSysIntr,
                                   sizeof( *pSysIntr ),
                                   NULL );

    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS!RequestSysIntr(%d) -> %d\r\n", Irq, *pSysIntr ) );

    if( *pSysIntr == -1 )
    {
        RetVal = FALSE;
    }

    if( !RetVal )
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIBUS!RequestSysIntr: Invalid SysIntr returned for Irq %d\r\n",
                    Irq ) );
    }

    return RetVal;
}
static BOOL ReleaseSysIntr( DWORD SysIntr )
{
    BOOL RetVal = KernelIoControl( IOCTL_HAL_RELEASE_SYSINTR,
                                   &SysIntr,
                                   sizeof( SysIntr ),
                                   NULL,
                                   0,
                                   NULL );
    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS!ReleaseSysIntr(%d) return %d\r\n", SysIntr, RetVal ) );
    return RetVal;
}

CCfgEntry::CCfgEntry( LPCTSTR lpConfigEntry,
                      LPCTSTR lpConfigDll,
                      DWORD dwDevFlag )
{
    m_hDll = NULL;
    m_ConfigEntryFn = NULL;
    if( lpConfigEntry && lpConfigDll )
    {
        m_hDll = ( ( dwDevFlag & DEVFLAGS_LOADLIBRARY ) !=
                   0 ?
                   LoadLibrary( lpConfigDll ) :
                   LoadDriver( lpConfigDll ) );
        if( m_hDll )
        {
            m_ConfigEntryFn = ( PFN_CONFIGENTRY )
                              GetProcAddress( m_hDll,
                                              lpConfigEntry );
        }
    }
};
CCfgEntry::~CCfgEntry()
{
    if( m_hDll )
    {
        FreeLibrary( m_hDll );
    }
};
DWORD CCfgEntry::CallEntry( DWORD dwOp,
                            struct _PCI_DEV_INFO* pInfo,
                            PPCI_RSRC pRrcMem,
                            PPCI_RSRC pResIo,
                            PDWORD pMemSize,
                            PDWORD pIoSize )
{
    if( m_ConfigEntryFn )
    {
        return m_ConfigEntryFn( dwOp,
                                pInfo,
                                pRrcMem,
                                pResIo,
                                pMemSize,
                                pIoSize );
    }
    else
    {
        return ERROR_NOT_SUPPORTED;
    }
}
static DWORD MinVal( DWORD List[], DWORD Len )
{
    DWORD i;
    DWORD Min = List[0];

    for( i = 1; i < Len; i++ )
    {
        if( List[i] < Min )
        {
            Min = List[i];
        }
    }

    return Min;
}
//
// Device/registry matching routine.
//
DWORD CPCIEnum::RegMatchOne( CRegistryEdit& Key, LPCWSTR DevName )
{
    DWORD Match = 0;
    DWORD BestMatch = 0;

    DWORD ClassVal;
    DWORD SubClassVal;
    DWORD Val32;

    DWORD ProgIFs[PCI_MAX_REG_LIST], ProgIFNum;
    DWORD VendorIDs[PCI_MAX_REG_LIST], VendorIDNum;
    DWORD DeviceIDs[PCI_MAX_REG_LIST], DeviceIDNum;
    DWORD SubVendorIDs[PCI_MAX_REG_LIST], SubVendorIDNum;
    DWORD SubSystemIDs[PCI_MAX_REG_LIST], SubSystemIDNum;
    DWORD RevisionIDs[PCI_MAX_REG_LIST], RevisionIDNum;

    DWORD ListNum[4], MaxListNum;
    DWORD i;


    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS!RegMatchOne+(%d, %d, %d)\r\n",
                m_dwBus,
                m_SlotNumber.u.bits.DeviceNumber,
                m_SlotNumber.u.bits.FunctionNumber ) );

    CRegistryEdit DevKey( Key.GetHKey(), DevName );
    if( !DevKey.IsKeyOpened() )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCIBUS!RegMatchOne RegOpenKeyEx(%s) returned \r\n" ),
                    DevName ) );
        return 0;
    }

    DEBUGMSG( ZONE_ENUM,
              ( TEXT( "PCIBUS!RegMatchOne Checking key %s.\r\n" ), DevName ) );

    //
    // Examine Class value, check against device's base class
    //
    if( !DevKey.GetRegValue( PCIBUS_CLASS_VALNAME,
                             ( PUCHAR ) & ClassVal,
                             sizeof( ClassVal ) ) ||
        ( UCHAR ) ClassVal != m_pciConfig.BaseClass )
    {
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for Class (0x%X) found.\r\n" ),
                    ClassVal ) );
        Match++;
    }

    //
    // Examine SubClass value, check against device's sub-class
    //
    if( !DevKey.GetRegValue( PCIBUS_SUBCLASS_VALNAME,
                             ( PUCHAR ) & SubClassVal,
                             sizeof( SubClassVal ) ) ||
        ( UCHAR ) SubClassVal != m_pciConfig.SubClass )
    {
        return Match;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for SubClass (0x%X) found.\r\n" ),
                    SubClassVal ) );
        Match++;
    }

    //
    // Get ProgIF value or list and check against device's programing interface
    //
    ProgIFNum = PCI_MAX_REG_LIST;
    if( !DevKey.RegGetList( PCIBUS_PROGIF_VALNAME, &ProgIFNum, ProgIFs ) &&
        ( ProgIFNum == PCI_MAX_REG_LIST ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value '%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    PCIBUS_PROGIF_VALNAME,
                    PCI_MAX_REG_LIST ) );
    }

    if( !ProgIFNum )
    {
        return Match;
    }

    for( i = 0; i < ProgIFNum; i++ )
    {
        if( ( UCHAR ) ProgIFs[i] == m_pciConfig.ProgIf )
        {
            break;
        }
    }

    if( i == ProgIFNum )
    {
        // No match found
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for ProgIF (0x%X) found.\r\n" ),
                    ProgIFs[i] ) );
        Match++;
    }

    //
    // Get Vendor ID, Device ID lists and look for match
    //
    VendorIDNum = PCI_MAX_REG_LIST;
    if( !DevKey.RegGetList( PCIBUS_VENDORID_VALNAME, &VendorIDNum, VendorIDs ) &&
        ( VendorIDNum == PCI_MAX_REG_LIST ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value '%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    PCIBUS_VENDORID_VALNAME,
                    PCI_MAX_REG_LIST ) );
    }

    if( !VendorIDNum )
    {
        // No VendorID value found
        return Match;
    }

    DeviceIDNum = PCI_MAX_REG_LIST;
    if( !DevKey.RegGetList( PCIBUS_DEVICEID_VALNAME, &DeviceIDNum, DeviceIDs ) &&
        ( DeviceIDNum == PCI_MAX_REG_LIST ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value '%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    PCIBUS_DEVICEID_VALNAME,
                    PCI_MAX_REG_LIST ) );
    }

    if( !DeviceIDNum )
    {
        // No DeviceID value found
        return Match;
    }

    if( ( m_pciConfig.HeaderType & ~PCI_MULTIFUNCTION ) == PCI_DEVICE_TYPE )
    {
        // Get Subsystem Vendor ID and Subsystem ID lists only if h/w is a device
        SubVendorIDNum = PCI_MAX_REG_LIST;
        if( !DevKey.RegGetList( PCIBUS_SUBVENDORID_VALNAME,
                                &SubVendorIDNum,
                                SubVendorIDs ) &&
            ( VendorIDNum == PCI_MAX_REG_LIST ) )
        {
            DEBUGMSG( ZONE_WARN,
                      ( L"PCIBUS!RegMatchOne: Registry value '%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                        PCIBUS_SUBVENDORID_VALNAME,
                        PCI_MAX_REG_LIST ) );
        }

        SubSystemIDNum = PCI_MAX_REG_LIST;
        if( !DevKey.RegGetList( PCIBUS_SUBSYSTEMID_VALNAME,
                                &SubSystemIDNum,
                                SubSystemIDs ) &&
            ( SubSystemIDNum == PCI_MAX_REG_LIST ) )
        {
            DEBUGMSG( ZONE_WARN,
                      ( L"PCIBUS!RegMatchOne: Registry value '%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                        PCIBUS_SUBSYSTEMID_VALNAME,
                        PCI_MAX_REG_LIST ) );
        }

        if( !SubVendorIDNum && SubSystemIDNum )
        {
            // If the Subsystem ID exists, but not Subsytem Vendor ID, then ignore SubSystem ID
            DEBUGMSG( ZONE_WARN,
                      ( L"PCIBUS!RegMatchOne: If registry value '%s' exists, then '%s' must exist.  Ignoring.\r\n",
                        PCIBUS_SUBSYSTEMID_VALNAME,
                        PCIBUS_SUBVENDORID_VALNAME ) );

            SubSystemIDNum = 0;
        }
    }
    else
    {
        SubVendorIDNum = 0;
        SubSystemIDNum = 0;
    }

    // Find smallest list length
    ListNum[0] = VendorIDNum;
    ListNum[1] = DeviceIDNum;
    ListNum[2] = SubVendorIDNum ? SubVendorIDNum : PCI_MAX_REG_LIST;
    ListNum[3] = SubSystemIDNum ? SubSystemIDNum : PCI_MAX_REG_LIST;
    MaxListNum = MinVal( ListNum, 4 );

    //
    // Adjust list lengths
    //
    if( VendorIDNum > MaxListNum )
    {
        VendorIDNum = MaxListNum;

        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value list '%s' truncated to %d items.\r\n",
                    PCIBUS_VENDORID_VALNAME,
                    MaxListNum ) );
    }

    if( DeviceIDNum > MaxListNum )
    {
        DeviceIDNum = MaxListNum;

        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value list '%s' truncated to %d items.\r\n",
                    PCIBUS_DEVICEID_VALNAME,
                    MaxListNum ) );
    }

    if( SubVendorIDNum > MaxListNum )
    {
        SubVendorIDNum = MaxListNum;

        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value list '%s' truncated to %d items.\r\n",
                    PCIBUS_SUBVENDORID_VALNAME,
                    MaxListNum ) );
    }

    if( SubSystemIDNum > MaxListNum )
    {
        SubSystemIDNum = MaxListNum;

        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value list '%s' truncated to %d items.\r\n",
                    PCIBUS_SUBSYSTEMID_VALNAME,
                    MaxListNum ) );
    }

    // Walk list pairs looking for match
    for( i = 0; i < MaxListNum; i++ )
    {
        if( ( ( USHORT ) VendorIDs[i] == m_pciConfig.VendorID ) &&
            ( ( USHORT ) DeviceIDs[i] == m_pciConfig.DeviceID ) )
        {
            if( !SubVendorIDNum )
                break;

            if( ( USHORT ) SubVendorIDs[i] == m_pciConfig.u.type0.SubVendorID )
            {
                if( !SubSystemIDNum )
                    break;

                if( ( USHORT ) SubSystemIDs[i] ==
                    m_pciConfig.u.type0.SubSystemID )
                    break;
            }
        }
    }

    if( i == MaxListNum )
    {
        // No match found
        Match = 0;
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne: Match for VendorID (0x%X), DeviceID (0x%X) found.\r\n" ),
                    VendorIDs[i],
                    DeviceIDs[i] ) );
#ifdef DEBUG
        if( SubVendorIDNum )
        {
            if( SubSystemIDNum )
            {
                DEBUGMSG( ZONE_ENUM,
                          ( L"PCIBUS!RegMatchOne: Match for SubVendorID (0x%X), SubSystemID (0x%X) found.\r\n",
                            SubVendorIDs[i],
                            SubSystemIDs[i] ) );
            }
            else
            {
                DEBUGMSG( ZONE_ENUM,
                          ( L"PCIBUS!RegMatchOne: Match for SubVendorID (0x%X) found.\r\n",
                            SubVendorIDs[i] ) );
            }
        }
#endif

        Match++;
    }

    //
    // Examine RevisionID value, check against device's revision identification
    //
    RevisionIDNum = PCI_MAX_REG_LIST;
    if( !DevKey.RegGetList( PCIBUS_REVISIONID_VALNAME,
                            &RevisionIDNum,
                            RevisionIDs ) &&
        ( RevisionIDNum == PCI_MAX_REG_LIST ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegMatchOne: Registry value '%s\\%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    PCIBUS_REVISIONID_VALNAME,
                    PCI_MAX_REG_LIST ) );
    }

    if( !RevisionIDNum )
    {
        // No VendorID value found
        return Match;
    }

    // Scan list for match
    for( i = 0; i < RevisionIDNum; i++ )
    {
        if( ( UCHAR ) RevisionIDs[i] == m_pciConfig.RevisionID )
        {
            break;
        }
    }

    if( i == RevisionIDNum )
    {
        // No match found
        Match = 0;
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( L"PCIBUS!RegMatchOne: Match for RevisionID (0x%X) found.\r\n",
                    RevisionIDs[i] ) );

        Match++;
    }

    //
    // Examine BusNumber value, check against device's bus number
    //
    if( !DevKey.GetRegValue( PCIBUS_BUSNUMBER_VALNAME,
                             ( PUCHAR ) & Val32,
                             sizeof( Val32 ) ) )
    {
        return Match;
    }
    else if( Val32 != m_Info.Bus ) 
        // Bus number doesn't match
    {
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for BusNumber (0x%X) found.\r\n" ),
                    Val32 ) );
        Match++;
    }

    //
    // Examine DeviceNumber value, check against device's device number
    //
    if( !DevKey.GetRegValue( PCIBUS_DEVICENUMBER_VALNAME,
                             ( PUCHAR ) & Val32,
                             sizeof( Val32 ) ) )
    {
        return Match;
    }
    else if( Val32 != m_Info.Device )
    {
        // Device number doesn't match
        return 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for DeviceNumber (0x%X) found.\r\n" ),
                    Val32 ) );
        Match++;
    }

    //
    // Examine FunctionNumber value, check against device's function number
    //
    if( !DevKey.GetRegValue( PCIBUS_FUNCTIONNUMBER_VALNAME,
                             ( PUCHAR ) & Val32,
                             sizeof( Val32 ) ) )
    {
        return Match;
    }
    else if( Val32 != m_Info.Function )
    {
        // Function number doesn't match
        Match = 0;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatchOne Match for FunctionNumber (0x%X) found.\r\n" ),
                    Val32 ) );

        Match++;
    }
    return Match;
}


//
// Search registry for best device/registry match.
//
DWORD CPCIEnum::RegMatch( LPTSTR KeyRegPath // In Out
)
{
    DWORD DevLoadOrder;
    DWORD LoadOrder;
    DWORD NumDevKeys;
    DWORD RegEnum;
    DWORD ValType;
    DWORD ValLen;
    DWORD Match = 0;
    DWORD BestMatch = 0;
    WCHAR DevName[DEVKEY_LEN];
    WCHAR RegPath[DEVKEY_LEN]; 

    //
    // Open key to be enumerated
    //
    CRegistryEdit Key( HKEY_LOCAL_MACHINE, KeyRegPath );
    if( !Key.IsKeyOpened() )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCIBUS!RegMatch RegOpenKeyEx(%s) Error\r\n" ),
                    KeyRegPath ) );
        return 0;
    }

    //
    // Find out how many sub-keys there are
    //
    if( !Key.RegQueryInfoKey( NULL,         // class name buffer (lpszClass)
        NULL,       // ptr to length of class name buffer (lpcchClass)
        NULL,           // reserved
        & NumDevKeys,    // ptr to number of subkeys (lpcSubKeys)
        & ValType,       // ptr to longest subkey name length (lpcchMaxSubKeyLen)
        & ValLen,        // ptr to longest class string length (lpcchMaxClassLen)
        & LoadOrder,     // ptr to number of value entries (lpcValues)
        & DevLoadOrder,  // ptr to longest value name length (lpcchMaxValueNameLen)
        & ValLen,        // ptr to longest value data length (lpcbMaxValueData)
        NULL,           // ptr to security descriptor length
        NULL ) )
    {
        // ptr to last write time

        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCIBUS!RegMatch RegQueryInfoKey(Class) returned fails\r\n" ) ) );
        return 0;
    }

    DEBUGMSG( ZONE_ENUM,
              ( TEXT( "PCIBUS!RegMatch %d devices to search\r\n" ),
                NumDevKeys ) );

    //
    // Base registry key
    //
    wcsncpy( RegPath, KeyRegPath, DEVKEY_LEN );
    RegPath[DEVKEY_LEN-1]=0;
    //
    // Search for closest match
    //
    RegEnum = 0; 

    for( RegEnum = 0; RegEnum < NumDevKeys; RegEnum++ )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCIBUS!RegMatch RegEnum = %d\r\n" ), RegEnum ) );

        // Get sub-key according to RegEnum
        ValLen = sizeof( DevName ) / sizeof( WCHAR );
        if( !Key.RegEnumKeyEx( RegEnum,
                               DevName,
                               &ValLen,
                               NULL,
                               NULL,
                               NULL,
                               NULL ) )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCIBUS!RegMatch RegEnumKeyEx(%d) returned fails\r\n" ),
                        RegEnum ) );
            break;
        }

        Match = RegMatchOne( Key, DevName );

        if( Match > BestMatch && _tcslen(KeyRegPath) +_tcslen(DevName)+1 < DEVKEY_LEN -1  )
        {
            BestMatch = Match;

            // There is at least a partial match, construct path to device key
            wcscpy( RegPath, KeyRegPath );
            wcscat( RegPath, TEXT( "\\" ) );
            wcscat( RegPath, DevName );
            RegPath[DEVKEY_LEN-1] = 0;
        }
    }


    wcscpy( KeyRegPath, RegPath );

    return BestMatch;
}


//
// Copy contents of one registry key to another.
//
BOOL CPCIEnum::RegCopyKey( CRegistryEdit& TKey, CRegistryEdit& IKey )
{
    DWORD NumSubKeys;
    DWORD MaxSubKeyLen;
    DWORD MaxClassLen;
    DWORD NumValues;
    DWORD MaxValueNameLen;
    DWORD MaxValueLen;
    DWORD Key;
    DWORD Val;
    WCHAR ValName[PCI_MAX_REG_NAME];
    BYTE ValData[PCI_MAX_REG_DATA];
    DWORD ValLen;
    DWORD ValType;
    DWORD Disposition;
    BOOL RetVal = TRUE;

    // Get info on Template Key
    BOOL bSuccess = TKey.RegQueryInfoKey( NULL,               // class name buffer (lpszClass)
                                          NULL,               // ptr to length of class name buffer (lpcchClass)
                                          NULL,               // reserved
                                          & NumSubKeys,        // ptr to number of sub-keys (lpcSubKeys)
                                          & MaxSubKeyLen,      // ptr to longest subkey name length (lpcchMaxSubKeyLen)
                                          & MaxClassLen,       // ptr to longest class string length (lpcchMaxClassLen)
                                          & NumValues,         // ptr to number of value entries (lpcValues)
                                          & MaxValueNameLen,  // ptr to longest value name length (lpcchMaxValueNameLen)
                                          & MaxValueLen,       // ptr to longest value data length (lpcbMaxValueData)
                                          NULL,               // ptr to security descriptor length
                                          NULL );              // ptr to last write time

    if( !bSuccess )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCIBUS::RegCopyKey RegQueryInfoKey returned fails.\r\n" ) ) );
        return FALSE;
    }

    // Recurse for each sub-key
    for( Key = 0; Key < NumSubKeys; Key++ )
    {
        // Get TKey sub-key according to Key
        ValLen = sizeof( ValName ) / sizeof( WCHAR );
        if( !TKey.RegEnumKeyEx( Key, ValName, &ValLen, NULL, NULL, NULL, NULL ) )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCIBUS::RegCopyKey RegEnumKeyEx(%d) returned Error\r\n" ),
                        ValName ) );
            break;
        }
        else
        {
            // Open sub-key under TKey
            CRegistryEdit TSubKey( TKey.GetHKey(), ValName );
            if( !TSubKey.IsKeyOpened() )
            {
                DEBUGMSG( ZONE_ENUM |
                          ZONE_ERROR,
                          ( TEXT( "PCIBUS::RegCopyKey RegOpenKeyEx(%s) returned Error\r\n" ),
                            ValName ) );

                continue;
            }

            // Open/create corresponding sub-key under IKey
            CRegistryEdit ISubKey( IKey.GetHKey(), ValName, &Disposition );
            if( !ISubKey.IsKeyOpened() )
            {
                DEBUGMSG( ZONE_ENUM |
                          ZONE_ERROR,
                          ( TEXT( "PCIBUS::RegCopyKey RegCreateKeyEx(%s) returned Error\r\n" ),
                            ValName ) );
                continue;
            }

            RegCopyKey( TSubKey, ISubKey );
        }
    }

    // Copy values
    for( Val = 0; Val < NumValues; Val++ )
    {
        // Get value names under TKey according to Val
        ValLen = sizeof( ValName ) / sizeof( WCHAR );
        if( !TKey.RegEnumValue( Val, ValName, &ValLen, NULL, NULL, NULL, NULL ) )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCIBUS::RegCopyKey RegEnumValue(%d) returned Error\r\n" ),
                        Val ) );
            break;
        }

        // If corresponding value under IKey already exists, skip
        if( IKey.RegQueryValueEx( ValName, NULL, NULL, NULL ) )
            continue;

        // Read value from TKey
        ValLen = sizeof( ValData );
        if( !TKey.RegQueryValueEx( ValName, &ValType, ValData, &ValLen ) )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCIBUS::RegCopyKey RegQueryValueEx(%s) returned Error\r\n" ),
                        ValName ) );
            break;
        }

        // Write value to IKey
        if( !IKey.RegSetValueEx( ValName, ValType, ValData, ValLen ) )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( TEXT( "PCIBUS::RegCopyKey RegSetValueEx(%s) returned Error.\r\n" ),
                        ValName ) );
            break;
        }
    }

    return RetVal;
}


//
// Copy registry information for device from template key to instance key.  Write device-specific information
// to instance key.
//
BOOL CPCIEnum::RegCopy( PPCI_COMMON_CONFIG pPciConfig,
                        LPCTSTR lpSrcReg,
                        LPCTSTR lpInsReg )
{
    //    DWORD ValType;
    //    DWORD ValLen;
    DWORD dwValue;
    DWORD Disposition;
    TCHAR IndexStr[DEVKEY_LEN];
    INTERFACE_TYPE IfcType = PCIBus;
    DWORD Irq, Pin;
    BOOL RetVal = TRUE;

    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS::RegCopy+(%s, %d, %d, %d)\r\n",
                m_Info.RegPath,
                m_Info.Bus,
                m_Info.Device,
                m_Info.Function ) );

    //
    // Open Template key
    //
    CRegistryEdit TKey( HKEY_LOCAL_MACHINE, lpSrcReg );
    if( !TKey.IsKeyOpened() )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( L"PCIBUS::RegCopy RegOpenKeyEx(%s) returned fails \r\n",
                    m_Info.RegPath ) );
        return FALSE;
    }
    for( DWORD Index = 1; Index < 100; Index++ )
    {
        //
        // Construct Instance key name
        //
        StringCchPrintf( IndexStr,_countof(IndexStr), L"%d", Index );

        if( lpInsReg == NULL ||
            wcslen( lpInsReg ) + wcslen( IndexStr ) >= DEVKEY_LEN )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( L"PCIBUS::RegCopy Error: Created instance key '%s%s' exceeds %d character limit\r\n",
                        m_Info.RegPath,
                        IndexStr,
                        DEVKEY_LEN -
                        1 ) );
            return FALSE;
        }
        wcscpy( m_Info.RegPath, lpInsReg );
        wcscat( m_Info.RegPath, IndexStr );

        // Create/open Instance key
        Disposition = 0;
        CRegistryEdit IKey( HKEY_LOCAL_MACHINE, m_Info.RegPath, &Disposition );

        if( !IKey.IsKeyOpened() )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( L"PCIBUS::RegCopy RegCreateKeyEx(%s) returned error.\r\n",
                        m_Info.RegPath ) );
            m_Info.RegPath[0] = 0;
            return FALSE;
        }
        if( Disposition == REG_OPENED_EXISTING_KEY )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( L"PCIBUS::RegCopy RegCreateKeyEx(%s) returned open exist.continue search\r\n",
                        m_Info.RegPath ) );
            m_Info.RegPath[0] = 0;
            continue;
        }
        DEBUGMSG( ZONE_ENUM,
                  ( L"PCIbus!RegCopy Instance Key = '%s'\r\n", m_Info.RegPath ) );

        //
        // Copy Template values to Instance values, without overwriting them
        //
        if( RegCopyKey( TKey, IKey ) == FALSE )
        {
            return FALSE;
        }

        // Set device-specific indentifier information
        dwValue = m_pciConfig.BaseClass;
        if( !IKey.RegSetValueEx( PCIBUS_CLASS_VALNAME,
                                 PCIBUS_CLASS_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_pciConfig.SubClass;
        if( !IKey.RegSetValueEx( PCIBUS_SUBCLASS_VALNAME,
                                 PCIBUS_SUBCLASS_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_pciConfig.ProgIf;
        if( !IKey.RegSetValueEx( PCIBUS_PROGIF_VALNAME,
                                 PCIBUS_PROGIF_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_pciConfig.VendorID;
        if( !IKey.RegSetValueEx( PCIBUS_VENDORID_VALNAME,
                                 PCIBUS_VENDORID_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_pciConfig.DeviceID;
        if( !IKey.RegSetValueEx( PCIBUS_DEVICEID_VALNAME,
                                 PCIBUS_DEVICEID_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_pciConfig.RevisionID;
        if( !IKey.RegSetValueEx( PCIBUS_REVISIONID_VALNAME,
                                 PCIBUS_REVISIONID_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        switch( m_pciConfig.HeaderType & ~PCI_MULTIFUNCTION )
        {
          case PCI_DEVICE_TYPE:
            dwValue = m_pciConfig.u.type0.SubVendorID;
            if( !IKey.RegSetValueEx( PCIBUS_SUBVENDORID_VALNAME,
                                     PCIBUS_SUBVENDORID_VALTYPE,
                                     ( PBYTE ) & dwValue,
                                     sizeof( dwValue ) ) )
                return FALSE;

            dwValue = m_pciConfig.u.type0.SubSystemID;
            if( !IKey.RegSetValueEx( PCIBUS_SUBSYSTEMID_VALNAME,
                                     PCIBUS_SUBSYSTEMID_VALTYPE,
                                     ( PBYTE ) & dwValue,
                                     sizeof( dwValue ) ) )
                return FALSE;

            break;

          case PCI_BRIDGE_TYPE:
            break;

          case PCI_CARDBUS_TYPE:
            dwValue = m_pciConfig.u.type2.SubVendorID;
            if( !IKey.RegSetValueEx( PCIBUS_SUBVENDORID_VALNAME,
                                     PCIBUS_SUBVENDORID_VALTYPE,
                                     ( PBYTE ) & dwValue,
                                     sizeof( dwValue ) ) )
                return FALSE;

            dwValue = m_pciConfig.u.type2.SubSystemID;
            if( !IKey.RegSetValueEx( PCIBUS_SUBSYSTEMID_VALNAME,
                                     PCIBUS_SUBSYSTEMID_VALTYPE,
                                     ( PBYTE ) & dwValue,
                                     sizeof( dwValue ) ) )
                return FALSE;        

            break;

          default:
            break;
        }

        dwValue = IfcType;
        // Set device location information: interface type (PCI) and bus, device and function numbers
        if( !IKey.RegSetValueEx( PCIBUS_IFCTYPE_VALNAME,
                                 PCIBUS_IFCTYPE_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_Info.Bus;
        if( !IKey.RegSetValueEx( PCIBUS_BUSNUMBER_VALNAME,
                                 PCIBUS_BUSNUMBER_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_Info.Device;
        if( !IKey.RegSetValueEx( PCIBUS_DEVICENUMBER_VALNAME,
                                 PCIBUS_DEVICENUMBER_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        dwValue = m_Info.Function;
        if( !IKey.RegSetValueEx( PCIBUS_FUNCTIONNUMBER_VALNAME,
                                 PCIBUS_FUNCTIONNUMBER_VALTYPE,
                                 ( PBYTE ) & dwValue,
                                 sizeof( dwValue ) ) )
            return FALSE;

        // Set Memory Base and Length values
        if( !IKey.RegSetList( PCIBUS_MEMBASE_VALNAME,
                              m_Info.MemBase.Num,
                              m_Info.MemBase.Reg ) )
        {
            return FALSE;
        }

        if( !IKey.RegSetList( PCIBUS_MEMLEN_VALNAME,
                              m_Info.MemLen.Num,
                              m_Info.MemLen.Reg ) )
        {
            return FALSE;
        }

        // Set I/O Base and Length values
        if( !IKey.RegSetList( PCIBUS_IOBASE_VALNAME,
                              m_Info.IoBase.Num,
                              m_Info.IoBase.Reg ) )
        {
            return FALSE;
        }

        if( !IKey.RegSetList( PCIBUS_IOLEN_VALNAME,
                              m_Info.IoLen.Num,
                              m_Info.IoLen.Reg ) )
        {
            return FALSE;
        }

        // Get IRQ and interrupt pin
        switch( m_pciConfig.HeaderType & ~PCI_MULTIFUNCTION )
        {
          case PCI_DEVICE_TYPE:
            // Devices
            Irq = m_pciConfig.u.type0.InterruptLine;
            Pin = m_pciConfig.u.type0.InterruptPin;
            break;

          case PCI_BRIDGE_TYPE:
            // PCI-PCI bridge
            Irq = m_pciConfig.u.type1.InterruptLine;
            Pin = m_pciConfig.u.type1.InterruptPin;
            break;

          case PCI_CARDBUS_TYPE:
            // PCI-Cardbus bridge
            Irq = m_pciConfig.u.type2.InterruptLine;
            Pin = m_pciConfig.u.type2.InterruptPin;
            break;

          default:
            DEBUGMSG( ZONE_ERROR,
                      ( L"PCIBUS::RegCopy ERROR: invalid header type %d detected.\r\n",
                        m_pciConfig.HeaderType ) );
            return FALSE;
        }

        // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
        if( !( ( Pin == 0 ) || ( Irq == 0 ) || ( Irq == 0xFF ) ) )
        {
            // Write Irq value
            if( IKey.RegSetValueEx( PCIBUS_IRQ_VALNAME,
                                    PCIBUS_IRQ_VALTYPE,
                                    ( PBYTE ) & Irq,
                                    sizeof( Irq ) ) == FALSE )
            {
                return FALSE;
            }

            // If driver not to be loaded (or will be unloaded immediately), don't request SysIntr
            if( !( m_Info.DevFlags & ( DEVFLAGS_NOLOAD | DEVFLAGS_UNLOAD ) ) )
            {
                // Request new SysIntr from Irq value
                if( RequestSysIntr( Irq, &m_SysIntr ) == FALSE )
                {
                    m_SysIntr = ( -1 );
                    return FALSE;
                }

                // Write SysIntr value
                if( IKey.RegSetValueEx( PCIBUS_SYSINTR_VALNAME,
                                        PCIBUS_SYSINTR_VALTYPE,
                                        ( PBYTE ) & m_SysIntr,
                                        sizeof( m_SysIntr ) ) == FALSE )
                {
                    return FALSE;
                }
            }
        }
        return TRUE;
    }
    DEBUGMSG( ZONE_ENUM |
              ZONE_ERROR,
              ( L"PCIBUS::RegCopy Error: can not create instance key !!! \r\n" ) );
    return FALSE;
};
CPCIEnum::CPCIEnum( DWORD dwBus,
                    PCI_SLOT_NUMBER slotNumber,
                    PCBCARD_INFO pInfo,
                    CPCEnableCBCard* pEnabler ) : m_dwBus( dwBus ),
                                                  m_SlotNumber( slotNumber ),
                                                  m_pEnabler( pEnabler )
{
    m_pConfigEntry = NULL;
    m_SysIntr = m_Irq = ( DWORD ) - 1;
    m_hDriver = NULL;
    memset( &m_Info, 0, sizeof( m_Info ) );
    if( pInfo )
    {
        m_CardInfo = *pInfo;
    }
    else
    {
        m_CardInfo.m_DeviceId[0] = 0;
    }
    memset( &m_pciConfig, 0, sizeof( m_pciConfig ) );
    m_InstRegPath[0] = 0;
    m_pMemHead = NULL;
    m_pIoHead = NULL;
    for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
    {
        m_IoWindow[dwIndex] = m_MemWindow[dwIndex] = NULL;
    }
}
CPCIEnum::~CPCIEnum()
{
    for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
    {
        if( m_IoWindow[dwIndex] )
        {
            CardReleaseWindow( m_IoWindow[dwIndex] );
            m_IoWindow[dwIndex] = NULL;
        }
        if( m_MemWindow[dwIndex] )
        {
            CardReleaseWindow( m_MemWindow[dwIndex] );
            m_MemWindow[dwIndex] = NULL;
        }
    }
    if( m_pMemHead )
    {
        PCIRsrc_DelList( m_pMemHead );
        m_pMemHead = NULL;
    }
    if( m_pIoHead )
    {
        PCIRsrc_DelList( m_pIoHead );
        m_pIoHead = NULL;
    }
    if( m_hDriver != NULL )
    {
        // We do have driver loaded. Unload it.
        BOOL bSuccess = DeactivateDevice( m_hDriver );
        DEBUGCHK( bSuccess == TRUE );
    }
    if( m_SysIntr != ( DWORD ) - 1 )
    {
        ReleaseSysIntr( m_SysIntr );
    }
    PCIReleaseResources();
    if( m_pConfigEntry )
    {
        delete m_pConfigEntry ;
    }
    if( m_InstRegPath[0] != 0 )
    {
        ::RegDeleteKey( HKEY_LOCAL_MACHINE, m_InstRegPath );
    }
}
void CPCIEnum::PCIInitInfo( LPCWSTR RegPath )
{
    if( RegPath )
    {
        wcsncpy( m_Info.RegPath, RegPath, DEVKEY_LEN );
    }
    else
    {
        m_Info.RegPath[0] = 0;
    }

    m_Info.Bus = m_dwBus;
    m_Info.Device = m_SlotNumber.u.bits.DeviceNumber;
    m_Info.Function = m_SlotNumber.u.bits.FunctionNumber;
    m_Info.Cfg = &m_pciConfig;

    m_Info.MemBase.Num = 0;
    m_Info.MemLen.Num = 0;
    m_Info.IoBase.Num = 0;
    m_Info.IoLen.Num = 0;
    m_Info.Matched = FALSE;
    m_Info.Configure = TRUE;

    m_Info.ConfigEntry = FALSE;
    wcscpy( m_Info.ConfigEntryName, L"" );
    wcscpy( m_Info.ConfigDllName, L"" );

    wcscpy( m_Info.DllName, L"" );
    m_Info.DevFlags = DEVFLAGS_NONE;

    m_Info.ConfigInfo = NULL;

    m_Info.Command = 0;
    m_Info.BridgeControl = 0x0;     // Don't enable ISA aliasing
    m_Info.Latency = 0;
    m_Info.SecondaryLatency = 0;
}
BOOL CPCIEnum::RegFindPnpKey()
{
    if( m_CardInfo.m_DeviceId[0] == 0 )
    {
        // There is no PnpString 
        return FALSE;
    }
    CRegistryEdit cardBusKey( HKEY_LOCAL_MACHINE, m_Info.RegPath );
    if( cardBusKey.IsKeyOpened() )
    {
        // Key is exist.
        // Construct Pnp String
        DEBUGMSG( ZONE_FUNCTION,
                  ( L"CardBUs! Locking for PnpString(%s) \r\n",
                    m_CardInfo.m_DeviceId ) );

        if( wcslen( m_Info.RegPath ) +
            wcslen( L"\\" ) +
            wcslen( m_CardInfo.m_DeviceId ) >=
            DEVKEY_LEN )
        {
            return FALSE;
        }
        wcscat( m_Info.RegPath, L"\\" );
        wcscat( m_Info.RegPath, m_CardInfo.m_DeviceId );
        CRegistryEdit pnpKey( HKEY_LOCAL_MACHINE, m_Info.RegPath );
        if( pnpKey.IsKeyOpened() )
        {
            DWORD dwDep = 0;
            // Fail if key name too long
            if( wcslen( CARDBUS_INSTANCE_PATH ) +
                wcslen( L"\\" ) +
                wcslen( m_CardInfo.m_DeviceId ) >=
                DEVKEY_LEN )
            {
                return FALSE;
            }
            // No exact instance match found, check for match under Template key
            wcscpy( m_InstRegPath, CARDBUS_INSTANCE_PATH );
            wcscat( m_InstRegPath, L"\\" );
            wcscat( m_InstRegPath, m_CardInfo.m_DeviceId );
            return TRUE;
        }
    }
    return FALSE;
}
BOOL CPCIEnum::RegGetFlags()
{
    DWORD ValLen, ValType, Val;
    CRegistryEdit DevKey( HKEY_LOCAL_MACHINE, m_Info.RegPath );

    if( !DevKey.IsKeyOpened() )
    {
        return FALSE;
    }
    ValLen = sizeof( m_Info.DevFlags );
    if( DevKey.RegQueryValueEx( DEVLOAD_FLAGS_VALNAME,
                                &ValType,
                                ( PUCHAR ) & Val,
                                &ValLen ) &&
        ( ValType == DEVLOAD_FLAGS_VALTYPE ) )
    {
        // Flags value exists
        m_Info.DevFlags = Val;
    }
    else
    {
        m_Info.DevFlags = DEVFLAGS_NONE;
    }
    return TRUE;
}
BOOL CPCIEnum::RegSetConfig()
{
    BOOL RetVal = TRUE;
    DWORD Irq, Pin;

    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS!RegSetConfig '%s' (%d/%d/%d)\r\n",
                m_Info.RegPath,
                m_Info.Bus,
                m_Info.Device,
                m_Info.Function ) );

    // Open  key
    CRegistryEdit Key( HKEY_LOCAL_MACHINE, m_Info.RegPath );
    if( !Key.IsKeyOpened() )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( L"PCIBUS!RegSetConfig RegOpenKeyEx(%s) returned fails.\r\n",
                    m_Info.RegPath ) );
        return FALSE;
    }

    // Set Memory Base and Length values
    if( !Key.RegSetList( PCIBUS_MEMBASE_VALNAME,
                         m_Info.MemBase.Num,
                         m_Info.MemBase.Reg ) )
    {
        RetVal = FALSE;
        return FALSE;
    }

    if( !Key.RegSetList( PCIBUS_MEMLEN_VALNAME,
                         m_Info.MemLen.Num,
                         m_Info.MemLen.Reg ) )
    {
        RetVal = FALSE;
        return FALSE;
    }

    // Set I/O Base and Length values
    if( !Key.RegSetList( PCIBUS_IOBASE_VALNAME,
                         m_Info.IoBase.Num,
                         m_Info.IoBase.Reg ) )
    {
        RetVal = FALSE;
        return FALSE;
    }

    if( !Key.RegSetList( PCIBUS_IOLEN_VALNAME,
                         m_Info.IoLen.Num,
                         m_Info.IoLen.Reg ) )
    {
        RetVal = FALSE;
        return FALSE;
    }

    // Get IRQ and interrupt pin
    switch( m_Info.Cfg->HeaderType & ~PCI_MULTIFUNCTION )
    {
      case PCI_DEVICE_TYPE:
        // Devices
        Irq = m_Info.Cfg->u.type0.InterruptLine;
        Pin = m_Info.Cfg->u.type0.InterruptPin;
        break;

      case PCI_BRIDGE_TYPE:
        // PCI-PCI bridge
        Irq = m_Info.Cfg->u.type1.InterruptLine;
        Pin = m_Info.Cfg->u.type1.InterruptPin;
        break;

      case PCI_CARDBUS_TYPE:
        // PCI-Cardbus bridge
        Irq = m_Info.Cfg->u.type2.InterruptLine;
        Pin = m_Info.Cfg->u.type2.InterruptPin;
        break;

      default:
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIbus!RegSetConfig ERROR: invalid header type %d detected.\r\n",
                    m_Info.Cfg->HeaderType ) );

        RetVal = FALSE;
        return FALSE;
    }

    // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
    if( !( ( Pin == 0 ) || ( Irq == 0 ) || ( Irq == 0xFF ) ) )
    {
        // Write Irq value
        if( Key.RegSetValueEx( PCIBUS_IRQ_VALNAME,
                               PCIBUS_IRQ_VALTYPE,
                               ( PBYTE ) & Irq,
                               sizeof( Irq ) ) == FALSE )
        {
            RetVal = FALSE;
            return FALSE;
        }

        // If driver not to be loaded (or will be unloaded immediately), don't request SysIntr
        if( !( m_Info.DevFlags & ( DEVFLAGS_NOLOAD | DEVFLAGS_UNLOAD ) ) )
        {
            // Request new SysIntr from Irq value
            if( RequestSysIntr( Irq, &m_SysIntr ) == FALSE )
            {
                RetVal = FALSE;
                m_SysIntr = ( DWORD ) - 1;
                return FALSE;
            }

            // Write SysIntr value
            if( Key.RegSetValueEx( PCIBUS_SYSINTR_VALNAME,
                                   PCIBUS_SYSINTR_VALTYPE,
                                   ( PBYTE ) & m_SysIntr,
                                   sizeof( m_SysIntr ) ) == FALSE )
            {
                RetVal = FALSE;
                return FALSE;
            }
        }
    }

    return RetVal;
}


#define PCIBUS_PATH TEXT("Drivers\\BuiltIn\\PCI")
BOOL CPCIEnum::PCICfgFindMatch()
{
    // Search PNP First
    if( wcslen( CARDBUS_PNP_PATH ) >= DEVKEY_LEN - 1 )
    {
        return FALSE;
    }

    wcscpy( m_Info.RegPath, CARDBUS_PNP_PATH );
    if( RegFindPnpKey() == TRUE )
    {
        // Partial template match found
        DEBUGMSG( ZONE_INIT,
                  ( L"PCIBUS!PCICfgFindMatch: Matched device %d/%d/%d with registry key '%s'\r\n",
                    m_Info.Bus,
                    m_Info.Device,
                    m_Info.Function,
                    m_Info.RegPath ) );

        RegGetFlags();
        return TRUE;
    };

    // Fail if key name too long
    if( wcslen( PCIBUS_PATH ) +
        wcslen( L"\\" ) +
        wcslen( PCIBUS_TEMPLATE_KEYNAME ) >=
        DEVKEY_LEN )
    {
        return FALSE;
    }

    // No exact instance match found, check for match under Template key
    wcscpy( m_Info.RegPath, PCIBUS_PATH );
    wcscat( m_Info.RegPath, L"\\" );
    wcscat( m_Info.RegPath, PCIBUS_TEMPLATE_KEYNAME );

    if( RegMatch( m_Info.RegPath ) != PCIBUS_MATCH_NONE )
    {
        // Partial template match found
        DEBUGMSG( ZONE_INIT,
                  ( L"PCIBUS!PCICfgFindMatch: Matched device %d/%d/%d with registry key '%s'\r\n",
                    m_Info.Bus,
                    m_Info.Device,
                    m_Info.Function,
                    m_Info.RegPath ) );

        // Get registry flags
        RegGetFlags();
        // Create Instantance 
        LPTSTR KeyPtr = NULL;
        LPTSTR TmpKeyPtr = wcsstr( m_Info.RegPath, PCIBUS_TEMPLATE_KEYNAME );
        while( TmpKeyPtr != NULL )
        {
            KeyPtr = TmpKeyPtr;
            TmpKeyPtr += wcslen( PCIBUS_TEMPLATE_KEYNAME );
            TmpKeyPtr = wcsstr( TmpKeyPtr, PCIBUS_TEMPLATE_KEYNAME );
        };

        if( KeyPtr == NULL )
        {
            // Template key didn't have the word Template in it, report error
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( L"PCIBUS!RegCopy Key %s doesn't contain %s.\r\n",
                        m_Info.RegPath,
                        PCIBUS_TEMPLATE_KEYNAME ) );
            return FALSE;
        }
        else
            KeyPtr += wcslen( PCIBUS_TEMPLATE_KEYNAME );

        // No exact instance match found, check for match under Template key
        wcscpy( m_InstRegPath, CARDBUS_INSTANCE_PATH );
        // Fail if key name too long
        if( wcslen( m_InstRegPath ) + wcslen( KeyPtr ) >= DEVKEY_LEN )
        {
            return FALSE;
        }
        wcscat( m_InstRegPath, KeyPtr );
        return TRUE;
    }
    // Check CardBusEntry
    return FALSE;
}
//
// Get registry information.
//
BOOL CPCIEnum::RegGetInfo()
{
    BOOL Status;
    DWORD ValLen;
    DWORD ValType;
    DWORD Val;
    BOOL ConfigDllExists = TRUE;
    BOOL DllExists = TRUE;

    //
    // Open key to be enumerated
    //
    CRegistryEdit Key( HKEY_LOCAL_MACHINE, m_Info.RegPath );
    if( !Key.IsKeyOpened() )
    {
        return FALSE;
    }

    //
    // Read NoConfig value
    //
    ValLen = sizeof( Val );
    if( Key.GetRegValue( PCIBUS_NOCONFIG_VALNAME,
                         ( PUCHAR ) & Val,
                         sizeof( Val ) ) && ( Val != 0 ) )
    {
        // If NoConfig exists and non-zero, don't configure bus
        m_Info.Configure = FALSE;
        return TRUE;
    }
    else
    {
        m_Info.Configure = TRUE;
    }

    //
    // Read MemBase and MemLen values.
    //
    m_Info.MemBase.Num = PCI_TYPE0_ADDRESSES;
    if( !Key.RegGetList( PCIBUS_MEMBASE_VALNAME,
                         &m_Info.MemBase.Num,
                         m_Info.MemBase.Reg ) &&
        ( m_Info.MemBase.Num == PCI_TYPE0_ADDRESSES ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegGetInfo: Registry value '%s\\%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    m_Info.RegPath,
                    PCIBUS_MEMBASE_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
    }

    m_Info.MemLen.Num = PCI_TYPE0_ADDRESSES;
    if( !Key.RegGetList( PCIBUS_MEMLEN_VALNAME,
                         &m_Info.MemLen.Num,
                         m_Info.MemLen.Reg ) &&
        ( m_Info.MemLen.Num == PCI_TYPE0_ADDRESSES ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegGetInfo: Registry value '%s\\%s' has more than %d items in multi_sz list.  Ignoring the additional items.\r\n",
                    m_Info.RegPath,
                    PCIBUS_MEMLEN_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
    }

    //
    // Read IoBase and IoLen values
    //
    m_Info.IoBase.Num = PCI_TYPE0_ADDRESSES;
    if( !Key.RegGetList( PCIBUS_IOBASE_VALNAME,
                         &m_Info.IoBase.Num,
                         m_Info.IoBase.Reg ) &&
        ( m_Info.IoBase.Num == PCI_TYPE0_ADDRESSES ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegGetInfo: Registry value '%s\\%s' has more than %d items in multi_sz list.  Ignoring the additional items\r\n",
                    m_Info.RegPath,
                    PCIBUS_IOBASE_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
    }

    m_Info.IoLen.Num = PCI_TYPE0_ADDRESSES;
    if( !Key.RegGetList( PCIBUS_IOLEN_VALNAME,
                         &m_Info.IoLen.Num,
                         m_Info.IoLen.Reg ) &&
        ( m_Info.IoLen.Num == PCI_TYPE0_ADDRESSES ) )
    {
        DEBUGMSG( ZONE_WARN,
                  ( L"PCIBUS!RegGetInfo: Registry value '%s\\%s' has more than %d items in multi_sz list.  Ignoring the additional items\r\n",
                    m_Info.RegPath,
                    PCIBUS_IOLEN_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
    }

    //
    // Read ConfigEntry value (if it exists)
    //
    ValLen = sizeof( m_Info.ConfigEntryName );
    Status = Key.RegQueryValueEx( PCIBUS_CONFIGENTRY_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) m_Info.ConfigEntryName,
                                  &ValLen );            
    if( Status && ( ValType == PCIBUS_CONFIGENTRY_VALTYPE ) )
    {
        // ConfigEntry value exists
        m_Info.ConfigEntry = TRUE;
    }
    else
    {
        m_Info.ConfigEntry = FALSE;
        wcscpy( m_Info.ConfigEntryName, L"" );
        ConfigDllExists = FALSE;
    }

    if( m_Info.ConfigEntry )
    {
        //
        // Read ConfigDllName value (if it exists)
        //
        ValLen = sizeof( m_Info.ConfigDllName );
        Status = Key.RegQueryValueEx( PCIBUS_CONFIGDLL_VALNAME,
                                      &ValType,
                                      ( PUCHAR ) m_Info.ConfigDllName,
                                      &ValLen );            
        if( Status || ( ValType != PCIBUS_CONFIGDLL_VALTYPE ) )
        {
            // ConfigDllName value doesn't exist
            wcscpy( m_Info.ConfigDllName, L"" );
            ConfigDllExists = FALSE;
        }
    }

    //
    // Read Dll value (if it exists)
    //
    ValLen = sizeof( m_Info.DllName );
    Status = Key.RegQueryValueEx( DEVLOAD_DLLNAME_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) m_Info.DllName,
                                  &ValLen );            
    if( Status || ( ValType != DEVLOAD_DLLNAME_VALTYPE ) )
    {
        // Dll value doesn't exist
        wcscpy( m_Info.DllName, L"" );
        DllExists = FALSE;
    }

    // Make sure there is a DLL to load with the ConfigEntry
    if( m_Info.ConfigEntry )
    {
        if( !ConfigDllExists && !DllExists )
        {
            // Neither ConfigDll or Dll names exist, so invalidate ConfigEntry
            DEBUGMSG( ZONE_WARN,
                      ( L"PCIBUS!RegGetInfo: Registry value '%s\\%s' exists but no '%s' or '%s' values are specified.  Ignoring '%s' value.\r\n",
                        m_Info.RegPath,
                        PCIBUS_CONFIGENTRY_VALNAME,
                        PCIBUS_CONFIGDLL_VALNAME,
                        DEVLOAD_DLLNAME_VALNAME,
                        PCIBUS_CONFIGENTRY_VALNAME ) );

            m_Info.ConfigEntry = FALSE;
            wcscpy( m_Info.ConfigEntryName, L"" );
        }
        else if( !ConfigDllExists && DllExists )
        {
            // No ConfigDll value, so use Dll value for ConfigEntry
            wcscpy( m_Info.ConfigDllName, m_Info.DllName );
        }
    }

    //
    // Read Flags value (if it exists)
    //
    ValLen = sizeof( m_Info.DevFlags );
    Status = Key.RegQueryValueEx( DEVLOAD_FLAGS_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) & Val,
                                  &ValLen );            
    if( Status && ( ValType == DEVLOAD_FLAGS_VALTYPE ) )
    {
        // Flags value exists
        m_Info.DevFlags = Val;
    }

    //
    // Read Command value (if it exists)
    //
    ValLen = sizeof( Val );
    Status = Key.RegQueryValueEx( PCIBUS_COMMAND_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) & Val,
                                  &ValLen );            
    if( Status && ( ValType == PCIBUS_COMMAND_VALTYPE ) )
    {
        // Command value exists
        m_Info.Command = Val;
    }

    //
    // Read BridgeCommand value (if it exists)
    //
    ValLen = sizeof( Val );
    Status = Key.RegQueryValueEx( PCIBUS_BRIDGECONTROL_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) & Val,
                                  &ValLen );            
    if( Status && ( ValType == PCIBUS_BRIDGECONTROL_VALTYPE ) )
    {
        // BridgeControl value exists
        m_Info.BridgeControl = Val;
    }

    //
    // Read Latency value (if it exists)
    //
    ValLen = sizeof( Val );
    Status = Key.RegQueryValueEx( PCIBUS_LATENCY_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) & Val,
                                  &ValLen );            
    if( Status && ( ValType == PCIBUS_LATENCY_VALTYPE ) )
    {
        // Latency value exists
        m_Info.Latency = Val;
    }

    //
    // Read Latency value (if it exists)
    //
    ValLen = sizeof( Val );
    Status = Key.RegQueryValueEx( PCIBUS_SECONDARYLATENCY_VALNAME,
                                  &ValType,
                                  ( PUCHAR ) & Val,
                                  &ValLen );            
    if( Status && ( ValType == PCIBUS_SECONDARYLATENCY_VALTYPE ) )
    {
        // Latency value exists
        m_Info.SecondaryLatency = Val;
    }

    return TRUE;
};

BOOL CPCIEnum::PCIGetBARs()
{
    BOOL RetVal = TRUE;

    DEBUGMSG( ZONE_FUNCTION,
              ( L"PCIBUS!PCIGetBARs+(Bus %d, Device %d, Function %d)\r\n",
                m_Info.Bus,
                m_Info.Device,
                m_Info.Function ) );

    // Save off registry path

    // Attempt to find match for this device in the registry and get info
    // Make sure the sum of Base entries is less than or equal to 6
    if( m_Info.MemBase.Num + m_Info.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",
                    m_Info.RegPath,
                    PCIBUS_MEMBASE_VALNAME,
                    PCIBUS_IOBASE_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
        return FALSE;
    }

    // Make sure the sum of Len entries is less than or equal to 6
    if( m_Info.MemLen.Num + m_Info.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",
                    m_Info.RegPath,
                    PCIBUS_MEMLEN_VALNAME,
                    PCIBUS_IOLEN_VALNAME,
                    PCI_TYPE0_ADDRESSES ) );
        return FALSE;
    }

    if( m_Info.MemLen.Num + m_Info.IoLen.Num > 0 )
    {
        // If there is any Length info, return
        return TRUE;
    }

    // If ConfigEntry point defined, call it to determine BAR lengths
    if( m_Info.ConfigEntry && m_pConfigEntry != NULL )
    {
        DWORD Status;

        // Load driver Dll and obtain ConfigEntry function address
        Status = m_pConfigEntry->CallEntry( PCIBUS_CONFIG_SIZE,
                                            &m_Info,
                                            NULL,
                                            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
                // Read the base address registers to determine BAR lengths
                if( !PCIReadBARs() )
                {
                    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() )
        {
            DEBUGMSG( ZONE_ERROR,
                      ( L"PCIBUS!PCIGetBARs: Failed PCIReadBARs call\r\n" ) );

            RetVal = FALSE;
        }
    }

    DEBUGMSG( ZONE_FUNCTION, ( L"PCIBUS!PCIGetBARs-\r\n" ) );

    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 ) );
}
//
// 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;
}
#define PCI_CONFIG_CARDBUS_CIS  (10 << 2)
BOOL CPCIEnum::PCIReadBARs()
{
    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",
                m_Info.Bus,
                m_Info.Device,
                m_Info.Function ) );

    // Determine number of BARs to examine from header type
    switch( m_Info.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;
    }
    DWORD dwCIS = PCIConfig_Read( m_Info.Bus,
                                  m_Info.Device,
                                  m_Info.Function,
                                  PCI_CONFIG_CARDBUS_CIS );
    BYTE uCisOption = ( BYTE ) ( dwCIS  & 7 );
    DWORD dwCISOffset = dwCIS & 0x0FFFFFF8;
    if( uCisOption != 0 && uCisOption != 7 )
    {
        // CIS is mapped to one or BAR.
        dwCISOffset --;
    }
    else
    {
        dwCISOffset = ( DWORD ) - 1;
    }

    for( i = 0, Offset = 0x10; i < NumberOfRegs; i++, Offset += 4 )
    {
        if( i == dwCISOffset ) // Do not include the CIS Bar.
            continue;
        // Get base address register value
        Reg = m_Info.Cfg->u.type0.BaseAddresses[i];

        // Get size info
        PCIConfig_Write( m_Info.Bus,
                         m_Info.Device,
                         m_Info.Function,
                         Offset,
                         0xFFFFFFFF );
        BaseAddress = PCIConfig_Read( m_Info.Bus,
                                      m_Info.Device,
                                      m_Info.Function,
                                      Offset );
        PCIConfig_Write( m_Info.Bus,
                         m_Info.Device,
                         m_Info.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)
                m_Info.IoLen.Reg[IoIndex] = Size + 1;
                m_Info.IoLen.Num++;
                m_Info.IoBase.Reg[IoIndex++] = Reg & PCI_ADDRESS_IO_ADDRESS_MASK;
                m_Info.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)
                m_Info.MemLen.Reg[MemIndex] = Size + 1;
                m_Info.MemLen.Num++;
                m_Info.MemBase.Reg[MemIndex++] = Reg & PCI_ADDRESS_MEMORY_ADDRESS_MASK;
                m_Info.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;
}

//
// Request I/O and Irq resources from I/O Resource Manager.
//
BOOL CPCIEnum::PCIRequestResources()
{
    // Retrieve IRQ
    switch( m_Info.Cfg->HeaderType & ~PCI_MULTIFUNCTION )
    {
      case PCI_DEVICE_TYPE:
        // Devices
        // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
        if( ( m_Info.Cfg->u.type0.InterruptPin == 0 ) ||
            ( m_Info.Cfg->u.type0.InterruptLine == 0 ) ||
            ( m_Info.Cfg->u.type0.InterruptLine == 0xFF ) )
        {
            return TRUE;
        }

        m_Irq = m_Info.Cfg->u.type0.InterruptLine;
        break;

      case PCI_BRIDGE_TYPE:
        // PCI-PCI bridges
        // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
        if( ( m_Info.Cfg->u.type1.InterruptPin == 0 ) ||
            ( m_Info.Cfg->u.type1.InterruptLine == 0 ) ||
            ( m_Info.Cfg->u.type1.InterruptLine == 0xFF ) )
        {
            return TRUE;
        }

        m_Irq = m_Info.Cfg->u.type1.InterruptLine;
        break;

      case PCI_CARDBUS_TYPE:
        // Cardbus controller
        // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
        if( ( m_Info.Cfg->u.type2.InterruptPin == 0 ) ||
            ( m_Info.Cfg->u.type2.InterruptLine == 0 ) ||
            ( m_Info.Cfg->u.type2.InterruptLine == 0xFF ) )
        {
            return TRUE;
        }

        m_Irq = m_Info.Cfg->u.type2.InterruptLine;
        break;

      default:
        return FALSE;
    }

    // Request Irq resource from IORM
    DEBUGMSG( ZONE_INIT,
              ( L"PCIBUS!PCIRequestResources: I/O Resource Manager request for IRQ %d\r\n",
                m_Irq ) );
    DWORD IrqFlag = ( ( m_Info.DevFlags& DEVFLAGS_IRQ_EXCLUSIVE ) !=
                      0 ?
                      RREXF_REQUEST_EXCLUSIVE :
                      0 );
    if( !ResourceRequestEx( RESMGR_IRQ, m_Irq, 1, IrqFlag ) )
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIBUS!PCIRequestResourcesEx: I/O Resource Manager request for IRQ %d with flag %x failed\r\n",
                    m_Irq,
                    IrqFlag ) );
        m_Irq = ( DWORD ) - 1;
        return FALSE;
    } 

    return TRUE;
}
BOOL CPCIEnum::PCIReleaseResources()
{
    if( m_Irq != ( DWORD ) - 1 )
    {
        // Request Irq resource from IORM
        DEBUGMSG( ZONE_INIT,
                  ( L"PCIBUS!PCIReleaseResources: I/O Resource Manager request for IRQ %d\r\n",
                    m_Irq ) );
        ResourceRelease( RESMGR_IRQ, m_Irq, 1 );
    }
    return TRUE;
}
BOOL CPCIEnum::Init()
{
    int length = HalGetBusData( PCIConfiguration,
                                m_dwBus,
                                m_SlotNumber.u.AsULONG,
                                &m_pciConfig,
                                sizeof( m_pciConfig ) -
                                sizeof( m_pciConfig.DeviceSpecific ) );

    if( length == 0 || m_pciConfig.VendorID == 0xFFFF )
    {
        return FALSE;
    }
    // Initialize out device info
    PCIInitInfo( NULL );

    // Attempt to find match for this device in the registry and get info
    if( !PCICfgFindMatch() )
    {
        return FALSE;
    }

    if( !RegGetInfo() )
    {
        return FALSE;
    }

    if( m_Info.Configure == FALSE )
    {
        // DO not use this one.
        return FALSE;
    }

    if( m_Info.ConfigEntry )
    {
        m_pConfigEntry = new CCfgEntry( m_Info.ConfigDllName,
                                        m_Info.ConfigEntryName,
                                        m_Info.DevFlags );
    }
    return TRUE;
};
BOOL CPCIEnum::ConfigGetUserResource()
{
    ULONG MemSize = 0;
    ULONG IoSize = 0;    
    if( !( m_pMemHead = PCIRsrc_New( m_dwBus,
                                     0,
                                     0,
                                     0,
                                     0,
                                     0,
                                     TRUE,
                                     0,
                                     TRUE,
                                     NULL ) ) )
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIBUS!PCICfg: Failed local alloc of g_MemHead)\r\n" ) );
        return FALSE;
    }

    if( !( m_pIoHead = PCIRsrc_New( m_dwBus,
                                    0,
                                    0,
                                    0,
                                    0,
                                    0,
                                    TRUE,
                                    0,
                                    TRUE,
                                    NULL ) ) )
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIBUS!PCICfg: Failed local alloc of g_IoHead)\r\n" ) );
        PCIRsrc_DelList( m_pMemHead );
        return FALSE;
    }
    if( m_Info.ConfigEntry && m_pConfigEntry != NULL )
    {
        DWORD dwMemSize = 0;
        DWORD dwIoSize = 0;
        DWORD Status = m_pConfigEntry->CallEntry( PCIBUS_CONFIG_RSRC,
                                                  &m_Info,
                                                  m_pMemHead,
                                                  m_pIoHead,
                                                  &dwMemSize,
                                                  &dwIoSize );        
        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
                return FALSE;
            }
            else
            {
                DEBUGMSG( ZONE_ERROR,
                          ( L"PCIBUS!PCICfgDevice: Failed %s:%s call\r\n",
                            m_Info.DllName,
                            m_Info.ConfigEntryName ) );
                return FALSE;
            }
        }
        return TRUE;
    }
    return FALSE;
}
BOOL CPCIEnum::ConfigSetUserResource()
{
    if( m_Info.ConfigEntry && m_pConfigEntry != NULL )
    {
        PPCI_RSRC Rsrc;
        DWORD dwCurIndex = 0;
        while( m_pMemHead &&
               ( Rsrc = PCIRsrc_GetNext( m_pMemHead, m_dwBus ) ) != NULL &&
               dwCurIndex < PCI_TYPE0_ADDRESSES )
        {
            // Request Windows
            CARD_WINDOW_PARMS CardWinParms;
            CardWinParms.hSocket = m_pEnabler->GetCardHandle();
            CardWinParms.fAttributes = 0 ; // Memory Window
            CardWinParms.fAccessSpeed = 0 ; // This is not used by cardbus.
            CardWinParms.uWindowSize = Rsrc->Size;
            m_MemWindow[dwCurIndex] = CardRequestWindow( m_pEnabler->GetClientHandle(),
                                                         &CardWinParms );

            CARD_WINDOW_ADDRESS CardWindowAddr;
            CardWindowAddr.hSocket = m_pEnabler->GetCardHandle();
            CardWindowAddr.uCardAddress = 0;
            CardWindowAddr.uSize = Rsrc->Size;
            if( m_MemWindow[dwCurIndex] !=
                NULL &&
                CardMapWindowPhysical( m_MemWindow[dwCurIndex],
                                       &CardWindowAddr ) ==
                CERR_SUCCESS )
            {
                // We found index of window that we can use.
                Rsrc->Base = CardWindowAddr.uCardAddress;
                // This resource needs to be set by the device's ConfigEntry routine (if supported)
                DWORD Status = m_pConfigEntry->CallEntry( PCIBUS_CONFIG_SET,
                                                          NULL,
                                                          Rsrc,
                                                          NULL,
                                                          NULL,
                                                          NULL );
                //
                if( Status != ERROR_SUCCESS )
                {
                    if( Status == ERROR_NOT_SUPPORTED &&
                        Rsrc->Offset < PCI_TYPE0_ADDRESSES )
                    {
                        // 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" ) );
                    }
                }
            }
            else
                DEBUGMSG( ZONE_ERROR,
                          ( L"CardBus:ConfigSetUserResource Failed @ CardMapWindow\r\n" ) );

            Rsrc->Next = Rsrc;
            Rsrc->Prev = Rsrc;
            PCIRsrc_DelList( Rsrc );
            dwCurIndex++;
        }
        if( m_pMemHead != NULL )
        {
            PCIRsrc_DelList( m_pMemHead );
            m_pMemHead = NULL;
        }
        dwCurIndex = 0;
        while( m_pIoHead &&
               ( Rsrc = PCIRsrc_GetNext( m_pIoHead, m_dwBus ) ) != NULL )
        {
            // This resource needs to be set by the device's ConfigEntry routine (if supported)
            CARD_WINDOW_PARMS CardWinParms;
            CardWinParms.hSocket = m_pEnabler->GetCardHandle();
            CardWinParms.fAttributes = WIN_ATTR_IO_SPACE; //IO Window
            CardWinParms.fAccessSpeed = 0 ; // This is not used by cardbus.
            CardWinParms.uWindowSize = Rsrc->Size;
            m_IoWindow[dwCurIndex] = CardRequestWindow( m_pEnabler->GetClientHandle(),
                                                        &CardWinParms );

            CARD_WINDOW_ADDRESS CardWindowAddr;
            CardWindowAddr.hSocket = m_pEnabler->GetCardHandle();
            CardWindowAddr.uCardAddress = 0;
            CardWindowAddr.uSize = Rsrc->Size;
            if( m_MemWindow[dwCurIndex] !=
                NULL &&
                CardMapWindowPhysical( m_MemWindow[dwCurIndex],
                                       &CardWindowAddr ) ==
                CERR_SUCCESS )
            {
                // We found index of window that we can use.
                Rsrc->Base = CardWindowAddr.uCardAddress;
                DWORD Status = m_pConfigEntry->CallEntry( PCIBUS_CONFIG_SET,
                                                          NULL,
                                                          Rsrc,
                                                          NULL,
                                                          NULL,
                                                          NULL );        
                if( Status != ERROR_SUCCESS )
                {
                    if( Status == ERROR_NOT_SUPPORTED &&
                        Rsrc->Offset < PCI_TYPE0_ADDRESSES )
                    {
                        // 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" ) );
                    }
                }
            }
            DEBUGMSG( ZONE_ERROR,
                      ( L"CardBus:ConfigSetUserResource Failed @ CardMapWindow\r\n" ) );
            Rsrc->Next = Rsrc;
            Rsrc->Prev = Rsrc;
            PCIRsrc_DelList( Rsrc );
        }
        if( m_pIoHead != NULL )
        {
            PCIRsrc_DelList( m_pIoHead );
            m_pIoHead = NULL;
        }
        return TRUE;
    }
    return FALSE;
}

BOOL CPCIEnum::LauchDriver()
{
    // We need Configuration space again after configured.
    HalGetBusData( PCIConfiguration,
                   m_dwBus,
                   m_SlotNumber.u.AsULONG,
                   &m_pciConfig,
                   sizeof( m_pciConfig ) -
                   sizeof( m_pciConfig.DeviceSpecific ) );

    // Get BAR length information, either from registry or from h/w
    if( !PCIGetBARs() )
    {
        return FALSE;
    }

    // Complete device's final initialization step (if it has one)
    if( m_Info.ConfigEntry && m_pConfigEntry != NULL )
    {
        DWORD Status;
        // Load driver Dll and obtain ConfigEntry function address
        Status = m_pConfigEntry->CallEntry( PCIBUS_CONFIG_INIT,
                                            &m_Info,
                                            NULL,
                                            NULL,
                                            NULL,
                                            NULL );
        if( ( Status != ERROR_SUCCESS ) && ( Status != ERROR_NOT_SUPPORTED ) )
            return FALSE;
    }

    // Request I/O and IRQ resources for device (phase 1 only)
    if( !PCIRequestResources() )
    {
        RETAILMSG( 1,
                   ( L"PCIBUS::CPCIEnum: WARNING: Resource request for device %d:%d:%d failed\r\n",
                     m_dwBus,
                     m_SlotNumber.u.bits.DeviceNumber,
                     m_SlotNumber.u.bits.FunctionNumber ) );
        return FALSE;
    }
    BOOL bReturn = TRUE;
    // A match in the registry exists, copy Template to Instance and generate device location info
    bReturn = RegCopy( &m_pciConfig, m_Info.RegPath, m_InstRegPath );
    _tcsncpy( m_InstRegPath, m_Info.RegPath, DEVKEY_LEN );
    m_InstRegPath[DEVKEY_LEN - 1] = 0;
    if( bReturn == TRUE )
    {
        return LoadDriver();
    }
    else
    {
        return FALSE;
    }
}

BOOL CPCIEnum::LoadDriver()
{
    if( m_hDriver == NULL )
    {
        DEBUGMSG( ZONE_INIT,
                  ( L"We Are loading Driver pointed by registry %s\r\n",
                    m_InstRegPath ) );
#define SUPPORT_DEPRECATED_ENTRY
#ifdef SUPPORT_DEPRECATED_ENTRY
        CRegistryEdit DriverKey( HKEY_LOCAL_MACHINE, m_InstRegPath );
        if( !DriverKey.IsKeyOpened() )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( TEXT( "CardBus!Load RegOpenKeyEx(%s) returned error \r\n" ),
                        m_InstRegPath ) );
            return FALSE;
        }
        // If an Entry value exists then this is a deprecated usage that precludes us
        // from being able to call ActivateDevice and also from being able to unload
        // the driver later.
        TCHAR DevDll[DEVDLL_LEN];
        TCHAR DevEntry[DEVENTRY_LEN];
        if( DriverKey.GetRegValue( DEVLOAD_ENTRYPOINT_VALNAME,
                                   ( PUCHAR ) DevEntry,
                                   sizeof( DevEntry ) ) )
        {
            // Entry value found
            DEBUGMSG( ZONE_WARN,
                      ( TEXT( "CardBus!Enumerate Found deprecated load instructions at (%s). Driver cannot be unloaded.\n" ),
                        m_InstRegPath ) );
        }
        else
        {
            DEBUGMSG( ZONE_ERROR,
                      ( TEXT( "CardBus!Enumerate Can not Found Entry Pointer Either (%s). Try ActivateDevice.\n" ),
                        m_InstRegPath ) );
            return ActivateDevice();
        }
        if( !DriverKey.GetRegValue( DEVLOAD_DLLNAME_VALNAME,
                                    ( PUCHAR ) DevDll,
                                    sizeof( DevDll ) ) )
        {
            // No DLL value found
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( TEXT( "CARDBUS!Enumerate RegQueryValueEx(%s\\%s) returned FAIL\r\n" ),
                        m_InstRegPath,
                        DEVLOAD_DLLNAME_VALNAME ) );
            return FALSE;
        }

        // Get Flags value
        DWORD DevFlags;
        if( !DriverKey.GetRegValue( DEVLOAD_FLAGS_VALNAME,
                                    ( PUCHAR ) & DevFlags,
                                    sizeof( DevFlags ) ) )
        {
            // No Flags value found, default is 0
            DevFlags = DEVFLAGS_NONE;
        }
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "REGENUM!Enumerate DevFlags = 0x%x\r\n" ), DevFlags ) );
        // If driver is not to be loaded, go on to next device
        if( DevFlags & DEVFLAGS_NOLOAD )
        {
            return FALSE;
        }
        // Load the driver now
        HMODULE hDevDll;
        PFN_DEV_ENTRY DevEntryFn;
        if( DevFlags & DEVFLAGS_LOADLIBRARY )
        {
            hDevDll = LoadLibrary( DevDll );
        }
        else
        {
            hDevDll = ::LoadDriver( DevDll );
        }
        if( hDevDll == NULL )
        {
            // Driver/library load failed
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( TEXT( "CARDBUS!Enumerate LoadDriver/Library(%s) failed %d\r\n" ),
                        DevDll,
                        GetLastError() ) );
            return FALSE;
        }

        DevEntryFn = ( PFN_DEV_ENTRY ) GetProcAddress( hDevDll, DevEntry );
        if( DevEntryFn == NULL )
        {
            DEBUGMSG( ZONE_ENUM |
                      ZONE_ERROR,
                      ( TEXT( "CARDBUS!Enumerate GetProcAddr(%s, %s) failed %d\r\n" ),
                        DevDll,
                        DevEntry,
                        GetLastError() ) );
            FreeLibrary( hDevDll );
            return FALSE;
        }

        //
        // Finally, call the device's initialization entrypoint
        //
        ( DevEntryFn ) ( m_InstRegPath );
        return TRUE;
#else
        return ActivateDevice();
#endif
    }
    else
    {
        return TRUE;
    }
};
BOOL CPCIEnum::ActivateDevice()
{
    if( m_hDriver == NULL )
    {
        REGINI reg[4];
        DWORD dwBusType = PCIBus;
        reg[0].lpszVal = DEVLOAD_INTERFACETYPE_VALNAME;
        reg[0].dwType = DEVLOAD_INTERFACETYPE_VALTYPE;
        reg[0].pData = ( PBYTE ) & dwBusType;
        reg[0].dwLen = sizeof( dwBusType );
        DWORD dwNumOfReg = 1;
        HANDLE hDevice;
        if( m_pEnabler->GetDeviceHandle() != NULL )
        {
            hDevice = m_pEnabler->GetDeviceHandle() ;
            reg[dwNumOfReg].lpszVal = DEVLOAD_BUSPARENT_VALNAME;
            reg[dwNumOfReg].dwType = DEVLOAD_BUSPARENT_VALTYPE;
            reg[dwNumOfReg].pData = ( PBYTE ) & hDevice;
            reg[dwNumOfReg].dwLen = sizeof( hDevice ) ;
            dwNumOfReg++;
        }
        if( m_pEnabler->GetDeviceBusName() != NULL )
        {
            reg[dwNumOfReg].lpszVal = DEVLOAD_BUSNAME_VALNAME;
            reg[dwNumOfReg].dwType = DEVLOAD_BUSNAME_VALTYPE;
            reg[dwNumOfReg].pData = ( PBYTE ) m_pEnabler->GetDeviceBusName();
            reg[dwNumOfReg].dwLen = ( _tcslen( m_pEnabler->GetDeviceBusName() ) +
                                      1 ) * sizeof( TCHAR );
            dwNumOfReg++;
        }
        if( ( m_hDriver = ActivateDeviceEx( m_InstRegPath,
                                            reg,
                                            dwNumOfReg,
                                            NULL ) ) ==
            NULL )
        {
            // Fail to load
            RETAILMSG( 1,
                       ( L"CPCIEnum::ActivateDevice: ActivateDeviceEx return fails for registry %s \r\n",
                         m_InstRegPath ) );
            return FALSE;
        }
        else
            return TRUE;
    }
    else
    {
        return TRUE;
    }
}
BOOL CPCIEnum::GetRegValue( LPCWSTR lpcName, LPBYTE lpData, DWORD dwDataLen )
{
    CRegistryEdit DriverKey( HKEY_LOCAL_MACHINE, m_Info.RegPath );
    if( !DriverKey.IsKeyOpened() ) {
        DEBUGMSG( ZONE_ENUM | ZONE_ERROR,( TEXT( "CardBus!Load RegOpenKeyEx(%s) returned error \r\n" ),m_Info.RegPath ) );
        return FALSE;
    }
    else {
        return DriverKey.GetRegValue(lpcName,lpData,dwDataLen);
    }
    
}

