//
// 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:

PcmBus.cpp

Abstract:

This file implements the PCMCIA model device driver load 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 "cardsv2.h"
#include "socksv2.h"
#include <ceddk.h>
#include <resmgr.h>
#include "debug.h"
#include "PcmciaEn.h"
#include "PcmBus.h"

CPcmciaEnum::CPcmciaEnum( LPTSTR lpPnpString,
                          UCHAR uFuncId,
                          PPARSED_PCMCFTABLE pPcmciaConfigure ) : m_CurPcmciaConfig( *pPcmciaConfigure ),
                                                                  m_uFuncId( uFuncId )
{
    m_lpPnpString = NULL;
    m_dwNumOfMatch = 0;
    m_RegPath[0] = 0;
    m_InstancePath[0] = 0;
    if( lpPnpString != NULL )
    {
        DWORD dwLength = _tcslen( lpPnpString );
        m_lpPnpString = new TCHAR[dwLength + 1];
        if( m_lpPnpString )
        {
            _tcsncpy( m_lpPnpString, lpPnpString, dwLength );
            m_lpPnpString[dwLength] = 0;
        }
    }
}

CPcmciaEnum::~CPcmciaEnum()
{
    if( m_InstancePath[0] != 0 )
    {
        ::RegDeleteKey( HKEY_LOCAL_MACHINE, m_InstancePath );
        m_InstancePath[0] = 0;
    }
    if( m_lpPnpString )
    {
        delete m_lpPnpString;
    }
}

BOOL CPcmciaEnum::Init()
{
    if( !PcmciaCfgFindMatch() )
    {
        return FALSE;
    }
    if( !InitInstances() )
    {
        return FALSE;
    }
    return TRUE;
}

//
// Copy contents of one registry key to another.
//
BOOL CPcmciaEnum::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( "PCMCIA::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;
}

BOOL CPcmciaEnum::InitInstances()
{
    TCHAR rgInstanceName[DEVKEY_LEN];
    DWORD ValLen, ValType;
    CRegistryEdit DeviceKey( HKEY_LOCAL_MACHINE, m_RegPath );
    ValLen = sizeof( rgInstanceName );
    if( DeviceKey.IsKeyOpened() &&
        DeviceKey.RegQueryValueEx( PCIBUS_INSTANCE_KEYNAME,
                                                              &ValType,
                                                              ( PUCHAR )
                                                              rgInstanceName,
                                                              &ValLen ) &&
        ( ValType == REG_SZ ) )
    {
        // Entry Exist
    }
    else
    {
        _tcsncpy( rgInstanceName, m_DevName, DEVKEY_LEN );
    }
    // NULL Terminate it.
    rgInstanceName[DEVKEY_LEN - 1] = 0;
    for( int iIndex = 1; iIndex < 100; iIndex++ )
    {
        _tcscpy( m_InstancePath, PCMCIA_INSTANCE_PATH );
        _tcscat( m_InstancePath, TEXT( "\\" ) );
        DWORD dwLength = _tcslen( m_InstancePath );
        DEBUGCHK( dwLength < DEVKEY_LEN );
        _tcsncpy( m_InstancePath + dwLength,
                  rgInstanceName,
                  DEVKEY_LEN - dwLength );
        m_InstancePath[DEVKEY_LEN - 1] = 0;
        dwLength = _tcslen( m_InstancePath );
        if( dwLength >= DEVKEY_LEN - 4 )
        {
            // The string is too long.
            DEBUGCHK( FALSE );
            return FALSE;
        }
        _stprintf( m_InstancePath + dwLength, TEXT( "%d" ), iIndex );
        DWORD dwDeposittion = 0;
        CRegistryEdit ISubKey( HKEY_LOCAL_MACHINE,
                               m_InstancePath,
                               &dwDeposittion );
        if( ISubKey.IsKeyOpened() && dwDeposittion != REG_OPENED_EXISTING_KEY )
        {
            // We created new retistry entry.
            CRegistryEdit TSubKey( HKEY_LOCAL_MACHINE, m_RegPath );
            if( !ISubKey.IsKeyOpened() )
            {
                break;
            }
            if( RegCopyKey( TSubKey, ISubKey ) /*&& RegSetKey(ISubKey)*/ )
                return TRUE;
            else
            {
                DEBUGCHK( FALSE );
                break;
            }
        }
        else
            m_InstancePath[0] = 0;
    }
    if( m_InstancePath[0] != 0 )
    {
        ::RegDeleteKey( HKEY_LOCAL_MACHINE, m_InstancePath );
        m_InstancePath[0] = 0;
    }
    DEBUGCHK( FALSE );
    return FALSE;
}

BOOL CPcmciaEnum::IsNoConfig()
{
    CRegistryEdit DeviceKey( HKEY_LOCAL_MACHINE, m_RegPath );
    DWORD Val;
    if( DeviceKey.IsKeyOpened() &&
        DeviceKey.GetRegValue( PCIBUS_NOCONFIG_VALNAME,
                                                          ( PUCHAR ) & Val,
                                                          sizeof( Val ) ) &&
        ( Val != 0 ) )
    {
        return TRUE;
    }
    return FALSE;
}

BOOL CPcmciaEnum::IsIoHighestBitSet()
{
    CRegistryEdit DeviceKey( HKEY_LOCAL_MACHINE, m_RegPath );
    DWORD Val;
    if( DeviceKey.IsKeyOpened() &&
        DeviceKey.GetRegValue( PCMCIA_IO_ADDR_HIGHEST_BIT_SET,
                                                          ( PUCHAR ) & Val,
                                                          sizeof( Val ) ) &&
        ( Val != 0 ) )
    {
        return TRUE;
    }
    return FALSE;
}

DWORD CPcmciaEnum::GetDevLoadFlag()
{
    DWORD dwReturn = DEVFLAGS_NONE;
    DWORD ValLen, ValType, Val;
    CRegistryEdit DeviceKey( HKEY_LOCAL_MACHINE, m_RegPath );
    ValLen = sizeof( DWORD );
    if( DeviceKey.IsKeyOpened() &&
        DeviceKey.RegQueryValueEx( DEVLOAD_FLAGS_VALNAME,
                                   &ValType,
                                   ( PUCHAR ) & Val,
                                   &ValLen ) &&
        ( ValType == DEVLOAD_FLAGS_VALTYPE ) )
    {
        // Flags value exists
        dwReturn = Val;
    }
    else
    {
        dwReturn = DEVFLAGS_NONE;
    }
    return dwReturn;
}    

BOOL CPcmciaEnum::PcmciaCfgFindMatch()
{
    //
    // Base registry key
    //
    TCHAR BaseRegPath[DEVKEY_LEN];
    TCHAR DevName[DEVKEY_LEN];
    m_dwNumOfMatch = 0;
    if( m_lpPnpString )
    {
        _tcscpy( m_RegPath, PCMCIA_PNP_PATH );
        _tcscat( m_RegPath, L"\\" );
        DWORD dwLength = _tcslen( m_RegPath );
        _tcsncpy( m_RegPath + dwLength, m_lpPnpString, DEVKEY_LEN - dwLength );
        m_RegPath[DEVKEY_LEN - 1] = 0;
        _tcsncpy( BaseRegPath, m_RegPath, DEVKEY_LEN );
        BaseRegPath[DEVKEY_LEN - 1] = 0;
        CRegistryEdit PnpKey( HKEY_LOCAL_MACHINE, m_RegPath );
        if( PnpKey.IsKeyOpened() )
        {
            DWORD DevLoadOrder;
            DWORD LoadOrder;
            DWORD NumDevKeys;
            DWORD ValType;
            DWORD ValLen;
            if( PnpKey.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
               &&
                NumDevKeys !=
                0 )
            {
                // Multiple Key Search.
                DWORD Match = 0;
                DWORD BestMatch = 0;                
                for( DWORD RegEnum = 0; RegEnum < NumDevKeys; RegEnum++ )
                {
                    DEBUGMSG( ZONE_ENUM,
                              ( TEXT( "PCMCIA!RegMatch RegEnum = %d\r\n" ),
                                RegEnum ) );

                    // Get sub-key according to RegEnum
                    ValLen = sizeof( DevName ) / sizeof( TCHAR );
                    if( !PnpKey.RegEnumKeyEx( RegEnum,
                                              DevName,
                                              &ValLen,
                                              NULL,
                                              NULL,
                                              NULL,
                                              NULL ) )
                    {
                        DEBUGMSG( ZONE_ENUM,
                                  ( TEXT( "PCMCIA!RegMatch RegEnumKeyEx(%d) returned fails\r\n" ),
                                    RegEnum ) );
                        break;
                    }

                    Match = RegMatchOne( PnpKey.GetHKey(), DevName );

                    if( Match > BestMatch )
                    {
                        BestMatch = Match;

                        // There is at least a partial match, construct path to device key
                        _tcscpy( m_RegPath, BaseRegPath );
                        dwLength = _tcslen( m_RegPath );
                        if( dwLength < DEVKEY_LEN )
                            m_RegPath[dwLength++] = _T( '\\' );
                        if( dwLength < DEVKEY_LEN )
                            _tcsncpy( m_RegPath + dwLength,
                                      DevName,
                                      DEVKEY_LEN - dwLength );
                        m_RegPath[DEVKEY_LEN - 1] = 0;
                        DEBUGMSG( ZONE_ENUM,
                                  ( TEXT( "CPCCard::CPcmciaEnum::PcmciaCfgFindMatch Matched(%s)=(%d)\r\n" ),
                                    m_RegPath,
                                    Match ) );            
                        _tcsncpy( m_DevName, m_lpPnpString, DEVKEY_LEN );
                    }
                } 
                m_dwNumOfMatch = BestMatch;
                return ( BestMatch != 0 );
            }
            else
            {
                if( ( m_dwNumOfMatch = RegMatchOne( HKEY_LOCAL_MACHINE,
                                                    m_RegPath ) ) !=
                    0 )
                {
                    // Matched one has been found.
                    _tcsncpy( m_DevName, m_lpPnpString, DEVKEY_LEN );
                    return TRUE;
                }
                else
                {
                    //DEBUGCHK(FALSE);
                    DEBUGMSG( ZONE_ENUM |
                              ZONE_ERROR,
                              ( TEXT( "PCMCIA!RegMatch RegQueryInfoKey(Class) %s  returned fails\r\n" ),
                                m_RegPath ) );
                    return FALSE;
                }
            }
        }
    }
    _tcscpy( m_RegPath, PCMCIA_TEMPLATE_PATH );
    CRegistryEdit TemplateKey( HKEY_LOCAL_MACHINE, m_RegPath );
    //
    // Find out how many sub-keys there are
    //
    DWORD DevLoadOrder;
    DWORD LoadOrder;
    DWORD NumDevKeys;
    DWORD ValType;
    DWORD ValLen;
    if( !TemplateKey.IsKeyOpened() ||
        !TemplateKey.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

        //DEBUGCHK(FALSE);
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCMCIA!RegMatch RegQueryInfoKey(Class) %s  returned fails\r\n" ),
                    m_RegPath ) );
        return FALSE;
    };
    DEBUGMSG( ZONE_ENUM,
              ( TEXT( "PCMCIA!RegMatch %d devices to search\r\n" ),
                NumDevKeys ) );
    //
    // Search for closest match
    //
    _tcsncpy( BaseRegPath, m_RegPath, DEVKEY_LEN );
    BaseRegPath[DEVKEY_LEN - 1] = 0;
    DWORD Match = 0;
    DWORD BestMatch = 0;

    for( DWORD RegEnum = 0; RegEnum < NumDevKeys; RegEnum++ )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatch RegEnum = %d\r\n" ), RegEnum ) );

        // Get sub-key according to RegEnum
        ValLen = sizeof( DevName ) / sizeof( TCHAR );
        if( !TemplateKey.RegEnumKeyEx( RegEnum,
                                       DevName,
                                       &ValLen,
                                       NULL,
                                       NULL,
                                       NULL,
                                       NULL ) )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatch RegEnumKeyEx(%d) returned fails\r\n" ),
                        RegEnum ) );
            break;
        }

        Match = RegMatchOne( TemplateKey.GetHKey(), DevName );

        if( Match > BestMatch )
        {
            BestMatch = Match;

            // There is at least a partial match, construct path to device key
            _tcscpy( m_RegPath, BaseRegPath );
            DWORD dwLength = _tcslen( m_RegPath );
            if( dwLength < DEVKEY_LEN )
                m_RegPath[dwLength++] = _T( '\\' );
            if( dwLength < DEVKEY_LEN )
                _tcsncpy( m_RegPath + dwLength,
                          DevName,
                          DEVKEY_LEN - dwLength );
            m_RegPath[DEVKEY_LEN - 1] = 0;
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "CPCCard::CPcmciaEnum::PcmciaCfgFindMatch Matched(%s)=(%d)\r\n" ),
                        m_RegPath,
                        Match ) );            
            _tcscpy( m_DevName, DevName );
        }
    }    
    m_dwNumOfMatch = BestMatch;
    return ( BestMatch != 0 );
}

//
// Device/registry matching routine.
//
DWORD CPcmciaEnum::RegMatchOne( HKEY  hKey, LPCWSTR DevName )
{
    CRegistryEdit DevKey( hKey, DevName );
    DWORD Mached = 1;
    if( !DevKey.IsKeyOpened() )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_ERROR,
                  ( TEXT( "PCMCIA!RegMatchOne RegOpenKeyEx(%s) not found. returned \r\n" ),
                    DevName ) );
        return 0;
    }
    //
    // Examine CONFIGURATION Index. If it have in registry. It is required to run and all the other fact ignored
    //
    DWORD dwIndex = -1;
    if( DevKey.GetRegValue( PCMCIA_CONFIGURATION_INDEX,
                            ( PUCHAR ) & dwIndex,
                            sizeof( dwIndex ) ) )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne CONFIGURATION_INDEX (0x%X) found.\r\n" ),
                    dwIndex ) );
        if( ( BYTE ) dwIndex != m_CurPcmciaConfig.ConfigIndex )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatchOne No Match for CONFIGURATION_INDEX (0x%X!=0x%X) found.\r\n" ),
                        dwIndex,
                        m_CurPcmciaConfig.ConfigIndex ) );
            return 0;
        }
        else
            return 2;
    }
    DWORD dwFunctionId = -1;
    if( !DevKey.GetRegValue( PCMCIA_FUNCTION_ID_VALNAME,
                             ( PUCHAR ) & dwFunctionId,
                             sizeof( dwFunctionId ) ) )
    {
        return Mached ;
    }
    else
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne FunctionID (0x%X) found.\r\n" ),
                    dwFunctionId ) );
        if( ( BYTE ) dwFunctionId < 0xff &&
            ( BYTE ) dwFunctionId != m_uFuncId )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatchOne No Match for FunctionID (0x%X) found.\r\n" ),
                        m_uFuncId ) );
            return 0;
        }
        else
            Mached++;
    }
    DDKWINDOWINFO dwi;
    if( DevKey.GetWindowInfo( &dwi ) == ERROR_SUCCESS )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne Find Address Limitation IO(0x%x),Mem(0x%X) found.\r\n" ),
                    dwi.dwNumIoWindows,
                    dwi.dwNumMemWindows ) );
        if( dwi.dwNumIoWindows > m_CurPcmciaConfig.NumIOEntries )
        {
            return 0;
        }
        for( dwIndex = 0; dwIndex < dwi.dwNumIoWindows; dwIndex++ )
        {
            if( m_CurPcmciaConfig.IOBase[dwIndex] !=
                dwi.ioWindows[dwIndex].dwBase ||
                m_CurPcmciaConfig.IOLength[dwIndex] <
                dwi.ioWindows[dwIndex].dwLen )
            {
                DEBUGMSG( ZONE_ENUM,
                          ( TEXT( "PCMCIA!RegMatchOne No Match IO address found for (0x%X)!=(0x%X) found at %d.\r\n" ),
                            m_CurPcmciaConfig.IOBase[dwIndex],
                            dwi.ioWindows[dwIndex],
                            dwIndex ) );
                return 0;
            }
        }
        if( dwi.dwNumIoWindows != 0 )
            Mached++;

        if( dwi.dwNumMemWindows > m_CurPcmciaConfig.NumMemEntries )
        {
            return 0;
        }
        for( dwIndex = 0; dwIndex < dwi.dwNumMemWindows; dwIndex++ )
        {
            if( m_CurPcmciaConfig.MemBase[dwIndex] !=
                dwi.memWindows[dwIndex].dwBase ||
                m_CurPcmciaConfig.MemLength[dwIndex] <
                dwi.memWindows[dwIndex].dwLen )
            {
                DEBUGMSG( ZONE_ENUM,
                          ( TEXT( "PCMCIA!RegMatchOne No Match MEM address found for (0x%X)!=(0x%X) found at %d.\r\n" ),
                            m_CurPcmciaConfig.MemBase[dwIndex],
                            dwi.memWindows[dwIndex],
                            dwIndex ) );
                return 0;
            }
        }
        if( dwi.dwNumMemWindows != 0 )
            Mached++;
    }

    DWORD dwIrqMask = 0;
    if( DevKey.GetRegValue( PCMCIA_INTERRUPT_MASK,
                            ( PUCHAR ) & dwIrqMask,
                            sizeof( dwIrqMask ) ) )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne Interrupt Mask (0x%X) found.\r\n" ),
                    dwIrqMask ) );
        if( ( ( WORD ) dwIrqMask & m_CurPcmciaConfig.wIrqMask ) == 0 )
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!Interrupt Mask is not match (0x%X) !=(0x%X.\r\n" ),
                        dwIrqMask,
                        m_CurPcmciaConfig.wIrqMask ) );
            return 0;
        }
        else
            Mached++;
    }
    DWORD dwVoltage;
    if( DevKey.GetRegValue( PCMCIA_VCC_NORM_VOLTAGE,
                            ( PUCHAR ) & dwVoltage,
                            sizeof( dwVoltage ) ) )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne VCC_NORM_VOLTAGE (0x%X) found.\r\n" ),
                    dwVoltage ) );
        if( ( m_CurPcmciaConfig.VccDescr.ValidMask & PWR_DESCR_NOMINALV ) !=
            0 )
        {
            DWORD dwDelta = m_CurPcmciaConfig.VccDescr.NominalV >
                            dwVoltage ?
                            m_CurPcmciaConfig.VccDescr.NominalV -
                            dwVoltage :
                            dwVoltage -
                            m_CurPcmciaConfig.VccDescr.NominalV;
            if( dwDelta > dwVoltage / 10 )
            {
                // Difference bigger than 10% will be considering fails.
                DEBUGMSG( ZONE_ENUM,
                          ( TEXT( "PCMCIA!RegMatchOne VCC_NORM_VOLTAGE (0x%X!=0x%X) found.\r\n" ),
                            dwVoltage,
                            m_CurPcmciaConfig.VccDescr.NominalV ) );
                return 0;
            }
        }
        else
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatchOne VCC_NORM_VOLTAGE do not know the VCC Mask=%x found.\r\n" ),
                        m_CurPcmciaConfig.VccDescr.ValidMask ) );
            return 0;
        }
        Mached++;
    }
    if( DevKey.GetRegValue( PCMCIA_VPP1_NORM_VOLTAGE,
                            ( PUCHAR ) & dwVoltage,
                            sizeof( dwVoltage ) ) )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne VPP1_NORM_VOLTAGE (0x%X) found.\r\n" ),
                    dwVoltage ) );
        if( ( m_CurPcmciaConfig.Vpp1Descr.ValidMask & PWR_DESCR_NOMINALV ) !=
            0 )
        {
            DWORD dwDelta = m_CurPcmciaConfig.Vpp1Descr.NominalV >
                            dwVoltage ?
                            m_CurPcmciaConfig.Vpp1Descr.NominalV -
                            dwVoltage :
                            dwVoltage -
                            m_CurPcmciaConfig.Vpp1Descr.NominalV;
            if( dwDelta > dwVoltage / 10 )
            {
                // Difference bigger than 10% will be considering fails.
                DEBUGMSG( ZONE_ENUM,
                          ( TEXT( "PCMCIA!RegMatchOne VPP1_NORM_VOLTAGE (0x%X!=0x%X) found.\r\n" ),
                            dwVoltage,
                            m_CurPcmciaConfig.Vpp1Descr.NominalV ) );
                return 0;
            }
        }
        else
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatchOne VPP1_NORM_VOLTAGE do not know the VCC Mask=%x found.\r\n" ),
                        m_CurPcmciaConfig.Vpp1Descr.ValidMask ) );
            return 0;
        }
        Mached++;
    }
    if( DevKey.GetRegValue( PCMCIA_VPP2_NORM_VOLTAGE,
                            ( PUCHAR ) & dwVoltage,
                            sizeof( dwVoltage ) ) )
    {
        DEBUGMSG( ZONE_ENUM,
                  ( TEXT( "PCMCIA!RegMatchOne VPP2_NORM_VOLTAGE (0x%X) found.\r\n" ),
                    dwVoltage ) );
        if( ( m_CurPcmciaConfig.Vpp2Descr.ValidMask & PWR_DESCR_NOMINALV ) !=
            0 )
        {
            DWORD dwDelta = m_CurPcmciaConfig.Vpp2Descr.NominalV >
                            dwVoltage ?
                            m_CurPcmciaConfig.Vpp2Descr.NominalV -
                            dwVoltage :
                            dwVoltage -
                            m_CurPcmciaConfig.Vpp2Descr.NominalV;
            if( dwDelta > dwVoltage / 10 )
            {
                // Difference bigger than 10% will be considering fails.
                DEBUGMSG( ZONE_ENUM,
                          ( TEXT( "PCMCIA!RegMatchOne VPP2_NORM_VOLTAGE (0x%X!=0x%X) found.\r\n" ),
                            dwVoltage,
                            m_CurPcmciaConfig.Vpp2Descr.NominalV ) );
                return 0;
            }
        }
        else
        {
            DEBUGMSG( ZONE_ENUM,
                      ( TEXT( "PCMCIA!RegMatchOne VPP2_NORM_VOLTAGE do not know the VCC Mask=%x found.\r\n" ),
                        m_CurPcmciaConfig.Vpp2Descr.ValidMask ) );
            return 0;
        }
        Mached++;
    }

    return Mached;
};

