//
// 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:

cbcarden.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 "cardsv2.h"
#include "socksv2.h"
#include <ceddk.h>
#include "CBCardEn.h"
#include "pcib.h"
#include "debug.h"

CPCEnableCBCard::CPCEnableCBCard( CARD_SOCKET_HANDLE hSocket,
                                  CARD_CLIENT_HANDLE hClient ) : CPCEnableCard( hSocket,
                                                                                hClient ),
                                                                 CMiniThread( 0,
                                                                              TRUE )
{
    m_bTerminated = FALSE;
    m_Configured = FALSE;
    m_pPCIEnum = 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 < PCI_TYPE0_ADDRESSES; dwIndex++ )
    {
        m_WinHandle[dwIndex] = NULL;
    }
};

CPCEnableCBCard::~CPCEnableCBCard()
{
    m_bTerminated = TRUE;
    ThreadStart();
    SetEvent( m_EventList[TERMINAGE_THREAD] );
    PostThreadMessage( GetThreadId(), WM_QUIT, 0, 0 );
    WaitThreadComplete( INFINITE );// Wait for Half-Second thread termination.
    UnLoadDriver();
    CardReleaseConfiguration( GetClientHandle(), GetCardHandle() );
    UnconfigureCBCard();
    for( DWORD dwIndex = 0; dwIndex < EVENT_NUMBER; dwIndex++ )
    {
        if( m_EventList[dwIndex] != NULL )
        {
            CloseHandle( m_EventList[dwIndex] );
        }
    }
};
STATUS CPCEnableCBCard::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;
      default:
        status = CPCEnableCard::HandleEvent( cEvent, pParms );
        break;
    }
    return status;
}

BOOL CPCEnableCBCard::LoadDriver()
{
    Lock();
    BOOL bReturn = FALSE;
    if( m_pPCIEnum )
    {
        if( m_pPCIEnum->LauchDriver() == TRUE )
        {
            // We success loaded driver
            bReturn = TRUE;
        }
        else
        {
            // We can not initialize it. delete it.
            delete m_pPCIEnum;
            m_pPCIEnum = NULL;
        }
    }
    Unlock();
    return bReturn;
}
void CPCEnableCBCard::UnLoadDriver()
{
    Lock();
    if( m_pPCIEnum )
    {
        delete m_pPCIEnum;
        m_pPCIEnum = NULL;
    }
    Unlock();
}
DWORD CPCEnableCBCard::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 )
            {
                //Sleep(100); // Wait for Card Settle First.
                BOOL bConfigured = FALSE;
                if( ConfigureCBCard() )
                {
                    if( !LoadDriver() )
                    {
                        // If loader driver fails. Power down the socket if we could
                        UnconfigureCBCard();
                    }
                    else
                        bConfigured = TRUE;
                }
                if( !bConfigured && !m_bTerminated )
                {
                    // Kill this object.
                    CardRequestDisable( GetClientHandle(), GetCardHandle() );
                    Unlock();
                    BOOL bReturn = QueryDriverName( m_CBCardCfgInfo.m_DeviceId );
                    Lock();
                    if( bReturn )
                        CardResetFunction( GetClientHandle(), GetCardHandle() );
                    m_bTerminated = TRUE;
                }
            }
            break;
          case WAIT_OBJECT_0+TERMINAGE_THREAD:
            m_bTerminated = TRUE;
            break;
          case WAIT_FAILED:
            DEBUGMSG( ZONE_ERROR,
                      ( L"CPCEnableCBCard::ThreadRun:Wait Fails with Error code %d\r\n",
                        GetLastError() ) );
            break;
        };
        Unlock();
    };
    return 0;
};
BOOL CPCEnableCBCard::LoadDeviceRegistry()
{
    if( m_pPCIEnum == NULL )
    {
        m_pPCIEnum = new CPCIEnum( m_dwBusNumber,
                                   m_SlotNumber,
                                   &m_CBCardCfgInfo,
                                   this );
        if( m_pPCIEnum && m_pPCIEnum->Init() )
        {
            return TRUE;
        }
    }
    DEBUGMSG( ZONE_ERROR,
              ( L"CPCEnableCBCard::LoadDeviceRegistry Fails!!!\r\n" ) );            
    return FALSE;
}
#define TUPLEBUFFER_SIZE 0x200 
BOOL CPCEnableCBCard::ConfigureCBCard()
{
    if( !m_Configured )
    {
        CARD_TUPLE_PARMS TupleParms;
        BYTE dataBuffer[TUPLEBUFFER_SIZE]; // Assume Maximun Turple Length is 512;
        memset( &m_CBCardCfgInfo, 0, sizeof( m_CBCardCfgInfo ) );
        if( CreateDeviceID( m_CBCardCfgInfo.m_DeviceId, DEVID_LEN ) ==
            CERR_SUCCESS )
        {
            m_CBCardCfgInfo.m_DeviceId[DEVID_LEN] = 0;
        }
        else
        {
            DEBUGMSG( ZONE_ERROR,
                      ( L"CPCEnableCBCard::ConfigureCBCard:CreateDeviceID Fails!!!\r\n" ) );            
            m_CBCardCfgInfo.m_DeviceId[0] = 0;
        }
        if( FindTuple( &TupleParms, TRUE, CISTPL_MANFID ) &&
            GetTupleData( &TupleParms,
                          dataBuffer,
                          TUPLEBUFFER_SIZE ) )
        {
            PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
            if( pData->uDataLen >= 4 ) // At lest 4 byte.
                m_CBCardCfgInfo.ManfID = ( DWORD )
                                         dataBuffer[sizeof( CARD_DATA_PARMS )] +
                                         ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 1] <<
                                           8 ) +
                                         ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 2] <<
                                           16 ) +
                                         ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 3] <<
                                           24 );
        }
        if( FindTuple( &TupleParms, TRUE, CISTPL_FUNCID ) &&
            GetTupleData( &TupleParms,
                          dataBuffer,
                          TUPLEBUFFER_SIZE ) )
        {
            PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
            if( pData->uDataLen >= 1 ) // At lest 4 byte.
                m_CBCardCfgInfo.FuncID = dataBuffer[sizeof( CARD_DATA_PARMS )];
        };
        //
        // Load Driver reuqirement for this device.
        //
        if( LoadDeviceRegistry() )
        {
            // Get BAR info From tuple.
            if( FindTuple( &TupleParms, TRUE, CISTPL_BAR ) &&
                GetTupleData( &TupleParms,
                              dataBuffer,
                              TUPLEBUFFER_SIZE ) )
            {
                do
                {
                    PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
                    if( pData->uDataLen >= 6 )
                    {
                        // PC Card Standard Volume 4 Metaformat spec 3.3.1 
                        DWORD dwSize = ( DWORD )
                                       dataBuffer[sizeof( CARD_DATA_PARMS ) + 2] +
                                       ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 3] <<
                                         8 ) +
                                       +
                                       ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 4] <<
                                         16 ) +
                                       ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 5] <<
                                         24 );
                        BYTE bBarType = dataBuffer[sizeof( CARD_DATA_PARMS )];
                        if( ( bBarType & 7 ) != 0 ||
                            ( ( bBarType & 7 ) - 1 < PCI_TYPE0_ADDRESSES ) )
                        {
                            m_CBCardCfgInfo.m_BARSize[( bBarType & 7 ) - 1] = dwSize;
                            m_CBCardCfgInfo.m_BARType[( bBarType & 7 ) - 1] = ( ( bBarType & 0x10 ) !=
                                                                                0 );
                        }
                    }
                }
                while( FindTuple( &TupleParms, FALSE, CISTPL_BAR ) &&
                       GetTupleData( &TupleParms,
                                     dataBuffer,
                                     TUPLEBUFFER_SIZE ) );
            }
            else
            {
                // We can not get BAR tuple. Do not use it.
                for( DWORD dwIndex = 0;
                     dwIndex < PCI_TYPE0_ADDRESSES;
                     dwIndex++ )
                    m_CBCardCfgInfo.m_BARSize[dwIndex] = 0 ;
            };
            DWORD dwRegValue = 0;
            if (!(m_pPCIEnum && m_pPCIEnum->GetRegValue(CARDBUS_IGNORE_TUPLE_VALNAME, (LPBYTE)&dwRegValue, sizeof(DWORD)) && dwRegValue!=0) && // If Registry said Ignoring it
                    FindTuple( &TupleParms, TRUE, CISTPL_CONFIG_CB ) && GetTupleData( &TupleParms,dataBuffer, TUPLEBUFFER_SIZE ) )
            {
                PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) dataBuffer;
                if( pData->uDataLen >= 5 )
                {
                    BYTE lastTuple = dataBuffer[sizeof( CARD_DATA_PARMS )];
                    DWORD dwStatusReg = ( DWORD )
                                        dataBuffer[sizeof( CARD_DATA_PARMS ) + 1] +
                                        ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 2] <<
                                          8 ) +
                                        +
                                        ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 3] <<
                                          16 ) +
                                        ( dataBuffer[sizeof( CARD_DATA_PARMS ) + 2] <<
                                          24 );
                    // We do not use status register. So Ignore this value.
                    PARSED_CBCFTABLE curTable, DefaultTable;
                    DefaultTable.ContainsDefaults = 0;
                    if( FindTuple( &TupleParms, TRUE, CISTPL_CFTABLE_ENTRY_CB ) &&
                        GetTupleData( &TupleParms,
                                      dataBuffer,
                                      TUPLEBUFFER_SIZE ) )
                    {
                        do
                        {
                            pData = ( PCARD_DATA_PARMS ) dataBuffer;
                            DWORD dwLength = min( TUPLEBUFFER_SIZE -
                                                  sizeof( PCARD_DATA_PARMS ),
                                                  pData->uDataLen );
                            if( ParseCfTable( ( PVOID ) ( pData + 1 ),
                                              dwLength,
                                              &curTable,
                                              &DefaultTable ) == CERR_SUCCESS )
                            {
                                if( /*RequiredResourceMatch(&curTable) &&*/ ConfigureCBCardResource( &curTable ) )
                                {
                                    m_Configured = TRUE;
                                    return TRUE;
                                }
                                else
                                    UnconfigureCBCard();
                            }
                            else
                                DEBUGCHK( FALSE );
                        }
                        while( FindTuple( &TupleParms,
                                          FALSE,
                                          CISTPL_CFTABLE_ENTRY_CB ) &&
                               GetTupleData( &TupleParms,
                                             dataBuffer,
                                             TUPLEBUFFER_SIZE ) );
                    };
                }
            }
            else
            {
                // We do not have Configuration Tuple. Conifig it with default.
                PARSED_CBCFTABLE curTable;
                CreateDefaultInfo( &curTable );
                if( /*RequiredResourceMatch(&curTable) &&*/ ConfigureCBCardResource( &curTable ) )
                {
                    m_Configured = TRUE;
                    return TRUE;
                }
                else
                    UnconfigureCBCard();
            }
        };
    }
    return m_Configured;
}

#define PCI_CONFIG_CARDBUS_CIS  (10 << 2)
#define PCI_CONFIG_BASE0            (4 << 2)
BOOL CPCEnableCBCard::ConfigureCBCardResource( PPARSED_CBCFTABLE curTable )
{
    // Checking Power Compatable between Socket and Card.
    CARD_CONFIG_INFO sCardConfigInfo;
    sCardConfigInfo.uVcc = ( ( curTable->VccDescr.ValidMask & 1 ) ?
                             ( UINT8 ) curTable->VccDescr.NominalV :
                             ( UINT ) -
                             1 );
    sCardConfigInfo.uVpp1 = ( ( curTable->Vpp1Descr.ValidMask & 1 ) ?
                              ( UINT8 ) curTable->Vpp1Descr.NominalV :
                              ( UINT ) -
                              1 );
    sCardConfigInfo.uVpp2 = ( ( curTable->Vpp2Descr.ValidMask & 1 ) ?
                              ( UINT8 ) curTable->Vpp2Descr.NominalV :
                              ( UINT ) -
                              1 );
    sCardConfigInfo.hSocket = GetCardHandle();
    if( CardRequestConfiguration( GetClientHandle(), &sCardConfigInfo ) !=
        CERR_SUCCESS )
    {
        DEBUGCHK( FALSE );
        return FALSE;
    }
    // Configutre the Windows.
    DWORD dwCIS = PCIConfig_Read( PCI_CONFIG_CARDBUS_CIS );
    BYTE uCisOption = ( BYTE ) ( dwCIS  & 7 );
    DWORD dwCISBar = -1;
    if( uCisOption != 0 && uCisOption != 7 )
    {
        // CIS is mapped to one or BAR.
        dwCISBar = uCisOption - 1;
    }
    else
    {
        dwCISBar = ( DWORD ) - 1;
    }
    // We only allow user to configure window
    if( m_pPCIEnum && m_pPCIEnum->ConfigGetUserResource() )
    {
        BOOL bReturn = m_pPCIEnum->ConfigSetUserResource();
        DEBUGCHK( bReturn == TRUE );
    }
    else
    {
        for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
        {
            if( dwIndex != dwCISBar )
            {
                DWORD dwOldAddress = PCIConfig_Read( dwIndex*4 +
                                                     PCI_CONFIG_BASE0 );
                BOOL bAddressValid = FALSE;
                PCIConfig_Write( dwIndex * 4 + PCI_CONFIG_BASE0, 0xFFFFFFFF );
                DWORD BaseAddress = PCIConfig_Read( dwIndex*4 +
                                                    PCI_CONFIG_BASE0 );
                BOOL bIsIo = ( ( BaseAddress& PCI_ADDRESS_IO_SPACE ) != 0 ); // It should be alwas as memory.
                DWORD Size = ~( BaseAddress& PCI_ADDRESS_MEMORY_ADDRESS_MASK );
                BOOL SizeFound = ( BaseAddress != 0 ) &&
                                 ( BaseAddress != 0xFFFFFFFF ) &&
                                 ( ( ( Size + 1 ) & Size ) == 0 );
                if( SizeFound )
                {
                    Size += 1;
                    CARD_WINDOW_PARMS CardWinParms;
                    CardWinParms.hSocket = GetCardHandle();
                    CardWinParms.fAttributes = ( bIsIo ?
                                                 WIN_ATTR_IO_SPACE :
                                                 0 ) ; // Memory Window
                    CardWinParms.fAccessSpeed = 0 ; // This is not used by cardbus.
                    CardWinParms.uWindowSize = Size;
                    m_WinHandle[dwIndex] = CardRequestWindow( GetClientHandle(),
                                                              &CardWinParms );

                    CARD_WINDOW_ADDRESS CardWindowAddr;
                    CardWindowAddr.hSocket = GetCardHandle();
                    CardWindowAddr.uCardAddress = 0;
                    CardWindowAddr.uSize = Size;
                    if( m_WinHandle[dwIndex] !=
                        NULL &&
                        CardMapWindowPhysical( m_WinHandle[dwIndex],
                                               &CardWindowAddr ) ==
                        CERR_SUCCESS )
                    {
                        // We found index of window that we can use.
                        PCIConfig_Write( dwIndex * 4 + PCI_CONFIG_BASE0,
                                         CardWindowAddr.uWindowPhAddr );
                        bAddressValid = TRUE;
                    }
                }
                if( !bAddressValid )
                    PCIConfig_Write( dwIndex * 4 + PCI_CONFIG_BASE0,
                                     dwOldAddress );
            }
        }
    }
    BOOL bReturn = TRUE;
    BOOL bIrqMapped = FALSE;
    m_bIrq = -1;
    // For Irq.
    if( curTable->wIrqMask )
    {
        // This device need interrupt.
        DWORD dwIrq = -1;
        DWORD dwIsSysIntr = 0;
        STATUS status = CardRequestIRQLine( GetClientHandle(),
                                            GetCardHandle(),
                                            curTable->wIrqMask,
                                            &dwIrq,
                                            &dwIsSysIntr ) ;
        if( status == CERR_SUCCESS )
        {
            // This is valid adapter info on this contition.
            if( dwIsSysIntr )
            {
                // CardBus Require IRQ support.
                CardReleaseIRQ( GetClientHandle(), GetCardHandle() );
                bReturn = FALSE;
            }
            else
            {
                //update the IRQ here.
                m_bIrq = ( BYTE ) dwIrq;
                DWORD dwValue = ( PCIConfig_Read( 15 * sizeof( DWORD ) ) & ~0xff ) |
                                m_bIrq ;
                PCIConfig_Write( 15 * sizeof( DWORD ), dwValue );
                bIrqMapped = TRUE;
            }
        }
        else
            bReturn = FALSE;
    }
    if( bReturn == FALSE )
    {
        // If it is fails, we need unmap mapped window for release resource.
        for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
        {
            if( m_WinHandle[dwIndex] != NULL )
                UnMapBar( dwIndex );
        }
        if( bIrqMapped )
        {
            CardReleaseIRQ( GetClientHandle(), GetCardHandle() );
            m_bIrq = -1;
        }
    }
    return bReturn;
}
BOOL CPCEnableCBCard::UnconfigureCBCard()
{
    // Check IO
    for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
    {
        if( m_WinHandle[dwIndex] != NULL )
        {
            CardReleaseWindow( m_WinHandle[dwIndex] );
            m_WinHandle[dwIndex] = NULL;
            PCIConfig_Write( dwIndex * 4 + PCI_CONFIG_BASE0, 0 );
        }
    }
    if( m_bIrq != -1 )
    {
        CardReleaseIRQ( GetClientHandle(), GetCardHandle() );
        DWORD dwValue = ( PCIConfig_Read( 15 * sizeof( DWORD ) ) & ~0xff ) |
                        0xff ;
        PCIConfig_Write( 15 * sizeof( DWORD ), dwValue );
        m_bIrq = -1;
    }
    return TRUE;
}
BOOL CPCEnableCBCard::UnMapBar( DWORD dwIndex )
{
    if( dwIndex < PCI_TYPE0_ADDRESSES )
    {
        if( m_WinHandle[dwIndex] != NULL )
        {
            CardReleaseWindow( m_WinHandle[dwIndex] );
            m_WinHandle[dwIndex] = NULL;
            PCIConfig_Write( dwIndex * 4 + PCI_CONFIG_BASE0, 0 );
            return TRUE;
        }
    }
    return FALSE;
};
void CPCEnableCBCard::CreateDefaultInfo( PPARSED_CBCFTABLE pCfTable )
{
    DEBUGCHK( pCfTable );
    memset( pCfTable, 0, sizeof( PARSED_CBCFTABLE ) );
    // Scan Bar
    BYTE bShiftBit = 1;
    for( DWORD dwIndex = 0; dwIndex < PCI_TYPE0_ADDRESSES; dwIndex++ )
    {
        DWORD dwPos = dwIndex*sizeof( DWORD ) + PCI_CONFIG_BASE0;
        // GetValue
        DWORD dwOldValue = PCIConfig_Read( dwPos );
        PCIConfig_Write( dwPos, ( DWORD ) - 1 );
        DWORD dwSizeValue = PCIConfig_Read( dwPos );
        PCIConfig_Write( dwPos, dwOldValue );

        if( dwSizeValue & 1 )
        {
            // IO
            dwSizeValue &= 0xfffffffc;
            m_CBCardCfgInfo.m_BARType[dwIndex] = 1;// for IO;
        }
        else
        {
            dwSizeValue &= 0xfffffff0;
            m_CBCardCfgInfo.m_BARType[dwIndex] = 0;// for Memory
        }
        m_CBCardCfgInfo.m_BARSize[dwIndex] = ( ~dwSizeValue ) + 1;
        if( m_CBCardCfgInfo.m_BARSize[dwIndex] != 0 )
        {
            if( m_CBCardCfgInfo.m_BARType[dwIndex] != 0 )
                pCfTable->bUsedBarForIo = bShiftBit;
            else
                pCfTable->bUsedBarForMem = bShiftBit;
        }
        bShiftBit <<= 1;
    }
    DWORD dwValue = PCIConfig_Read( 15 * sizeof( DWORD ) );
    if( ( dwValue & 0xff00 ) != 0 )
    {
        // If there is interrupt PIN value, Assume it need irq
        pCfTable->wIrqMask = ( WORD ) - 1;
    };
}

STATUS CPCEnableCBCard::ParseCfTable( PVOID pBuf,
                                      DWORD dwLength,
                                      PPARSED_CBCFTABLE pCfTable,
                                      PPARSED_CBCFTABLE 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_CBCFTABLE ) );
    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
    //
    pCIS++;
    if( dwLength-- == 0 )
    {
        return CERR_NO_MORE_ITEMS;
    }


    //
    // Parse the feature select byte
    //

    //
    // Power requirements present
    //
    BYTE bmPowerPresent = *pCIS & 0x03;

    //
    // I/O space descriptions present
    //
    BYTE IOPresent = *pCIS & 0x08;
    //
    // Irq description present
    //
    BYTE IrqPresent = *pCIS & 0x10;
    //
    // Memory description present
    //
    BYTE MemPresent = *pCIS & 0x20;
    //
    // Misc description present
    //
    BYTE MiscPresent = *pCIS & 0x80;


    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;
    }


    //
    // Process the I/O address ranges (up to MAX_IO_RANGES)
    //
    if( IOPresent )
    {
        pCfTable->bUsedBarForIo = ( ( *pCIS ) >> 1 ) & 0x1f;
        pCIS++;
        if( dwLength-- == 0 )
            return CERR_NO_MORE_ITEMS;
    }
    //
    // 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;
        };
    }
    //
    // Memory description present
    //
    if( MemPresent )
    {
        pCfTable->bUsedBarForMem = ( ( *pCIS ) >> 1 ) & 0x7f; // 7 Bit. INCLUDE ROM.
        pCIS++;
        if( dwLength-- == 0 )
            return CERR_NO_MORE_ITEMS;
    }
    //
    // Misc description present
    //
    // We do not card Miscellaneous feature feild for now.
    if( pCfTable->ContainsDefaults && pDefaultCfTable ) // Make a default copy
    {
        *pDefaultCfTable = *pCfTable;
    }

    return CERR_SUCCESS;
}
//
// ConvertVoltage - normalize a single CISTPL_CFTABLE_ENTRY voltage entry and
//                  return its length in bytes
//
UINT CPCEnableCBCard::ConvertVoltage( PUCHAR pCIS, PUSHORT pDescr )
{
    UINT Length;
    SHORT power;
    USHORT value;
    UCHAR MantissaExponentByte;
    UCHAR ExtensionByte;
    const USHORT VoltageConversionTable[16] =
    {
        10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90
    };


    Length = 1;
    power = 1;
    MantissaExponentByte = pCIS[0];
    ExtensionByte = pCIS[1];
    value = VoltageConversionTable[( MantissaExponentByte >> 3 ) & 0x0f];

    if( ( MantissaExponentByte & TPCE_PD_EXT ) && ( ExtensionByte < 100 ) )
    {
        value = ( 100 * value + ( ExtensionByte & 0x7f ) );
        power += 2;
    }

    power = ( MantissaExponentByte & 0x07 ) - 4 - power;

    while( power > 0 )
    {
        value *= 10;
        power--;
    }

    while( power < 0 )
    {
        value /= 10;
        power++;
    }

    *pDescr = value;

    //
    // Skip any subsequent extension bytes for now.
    //
    while( *pCIS & TPCE_PD_EXT )
    {
        Length++;
        pCIS++;
    }

    return Length;
}   // ConvertVoltage



//
UINT CPCEnableCBCard::ParseVoltageDescr( PUCHAR pVolt, PPOWER_DESCR pDescr )
{
    UINT Length;
    UINT Mask;
    UINT i;
    PUSHORT pDVolt;

    Length = 1;
    Mask = pDescr->ValidMask = ( UINT ) * pVolt;
    pVolt++;
    pDVolt = &( pDescr->NominalV );

    while( Mask )
    {
        if( Mask & 1 )
        {
            i = ConvertVoltage( pVolt, pDVolt );
            Length += i;
            pVolt += i;
        }
        else
        {
            *pDVolt = 0;
        }
        pDVolt++;
        Mask >>= 1;
    }

    return Length;
}   // ParseVoltageDescr


CPCEnableCard* CreateCardBusEnabler( CARD_SOCKET_HANDLE hSocket,
                                     CARD_CLIENT_HANDLE hClient )
{
    return new CPCEnableCBCard( hSocket, hClient );
}

