//
// 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:

pccarden.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 <netui.h>
#include "debug.h"
#include "pccarden.h"

#define NUM_PCENCARD 0x10
class CPCEnableCardContainer : public CStaticContainer <CPCEnableCard, NUM_PCENCARD>
{
public:
    CPCEnableCard* FoundCardBySockedHandle( CARD_SOCKET_HANDLE hSocket,
                                            PDWORD pdwIndex )
    {
        Lock();
        CPCEnableCard*  pReturn = NULL;
        for( DWORD dwIndex = 0; dwIndex < m_dwArraySize; dwIndex++ )
        {
            if( m_rgObjectArray[dwIndex] != NULL )
            {
                CARD_SOCKET_HANDLE  objSocket = m_rgObjectArray[dwIndex]->GetCardHandle();
                if( hSocket.uSocket == objSocket.uSocket &&
                    hSocket.uFunction == objSocket.uFunction )
                    break;
            }
        }
        if( dwIndex < m_dwArraySize )
        {
            pReturn = m_rgObjectArray[dwIndex];
            pReturn->AddRef();
            if( pdwIndex )
                *( pdwIndex ) = dwIndex;
        };
        Unlock();
        return pReturn;
    };
private :
    CARD_CLIENT_HANDLE  m_hClientHandle;
public:
    CPCEnableCardContainer()
    {
        m_hClientHandle = NULL;
    };
    CARD_CLIENT_HANDLE SetClientHandle( CARD_CLIENT_HANDLE hClient )
    {
        return m_hClientHandle = hClient;
    };
    CARD_CLIENT_HANDLE GetClientHandle()
    {
        return m_hClientHandle;
    };
};

CPCEnableCardContainer* pCardEnableContainer = NULL;

CPCEnableCard* CreatePCCardEnabler( CARD_SOCKET_HANDLE hSocket, DWORD dwParam1 )
{
    CPCEnableCard* pEnableCard = NULL;
    if( pCardEnableContainer )
    {
        if( dwParam1 & CFG_IFACE_CARDBUS )
        {
#ifndef NO_32BIT_PCCARD
            pEnableCard = CreateCardBusEnabler( hSocket,
                                                pCardEnableContainer->GetClientHandle() );
#else           
            RETAILMSG( 1,
                       ( TEXT( "pcc_serv.DLL : 32 Bit PCMCIA Card has not been supported!!!\r\n" ) ) );
#endif
        }
        else
        {
#ifndef NO_16BIT_PCARD
            pEnableCard = CreatePcmciaEnabler( hSocket,
                                               pCardEnableContainer->GetClientHandle() );
#else
            RETAILMSG( 1,
                       ( TEXT( "pcc_serv.DLL : 16 Bit PCMCIA Card has not been supported!!!\r\n" ) ) );
#endif
        }
    }
    return pEnableCard;
}

extern "C" STATUS EnablerClienCallBack( CARD_EVENT cEvent,
                                        CARD_SOCKET_HANDLE hSocket,
                                        PCARD_EVENT_PARMS pParms )
{
    STATUS status = CERR_BAD_HANDLE;
    if( pParms &&
        pParms->uClientData == ( UINT32 ) pCardEnableContainer &&
        pCardEnableContainer )
    {
        if( cEvent == CE_CARD_INSERTION )
        {
            DWORD dwParm1 = pParms->Parm1;
            DWORD dwIndex = 0;
            CPCEnableCard* pCard = pCardEnableContainer->FoundCardBySockedHandle( hSocket,
                                                                                  &dwIndex );
            if( pCard )
            {
                pCard->DeRef();
                if( pCard->IsCardBusCard() !=
                    ( ( dwParm1 & CFG_IFACE_CARDBUS ) != 0 ) )
                {
                    // Card is not mathed .. delete it and the rereated
                    pCardEnableContainer->RemoveObjectBy( dwIndex );
                    pCard = NULL;
                }
            }
            if( pCard == NULL )
            {
                // Create and Insert New Card Enabler.
                pCard = CreatePCCardEnabler( hSocket, dwParm1 );
                if( pCard )
                {
                    if( pCardEnableContainer->InsertObjectAtEmpty( NULL,
                                                                   pCard ) ==
                        NULL )
                    {
                        // Can not Inserted.
                        delete pCard;
                        pCard = NULL;
                    }
                    else if( !pCard->Init() )
                    {
                        // Can not initialize this enabler.
                        DEBUGMSG( ZONE_WARN,
                                  ( TEXT( "EnableClientCallBack :Card (%d,%d) can not be-intialized by default enabler!!\r\n" ),
                                    hSocket.uSocket,
                                    hSocket.uFunction ) );
                        pCardEnableContainer->RemoveObjectBy( pCard );
                    }
                }
            }
            if( pCard )
                status = CERR_SUCCESS;
        }
        else if( cEvent == CE_CARD_REMOVAL )
        {
            DWORD dwIndex = 0;
            CPCEnableCard* pCard = pCardEnableContainer->FoundCardBySockedHandle( hSocket,
                                                                                  &dwIndex );
            if( pCard )
            {
                // Found
                status = pCard->HandleEvent( cEvent, pParms );
                pCard->DeRef();
                pCardEnableContainer->RemoveObjectBy( dwIndex );
                status = CERR_SUCCESS;
            }
        }
        else
        {
            CPCEnableCard* pCard = pCardEnableContainer->FoundCardBySockedHandle( hSocket,
                                                                                  NULL );
            if( pCard )
            {
                // Found
                status = pCard->HandleEvent( cEvent, pParms );
                pCard->DeRef();
            }
        };
    }
    return status;
}

extern "C" void EnEnableClient()
{
    if( pCardEnableContainer == NULL )
    {
        pCardEnableContainer = new CPCEnableCardContainer();
    }
    if( pCardEnableContainer )
    {
        CARD_REGISTER_PARMS Parms;
        Parms.fAttributes = CLIENT_ATTR_MEM_DRIVER |
                            CLIENT_ATTR_IO_DRIVER |
                            CLIENT_ATTR_NOTIFY_SHARED |
                            CLIENT_ATTR_CARDBUS_DRIVER;
        Parms.fEventMask = EVENT_MASK_CARD_DETECT |
                           EVENT_MASK_STATUS_CHANGE |
                           EVENT_MASK_RESET |
                           EVENT_MASK_CARD_READY |
                           EVENT_MASK_POWER_CYCLE |
                           EVENT_MASK_POWER_MGMT;
        Parms.uClientData = ( DWORD ) pCardEnableContainer;
        pCardEnableContainer->SetClientHandle( CardRegisterClient( EnablerClienCallBack,
                                                                   &Parms ) );
        DEBUGCHK( pCardEnableContainer->GetClientHandle() != NULL );
    }
}

extern "C" void DisEnableClient()
{
    if( pCardEnableContainer )
    {
        CardDeregisterClient( pCardEnableContainer->GetClientHandle() );
        delete pCardEnableContainer;
        pCardEnableContainer = NULL;
    }
}

CPCEnableCard::CPCEnableCard( CARD_SOCKET_HANDLE hSocket,
                              CARD_CLIENT_HANDLE hClient ) : m_hCard( hSocket ),
                                                             m_hClient( hClient )
{
    m_IntefaceType = InterfaceTypeUndefined;
    m_dwBusNumber = 0 ;
    m_SlotNumber.u.AsULONG = 0;       
    m_hDeviceHandle = NULL;
    m_lpDeviceBusName = NULL;;
};

BOOL CPCEnableCard::Init()
{
    STATUS status = CardRequestExclusive( m_hClient, m_hCard );
    m_bSuccess = ( status == CERR_SUCCESS );
    CPCCardAccessCoreDll::Init();
    return m_bSuccess;
}

CPCEnableCard::~CPCEnableCard()
{
    CardRequestDisable( m_hClient, m_hCard );
    if( m_bSuccess )
    {
        CardReleaseExclusive( m_hClient, m_hCard );
    }
    if( m_lpDeviceBusName )
    {
        delete m_lpDeviceBusName;
    }
}

STATUS CPCEnableCard::HandleEvent( CARD_EVENT cEvent, PCARD_EVENT_PARMS pParms )
{
    STATUS status = CERR_BAD_ARGS;
    switch( cEvent )
    {
      case CE_CARD_BUS_INFO:
        Lock();
        m_hDeviceHandle = ( HANDLE ) pParms->Parm1;
        if( m_lpDeviceBusName == NULL && pParms->Parm2 != NULL && m_lpDeviceBusName == NULL )
        {
            LPTSTR pBusName = ( LPTSTR ) pParms->Parm2;
            DWORD dwLength = _tcslen( pBusName );
            m_lpDeviceBusName = new TCHAR[dwLength + 1];
            if( m_lpDeviceBusName )
                _tcscpy( m_lpDeviceBusName, pBusName );
        }
        Unlock();
        break;
    }
    return status;
}

BOOL CPCEnableCard::ReleaesExclusive()
{
    if( m_bSuccess )
    {
        CardReleaseExclusive( m_hClient, m_hCard );
        m_bSuccess;
        return TRUE;
    }
    return FALSE;
}

BOOL CPCEnableCard::QueryDriverName( LPTSTR lpPnpStr )
{
    BOOL bReturn = FALSE;
    GETDRIVERNAMEPARMS GDNP;
    GDNP.PCCardType = PCCARD_TYPE_UNKNOWN;
    DWORD dwSocketIndex = -1;
    GetSocketIndex( m_hCard, &dwSocketIndex );
    GDNP.Socket = dwSocketIndex ;
    if( !WaitOnGwesEvent( INFINITE ) || !CallGetDriverName( NULL, &GDNP ) )
    {
        DEBUGMSG( ZONE_INIT,
                  ( TEXT( "DEVICE: CallGetDriverName failed %d\r\n" ),
                    GetLastError() ) );
    }
    else
    {
        HMODULE hInstallDll = ::LoadDriver( GDNP.DriverName );
        if( hInstallDll == NULL )
        {
            DEBUGMSG( ZONE_INIT |
                      ZONE_ERROR,
                      ( TEXT( "DEVICE: LoadDriver(%s) failed %d\r\n" ),
                        GDNP.DriverName,
                        GetLastError() ) );
            return FALSE;
        }

        PFN_INSTALL_DRIVER pfnInstall = ( PFN_INSTALL_DRIVER )
                                        GetProcAddress( hInstallDll,
                                                        TEXT( "Install_Driver" ) );
        //
        // Tell it to install itself
        //
        if( pfnInstall )
        {
            __try
            {
                LPTSTR lpStr = pfnInstall( lpPnpStr,
                                           GDNP.DriverName,
                                           sizeof( GDNP.DriverName ) );
                if( lpStr && _tcslen( lpStr ) > 0 && *lpStr > _T( ' ' ) )
                    bReturn = TRUE;
            } __except( EXCEPTION_EXECUTE_HANDLER )
            {
                RETAILMSG( 1,
                           ( TEXT( "PCMCIA:QueryDriverName install func in %s faulted!\n" ),
                             GDNP.DriverName ) );
            }
        }
        FreeLibrary( hInstallDll );
    }
    return bReturn;
}

BOOL CPCEnableCard::GetTupleData( PCARD_TUPLE_PARMS pTupleParams,
                                  PVOID pTupleData,
                                  DWORD dwLength,
                                  DWORD dwOffset )
{
    if( pTupleParams && pTupleData && dwLength > sizeof( CARD_DATA_PARMS ) )
    {
        memcpy( pTupleData, pTupleParams, sizeof( CARD_TUPLE_PARMS ) );
        PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) pTupleData;
        pData->uBufLen = ( UINT16 ) ( dwLength - sizeof( CARD_DATA_PARMS ) );
        pData->uDataLen = 0;
        pData->uTupleOffset = ( UINT8 ) dwOffset;
        if( CardGetTupleData( pData ) == CERR_SUCCESS )
            return TRUE;
    }
    return FALSE;
}

BOOL CPCEnableCard::FindTuple( PCARD_TUPLE_PARMS pTupleParams,
                               BOOL bFirst,
                               UINT8 uDesiredTuple,
                               UINT16 attributes )
{
    if( pTupleParams )
    {
        if( bFirst )
        {
            // Intialize 
            memset( pTupleParams, 0, sizeof( CARD_TUPLE_PARMS ) );
            pTupleParams->hSocket = GetCardHandle();
            pTupleParams->fAttributes = attributes;
            pTupleParams->uDesiredTuple = uDesiredTuple;
            if( CardGetFirstTuple( pTupleParams ) == CERR_SUCCESS )
                return TRUE;
        }
        else if( CardGetNextTuple( pTupleParams ) == CERR_SUCCESS )
        {
            return TRUE;
        }
    }
    return FALSE;
}

//
// ConvertVoltage - normalize a single CISTPL_CFTABLE_ENTRY voltage entry and
//                  return its length in bytes
//
UINT CPCEnableCard::ConvertVoltage( PUCHAR pCIS, PUSHORT pDescr ) const
{
    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 CPCEnableCard::ParseVoltageDescr( PUCHAR pVolt, PPOWER_DESCR pDescr ) const
{
    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

/********************************************************************
*
*    CreateDeviceID - Create the DeviceID for the PC Card.
*
*    The device ID is created from tuple information on the PC Card and saved
*    in the CIC socket data.  The goal is to create a unique ID for each
*    card.  The device ID is created from the manufacturer name string,
*    the product name string, and a 16-bit CRC of a set of tuples.
*
*    The ID is created by concatenating the PCMCIA" prefix, the manufacturer
*    name string, the product name string, a 16-bit CRC, and the instance
*    value for the card.
*
*        PCMCIA\<manuf_name>-<prod_name>-<crc>\<instance>
*
*    If the CISTPL_VERS_1 tuple is not available or the manufacturer name is
*    NULL, the string "UNKNOWN_MANUFACTURER" will be included in its place.
*
*        PCMCIA\UNKNOWN_MANUFACTURER-<crc>\<instance>
*
*    The CRC will be created from the following tuple data.
*
*        CISTPL_DEVICE
*        CISTPL_VERS_1
*        CISTPL_CONFIG
*        CISTPL_CFTABLE_ENTRY
*        CISTPL_MANFID
*
*    The total length of the device ID string is limited to 128 characters,
*    including the null terminator.    The manufacturer and product name will
*    be truncated to maintain this length restriction in the ID string.
*
*    The characters in the manufacturer and product name strings that are
*    greater than or equal to a space (0x20) or less than (0x7F) will be
*    copied into the name string.  Any other characters outside this range
*    will be skipped.  This makes it easier to include these characters in
*    the .INF files for the device.
*
*    Note the Configuration Manager converts spaces (' ') and commas (',')
*    in the device ID that is passed during devnode creation to
*    underscores ('_') and converts all characters to upper case.
*
********************************************************************/
#define BUFFER_SIZE 0x200
STATUS CPCEnableCard::CreateDeviceID( LPTSTR lpDeviceId, DWORD dwNumOfUnit )
{
    TCHAR product_name[PROD_LEN];
    TCHAR manf_name[MANFNAME_END];
#define RESERVED_FOR_CRC 8
    DEBUGMSG( ZONE_TUPLE, ( TEXT( "CreateDeviceID entered\r\n" ) ) );
    if( lpDeviceId == NULL || dwNumOfUnit < DEVID_LEN )
    {
        DEBUGCHK( FALSE );
        return CERR_BAD_ARGS;
    }
    product_name[0] = 0;      // Initialize product name string
    manf_name[0] = 0;
    TCHAR* ps = STR_UNKNOWN_MANID;   // UNKNOWN_MANUFACTURER string


    //
    // Try to get the manufacturer name and product name strings from
    // the Level 1 Version tuple
    //
    CARD_TUPLE_PARMS TupleParm;
    if( FindTuple( &TupleParm, TRUE, CISTPL_VERS_1 ) )
    {
        UCHAR buffer[BUFFER_SIZE + sizeof( CARD_DATA_PARMS )];
        if( GetTupleData( &TupleParm,
                          buffer,
                          BUFFER_SIZE + sizeof( CARD_DATA_PARMS ) ) )
        {
            PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) buffer;;
            PUCHAR tupleData = ( PUCHAR ) pData;
            tupleData += sizeof( CARD_DATA_PARMS );    // point at returned data
            if( ( pData->uDataLen > 4 ) && ( tupleData[2] != 0 ) )
            {
                //
                // Have a valid manufacturer name string.
                //
                // Force a null terminator at the end of the data, just in case
                // the string is not properly terminated.
                //
                tupleData[pData->uDataLen] = 0;
                tupleData = &tupleData[2];
                int i = CisToChar( tupleData, manf_name, MANFNAME_END ) + 1;
                ps = manf_name;
                tupleData += i;
                DEBUGMSG( ZONE_TUPLE,
                          ( TEXT( "Manufacturer = %s\r\n" ), manf_name ) );

                //
                // Copy product name if manufacturer name is not too long
                //
                if( i < pData->uDataLen - 2 )
                {
                    CisToChar( tupleData, product_name, PROD_LEN );
                    DEBUGMSG( ZONE_TUPLE,
                              ( TEXT( "Product Name = %s\r\n" ), product_name ) );
                }
                else
                {
                    product_name[0] = ( TCHAR ) 0;
                }
            } 
#ifdef DEBUG
            else
            {
                if( pData->uDataLen > 2 )
                {
                    for( UINT16 i = 0; i < pData->uDataLen; i++ )
                    {
                        if( ( i ) && ( ( i & 0x0F ) == 0 ) )
                        {
                            DEBUGMSG( ZONE_TUPLE, ( TEXT( "\r\n" ) ) );
                        }
                        DEBUGMSG( ZONE_TUPLE,
                                  ( TEXT( " %2x" ), tupleData[i] ) );
                    }
                    DEBUGMSG( ZONE_TUPLE, ( TEXT( "\r\n" ) ) );
                }
            }
#endif

        }
    }

    //
    // Copy manufacturer name into the device ID.
    //
    DWORD pos = _tcslen( ps );
    if( pos > dwNumOfUnit - RESERVED_FOR_CRC )
    {
        pos = dwNumOfUnit - RESERVED_FOR_CRC;
    }
    _tcsncpy( lpDeviceId, ps, pos );
    lpDeviceId[pos++] = ( TCHAR ) '-';         // Append '-' to device ID
    lpDeviceId[pos] = 0;                    // Terminage it.

    //
    // If product name is valid, copy it into the device ID.
    //
    if( product_name[0] != ( TCHAR ) 0 &&
        pos < dwNumOfUnit - RESERVED_FOR_CRC )
    {
        int len = _tcslen( product_name );
        if( pos + len > dwNumOfUnit - RESERVED_FOR_CRC )
            len = dwNumOfUnit - RESERVED_FOR_CRC - pos;
        // Append product name to device ID.
        _tcsncpy( lpDeviceId + pos, product_name, len );
        pos += len;                 // advance destination pointer
        lpDeviceId[pos++] = ( TCHAR ) '-';         // Append '-' to device ID
        lpDeviceId[pos] = 0;                    // Terminage it.
    }

    //
    // Compute CRC for desired tuples and
    // convert to ascii and append to device ID.
    //
    itoah( lpDeviceId + pos, GetCardCRC() );
    DEBUGMSG( ZONE_TUPLE,
              ( TEXT( "CreateDeviceID %s done\r\n" ), lpDeviceId ) );
    return CERR_SUCCESS;
}   // CreateDeviceID
const unsigned short CPCEnableCard::m_rgCRC16a[] =
{
    0000000,
    0140301,
    0140601,
    0000500,
    0141401,
    0001700,
    0001200,
    0141101,
    0143001,
    0003300,
    0003600,
    0143501,
    0002400,
    0142701,
    0142201,
    0002100,
};
const unsigned short CPCEnableCard::m_rgCRC16b[] =
{
    0000000,
    0146001,
    0154001,
    0012000,
    0170001,
    0036000,
    0024000,
    0162001,
    0120001,
    0066000,
    0074000,
    0132001,
    0050000,
    0116001,
    0104001,
    0043000,
};
const unsigned short CPCEnableCard::m_rgTplList[] =
{
    CISTPL_DEVICE,
    CISTPL_VERS_1,
    CISTPL_CONFIG,
    CISTPL_CFTABLE_ENTRY,
    CISTPL_MANFID,
    CISTPL_END
};


//
// Using the same algorithm as Windows 95, calculate the CRC value
// to be appended with the manufacturer name and device name to
// obtain the unique identifier for the PCCARD.
//
// Return: USHORT CRC.
//
USHORT CPCEnableCard::GetCardCRC()
{
    USHORT crc = 0;
    //
    // Calculate CRC
    //

    DEBUGMSG( ZONE_TUPLE, ( TEXT( "GetCardCRC: Calculating CRC\r\n" ) ) );

    CARD_TUPLE_PARMS TupleParm;
    if( FindTuple( &TupleParm, TRUE, 0xff ) )
    {
        //
        // Walk the whole CIS, processing only those tuples that are used in CRC
        // generation.
        //
        for( int i = 0; i < MAX_TUPLES; i++ )
        {
            BYTE tupleCode = TupleParm.uTupleCode;

            for( int index = 0; m_rgTplList[index] != CISTPL_END; index++ )
            {
                if( tupleCode != m_rgTplList[index] )
                {
                    continue;
                }

                UCHAR buffer[BUFFER_SIZE + sizeof( CARD_DATA_PARMS )];
                if( !GetTupleData( &TupleParm,
                                   buffer,
                                   BUFFER_SIZE + sizeof( CARD_DATA_PARMS ) ) )
                {
                    DEBUGMSG( ZONE_TUPLE |
                              ZONE_WARN,
                              ( TEXT( "GetCardCRC: CardGetTupleData returned fails!!!\r\n" ) ) );
                    break;
                }
                PCARD_DATA_PARMS pData = ( PCARD_DATA_PARMS ) buffer;
                PBYTE tupleData = ( PBYTE ) ( pData + 1 );
                USHORT length = ( USHORT ) pData->uDataLen;
                PBYTE cp;

                //
                // This one is included in the CRC calculation
                //
                if( tupleCode == CISTPL_VERS_1 )
                {
                    cp = tupleData + 2;

                    //
                    // Include all of the manufacturer name.
                    //
                    while( *cp )
                    {
                        cp++;
                    }

                    cp++;        // Include the product string

                    while( *cp )
                    {
                        cp++;
                    }
                    cp++;

                    length = cp - tupleData;
                }

                for( cp = tupleData; length; length--, cp++ )
                {
                    BYTE tmp = *cp ^ ( UCHAR ) crc;
                    crc = ( crc >> 8 ) ^ m_rgCRC16a[tmp & 0x0f] ^ m_rgCRC16b[tmp >> 4];
                }
                break;
            }    // for (all CRC tuples)
            if( !FindTuple( &TupleParm, FALSE, 0xff ) )
            {
                DEBUGMSG( ZONE_TUPLE |
                          ZONE_WARN,
                          ( TEXT( "GetCardCRC: CardGetNextTuple returned fails\r\n" ) ) );
                break;
            }
        }    // for MAX_TUPLES
    }
    else
    {
        DEBUGMSG( ZONE_TUPLE |
                  ZONE_WARN,
                  ( TEXT( "GetCardCRC: CardGetFirstTuple returned fails\r\n" ) ) );
    }

    DEBUGMSG( ZONE_TUPLE, ( TEXT( "GetCardCRC: CRC = %x\r\n" ), crc ) );
    return crc;
}    // GetCardCRC


//
// Convert a single byte character string from a PCMCIA CIS tuple to
// a TCHAR string limiting the range to 0x20-0x7F and changing space and
// comma to underscore.
//
// Returns number of bytes of source that where used.
//
UINT CPCEnableCard::CisToChar( BYTE* pcstr, // source
                           LPTSTR ptstr,    // destination
                           DWORD dwNumOfCharsOfTstr
) const
{
    UCHAR c;
    UINT i = 0;
    PREFAST_ASSERT(pcstr!=NULL);
    PREFAST_ASSERT(ptstr!=NULL);
    PREFAST_ASSERT(dwNumOfCharsOfTstr > 1);

    while( (c = *pcstr) != 0 && dwNumOfCharsOfTstr > 1  )
    {
        if( ( c > 0x20 ) && ( c < 0x7f ) )
        {
            *ptstr = ( TCHAR ) c;
            ptstr++;
            dwNumOfCharsOfTstr --;
        }
        else if( ( c == ' ' ) || ( c == ',' ) )
        {
            *ptstr = ( TCHAR ) '_';
            ptstr++;
            dwNumOfCharsOfTstr --;
        }
        pcstr++;
        i++;
    }
    *ptstr = 0 ;    // NULL terminator
    return i;
}



const TCHAR CPCEnableCard::m_rgcHexDigit[] =
{
    ( TCHAR ) '0',
    ( TCHAR ) '1',
    ( TCHAR ) '2',
    ( TCHAR ) '3',
    ( TCHAR ) '4',
    ( TCHAR ) '5',
    ( TCHAR ) '6',
    ( TCHAR ) '7',
    ( TCHAR ) '8',
    ( TCHAR ) '9',
    ( TCHAR ) 'A',
    ( TCHAR ) 'B',
    ( TCHAR ) 'C',
    ( TCHAR ) 'D',
    ( TCHAR ) 'E',
    ( TCHAR ) 'F'
};

//
// Convert USHORT value to ascii using hex radix
//
// Returns pointer to end of converted string.
//
TCHAR* CPCEnableCard::itoah( TCHAR* ptr, USHORT val ) const
{
    int i;

    for( i = 0; i < 4; i++ )
    {
        *ptr = m_rgcHexDigit[val >> 12];
        ptr++;
        val <<= 4;
    }
    *ptr = ( TCHAR ) 0;
    return ptr;
}    // itoah
