/*++

Copyright (c) 1997 - 1999 SCM Microsystems, Inc.

Module Name:

    PscrCB.c

Abstract:

	callback handler for PSCR.xxx driver

Author:

	Andreas Straub

Environment:

	Win 95		Sys... calls are resolved by Pscr95Wrap.asm functions and
				Pscr95Wrap.h macros, resp.

	NT	4.0		Sys... functions resolved by PscrNTWrap.c functions and
				PscrNTWrap.h macros, resp.

Revision History:

	Andreas Straub			8/18/1997	1.00	Initial Version
	Andreas Straub			9/24/1997	1.02	Flush Interface if card tracking
												requested

--*/

#if defined( SMCLIB_VXD )

#include <Pscr95.h>

#else	//	SMCLIB_VXD

#include <PscrNT.h>

#endif	//	SMCLIB_VXD


#include <PscrRdWr.h>
#include <PscrCmd.h>
#include <PscrCB.h>


NTSTATUS
CBCardPower( 
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBCardPower:
	callback handler for SMCLIB RDF_CARD_POWER

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_BUFFER_TOO_SMALL

--*/
{
	NTSTATUS			NTStatus = STATUS_SUCCESS;
	UCHAR				ATRBuffer[ ATR_SIZE ], TLVList[16];
	ULONG				Command,
						ATRLength;
	PREADER_EXTENSION	ReaderExtension;
    BYTE                CardState;
#if DBG || DEBUG
    static PCHAR request[] = { "PowerDown",  "ColdReset", "WarmReset" };
#endif

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBCardPower: Enter, Request = %s\n",
        request[SmartcardExtension->MinorIoControlCode])
		);

	ReaderExtension = SmartcardExtension->ReaderExtension;

	//
	//	update actual power state
	//
	Command = SmartcardExtension->MinorIoControlCode;

	switch ( Command )
	{
		case SCARD_WARM_RESET:

			//	if the card was not powerd, fall thru to cold reset
			if( SmartcardExtension->ReaderCapabilities.CurrentState >
				SCARD_SWALLOWED )
			{
				//	reset the card
				ATRLength = ATR_SIZE;
				NTStatus = CmdReset(
					ReaderExtension,
					ReaderExtension->Device,
					TRUE,				// warm reset
					ATRBuffer,
					&ATRLength
					);

				break;
			}

			//	warm reset not possible because card was not powerd
		case SCARD_COLD_RESET:

			//	reset the card
			ATRLength = ATR_SIZE;
			NTStatus = CmdReset(
				ReaderExtension,
				ReaderExtension->Device,
				FALSE,				// cold reset
				ATRBuffer,
				&ATRLength
				);
			break;

		case SCARD_POWER_DOWN:
			ATRLength = 0;
			NTStatus = CmdDeactivate(	
				ReaderExtension,
				ReaderExtension->Device
				);

			//	discard old card status
            CardState = CBGetCardState(SmartcardExtension);
            CBUpdateCardState(SmartcardExtension, CardState, FALSE);
			break;
	}

    if (NTStatus == STATUS_SUCCESS) {
     	
        //
        // Set the 'restart of work waiting time' counter for T=0
        // This will send a WTX request for n NULL bytes received
        //
        TLVList[0] = TAG_SET_NULL_BYTES;
        TLVList[1] = 1;
        TLVList[2] = 0x05; 

        NTStatus = CmdSetInterfaceParameter(
	        ReaderExtension,
	        DEVICE_READER,
	        TLVList,
	        3
	        );
    }

    ASSERT(NTStatus == STATUS_SUCCESS);

	//	finish the request
	if( NT_SUCCESS( NTStatus ))
	{
		//	update all neccessary data if an ATR was received
		if( ATRLength > 2 )
		{
			//
			//	the lib expects only the ATR, so we skip the 
			//	900x from the reader
			//
			ATRLength -= 2;

			//	copy ATR to user buffer buffer
			if( ATRLength <= SmartcardExtension->IoRequest.ReplyBufferLength )
			{
				SysCopyMemory(
					SmartcardExtension->IoRequest.ReplyBuffer,
					ATRBuffer,
					ATRLength
					);
				*SmartcardExtension->IoRequest.Information = ATRLength;
			}
			else
			{
				NTStatus = STATUS_BUFFER_TOO_SMALL;
			}

			//	copy ATR to card capability buffer
			if( ATRLength <= MAXIMUM_ATR_LENGTH )
			{
				SysCopyMemory(
					SmartcardExtension->CardCapabilities.ATR.Buffer,
					ATRBuffer,
					ATRLength
					);

				SmartcardExtension->CardCapabilities.ATR.Length = 
					( UCHAR )ATRLength;

				//	let the lib update the card capabilities
				NTStatus = SmartcardUpdateCardCapabilities(
					SmartcardExtension 
					);
			}
			else
			{
				NTStatus = STATUS_BUFFER_TOO_SMALL;
			}
		}
	}

	if( !NT_SUCCESS( NTStatus ))
	{
		switch( NTStatus )
		{
			case STATUS_NO_MEDIA:
			case STATUS_BUFFER_TOO_SMALL:
				break;

			case STATUS_TIMEOUT:
                NTStatus = STATUS_IO_TIMEOUT;
                break;

			default:
				NTStatus = STATUS_UNRECOGNIZED_MEDIA;
                break;
		}
	}

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBCardPower: Exit (%lx)\n", NTStatus )
		);
	
	return( NTStatus );
}

NTSTATUS
CBSetProtocol( 
	PSMARTCARD_EXTENSION SmartcardExtension 
	)

/*++

CBSetProtocol:
	callback handler for SMCLIB RDF_SET_PROTOCOL

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_BUFFER_TOO_SMALL
	STATUS_INVALID_DEVICE_STATE
	STATUS_INVALID_DEVICE_REQUEST

--*/
{
	NTSTATUS NTStatus = STATUS_PENDING;
	USHORT SCLibProtocol;
	UCHAR TLVList[ TLV_BUFFER_SIZE ];
	PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;

    if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC) {

        return STATUS_SUCCESS;
    }

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBSetProtocol: Enter\n" )
		);

	SCLibProtocol = ( USHORT )( SmartcardExtension->MinorIoControlCode );

    if (SCLibProtocol & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1))
    {
		//
		//	setup the TLV list for the Set Interface Parameter List
		//
		TLVList[ 0 ] = TAG_ICC_PROTOCOLS;
		TLVList[ 1 ] = 0x01;
		TLVList[ 2 ] = 
            (SCLibProtocol & SCARD_PROTOCOL_T1 ? PSCR_PROTOCOL_T1 : PSCR_PROTOCOL_T0);

		//	do the PTS
		NTStatus = CmdSetInterfaceParameter(
			ReaderExtension,
			ReaderExtension->Device,
			TLVList,
			3			// size of list
			);		

    } else {

		//	we don't support other modi
		NTStatus = STATUS_INVALID_DEVICE_REQUEST;
	}

	//	if protocol selection failed, prevent from calling invalid protocols
	if( NT_SUCCESS( NTStatus ))
	{
		SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
		SCLibProtocol = (SCLibProtocol & SCARD_PROTOCOL_T1 &
                         SmartcardExtension->CardCapabilities.Protocol.Supported) ?
						 SCARD_PROTOCOL_T1 :
						 SCARD_PROTOCOL_T0;
	}
	else
	{
		SCLibProtocol = SCARD_PROTOCOL_UNDEFINED;
	}

	//	Return the selected protocol to the caller.
	SmartcardExtension->CardCapabilities.Protocol.Selected = SCLibProtocol;
	*( PULONG )( SmartcardExtension->IoRequest.ReplyBuffer ) = SCLibProtocol;
	*( SmartcardExtension->IoRequest.Information ) = sizeof( ULONG );
	
	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBSetProtocol: Exit (%lx)\n", NTStatus )
		);

	return ( NTStatus );
}

NTSTATUS
CBTransmit( 
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBTransmit:
	callback handler for SMCLIB RDF_TRANSMIT

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_INVALID_DEVICE_REQUEST

--*/
{
	NTSTATUS  NTStatus = STATUS_SUCCESS;

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBTransmit: Enter\n" )
		);

	//	dispatch on the selected protocol
	switch( SmartcardExtension->CardCapabilities.Protocol.Selected )
	{
		case SCARD_PROTOCOL_T0:
			NTStatus = CBT0Transmit( SmartcardExtension );
			break;

		case SCARD_PROTOCOL_T1:
			NTStatus = CBT1Transmit( SmartcardExtension );
			break;

		case SCARD_PROTOCOL_RAW:
			NTStatus = CBRawTransmit( SmartcardExtension );
			break;

		default:
			NTStatus = STATUS_INVALID_DEVICE_REQUEST;
			break;
	}

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBTransmit: Exit (%lx)\n", NTStatus )
		);

	return( NTStatus );
}

NTSTATUS
CBRawTransmit(
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBRawTransmit:
	finishes the callback RDF_TRANSMIT for the RAW protocol

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_INVALID_DEVICE_REQUEST

--*/
{
    NTSTATUS			NTStatus = STATUS_SUCCESS;
	UCHAR				TLVList[ TLV_BUFFER_SIZE ],
						Val,
						Len;
	ULONG				TLVListLen;
	PREADER_EXTENSION	ReaderExtension;

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBRawTransmit: Enter\n" )
		);

	ReaderExtension	= SmartcardExtension->ReaderExtension;
	//
	//	read the status file of ICC1 from the reader
	//
	TLVListLen = TLV_BUFFER_SIZE;
	NTStatus = CmdReadStatusFile(
		ReaderExtension,
		ReaderExtension->Device,
		TLVList,
		&TLVListLen
		);

	//
	//	check the active protocol of the reader
	//
	if( NT_SUCCESS( NTStatus ))
	{
		NTStatus = CmdGetTagValue(
			TAG_ICC_PROTOCOLS,
			TLVList,
			TLVListLen,
			&Len,
			( PVOID ) &Val
			);

		//	execute the active protocol
		if( NT_SUCCESS( NTStatus ))
		{

			//	translate the actual protocol to a value the lib can understand
			switch( Val )
			{
				case PSCR_PROTOCOL_T0:
					NTStatus = CBT0Transmit( SmartcardExtension );
					break;
				case PSCR_PROTOCOL_T1:
					NTStatus = CBT1Transmit( SmartcardExtension );
					break;
				default:
					NTStatus = STATUS_UNSUCCESSFUL;
					break;
			}
		}
	}
	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBRawTransmit: Exit (%lx)\n", NTStatus )
		);
	return ( NTStatus );
}

NTSTATUS
CBT1Transmit(
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBT1Transmit:
	finishes the callback RDF_TRANSMIT for the T1 protocol

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_INVALID_DEVICE_REQUEST

--*/
{
    NTSTATUS	NTStatus = STATUS_SUCCESS;
	ULONG		IOBytes;

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBT1Transmit: Enter\n" )
		);
	//
	//	use the lib support to construct the T=1 packets
	//
	do {
		//
		//	no header for the T=1 protocol
		//
		SmartcardExtension->SmartcardRequest.BufferLength = 0;
		//
		//	SCM-TM: Siemens 4440 accepts only NAD=0!!!
		//
		SmartcardExtension->T1.NAD = 0;
		//
		//	let the lib setup the T=1 APDU & check for errors
		//
		NTStatus = SmartcardT1Request( SmartcardExtension );
		if( NT_SUCCESS( NTStatus ))
		{

			//	send command (don't calculate LRC because CRC may be used!)
			IOBytes = 0;
			NTStatus = PscrWriteDirect(
				SmartcardExtension->ReaderExtension,
				SmartcardExtension->SmartcardRequest.Buffer,
				SmartcardExtension->SmartcardRequest.BufferLength,
				&IOBytes
				);
			//
			//	extend the timeout if a Wtx request was sent by the card. if the 
			//	card responds before the waiting time extension expires, the data are
			//	buffered in the reader. A delay without polling the reader status
			//	slows down the performance of the driver, but wtx is an exeption,
			//	not the rule.
			//
			if (SmartcardExtension->T1.Wtx)
			{
					SysDelay(
					(( SmartcardExtension->T1.Wtx * 
					SmartcardExtension->CardCapabilities.T1.BWT + 999L )/
					1000L) 
					);

			}

			//	get response
			SmartcardExtension->SmartcardReply.BufferLength = 0;
			NTStatus = PscrRead(
				SmartcardExtension->ReaderExtension,
				SmartcardExtension->SmartcardReply.Buffer,
				MAX_T1_BLOCK_SIZE,
				&SmartcardExtension->SmartcardReply.BufferLength
				);

			//	if PscrRead detects an LRC error, ignore it (maybe CRC used)
			if( NTStatus == STATUS_CRC_ERROR )
			{
				NTStatus = STATUS_SUCCESS;
			}

            //
            // We even continue if the prev. read failed.
            // We let the smart card library continue, because it might
            // send a resynch. request in case of a timeout
            //
			NTStatus = SmartcardT1Reply( SmartcardExtension );
		}

	//	continue if the lib wants to send the next packet
	} while( NTStatus == STATUS_MORE_PROCESSING_REQUIRED );

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBT1Transmit: Exit (%lx)\n", NTStatus )
		);

	return ( NTStatus );
}

NTSTATUS
CBT0Transmit( 
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBT0Transmit:
	finishes the callback RDF_TRANSMIT for the T0 protocol

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_TIMEOUT
	STATUS_INVALID_DEVICE_REQUEST

--*/
{
    NTSTATUS NTStatus = STATUS_SUCCESS;
	PUCHAR pRequest,pReply;
	ULONG IOBytes, APDULength, RequestLength;
	UCHAR IOData[ MAX_T1_BLOCK_SIZE ];
    UCHAR WtxReply[16];

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBT0Transmit: Enter\n" )
		);

	pRequest	= SmartcardExtension->SmartcardRequest.Buffer;
	pReply		= SmartcardExtension->SmartcardReply.Buffer;

	//	setup the command header
	pRequest[ PSCR_NAD ] = 
		( SmartcardExtension->ReaderExtension->Device == DEVICE_ICC1 ) ? 
		NAD_TO_ICC1 : NAD_TO_ICC1;

	pRequest[ PSCR_PCB ] = PCB_DEFAULT;
	//
	//	get the length of the user data packet & set the appropriate LEN
	//	information the complete user packet consists of a SCARD_IO_REQUEST
	//	structure followed by the APDU. the length of SCARD_IO_REQUEST is
	//	transferred in the member cbPciLength of the structure
	//
	APDULength = SmartcardExtension->IoRequest.RequestBufferLength;
	APDULength -= ((PSCARD_IO_REQUEST) SmartcardExtension->
		IoRequest.RequestBuffer)->cbPciLength;
	//
	//	a 4 byte APDU will be patched to a 5 byte TPDU by the lib; see
	//	annex of the ISO
	//
	if( APDULength == 4 ) APDULength++;
	//
	//	if the total length of the T1 (reader) packet is larger than 0xFF
	//	the extended length notation will be used
	//
	if( APDULength >= 0xFF )
	{
		pRequest[ PSCR_LEN ]	= 0xFF;
		pRequest[ PSCR_LEN+1 ]	= HIBYTE( APDULength );
		pRequest[ PSCR_LEN+2 ]	= LOBYTE( APDULength );
		SmartcardExtension->SmartcardRequest.BufferLength =
			PSCR_EXT_PROLOGUE_LENGTH;
	}
	else
	{
		pRequest[ PSCR_LEN ] = ( UCHAR ) APDULength;
		SmartcardExtension->SmartcardRequest.BufferLength = 
			PSCR_PROLOGUE_LENGTH;
	}

	//	let the lib setup the T=1 APDU & check for errors
	NTStatus = SmartcardT0Request( SmartcardExtension );
    RequestLength = SmartcardExtension->SmartcardRequest.BufferLength;

	while( NT_SUCCESS( NTStatus ))
	{
		//	send command
		IOBytes = 0;
		NTStatus = PscrWrite(
			SmartcardExtension->ReaderExtension,
			pRequest,
			RequestLength,
			&IOBytes
			);

		//	get response
		if( NT_SUCCESS( NTStatus ))
		{
			IOBytes = 0;
			NTStatus = PscrRead(
				SmartcardExtension->ReaderExtension,
				IOData,
				MAX_T1_BLOCK_SIZE,
				&IOBytes
				);

			//	extract APDU from T=1 transport packet
			if( NT_SUCCESS( NTStatus ))
			{
                if (IOData[ PSCR_PCB ] == WTX_REQUEST) {

                    WtxReply[PSCR_NAD] = NAD_TO_PSCR;
                    WtxReply[PSCR_PCB] = WTX_REPLY;
                    WtxReply[PSCR_LEN] = 1;
                    WtxReply[PSCR_INF] = IOData[PSCR_INF];

                    RequestLength = 4;
                    pRequest = WtxReply;
                    continue;
                }

				if( IOData[ PSCR_LEN ] == 0xFF )
				{
					//
					//	extended length byte used
					//
					APDULength  = IOData[ PSCR_LEN + 1 ] << 8;
					APDULength += IOData[ PSCR_LEN + 2 ];

					SmartcardExtension->SmartcardReply.BufferLength = APDULength ;
					SysCopyMemory( pReply, &IOData[ PSCR_APDU + 2 ], APDULength );
				}
				else
				{
					SmartcardExtension->SmartcardReply.BufferLength = 
						IOData[ PSCR_LEN ];

					SysCopyMemory( 
						pReply, 
						&IOData[ PSCR_APDU ], 
						IOData[ PSCR_LEN ] 
						);
				}

				// let the lib evaluate the result & tansfer the data
				NTStatus = SmartcardT0Reply( SmartcardExtension );
                break;
			}
		}
	}

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBT0Transmit: Exit (%lx)\n", NTStatus )
		);

    return( NTStatus );
}

NTSTATUS
CBCardTracking( 
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBCardTracking:
	callback handler for SMCLIB RDF_CARD_TRACKING. the requested event was 
	validated by the smclib (i.e. a card removal request will only be passed 
	if a card is present).
	for a win95 build STATUS_PENDING will be returned without any other action. 
	for NT the cancel routine for the irp will be set to the drivers cancel
	routine.

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_PENDING

--*/
{
	KIRQL CancelIrql;

	SmartcardDebug( 
		DEBUG_TRACE, 
		( "PSCR!CBCardTracking: Enter\n" )
		);

	//	set cancel routine
	IoAcquireCancelSpinLock( &CancelIrql );

	IoSetCancelRoutine(
		SmartcardExtension->OsData->NotificationIrp, 
		PscrCancel
		);

	IoReleaseCancelSpinLock( CancelIrql );

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBCardTracking: Exit \n" )
		);

	return( STATUS_PENDING );
}

VOID
CBUpdateCardState(
	PSMARTCARD_EXTENSION SmartcardExtension,
    UCHAR IccState,
    BOOLEAN SystemWakeUp
	)
{
    ULONG oldState;
    KIRQL currentIrql, irql;

    KeAcquireSpinLock(
        &SmartcardExtension->OsData->SpinLock,
        &irql
        );

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBUpdateCardState: Enter \n" )
		);

    oldState = 
        (SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT ?
        SCARD_PRESENT : SCARD_ABSENT);

	SmartcardExtension->ReaderCapabilities.CurrentState = 
        (IccState == PSCR_ICC_PRESENT ? SCARD_PRESENT : SCARD_ABSENT);

	SmartcardDebug( 
		DEBUG_DRIVER, 
		( "PSCR!CBUpdateCardState: Smart card %s\n",
        IccState == PSCR_ICC_PRESENT ? "inserted" : "removed")
		);

    IoAcquireCancelSpinLock( &currentIrql );

	if( SmartcardExtension->OsData->NotificationIrp != NULL && (
            SystemWakeUp && (
                oldState == SCARD_PRESENT || 
                SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_PRESENT) || 
            SmartcardExtension->ReaderCapabilities.CurrentState != oldState)) {

        PIRP notificationIrp = InterlockedExchangePointer(
            &(SmartcardExtension->OsData->NotificationIrp),
            NULL
            );

		IoSetCancelRoutine( 
            notificationIrp, 
            NULL 
            );

		IoReleaseCancelSpinLock( currentIrql );

        if (notificationIrp->Cancel == FALSE) {
         	
		    //	finish the request
		    notificationIrp->IoStatus.Status	= STATUS_SUCCESS;
		    notificationIrp->IoStatus.Information = 0;

	        SmartcardDebug( 
		        DEBUG_DRIVER, 
		        ( "PSCR!CBUpdateCardState: Completing Irp %lx\n",
                notificationIrp)
		        );

		    IoCompleteRequest(notificationIrp, IO_NO_INCREMENT );
        }

    } else {
     	
		IoReleaseCancelSpinLock( currentIrql );
    }

	SmartcardDebug( 
		DEBUG_TRACE,
		( "PSCR!CBUpdateCardState: Exit \n" )
		);

	KeReleaseSpinLock(
		&SmartcardExtension->OsData->SpinLock,
		irql
		);
}

UCHAR 
CBGetCardState(
	PSMARTCARD_EXTENSION SmartcardExtension 
	)
/*++

CBUpdateCardState:
	updates the variable CurrentState in SmartcardExtension

Arguments:
	SmartcardExtension	context of call

Return Value:
	STATUS_SUCCESS

--*/
{
	NTSTATUS NTStatus = STATUS_SUCCESS;
	UCHAR TLVList[ TLV_BUFFER_SIZE ],	Val, Len;
	ULONG TLVListLen;
	PREADER_EXTENSION	ReaderExtension = SmartcardExtension->ReaderExtension;

	//	read the status file of ICC1 from the reader
	TLVListLen = TLV_BUFFER_SIZE;

	if( NT_SUCCESS( CmdReadStatusFile(
		ReaderExtension,
		ReaderExtension->Device,
		TLVList,
		&TLVListLen
		)))	{

		//	get reader status value
		CmdGetTagValue(
			TAG_READER_STATUS,
			TLVList,
			TLVListLen,
			&Len,
			( PVOID ) &Val
			);
	}
	else
	{
		//	IO-error is interpreted as card absent
        ASSERT(FALSE);
		Val = PSCR_ICC_ABSENT;
	}

    return Val;
}

//	-------------------------------- END OF FILE ------------------------------



