//
// 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:

PcmciaEn.cpp

Abstract:

This file implements the PCMCIA model device driver Enabler 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 <Nkintr.h>
#include "PcmciaEn.h"
#include "debug.h"

CPCEnablePcmcia::CPCEnablePcmcia( CARD_SOCKET_HANDLE hSocket,
                                  CARD_CLIENT_HANDLE hClient ) : CPCEnableCard( hSocket,
                                                                                hClient ),
                                                                 CMiniThread( 0,
                                                                              TRUE )
{
    m_bTerminated = FALSE;
    m_Configured = FALSE;
    m_pPcmciaEnum = NULL;
    m_hDriver = NULL;
    for( DWORD dwIndex = 0; dwIndex < EVENT_NUMBER; dwIndex++ )
    {
        m_EventList[dwIndex] = CreateEvent( NULL, FALSE, FALSE, NULL );
    }
    m_bIrq = ( BYTE ) - 1;
    for( dwIndex = 0; dwIndex < MAX_WINDOWS_RANGES; dwIndex++ )
    {
        m_IoHandle[dwIndex]  = NULL ;
        m_MemHandle[dwIndex] = NULL;
    }
    for( int iIndex = 0; iIndex < MAX_WINDOWS_RANGES; iIndex++ )
    {
        m_IOPhysicalAddr[iIndex] = 0;
        m_MemHandle[iIndex] = NULL;
        m_MemPhysicalAddr[iIndex] = 0;
    }

    // Mem Window

    memset( &m_CurConfigure, 0, sizeof( m_CurConfigure ) );
    m_CurConfigure.bIrqNumber = -1;
    m_CurConfigure.dwSysIntr = ( DWORD ) - 1;
    m_FuncID = 0xff; // Initial to unsported.
    m_DeviceId[0] = 0;
    m_DriverInstanceRegPath[0] = 0;
};
CPCEnablePcmcia::~CPCEnablePcmcia()
{
    m_bTerminated = TRUE;
    ThreadStart();
    SetEvent( m_EventList[TERMINAGE_THREAD] );
    PostThreadMessage( GetThreadId(), WM_QUIT, 0, 0 );
    WaitThreadComplete( INFINITE );// Wait for thread termination.
    UnLoadDriver();
    Lock();
    CardReleaseConfiguration( GetClientHandle(), GetCardHandle() );
    UnconfigurePcmciaCard();
    for( DWORD dwIndex = 0; dwIndex < EVENT_NUMBER; dwIndex++ )
    {
        if( m_EventList[dwIndex] != NULL )
        {
            CloseHandle( m_EventList[dwIndex] );
        }
    }
    Unlock();
};
STATUS CPCEnablePcmcia::HandleEvent( CARD_EVENT cEvent,
                                     PCARD_EVENT_PARMS pParms )
{
    STATUS status = CERR_BAD_ARGS;
    switch( cEvent )
    {
      case CE_POWER_CYCLE_ON:
        Lock();
        if( m_Configured == FALSE )
        {
            // Card is ready to use.
            m_dwBusNumber = pParms->Parm1 & 0xffff;
            m_IntefaceType = ( INTERFACE_TYPE )
                             ( ( pParms->Parm1 >> 16 ) & 0xffff );
            m_SlotNumber.u.AsULONG = pParms->Parm2;
            ThreadStart();
            SetEvent( m_EventList[CARD_READY] );
        }
        status = CERR_SUCCESS;
        Unlock();
        break;
      case CE_PM_RESUME:
        Lock();
        if( m_Configured )
        {
            ThreadStart();
            SetEvent( m_EventList[CARD_RESUME] );
        }
        Unlock();
        break;
      default:
        status = CPCEnableCard::HandleEvent( cEvent, pParms );
        break;
    }
    return status;
}
BOOL CPCEnablePcmcia::RegSetKey( CRegistryEdit& IKey )
{
    if( !IKey.IsKeyOpened() )
    {
        return FALSE;
    }
    IKey.RegDeleteValue( DEVLOAD_IFCTYPE_VALNAME );
    if( !IKey.RegSetValueEx( DEVLOAD_IFCTYPE_VALNAME,
                             PCIBUS_IFCTYPE_VALTYPE,
                             ( PBYTE ) & m_IntefaceType,
                             sizeof( m_IntefaceType ) ) )
    {
        return FALSE;
    }

    IKey.RegDeleteValue( PCIBUS_BUSNUMBER_VALNAME );
    if( !IKey.RegSetValueEx( PCIBUS_BUSNUMBER_VALNAME,
                             PCIBUS_BUSNUMBER_VALTYPE,
                             ( PBYTE ) & m_dwBusNumber,
                             sizeof( m_dwBusNumber ) ) )
    {
        return FALSE;
    }
    DWORD dwValue = m_SlotNumber.u.bits.DeviceNumber;
    IKey.RegDeleteValue(PCIBUS_DEVICENUMBER_VALNAME);
    if( !IKey.RegSetValueEx( PCIBUS_DEVICENUMBER_VALNAME,
                             PCIBUS_DEVICENUMBER_VALTYPE,
                             ( PBYTE ) & dwValue,
                             sizeof( dwValue ) ) )
    {
        return FALSE;
    }
    dwValue =  m_SlotNumber.u.bits.FunctionNumber;
    IKey.RegDeleteValue( PCIBUS_FUNCTIONNUMBER_VALNAME );
    if( !IKey.RegSetValueEx( PCIBUS_FUNCTIONNUMBER_VALNAME,
                             PCIBUS_FUNCTIONNUMBER_VALTYPE,
                             ( PBYTE ) & dwValue,
                             sizeof( dwValue ) ) )
    {
        return FALSE;
    }

    IKey.RegDeleteValue( PCIBUS_MEMBASE_VALNAME );
    IKey.RegDeleteValue( PCIBUS_MEMLEN_VALNAME );
    IKey.RegDeleteValue( PCIBUS_IOBASE_VALNAME );
    IKey.RegDeleteValue( PCIBUS_IOLEN_VALNAME );
    IKey.RegDeleteValue( PCIBUS_IRQ_VALNAME );
    IKey.RegDeleteValue( PCIBUS_SYSINTR_VALNAME );
    if( m_pPcmciaEnum == NULL || !m_pPcmciaEnum->IsNoConfig() )
    {
        if( m_CurConfigure.NumMemEntries != 0 )
        {
            // Set Memory Base and Length values
            if( !IKey.RegSetList( PCIBUS_MEMBASE_VALNAME,
                                  m_CurConfigure.NumMemEntries,
                                  m_MemPhysicalAddr ) )
            {
                return FALSE;
            }

            if( !IKey.RegSetList( PCIBUS_MEMLEN_VALNAME,
                                  m_CurConfigure.NumMemEntries,
                                  m_CurConfigure.MemLength ) )
            {
                return FALSE;
            }
        }
        if( m_CurConfigure.NumIOEntries != 0 )
        {
            // Set I/O Base and Length values
            if( !IKey.RegSetList( PCIBUS_IOBASE_VALNAME,
                                  m_CurConfigure.NumIOEntries,
                                  m_IOPhysicalAddr ) )
            {
                return FALSE;
            }

            if( !IKey.RegSetList( PCIBUS_IOLEN_VALNAME,
                                  m_CurConfigure.NumIOEntries,
                                  m_CurConfigure.IOLength ) )
            {
                return FALSE;
            }
        }
        // If Interrupt Pin register is 0, or if there is an invalid Interrupt Line value, there is no Irq
        if( m_CurConfigure.bIrqNumber != (BYTE)-1 )
        {
            // Write Irq value
            dwValue = m_CurConfigure.bIrqNumber;
            if( IKey.RegSetValueEx( PCIBUS_IRQ_VALNAME,
                                    PCIBUS_IRQ_VALTYPE,
                                    ( PBYTE ) & dwValue,
                                    sizeof( dwValue ) ) == FALSE )
            {
                return FALSE;
            }
        }
        if( m_CurConfigure.dwSysIntr != -1 )
        {
            // Write SysIntr value
            if( IKey.RegSetValueEx( PCIBUS_SYSINTR_VALNAME,
                                    PCIBUS_SYSINTR_VALTYPE,
                                    ( PBYTE ) & m_CurConfigure.dwSysIntr,
                                    sizeof( m_CurConfigure.dwSysIntr ) ) ==
                FALSE )
            {
                return FALSE;
            }
        };
    }
    return TRUE;
}


BOOL CPCEnablePcmcia::LoadDriver()
{
    Lock();
    BOOL bReturn = FALSE;
    LPCTSTR lpInstanceRegPath = NULL;
    if( m_pPcmciaEnum != NULL &&
        ( lpInstanceRegPath = m_pPcmciaEnum->GetInstances() ) != NULL )
    {
        if( m_hDriver == NULL )
        {
            CRegistryEdit InstanceKey( HKEY_LOCAL_MACHINE, lpInstanceRegPath );
            if( InstanceKey.IsKeyOpened() && RegSetKey( InstanceKey ) )
            {
                REGINI reg[4];
                DWORD dwNumOfReg;
                CARD_SOCKET_HANDLE hSock = GetCardHandle() ;
                if( m_pPcmciaEnum->IsNoConfig() )
                {
                    reg[0].lpszVal = DEVLOAD_SOCKET_VALNAME;
                    reg[0].dwType = DEVLOAD_SOCKET_VALTYPE;
                    reg[0].pData = ( PBYTE ) & hSock;
                    reg[0].dwLen = sizeof( hSock );
                    reg[1].lpszVal = DEVLOAD_INTERFACETYPE_VALNAME;
                    reg[1].dwType = DEVLOAD_INTERFACETYPE_VALTYPE;
                    reg[1].pData = ( PBYTE ) & m_IntefaceType;
                    reg[1].dwLen = sizeof( m_IntefaceType );                    
                    dwNumOfReg = 2;
                }
                else
                {
                    reg[0].lpszVal = DEVLOAD_INTERFACETYPE_VALNAME;
                    reg[0].dwType = DEVLOAD_INTERFACETYPE_VALTYPE;
                    reg[0].pData = ( PBYTE ) & m_IntefaceType;
                    reg[0].dwLen = sizeof( m_IntefaceType );
                    dwNumOfReg = 1;
                }
                if( m_hDeviceHandle != NULL )
                {
                    reg[dwNumOfReg].lpszVal = DEVLOAD_BUSPARENT_VALNAME;
                    reg[dwNumOfReg].dwType = DEVLOAD_BUSPARENT_VALTYPE;
                    reg[dwNumOfReg].pData = ( PBYTE ) & m_hDeviceHandle;
                    reg[dwNumOfReg].dwLen = sizeof( m_hDeviceHandle ) ;
                    dwNumOfReg++;
                }
                if( m_lpDeviceBusName != NULL )
                {
                    reg[dwNumOfReg].lpszVal = DEVLOAD_BUSNAME_VALNAME;
                    reg[dwNumOfReg].dwType = DEVLOAD_BUSNAME_VALTYPE;
                    reg[dwNumOfReg].pData = ( PBYTE ) m_lpDeviceBusName;
                    reg[dwNumOfReg].dwLen = ( _tcslen( m_lpDeviceBusName ) +
                                              1 ) * sizeof( TCHAR );
                    dwNumOfReg++;
                }
                if( ( m_hDriver = ActivateDeviceEx( lpInstanceRegPath,
                                                    reg,
                                                    dwNumOfReg,
                                                    NULL ) ) ==
                    NULL )
                {
                    // Fail to load
                    RETAILMSG( 1,
                               ( L"CPCIEnum::ActivateDevice: ActivateDeviceEx return fails for registry %s \r\n",
                                 lpInstanceRegPath ) );
                }
                else
                {
                    DEBUGMSG( ZONE_ENUM,
                              ( L"CPCEnablePcmcia::LoadDriver:Success on ActivateDeviceEx(%s)\r\n",
                                lpInstanceRegPath ) );
                    bReturn = TRUE;
                }
            }
            else
            {
                DEBUGCHK( FALSE );
            }
        }
        else
        {
            bReturn = FALSE;
        }
    }
    Unlock();
    return bReturn;
}
void CPCEnablePcmcia::UnLoadDriver()
{
    Lock();
    if( m_hDriver != NULL )
    {
        DeactivateDevice( m_hDriver );
        m_hDriver = NULL;
    }
    if( m_pPcmciaEnum )
    {
        delete m_pPcmciaEnum;
        m_pPcmciaEnum = NULL;
    }
    Unlock();
}
#ifdef DEBUG
BOOL CPCEnablePcmcia::Testing()
{
    if( ZONE_ENUM )
    {
        for( UINT8 uIndex = 0;
             uIndex < m_CurConfigure.NumIOEntries;
             uIndex++ )
        {
            PUCHAR ioPortBase = NULL; 
            ULONG inIoSpace = 1;
            PHYSICAL_ADDRESS ioPhysicalBase =
            {
                m_IOPhysicalAddr[uIndex], 0
            };

            NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : HalTranslateBusAddress\r\n" ) );
            if( HalTranslateBusAddress( m_IntefaceType,
                                        m_dwBusNumber,
                                        ioPhysicalBase,
                                        &inIoSpace,
                                        &ioPhysicalBase ) )
            {
                NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : HalTranslateBusAddress - OK\r\n" ) );
                if( !inIoSpace )
                {
                    NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : ! IO Space\r\n" ) );
                    if( ( ioPortBase = ( PUCHAR )
                          MmMapIoSpace( ioPhysicalBase,
                                        m_CurConfigure.IOLength[uIndex],
                                        FALSE ) ) ==
                        NULL )
                    {
                        // We may as well not continue
                        NKDbgPrintfW( TEXT( "Error mapping I/O Ports\r\n" ) );
                        return ( FALSE );
                    }
                }
                else
                {
                    NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : IO Space\r\n" ) );
                    ioPortBase = ( PUCHAR ) ioPhysicalBase.LowPart;
                }
            }
            else
            {
                NKDbgPrintfW( TEXT( "Error translating I/O Ports.\r\n" ) );
                return FALSE;
            }
            if( ioPortBase )
            {
                NKDbgPrintfW( TEXT( "Read From Port for (%d) Byte" ),
                              m_CurConfigure.IOLength[uIndex] );
                for( DWORD dwIndex = 0;
                     dwIndex < m_CurConfigure.IOLength[uIndex];
                     dwIndex++ )
                {
                    NKDbgPrintfW( TEXT( "%02X " ),
                                  READ_PORT_UCHAR( ioPortBase + dwIndex ) );
                }
                NKDbgPrintfW( TEXT( "\r\n" ) );
                for( dwIndex = 0;
                     dwIndex < m_CurConfigure.IOLength[uIndex] / 2;
                     dwIndex++ )
                {
                    NKDbgPrintfW( TEXT( "%04X " ),
                                  READ_PORT_USHORT( ( ( PUSHORT ) ioPortBase ) +
                                                    dwIndex ) );
                }
                if( !inIoSpace )
                {
                    MmUnmapIoSpace( ioPortBase,
                                    m_CurConfigure.IOLength[uIndex] );
                }
                NKDbgPrintfW( TEXT( "\r\n" ) );
            }
        }
        for( uIndex = 0; uIndex < m_CurConfigure.NumMemEntries; uIndex++ )
        {
            PUCHAR ioPortBase = NULL; 
            ULONG inIoSpace = 0;
            PHYSICAL_ADDRESS ioPhysicalBase =
            {
                m_MemPhysicalAddr[uIndex], 0
            };

            NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : HalTranslateBusAddress\r\n" ) );
            if( HalTranslateBusAddress( m_IntefaceType,
                                        m_dwBusNumber,
                                        ioPhysicalBase,
                                        &inIoSpace,
                                        &ioPhysicalBase ) )
            {
                NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : HalTranslateBusAddress - OK\r\n" ) );
                if( !inIoSpace )
                {
                    NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : ! IO Space\r\n" ) );
                    if( ( ioPortBase = ( PUCHAR )
                          MmMapIoSpace( ioPhysicalBase,
                                        m_CurConfigure.MemLength[uIndex],
                                        FALSE ) ) ==
                        NULL )
                    {
                        // We may as well not continue
                        NKDbgPrintfW( TEXT( "Error mapping I/O Ports\r\n" ) );
                        return ( FALSE );
                    }
                }
                else
                {
                    NKDbgPrintfW( TEXT( "CPCEnablePcmcia::Testing() : IO Space\r\n" ) );
                    ioPortBase = ( PUCHAR ) ioPhysicalBase.LowPart;
                }
            }
            else
            {
                NKDbgPrintfW( TEXT( "Error translating I/O Ports.\r\n" ) );
                return FALSE;
            }
            if( ioPortBase )
            {
                NKDbgPrintfW( TEXT( "Read From Port for (%d) Byte" ),
                              m_CurConfigure.MemLength[uIndex] );
                for( DWORD dwIndex = 0;
                     dwIndex < m_CurConfigure.MemLength[uIndex] &&
                     dwIndex < 0x10;
                     dwIndex++ )
                {
                    NKDbgPrintfW( TEXT( "%02X " ), *( ioPortBase + dwIndex ) );
                }
                NKDbgPrintfW( TEXT( "\r\n" ) );
                if( !inIoSpace )
                {
                    MmUnmapIoSpace( ioPortBase,
                                    m_CurConfigure.MemLength[uIndex] );
                }
            }
        }
    }
    return TRUE;
}
static void DumpPowerDescr( PPOWER_DESCR pPowerDescr )
{
    NKDbgPrintfW( TEXT( "ValidMask = 0x%x " ), pPowerDescr->ValidMask );
    if( pPowerDescr->ValidMask & PWR_AVAIL_NOMINALV )
    {
        NKDbgPrintfW( TEXT( "NominalV = 0x%x " ), pPowerDescr->NominalV );
    }

    if( pPowerDescr->ValidMask & PWR_AVAIL_MINV )
    {
        NKDbgPrintfW( TEXT( "MinV = 0x%x " ), pPowerDescr->MinV );
    }

    if( pPowerDescr->ValidMask & PWR_AVAIL_MAXV )
    {
        NKDbgPrintfW( TEXT( "MinV = 0x%x " ), pPowerDescr->MaxV );
    }
    NKDbgPrintfW( TEXT( "\r\n" ) );
}
BOOL CPCEnablePcmcia::DumpCfTable( PPARSED_PCMCFTABLE pCurTable )
{
    if( ZONE_ENUM )
    {
        NKDbgPrintfW( TEXT( "CPCEnablePcmcia::DumpCfTable  @0x%x\r\n" ),
                      pCurTable );
        NKDbgPrintfW( TEXT( "ConfigIndex=0x%x \r\n" ), pCurTable->ConfigIndex );
        NKDbgPrintfW( TEXT( "ContainsDefaults=0x%x \r\n" ),
                      pCurTable->ContainsDefaults );
        NKDbgPrintfW( TEXT( "IFaceType=0x%x \r\n" ), pCurTable->IFaceType );
        NKDbgPrintfW( TEXT( "Vcc PowerDesc " ) );
        DumpPowerDescr( &( pCurTable->VccDescr ) );
        NKDbgPrintfW( TEXT( "Vpp1 PowerDesc " ) );
        DumpPowerDescr( &( pCurTable->Vpp1Descr ) );
        NKDbgPrintfW( TEXT( "NumIOEntries=0x%x \r\n" ),
                      pCurTable->NumIOEntries );
        for( DWORD dwIndex = 0; dwIndex < pCurTable->NumIOEntries; dwIndex++ )
        {
            NKDbgPrintfW( TEXT( "   Io(%x) Addr = 0x%x, Lenth=0x%x\r\n" ),
                          dwIndex,
                          pCurTable->IOBase[dwIndex],
                          pCurTable->IOLength[dwIndex] );
        }
        NKDbgPrintfW( TEXT( "NumMemEntries=0x%x \r\n" ),
                      pCurTable->NumMemEntries );
        for( dwIndex = 0; dwIndex < pCurTable->NumMemEntries; dwIndex++ )
        {
            NKDbgPrintfW( TEXT( "   Mem(%x) Addr = 0x%x, Lenth=0x%x\r\n" ),
                          dwIndex,
                          pCurTable->MemBase[dwIndex],
                          pCurTable->MemLength[dwIndex] );
        }
        NKDbgPrintfW( TEXT( "NumIOAddrLines=0x%x \r\n" ),
                      pCurTable->NumIOAddrLines );
        NKDbgPrintfW( TEXT( "IOAccess=0x%x \r\n" ), pCurTable->IOAccess );
        NKDbgPrintfW( TEXT( "wIrqMask=0x%x \r\n" ), pCurTable->wIrqMask );
    }
    return TRUE;
}
#endif

DWORD CPCEnablePcmcia::ThreadRun()
{
    while( !m_bTerminated )
    {
        // Processing Event
        DWORD dwReturn = WaitForMultipleObjects( EVENT_NUMBER,
                                                 m_EventList,
                                                 FALSE,
                                                 INFINITE );
        Lock();
        switch( dwReturn )
        {
          case WAIT_OBJECT_0+CARD_READY:
            if( !m_Configured && !m_bTerminated )
            {
                //Sleep(100); // Wait for Card Settle First.
                BOOL bConfigured = FALSE;
                if( ConfigurePcmciaCard() && !m_bTerminated )
                {
#ifdef DEBUG
                    if( ZONE_ENUM )
                    {
                        Testing();
                    }
#endif
                    if( !LoadDriver() )
                    {
                        // If loader driver fails. Power down the socket if we could
                        UnconfigurePcmciaCard();
                    }
                    else
                        bConfigured = TRUE;
                }
                if( !bConfigured && !m_bTerminated )
                {
                    // We should kill this object.
                    CardRequestDisable( GetClientHandle(), GetCardHandle() );
                    Unlock();
                    BOOL bReturn = QueryDriverName( m_DeviceId );
                    Lock();
                    if( bReturn )
                        CardResetFunction( GetClientHandle(), GetCardHandle() );
                    m_bTerminated = TRUE;
                }
            }
            break;
          case WAIT_OBJECT_0+CARD_RESUME:
            DEBUGMSG( ZONE_ENUM,
                      ( L"CPCEnablePcmcia::Resume Event has been signaled \r\n" ) );
            if( m_Configured )
            {
                //Sleep(100);
                CheckPnp();
            }
            break;
          case WAIT_OBJECT_0+TERMINAGE_THREAD:
            m_bTerminated = TRUE;
            break;
          case WAIT_FAILED:
            DEBUGMSG( ZONE_ERROR,
                      ( L"CPCEnablePcmcia::ThreadRun:Wait Fails with Error code %d\r\n",
                        GetLastError() ) );
            break;
        };
        Unlock();
    };
    return 0;
};

#define TUPLEBUFFER_SIZE 0x200 
BOOL CPCEnablePcmcia::ConfigurePcmciaCard()
{
    if( !m_Configured )
    {
        // Get Function ID from tuple.
        CARD_TUPLE_PARMS TupleParms;
        UCHAR dataBuffer[TUPLEBUFFER_SIZE + sizeof( CARD_DATA_PARMS )];
        m_DeviceId[0] = 0;
        // Create PnpID.
        STATUS status = CreateDeviceID( m_DeviceId, DEVID_LEN );
        DEBUGCHK( status == CERR_SUCCESS );
        m_DeviceId[DEVID_LEN] = 0;
        if( GetCardHandle().uFunction > 0 )
        {
            DWORD ValLen = _tcslen( m_DeviceId );
            if( ValLen >= DEVID_LEN - 2 )
                ValLen = DEVID_LEN - 2;
            m_DeviceId[ValLen] = ( TCHAR ) '-';
            m_DeviceId[ValLen + 1] = ( TCHAR ) '0' +
            GetCardHandle().uFunction;
            m_DeviceId[ValLen + 2] = ( TCHAR ) 0;
        }
        // Get Function ID.
        BYTE bFuncID = 0;
        BOOL bFirst = TRUE;
        while( bFuncID == 0 )
        {
            if( FindTuple( &TupleParms, bFirst, CISTPL_FUNCID ) &&
                GetTupleData( &TupleParms,
                              dataBuffer,
                              TUPLEBUFFER_SIZE +
                              sizeof( CARD_DATA_PARMS ) ) )
            {
                PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
                if( pData->uDataLen >= 1 ) // At lest 4 byte.
                    bFuncID = dataBuffer[sizeof( CARD_DATA_PARMS )];
                bFirst = FALSE;
            }
            else
            {
                DEBUGCHK( FALSE );
                break;
            }
        }
        m_FuncID = bFuncID;

        // Get Valid Configuration From Tuple.
        if( FindTuple( &TupleParms, TRUE, CISTPL_CONFIG ) &&
            GetTupleData( &TupleParms,
                          dataBuffer,
                          TUPLEBUFFER_SIZE ) )
        {
            PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
            if( pData->uDataLen >= 2 )
            {
                BYTE lastTuple = dataBuffer[sizeof( CARD_DATA_PARMS ) + 1] & 0x3f;
                PARSED_PCMCFTABLE curTable, DefaultTable;
                DefaultTable.ContainsDefaults = 0;
                if( FindTuple( &TupleParms, TRUE, CISTPL_CFTABLE_ENTRY ) &&
                    GetTupleData( &TupleParms,
                                  dataBuffer,
                                  TUPLEBUFFER_SIZE ) )
                {
                    do
                    {
                        pData = ( PCARD_DATA_PARMS ) dataBuffer;
                        DWORD dwLength = min( TUPLEBUFFER_SIZE -
                                              sizeof( CARD_DATA_PARMS ),
                                              pData->uDataLen );
                        if( ParseCfTable( ( PVOID ) ( pData + 1 ),
                                          dwLength,
                                          &curTable,
                                          &DefaultTable ) == CERR_SUCCESS )
                        {
                            if( curTable.ContainsDefaults != 0 )
                            {
                                // found Default, need Update.
                                DefaultTable = curTable;
                            }
#ifdef DEBUG
                            if( ZONE_ENUM )
                            {
                                DumpCfTable( &curTable );
                            }
#endif
                            if( m_pPcmciaEnum != NULL )
                                delete m_pPcmciaEnum;
                            m_pPcmciaEnum = new CPcmciaEnum( m_DeviceId,
                                                             m_FuncID,
                                                             &curTable );
                            if( m_pPcmciaEnum != NULL &&
                                m_pPcmciaEnum->Init() )
                            {
                                if( m_pPcmciaEnum->IsNoConfig() )
                                {
                                    // Do not need to configure this card.
                                    if( m_pPcmciaEnum->GetNumOfMatch() > 1 )
                                    {
                                        // It is not Empty Entry for NoConfig.
                                        m_Configured = TRUE;
                                        ReleaesExclusive();
                                        return TRUE;
                                    }
                                }
                                else if( ConfigurePcmciaCardResource( &curTable,
                                                                      m_pPcmciaEnum->IsIoHighestBitSet(),
                                                                      m_pPcmciaEnum->GetDevLoadFlag() ) )
                                {
                                    m_Configured = TRUE;
                                    m_CurConfigure = curTable;
                                    return TRUE;
                                }
                                else
                                    ReleasePcmciaCardResources( &curTable );
                            }
                            if( m_pPcmciaEnum != NULL )
                            {
                                delete m_pPcmciaEnum;
                                m_pPcmciaEnum = NULL;
                            }
                        }
                        else
                            DEBUGCHK( FALSE );
                    }
                    while( FindTuple( &TupleParms,
                                      FALSE,
                                      CISTPL_CFTABLE_ENTRY ) &&
                           GetTupleData( &TupleParms,
                                         dataBuffer,
                                         TUPLEBUFFER_SIZE ) );
                };
            }
        };
    }
#define DEFAULT_MEM_WINDOW_SIZE 0x1000 // 4K
    if( !m_Configured )
    {
        // We have to assume this is a memory Card. So do follows.
        PARSED_PCMCFTABLE DefaultTable;
        memset( &DefaultTable, 0, sizeof( DefaultTable ) );
        DefaultTable.NumMemEntries = 1;
        DefaultTable.MemBase[0] = 0 ;
        DefaultTable.MemLength[0] = DEFAULT_MEM_WINDOW_SIZE;
        if( m_pPcmciaEnum != NULL )
            delete m_pPcmciaEnum;
        m_pPcmciaEnum = new CPcmciaEnum( m_DeviceId, m_FuncID, &DefaultTable );
        if( m_pPcmciaEnum != NULL && m_pPcmciaEnum->Init() )
        {
            if( m_pPcmciaEnum->IsNoConfig() )
            {
                // Do not need to configure this card.
                m_Configured = TRUE;
                ReleaesExclusive();
                return TRUE;
            }
            else if( ConfigurePcmciaCardResource( &DefaultTable,
                                                  m_pPcmciaEnum->IsIoHighestBitSet(),
                                                  m_pPcmciaEnum->GetDevLoadFlag() ) )
            {
                m_Configured = TRUE;
                m_CurConfigure = DefaultTable;
                return TRUE;
            }
            else
                ReleasePcmciaCardResources( &DefaultTable );
        }
        if( m_pPcmciaEnum != NULL )
        {
            delete m_pPcmciaEnum;
            m_pPcmciaEnum = NULL;
        }
    }
    //DEBUGCHK( m_Configured == TRUE);
    DEBUGMSG( ZONE_ENUM |
              ZONE_ERROR,
              ( TEXT( "CPCEnablePcmcia::ConfigurePcmciaCard() returned %d \r\n" ),
                m_Configured ) );
    return m_Configured;
}
BOOL CPCEnablePcmcia::UnconfigurePcmciaCard()
{
    if( m_Configured )
    {
        m_Configured = FALSE;
        ReleasePcmciaCardResources( &m_CurConfigure );
    }
    return TRUE;
}
BOOL CPCEnablePcmcia::RequestSysIntr( DWORD Irq, DWORD* pSysIntr ) const
{
    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 == SYSINTR_UNDEFINED )
    {
        RetVal = FALSE;
    }

    if( !RetVal )
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"PCIBUS!RequestSysIntr: Invalid SysIntr returned for Irq %d\r\n",
                    Irq ) );
    }

    return RetVal;
}
BOOL CPCEnablePcmcia::ReleaseSysIntr( DWORD SysIntr ) const
{
    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;
}

#define ALTER_HIGH_IO_ADDR 0x8000
BOOL CPCEnablePcmcia::ConfigurePcmciaCardResource( PPARSED_PCMCFTABLE curTable,
                                                   BOOL bSetIoAddrHighestBit,
                                                   DWORD dwDevFlags )
{
    for( int iIndex = 0; iIndex < curTable->NumIOEntries ; iIndex++ )
    {
        if (m_IoHandle[iIndex] == NULL ) {
            // Alter the IO address according Registry said.
            if( curTable->NumIOAddrLines < 16 && bSetIoAddrHighestBit ) {
                curTable->IOBase[iIndex] |= ALTER_HIGH_IO_ADDR;
            }
            DWORD dwBaseAddr = curTable->IOBase[iIndex];
            DWORD dwAddrLength = curTable->IOLength[iIndex] ;
            // Align with order of 2
            if( ( dwAddrLength & ( dwAddrLength - 1 ) ) != 0 )
            {
                DEBUGMSG( ZONE_ENUM |
                          ZONE_WARN,
                          ( L"CPCEnablePcmcia:: CardRequestConfiguration: Address Size is not order of 2(Addr=%x Length=%x)! We enforce it.  \r\n",
                            dwBaseAddr,
                            dwAddrLength ) );
                for( int i = 31; i > 1 ; i -- )
                {
                    if( dwAddrLength & ( 1 << i ) )
                    {
                        dwAddrLength = ( 1 << ( i + 1 ) );
                        break;
                    }
                }
            }
                
            CARD_WINDOW_PARMS CardWinParms;
            CardWinParms.hSocket = GetCardHandle();
            CardWinParms.fAttributes = WIN_ATTR_IO_SPACE; // IO Window
            CardWinParms.fAttributes |= ( ( curTable->IOAccess & 1 ) ==
                                          0 ?
                                          WIN_ATTR_16BIT :
                                          0 );
            CardWinParms.fAccessSpeed = WIN_SPEED_USE_WAIT ; // Default To slow mode first. Can we figure out this from tuple???
            CardWinParms.uWindowSize = dwAddrLength ;
            m_IoHandle[iIndex] = CardRequestWindow( GetClientHandle(), &CardWinParms );
            
            CARD_WINDOW_ADDRESS CardWindowAddr;
            CardWindowAddr.hSocket = GetCardHandle();
            CardWindowAddr.uCardAddress = dwBaseAddr;
            CardWindowAddr.uSize = dwAddrLength;
            if( m_IoHandle[iIndex] != NULL &&
                CardMapWindowPhysical( m_IoHandle[iIndex], &CardWindowAddr ) == CERR_SUCCESS )
            {
                // We found index of window that we can use.
                WriteAddressReg( dwBaseAddr, dwAddrLength );
                DWORD dwGranularity = ( CardWindowAddr.uGranularity >=
                                        1 ?
                                        CardWindowAddr.uGranularity :
                                        1 );
                m_IOPhysicalAddr[iIndex] = CardWindowAddr.uWindowPhAddr +
                                           ( ( curTable->IOBase[iIndex] -
                                               CardWindowAddr.uCardAddress ) * dwGranularity );
                if( ( CardWindowAddr.fAttributes & WIN_ATTR_SHARED_IO ) != 0 )
                {
                    PHYSICAL_ADDRESS ioPhysicalBase =
                    {
                        m_IOPhysicalAddr[iIndex], 0
                    };
                    ULONG inIoSpace = 1;
                    if( HalTranslateBusAddress( m_IntefaceType,
                                                m_dwBusNumber,
                                                ioPhysicalBase,
                                                &inIoSpace,
                                                &ioPhysicalBase ) )
                    {
                        DEBUGMSG( ZONE_FUNCTION,
                                  ( TEXT( "CPCEnablePcmcia::ConfigurePcmciaCardResource() : HalTranslateBusAddress - OK\r\n" ) ) );
                        if( inIoSpace &&
                            !ResourceRequest( RESMGR_IOSPACE,
                                              ioPhysicalBase.LowPart,
                                              curTable->IOLength[iIndex] * dwGranularity ) )
                        {
                            DEBUGMSG( ZONE_ERROR,
                                      ( L"PCMCIA!ResourceRequest(RESMGR_IOSPACE) Fails for IO addr 0x%x translate to=%x, length=0x%x !!!!\r\n",
                                        curTable->IOBase[iIndex],
                                        ioPhysicalBase.LowPart,
                                        curTable->IOLength[iIndex] ) );
                            m_IOPhysicalAddr[iIndex] = NULL;
                            return FALSE;
                        }
                    }
                }
            }
            else
            {
                DEBUGMSG(ZONE_WARN,
                      ( TEXT( "CPCEnablePcmcia::ConfigurePcmciaCardResource() : Can't mapped IO (address: 0x%x, length : 0x%x) - Fails\r\n" ),
                      dwBaseAddr,dwAddrLength) );
                return FALSE;
            }
        }
    }
    for( int iIndex = 0;
         iIndex < curTable->NumMemEntries && iIndex < MAX_WINDOWS_RANGES ;
         iIndex++ )
    {
        if( m_MemHandle[iIndex] == NULL )
        {
            CARD_WINDOW_PARMS CardWinParms;
            CardWinParms.hSocket = GetCardHandle();
            CardWinParms.fAttributes = 0; // Memory Window
            CardWinParms.fAccessSpeed = WIN_SPEED_USE_WAIT  ; //Default to slow mode.
            CardWinParms.uWindowSize = curTable->MemLength[iIndex];
            m_MemHandle[iIndex] = CardRequestWindow( GetClientHandle(),
                                                     &CardWinParms );
            CARD_WINDOW_ADDRESS CardWindowAddr;
            CardWindowAddr.hSocket = GetCardHandle();
            CardWindowAddr.uCardAddress = curTable->MemBase[iIndex];
            CardWindowAddr.uSize = curTable->MemLength[iIndex];
            if( m_MemHandle[iIndex] !=
                NULL &&
                CardMapWindowPhysical( m_MemHandle[iIndex],
                                       &CardWindowAddr ) ==
                CERR_SUCCESS )
            {
                // We found index of window that we can use.
                DWORD dwGranularity = ( CardWindowAddr.uGranularity >=
                                        1 ?
                                        CardWindowAddr.uGranularity :
                                        1 );
                m_MemPhysicalAddr[iIndex] = CardWindowAddr.uWindowPhAddr +
                                            ( ( curTable->MemBase[iIndex] -
                                                CardWindowAddr.uCardAddress ) * dwGranularity );
            }
            else
            {
                DEBUGCHK( FALSE );
                return FALSE;
            }
        }
    }
    // Interrupt.
    if( curTable->wIrqMask != 0 && curTable->dwSysIntr == -1 )
    {
        // It requires some interrupt.
        DWORD dwIrqNumber = ( -1 );
        DWORD dwIsSysIntr = 0;
        if( CardRequestIRQLine( GetClientHandle(),
                                GetCardHandle(),
                                curTable->wIrqMask,
                                &dwIrqNumber,
                                &dwIsSysIntr ) != CERR_SUCCESS )
        {
            return FALSE;
        }
        else
        {
            if( dwIsSysIntr )
            {
                m_bIsSysIntr = TRUE;
                curTable->dwSysIntr = dwIrqNumber;
            }
            else
            {
                DWORD IrqFlag = ( ( dwDevFlags& DEVFLAGS_IRQ_EXCLUSIVE ) !=
                                  0 ?
                                  RREXF_REQUEST_EXCLUSIVE :
                                  0 );
                if( !ResourceRequestEx( RESMGR_IRQ, dwIrqNumber, 1, IrqFlag ) )
                {
                    DEBUGMSG( ZONE_ERROR,
                              ( L"PCMCIA!PCIRequestResourcesEx: I/O Resource Manager request for IRQ %d with Flag %x IrqFlag failed\r\n",
                                dwIrqNumber,
                                IrqFlag ) );
                    CardReleaseIRQ( GetClientHandle(), GetCardHandle() );
                    curTable->bIrqNumber = ( BYTE ) - 1;
                    return FALSE;
                } 
                m_bIsSysIntr = FALSE;
                curTable->bIrqNumber = ( BYTE ) dwIrqNumber;
            }
        }
    }
    if( curTable->bIrqNumber != ( BYTE ) - 1 && curTable->dwSysIntr == -1 )
    {
        if( !RequestSysIntr( curTable->bIrqNumber, &( curTable->dwSysIntr ) ) )
        {
            DEBUGCHK( FALSE );
            curTable->dwSysIntr = -1;
            DEBUGMSG( ZONE_ERROR,
                      ( L"PCMCIA!RequestSysIntr Fails Resource Manager request for IRQ %d failed\r\n",
                        curTable->bIrqNumber ) );
            return FALSE;
        }
    }
    CARD_CONFIG_INFO sCardConfigInfo;
    sCardConfigInfo.uVcc = ( ( curTable->VccDescr.ValidMask & PWR_DESCR_NOMINALV ) ?
                             ( UINT8 )
                             curTable->VccDescr.NominalV :
                             ( UINT ) -
                             1 );
    sCardConfigInfo.uVpp1 = ( ( curTable->Vpp1Descr.ValidMask & PWR_DESCR_NOMINALV ) ?
                              ( UINT8 )
                              curTable->Vpp1Descr.NominalV :
                              ( UINT ) -
                              1 );
    sCardConfigInfo.uVpp2 = ( ( curTable->Vpp2Descr.ValidMask & PWR_DESCR_NOMINALV ) ?
                              ( UINT8 )
                              curTable->Vpp2Descr.NominalV :
                              ( UINT ) -
                              1 );
    sCardConfigInfo.hSocket = GetCardHandle();

    sCardConfigInfo.fInterfaceType = ( curTable->IFaceType !=
                                       0 ?
                                       CFG_IFACE_MEMORY_IO :
                                       CFG_IFACE_MEMORY );
    sCardConfigInfo.fAttributes = CFG_ATTR_IRQ_STEERING | CFG_ATTR_NO_SUSPEND_UNLOAD ;
    sCardConfigInfo.fRegisters = CFG_REGISTER_CONFIG | CFG_REGISTER_STATUS  ;
    sCardConfigInfo.uConfigReg = curTable->ConfigIndex | 0x40 ; // Always use Level Mode and Interrupt is same as client.
    //sCardConfigInfo.uConfigReg |=((curTable->IOAccess & 1)!=0?0x20:0); // Active Mode.
    sCardConfigInfo.uStatusReg = FCR_FCSR_IO_IS_8;

    if( CardRequestConfiguration( GetClientHandle(), &sCardConfigInfo ) !=
        CERR_SUCCESS )
    {
        DEBUGMSG( ZONE_ENUM |
                  ZONE_WARN,
                  ( L"CPCEnablePcmcia:: CardRequestConfiguration Fails !!!\r\n" ) );
        return FALSE;
    }
    return TRUE;
}
BOOL CPCEnablePcmcia::ReleasePcmciaCardResources( PPARSED_PCMCFTABLE curTable )
{
    CardReleaseConfiguration( GetClientHandle(), GetCardHandle() );
    for( int iIndex = 0 ;
         iIndex < curTable->NumIOEntries && iIndex < MAX_WINDOWS_RANGES ;
         iIndex++ )
    {
        if( m_IOPhysicalAddr[iIndex] != 0 )
        {
            ResourceRelease( RESMGR_IOSPACE,
                             curTable->IOBase[iIndex],
                             curTable->IOLength[iIndex] );
            m_IOPhysicalAddr[iIndex] = 0;
        }
        if( m_IoHandle[iIndex] != NULL )
        {
            CardReleaseWindow( m_IoHandle[iIndex] );   
            m_IoHandle[iIndex]  = NULL;
        };
    }
    for( iIndex = 0;
         iIndex < curTable->NumMemEntries && iIndex < MAX_WINDOWS_RANGES ;
         iIndex++ )
    {
        if( m_MemHandle[iIndex] != NULL )
        {
            CardReleaseWindow( m_MemHandle[iIndex] );   
            m_MemHandle[iIndex] = NULL;
        }
    }
    if( curTable->bIrqNumber != ( BYTE ) - 1 )
    {
        ResourceRelease( RESMGR_IRQ, curTable->bIrqNumber, 1 );
        CardReleaseIRQ( GetClientHandle(), GetCardHandle() );
        curTable->bIrqNumber = ( BYTE ) ( -1 );
    };
    if( curTable->dwSysIntr != -1 && !m_bIsSysIntr )
    {
        ReleaseSysIntr( curTable->dwSysIntr );
        curTable->dwSysIntr = 0;
    }
    return TRUE;
}
BOOL CPCEnablePcmcia::WriteAddressReg( DWORD dwBaseAddr, DWORD dwLength )
{
    for( int i = FCR_OFFSET_IOB0; i <= FCR_OFFSET_IOB3; i ++ )
    {
        BYTE bRegValue = ( BYTE ) dwBaseAddr;
        if( CardAccessConfigurationRegister( GetClientHandle(),
                                             GetCardHandle(),
                                             CARD_FCR_WRITE,
                                             i,
                                             &bRegValue ) != CERR_SUCCESS )
        {
            return FALSE;
        }
        dwBaseAddr = ( dwBaseAddr >> 8 );
    }
    dwLength -= 1; 
    if( CardAccessConfigurationRegister( GetClientHandle(),
                                         GetCardHandle(),
                                         CARD_FCR_WRITE,
                                         FCR_OFFSET_IOSZ,
                                         ( PBYTE ) & dwLength ) !=
        CERR_SUCCESS )
    {
        return FALSE;
    }
    return TRUE;
}

BOOL CPCEnablePcmcia::CheckPnp()
{
    TCHAR renewPnp[DEVKEY_LEN + 1];
    STATUS status = CreateDeviceID( renewPnp, DEVID_LEN );
    if( status == CERR_SUCCESS )
    {
        renewPnp[DEVKEY_LEN] = 0;
        DWORD dwLength = _tcslen( STR_UNKNOWN_MANID );
        if( _tcscmp( renewPnp, m_DeviceId ) !=
            0 &&
            _tcsncmp( renewPnp,
                      STR_UNKNOWN_MANID,
                      _tcslen( STR_UNKNOWN_MANID ) ) !=
            0 )
        {
            DEBUGMSG( ZONE_ERROR,
                      ( L"CPCEnablePcmcia::ThreadRun:Pnp has change during suspend / Resume. Card Will be Force Remove and Inserted. old=%s,new=%s\r\n",
                        m_DeviceId,
                        renewPnp ) );
            CardResetFunction( GetClientHandle(), GetCardHandle() );
        }
    }
    else
    {
        DEBUGMSG( ZONE_ERROR,
                  ( L"CPCEnablePcmcia::ThreadRun:CreateDewviceID return Error =%d!!!\r\n",
                    status ) );
    }
    return TRUE;
}

//
// VarToFixed - convert the bytes of a variable length field into a UINT32
//              and return fixed length value.
//
UINT32 CPCEnablePcmcia::VarToFixed( UINT32 VarSize, PUCHAR pBytes ) const
{
    UINT32 Fixed = 0;

    //
    // Parse the bytes starting from the MSB and shift them into place.
    //
    while( VarSize )
    {
        VarSize--;
        Fixed <<= 8;
        Fixed |= ( UINT32 ) pBytes[VarSize];
    }
    return Fixed;
}


//
// ParseCfTable - Read the CISTPL_CFTABLE_ENTRY tuples from the CIS and format them into
// an array of PARSED_PCMCFTABLE structures.
//
STATUS CPCEnablePcmcia::ParseCfTable( PVOID pBuf,
                                      DWORD dwLength,
                                      PPARSED_PCMCFTABLE pCfTable,
                                      PPARSED_PCMCFTABLE pDefaultCfTable )
{
    if( pBuf == NULL || dwLength == 0 || pCfTable == NULL )
    {
        return CERR_NO_MORE_ITEMS;
    }
    PBYTE pCIS = ( PBYTE ) pBuf;
    // Default all the fields of the current parsed entry if necessary
    //
    memset( pCfTable, 0, sizeof( PARSED_PCMCFTABLE ) );
    pCfTable->bIrqNumber = -1;
    pCfTable->dwSysIntr = ( DWORD ) - 1;

    if( ( *pCIS & 0x40 ) == 0 )
    {
        // It is not default, need default it first.
        if( pDefaultCfTable && pDefaultCfTable->ContainsDefaults ) // Default available.
            *pCfTable = *pDefaultCfTable;
    }
    // !! The ContainsDefaults field is now deprecated and should be
    // !! set to non-zero in every parsed entry.
    //
    pCfTable->ContainsDefaults = *pCIS & 0x40;
    pCfTable->ConfigIndex = *pCIS & 0x3F;
    // Parse the interface type byte if present
    //

    //
    // Parse the interface type byte if present
    //
    if( *pCIS & 0x80 )
    {
        pCIS++;
        if( dwLength-- == 0 )
            return CERR_NO_MORE_ITEMS;
        pCfTable->IFacePresent = TRUE;
        pCfTable->IFaceType = *pCIS & 0x0F;
        pCfTable->BVDActive = *pCIS & 0x10;
        pCfTable->WPActive = *pCIS & 0x20;
        pCfTable->ReadyActive = *pCIS & 0x40;
        pCfTable->WaitRequired = *pCIS & 0x80;
    }
    else
    {
        //pCfTable->IFacePresent = FALSE;
    }
    pCIS++;
    if( dwLength-- == 0 )
    {
        return CERR_NO_MORE_ITEMS;
    }

    //
    // Parse the feature select byte
    //

    //
    // Power requirements present
    //
    BYTE bmPowerPresent = *pCIS & 0x03;

    //
    // Timing description present
    //
    BYTE TimingPresent = *pCIS & 0x04;

    //
    // I/O space descriptions present
    //
    BYTE IOPresent = *pCIS & 0x08;
    //
    // Irq description present
    //
    BYTE IrqPresent = *pCIS & 0x10;
    //
    // Memory description present
    //
    BYTE MemPresent = *pCIS & 0x60;

    pCIS++;
    if( dwLength-- == 0 )
    {
        return CERR_NO_MORE_ITEMS;
    }

    //
    // Parse the power description structures if present
    //
    DWORD dwParsedLength;
    if( bmPowerPresent >= 1 )
    {
        pCfTable->VccDescr.ValidMask = 0xFF;
        pCIS += ( dwParsedLength = ParseVoltageDescr( pCIS,
                                                      &pCfTable->VccDescr ) );
        if( dwParsedLength > dwLength )
            return CERR_NO_MORE_ITEMS;
        dwLength -= dwParsedLength;
        for( int i = 0; i < 3; i++ )
        {
            if( ( PWR_DESCR_NOMINALV << i ) & pCfTable->VccDescr.ValidMask )
                pCfTable->VccDescr.ValidMask |= ( PWR_AVAIL_NOMINALV << i );
        }
    }
    if( bmPowerPresent >= 2 )
    {
        pCfTable->Vpp1Descr.ValidMask = 0xFF;
        pCIS += ( dwParsedLength = ParseVoltageDescr( pCIS,
                                                      &pCfTable->Vpp1Descr ) );
        if( dwParsedLength > dwLength )
            return CERR_NO_MORE_ITEMS;
        dwLength -= dwParsedLength;

        for( int i = 0; i < 3; i++ )
        {
            if( ( PWR_DESCR_NOMINALV << i ) & pCfTable->Vpp1Descr.ValidMask )
                pCfTable->Vpp1Descr.ValidMask |= ( PWR_AVAIL_NOMINALV << i );
        }

        pCfTable->Vpp2Descr.ValidMask = 0xFF;
        if( bmPowerPresent == 3 )
        {
            pCIS += ( dwParsedLength = ParseVoltageDescr( pCIS,
                                                          &pCfTable->Vpp2Descr ) );
            if( dwParsedLength > dwLength )
                return CERR_NO_MORE_ITEMS;
            dwLength -= dwParsedLength;
            for( i = 0; i < 3; i++ )
            {
                if( ( PWR_DESCR_NOMINALV << i ) & pCfTable->Vpp1Descr.ValidMask )
                    pCfTable->Vpp2Descr.ValidMask |= ( PWR_AVAIL_NOMINALV <<
                                                       i );
            }
        }
        else
            pCfTable->Vpp2Descr = pCfTable->Vpp1Descr;
    }


    //
    // Skip the timing information
    //
    if( TimingPresent )
    {
        BYTE bTmp = *pCIS;
        pCIS++;
        if( dwLength-- == 0 )
            return CERR_NO_MORE_ITEMS;

        if( ( bTmp & 0x03 ) != 0x03 )
        {
            while( *pCIS++ & TPCE_PD_EXT )
            {
                if( dwLength-- == 0 )
                    return CERR_NO_MORE_ITEMS;
            };
        }
        if( ( bTmp & 0x1C ) != 0x1C )
        {
            while( *pCIS++ & TPCE_PD_EXT )
            {
                if( dwLength-- == 0 )
                    return CERR_NO_MORE_ITEMS;
            };
        }
    }


    //
    // Process the I/O address ranges (up to MAX_IO_RANGES)
    //
    if( IOPresent )
    {
        pCfTable->NumIOAddrLines = *pCIS & 0x1F;
        pCfTable->IOAccess = ( *pCIS & 0x60 ) >> 5;   // type of access allowed
        if( *pCIS & 0x80 )
        {
            // range present bit
            UINT8 AddrSize, LengthSize, NumOfEntries;
            pCIS++;
            if( dwLength-- == 0 )
                return CERR_NO_MORE_ITEMS;
            pCfTable->NumIOEntries = NumOfEntries = ( *pCIS & 0x0F ) + 1;
            if( pCfTable->NumIOEntries > MAX_WINDOWS_RANGES )
            {
                DEBUGMSG( ZONE_TUPLE |
                          ZONE_WARN,
                          ( TEXT( "PCMCIA ParseCfTable: too many I/O ranges; ignoring some.\n" ) ) );
                pCfTable->NumIOEntries = MAX_IO_RANGES;
            }
            AddrSize = ( *pCIS & 0x30 ) >> 4;
            if( AddrSize == 3 )
            {
                AddrSize = 4;
            }
            LengthSize = ( *pCIS & 0xC0 ) >> 6;
            if( LengthSize == 3 )
            {
                LengthSize = 4;
            }
            pCIS++;
            if( dwLength-- == 0 )
                return CERR_NO_MORE_ITEMS;

            for( int i = 0; i < NumOfEntries; i++ )
            {
                if( i < MAX_WINDOWS_RANGES )
                    pCfTable->IOBase[i] = VarToFixed( AddrSize, pCIS );
                pCIS += AddrSize;
                if( dwLength < AddrSize )
                    return CERR_NO_MORE_ITEMS;
                dwLength -= AddrSize;
                if( i < MAX_WINDOWS_RANGES )
                    pCfTable->IOLength[i] = VarToFixed( LengthSize, pCIS ) +
                                            1;
                pCIS += LengthSize;
                if( dwLength < LengthSize )
                    return CERR_NO_MORE_ITEMS;
                dwLength -= LengthSize;
            }
        }
        else
        {
            // NumIOEntries is a misnomer; it's really the number of IO ranges.
            pCfTable->NumIOEntries = 1;
            pCfTable->IOBase[0] = 0;
            pCfTable->IOLength[0] = ( 1 << pCfTable->NumIOAddrLines );
        }
    }
    //
    // Irq description present
    //
    if( IrqPresent )
    {
        if( ( *pCIS & 0x10 ) == 0 )
        {
            // Mask Bit is set
            pCfTable->wIrqMask = ( 1 << ( *pCIS & 0xf ) );
            pCIS++;
            if( dwLength-- == 0 )
                return CERR_NO_MORE_ITEMS;
        }
        else
        {
            if( dwLength < 3 )
                return CERR_NO_MORE_ITEMS;
            pCIS++;
            pCfTable->wIrqMask = *pCIS + ( ( ( WORD ) * ( pCIS + 1 ) ) << 8 );
            pCIS += 2;
            dwLength -= 3;
        };
    }
    if( MemPresent )
    {
        if( MemPresent == 0x20 )
        {
            //Signle 2 byte length present
            if( dwLength < 2 )
                return CERR_NO_MORE_ITEMS;
            UINT32 uSize = *pCIS + ( *( pCIS + 1 ) << 8 );
            DEBUGCHK( uSize != 0 );
            pCIS += 2;
            pCfTable->NumMemEntries = 1;
            pCfTable->MemBase[0] = 0;
            pCfTable->MemLength[0] = uSize * 0x100;
            dwLength -= 2;
        }
        else if( MemPresent == 0x40 )
        {
            //Signle 2 byte length & base present
            if( dwLength < 4 )
                return CERR_NO_MORE_ITEMS;
            UINT32 uSize = *pCIS + ( *( pCIS + 1 ) << 8 );
            DEBUGCHK( uSize != 0 );
            pCIS += 2;
            UINT32 uBase = *pCIS + ( *( pCIS + 1 ) << 8 );
            pCIS += 2;
            pCfTable->NumMemEntries = 1;
            pCfTable->MemLength[0] = uSize * 0x100;
            pCfTable->MemBase[0] = uBase * 0x100;
        }
        else
        {
            // Memory Descriptor structure present.
            UINT8 AddrSize, LengthSize, bHostAddress, NumOfEntries;
            pCfTable->NumMemEntries = NumOfEntries = ( *pCIS & 0x07 ) + 1;
            LengthSize = ( ( *pCIS & 0x18 ) >> 3 );
            AddrSize = ( ( *pCIS & 0x60 ) >> 5 );
            bHostAddress = ( *pCIS & 0x80 );
            if( pCfTable->NumMemEntries > MAX_WINDOWS_RANGES )
            {
                DEBUGMSG( ZONE_TUPLE |
                          ZONE_WARN,
                          ( TEXT( "PCMCIA ParseCfTable: too many I/O ranges; ignoring some.\n" ) ) );
                pCfTable->NumMemEntries = MAX_IO_RANGES;
            }
            pCIS++;
            if( dwLength-- == 0 )
                return CERR_NO_MORE_ITEMS;
            for( int i = 0 ; i < NumOfEntries; i ++ )
            {
                UINT32 uSize = 0;
                UINT32 uBase = 0;
                UINT32 uShift = 0;
                for( int NumOfBytes = 0;
                     NumOfBytes < LengthSize;
                     NumOfBytes++ )
                {
                    uSize += ( ( *pCIS ) << uShift );
                    pCIS++;
                    uShift += 8;
                    if( dwLength-- == 0 )
                        return CERR_NO_MORE_ITEMS;
                }
                for( NumOfBytes = 0; NumOfBytes < AddrSize; NumOfBytes++ )
                {
                    uBase += ( ( *pCIS ) << uShift );
                    pCIS++;
                    uShift += 8;
                    if( dwLength-- == 0 )
                        return CERR_NO_MORE_ITEMS;
                }
                // We ignore host address. but we have to parsed it.
                for( NumOfBytes = 0;
                     NumOfBytes < AddrSize && bHostAddress != 0;
                     NumOfBytes++ )
                {
                    pCIS++;
                    if( dwLength-- == 0 )
                        return CERR_NO_MORE_ITEMS;
                }
                DEBUGCHK( uSize != 0 );
                if( i < MAX_WINDOWS_RANGES )
                {
                    pCfTable->MemBase[i] = uBase * 0x100;
                    pCfTable->MemLength[i] = uSize * 0x100;
                }
            }
        }
    }

    // else it has already been either defaulted or memset to zeros.
    return CERR_SUCCESS;
}   // ParseCfTable
CPCEnableCard* CreatePcmciaEnabler( CARD_SOCKET_HANDLE hSocket,
                                    CARD_CLIENT_HANDLE hClient )
{
    return new CPCEnablePcmcia( hSocket, hClient );
}

