/*++

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

Module Name:

    PscrCmd.c

Abstract:

	Basic command functions for SCM PSCR smartcard reader

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	delay for read/write removed

--*/

#if defined( SMCLIB_VXD )

#include <Pscr95.h>

#else	//	SMCLIB_VXD

#include <PscrNT.h>

#endif	//	SMCLIB_VXD

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

NTSTATUS
CmdResetInterface(	
	PREADER_EXTENSION ReaderExtension 
	)
/*++
CmdResetInterface:

	Performs a reset of the reader interface (NOT of the PCMCIA controller)
	- flush available data
	- set RESET bit
	- perform a buffer size exchange between reader & host
	- enables interrupts for freeze events
	- disables default PTS

Arguments:
	ReaderExtension	context of call

Return Value:
	STATUS_SUCCESS
	STATUS_IO_DEVICE_ERROR

--*/
{

	NTSTATUS		NTStatus = STATUS_SUCCESS;
	ULONG			IOBytes;
	UCHAR			Len,
					Tag,
					Cnt,
					InData[ TLV_BUFFER_SIZE ];
	PPSCR_REGISTERS	IOBase;
	UCHAR			EnableInterrupts[]	= { 0x28, 0x01, 0x01 };


	IOBase = ReaderExtension->IOBase;

	//	discard any data
	PscrFlushInterface( ReaderExtension );

	//	reset reader
	WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_RESET_BIT );
	SysDelay( DELAY_WRITE_PSCR_REG );
	WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );

	NTStatus = PscrWait( ReaderExtension, PSCR_DATA_AVAIL_BIT );

	//	read & check vendor string
	if( NT_SUCCESS( NTStatus ))
	{

		WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
		SysDelay( DELAY_WRITE_PSCR_REG );
		//
		//	get actual len from TLV list
		//
		READ_PORT_UCHAR( &IOBase->SizeMSReg );
		READ_PORT_UCHAR( &IOBase->SizeLSReg );

		Tag = READ_PORT_UCHAR( &IOBase->DataReg );
		Len = READ_PORT_UCHAR( &IOBase->DataReg );
		//
		//	avoid overwrite of buffer
		//
		if( Len > TLV_BUFFER_SIZE )
		{
			Len = TLV_BUFFER_SIZE;
		}
		for ( Cnt = 0; Cnt < Len; Cnt++ )
		{
			InData[ Cnt ] = READ_PORT_UCHAR( &IOBase->DataReg );
		}
		WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
		//
		//	check vendor string
		//
		if( SysCompareMemory(
						InData,
						PSCR_ID_STRING,
						sizeof( PSCR_ID_STRING )
						))
		{
			NTStatus = STATUS_IO_DEVICE_ERROR;
		}
		else
		{
			//
			//	vendor string was correct, check buffer size
			//
			WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_SIZE_READ_BIT );
			NTStatus = PscrWait( ReaderExtension, PSCR_DATA_AVAIL_BIT );
			//
			//	reader ready to transfer interface buffer size
			//
			if( NT_SUCCESS( NTStatus ))
			{
				//
				//	set size read & host control
				//
				WRITE_PORT_UCHAR(
					&IOBase->CmdStatusReg,
					PSCR_SIZE_READ_BIT | PSCR_HOST_CONTROL_BIT
					);

				SysDelay( DELAY_WRITE_PSCR_REG );
				//
				//	read buffer length
				//
				Len = READ_PORT_UCHAR( &IOBase->SizeMSReg );
				Len = READ_PORT_UCHAR( &IOBase->SizeLSReg );
				for ( Cnt = 0; Cnt < Len; Cnt++ )
				{
					InData[ Cnt ] = READ_PORT_UCHAR( &IOBase->DataReg );
				}
				//
				//	transfer of interface buffer size okay
				//
				WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
				SysDelay( DELAY_WRITE_PSCR_REG );
				//
				//	notify the reader about the supported buffer size
				//
				WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
				SysDelay( DELAY_WRITE_PSCR_REG );

				WRITE_PORT_UCHAR( &IOBase->SizeMSReg, 0 );
				WRITE_PORT_UCHAR( &IOBase->SizeLSReg, 2 );
				//
				//	Write the same data buffer size as the one we just got.
				//
				WRITE_PORT_UCHAR( &IOBase->DataReg, InData[ 0 ] );
				WRITE_PORT_UCHAR( &IOBase->DataReg, InData[ 1 ] );
				//
				//	store the size to report to the lib
				//  The maximum buffer size of the reader is to betrieved with 
                //  ((ULONG)InData[ 1 ] << 8) | InData[ 0 ]
                //
				ReaderExtension->MaxIFSD = 254;
					
				//
				//	let the reader process the size write command
				//
				WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_SIZE_WRITE_BIT );
				NTStatus = PscrWait( ReaderExtension, PSCR_FREE_BIT );
			}
		}
	}
	//
	//	clean up any host control settings
	//
	WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
	//
	//	enable interrupts
	//
	CmdSetInterfaceParameter( 
		ReaderExtension, 
		ReaderExtension->Device,
		EnableInterrupts,
		sizeof( EnableInterrupts )
		);

	return ( NTStatus );
}

NTSTATUS
CmdReset( 
	PREADER_EXTENSION	ReaderExtension,
	UCHAR				Device,
	BOOLEAN				WarmReset,
	PUCHAR				pATR,
	PULONG				pATRLength
	)
/*++
CmdReset:
	performs a reset of the reader / ICC

Arguments:
	ReaderExtension		context of call
	Device				device requested ( ICC_1, ICC_2, PSCR )
	WarmReset			kind of ICC reset
	pATR				ptr to ATR buffer, NULL if no ATR required
	pATRLength			size of ATR buffer / length of ATR

Return Value:
	STATUS_SUCCESS
	STATUS_NO_MEDIA
	STATUS_UNRECOGNIZED_MEDIA
	error values from PscrRead / PscrWrite

--*/
{
	NTSTATUS	NTStatus = STATUS_SUCCESS;
	UCHAR		IOData[ MAX_T1_BLOCK_SIZE ],
				P2;
	USHORT		ICCStatus;
	ULONG		IOBytes;

	//	ATR from the smartcard requestet? P2 = 1
	P2 = 0;
	if(( pATR != NULL ) && ( pATRLength != NULL ))
	{
		if( *pATRLength > 0 )
			P2 = 0x01;
	}

	//	build the RESET command.
	IOData[ PSCR_NAD ] = NAD_TO_PSCR;
	IOData[ PSCR_PCB ] = PCB_DEFAULT;
	IOData[ PSCR_LEN ] = 0x05;

	if( WarmReset == TRUE )
	{
		IOData[ PSCR_INF+0 ] = CLA_WARM_RESET;
		IOData[ PSCR_INF+1 ] = INS_WARM_RESET;
	}
	else
	{
		IOData[ PSCR_INF+0 ] = CLA_RESET;
		IOData[ PSCR_INF+1 ] = INS_RESET;
	}
	IOData[ PSCR_INF+2 ] = Device;
	IOData[ PSCR_INF+3 ] = P2;
	IOData[ PSCR_INF+4 ] = 0x00;

	//	write command
	NTStatus = PscrWrite(
		ReaderExtension,
		IOData,
		8,
		&IOBytes
		);

	if( NT_SUCCESS( NTStatus ))
	{
		//	read data
		IOBytes = 0;
		NTStatus = PscrRead(
			ReaderExtension,
			IOData,
			MAX_T1_BLOCK_SIZE,
			&IOBytes
			);

		//	error detection
		if( NT_SUCCESS( NTStatus ))
		{
			//
			// the location of the error code in the buffer
			// is: ( data ) - STATUS_MSB - STATUS_LSB - EPILOGUE
			//
			ICCStatus = (( USHORT )IOData[ IOBytes-PSCR_EPILOGUE_LENGTH-2 ]) << 8;
			ICCStatus |= ( USHORT )IOData[ IOBytes-PSCR_EPILOGUE_LENGTH-1 ];

			switch( ICCStatus )
			{
				case PSCR_SW_SYNC_ATR_SUCCESS:
				case PSCR_SW_ASYNC_ATR_SUCCESS:
					break;

				case PSCR_SW_NO_ICC:
					NTStatus = STATUS_NO_MEDIA;
					break;

				case PSCR_SW_NO_PROTOCOL:
				case PSCR_SW_NO_ATR:
				case PSCR_SW_NO_ATR_OR_PROTOCOL:
				case PSCR_SW_NO_ATR_OR_PROTOCOL2:
				case PSCR_SW_ICC_NOT_ACTIVE:
				case PSCR_SW_NON_SUPPORTED_PROTOCOL:
				case PSCR_SW_PROTOCOL_ERROR:
				default:
					NTStatus = STATUS_UNRECOGNIZED_MEDIA;
			}
			//
			//	copy ATR if required
			//
			if( NT_SUCCESS( NTStatus ))
			{
				if( P2 == 0x01 )
				{
					IOBytes -= PSCR_PROLOGUE_LENGTH + PSCR_EPILOGUE_LENGTH;
					if( IOBytes > *pATRLength )
					{
						IOBytes = *pATRLength;
					}
					SysCopyMemory(	
						pATR, 
						&IOData[ PSCR_PROLOGUE_LENGTH ], 
						IOBytes 
						);
					*pATRLength = IOBytes;
				}
			}
		}
	}
	return( NTStatus );
}


NTSTATUS
CmdDeactivate( 
	PREADER_EXTENSION	ReaderExtension,
	UCHAR				Device
	)
/*++
CmdDeactivate:
	Deactivates the requested device

Arguments:
	ReaderExtension		context of call
	Device				requested device

Return Value:
	STATUS_SUCCESS
	error values from PscrRead / PscrWrite

--*/
{
	NTSTATUS	NTStatus = STATUS_SUCCESS;
	UCHAR		IOData[  MAX_T1_BLOCK_SIZE ];
	ULONG		IOBytes;

	//
	//	build the DEACTIVATE command.
	//
	IOData[ PSCR_NAD ] = NAD_TO_PSCR;
	IOData[ PSCR_PCB ] = PCB_DEFAULT;
	IOData[ PSCR_LEN ] = 0x05;

	IOData[ PSCR_INF+0 ] = CLA_DEACTIVATE;
	IOData[ PSCR_INF+1 ] = INS_DEACTIVATE;
	IOData[ PSCR_INF+2 ] = Device;
	IOData[ PSCR_INF+3 ] = 0x00;
	IOData[ PSCR_INF+4 ] = 0x00;
	//
	//	write command
	//
	NTStatus = PscrWrite(
		ReaderExtension,
		IOData,
		8,
		&IOBytes
		);

	if( NT_SUCCESS( NTStatus ))
	{
		//
		//	read data to trap communication errors
		//
		IOBytes = 0;
		NTStatus = PscrRead(
			ReaderExtension,
			IOData,
			MAX_T1_BLOCK_SIZE,
			&IOBytes
			);
	}
	return( NTStatus );
}

NTSTATUS
CmdReadBinary(
	PREADER_EXTENSION	ReaderExtension,
	USHORT				Offset,
	PUCHAR				pBuffer,
	PULONG				pBufferLength
	)
/*++
CmdReadBinary:
	read binary data from an PSCR data file

Arguments:
	ReaderExtension		context of call
	Offset				offset in file
	pBuffer				ptr to data buffer
	pBufferLength		length of buffer / number of bytes read

Return Value:

	STATUS_SUCCESS
	STATUS_UNSUCCESSFUL
	error values from PscrRead / PscrWrite

--*/
{
    NTSTATUS	NTStatus = STATUS_SUCCESS;
    UCHAR		IOData[ MAX_T1_BLOCK_SIZE ];
	USHORT		ICCStatus;
	ULONG		IOBytes;
	// 
	//	check parameters
	//
	if(( pBuffer == NULL ) || ( pBufferLength == NULL))
	{
		NTStatus = STATUS_INVALID_PARAMETER;
	}
	else
	{
		//
		//	build the READ BINARY command
		//
		IOData[ PSCR_NAD] = NAD_TO_PSCR;
		IOData[ PSCR_PCB] = PCB_DEFAULT;
		IOData[ PSCR_LEN] = 0x05;

		IOData[ PSCR_INF+0 ] = CLA_READ_BINARY;
		IOData[ PSCR_INF+1 ] = INS_READ_BINARY;
		IOData[ PSCR_INF+2 ] = HIBYTE( Offset );
		IOData[ PSCR_INF+3 ] = LOBYTE( Offset );
		IOData[ PSCR_INF+4 ] = 0x00;
		//
		//	write command
		//
		NTStatus = PscrWrite(
			ReaderExtension,
			IOData,
			8,
			&IOBytes
			);

		if( NT_SUCCESS( NTStatus ))
		{
			//
			//	read data
			//
			IOBytes = 0;
			NTStatus = PscrRead(
				ReaderExtension,
				IOData,
				MAX_T1_BLOCK_SIZE,
				&IOBytes
				);
			
			if( NT_SUCCESS( NTStatus ))
			{
				//
				//	error check
				//
				ICCStatus = 
					((USHORT)IOData[ IOBytes-2-PSCR_EPILOGUE_LENGTH ]) << 8;
				ICCStatus |= 
					(USHORT)IOData[ IOBytes-1-PSCR_EPILOGUE_LENGTH ];

				switch( ICCStatus )
				{
					case PSCR_SW_FILE_NO_ACCEPPTED_AUTH:
					case PSCR_SW_FILE_NO_ACCESS:
					case PSCR_SW_FILE_BAD_OFFSET:
					case PSCR_SW_END_OF_FILE_READ:
						NTStatus = STATUS_UNSUCCESSFUL;
						break;
					default:
						break;
				}
				//
				//	copy data
				//
				if( NT_SUCCESS( NTStatus ))
				{
					if( *pBufferLength < 
						IOBytes-PSCR_PROLOGUE_LENGTH-PSCR_STATUS_LENGTH )
					{
						NTStatus = STATUS_BUFFER_TOO_SMALL;
					}
					else
					{
						( *pBufferLength ) = 
							IOBytes-PSCR_PROLOGUE_LENGTH-PSCR_STATUS_LENGTH;
						SysCopyMemory( 
							pBuffer, 
							&IOData[ PSCR_PROLOGUE_LENGTH ], 
							*pBufferLength 
							);
					}
				}
			}
		}
	}
	return( NTStatus );
}

NTSTATUS
CmdSelectFile(
	PREADER_EXTENSION	ReaderExtension,
	USHORT				FileId
	)
/*++
CmdSelectFile:
	selects a file/directoy of the reader

Arguments:
	ReaderExtension		context of call
	FileId				ID of file

Return Value:
	STATUS_SUCCESS
	STATUS_UNSUCCESSFUL
	error values from PscrRead / PscrWrite
--*/
{
    NTSTATUS	NTStatus = STATUS_SUCCESS;
    UCHAR		IOData[ MAX_T1_BLOCK_SIZE ];
	USHORT		ICCStatus;
	ULONG		IOBytes;

	//
	//	build the SELECT FILE command
	//
	IOData[ PSCR_NAD ] = NAD_TO_PSCR;
	IOData[ PSCR_PCB ] = PCB_DEFAULT;
	IOData[ PSCR_LEN ] = 0x07;

	IOData[ PSCR_INF+0 ] = CLA_SELECT_FILE;
	IOData[ PSCR_INF+1 ] = INS_SELECT_FILE;
	IOData[ PSCR_INF+2 ] = 0x00;
	IOData[ PSCR_INF+3 ] = 0x00;
	IOData[ PSCR_INF+4 ] = 0x02;
	IOData[ PSCR_INF+5 ] = HIBYTE( FileId );
	IOData[ PSCR_INF+6 ] = LOBYTE( FileId );
	//
	//	write command
	//
	NTStatus = PscrWrite(
		ReaderExtension,
		IOData,
		10,
		&IOBytes
		);

	if( NT_SUCCESS( NTStatus ))
	{
		//
		//	get the response of the reader
		//
		IOBytes = 0;
		NTStatus = PscrRead(
			ReaderExtension,
			IOData,
			MAX_T1_BLOCK_SIZE,
			&IOBytes
			);

		if( NT_SUCCESS( NTStatus ))
		{
			//
			//	check errors
			//
			ICCStatus = 
				((USHORT)IOData[ IOBytes-2-PSCR_EPILOGUE_LENGTH ]) << 8;
			ICCStatus |=
				(USHORT)IOData[ IOBytes-1-PSCR_EPILOGUE_LENGTH ];

			if( ICCStatus == PSCR_SW_FILE_NOT_FOUND )
			{
				NTStatus = STATUS_UNSUCCESSFUL;
			}
		}
	}
	return( NTStatus );
}

NTSTATUS
CmdSetInterfaceParameter(
	PREADER_EXTENSION	ReaderExtension,
	UCHAR				Device,
	PUCHAR				pTLVList,
	UCHAR				TLVListLen
	)
/*++
CmdSetInterfaceParameter:
	Sets the interface pareameter of the ICC interface to the values specified
	in the TLV list

Arguments:
	ReaderExtension		context of call
	Device				device
	pTLVList			ptr to list of tag-len-value's specified by caller
	TLVListLen			length of list

Return Value:
	STATUS_SUCCESS
	STATUS_INVALID_PARAMETER
	STATUS_INVALID_DEVICE_STATE
	error values from PscrRead / PscrWrite

--*/
{
    NTSTATUS	NTStatus = STATUS_SUCCESS;
    UCHAR		IOData[ MAX_T1_BLOCK_SIZE ];
	USHORT		ICCStatus;
	ULONG		IOBytes;

	// 
	//	check parameter
	//
	if( pTLVList == NULL )
	{
		NTStatus = STATUS_INVALID_PARAMETER;
	}
	else 
	{
		//
		//	build the SET INTERFACE PARAMETER command
		//
		IOData[ PSCR_NAD ] = NAD_TO_PSCR;
		IOData[ PSCR_PCB ] = PCB_DEFAULT;
		IOData[ PSCR_LEN ] = 0x05 + TLVListLen;

		IOData[ PSCR_INF+0 ] = CLA_SET_INTERFACE_PARAM;
		IOData[ PSCR_INF+1 ] = INS_SET_INTERFACE_PARAM;
		IOData[ PSCR_INF+2 ] = Device;
		IOData[ PSCR_INF+3 ] = 0x00;
		IOData[ PSCR_INF+4 ] = TLVListLen;

		SysCopyMemory( &IOData[ PSCR_INF+5 ], pTLVList, TLVListLen );
		//
		//	write command
		//
		NTStatus = PscrWrite(
			ReaderExtension,
			IOData,
			8 + TLVListLen,
			&IOBytes
			);

		if( NT_SUCCESS( NTStatus ))
		{

			//	do an dummy read to catch errors.
			IOBytes = 0;
			NTStatus = PscrRead(
				ReaderExtension,
				IOData,
				MAX_T1_BLOCK_SIZE,
				&IOBytes
				);
			
			if( NT_SUCCESS( NTStatus ))
			{

				//	check error
				ICCStatus = 
					((USHORT)IOData[ IOBytes - 2 - PSCR_EPILOGUE_LENGTH ]) << 8;
				ICCStatus |= 
					(USHORT)IOData[ IOBytes - 1 - PSCR_EPILOGUE_LENGTH ];

				if( ICCStatus != 0x9000 )
				{
					NTStatus = STATUS_INVALID_DEVICE_STATE;
				}
			}
		}
	}
	return( NTStatus );
}


NTSTATUS
CmdReadStatusFile (
	PREADER_EXTENSION	ReaderExtension,
	UCHAR				Device,
	PUCHAR				pTLVList,
	PULONG				pTLVListLen
	)
/*++
CmdReadStatusFile:
	read the status file of the requested device from the reader filesystem

Arguments:
	ReaderExtension		context of call
	Device				requested device
	pTLVList			ptr to list (i.e. the status file)
	pTLVListLen			length of buffer / returned list

Return Value:
	STATUS_SUCCESS
	STATUS_BUFFER_TOO_SMALL
	error values from PscrRead / PscrWrite

--*/
{
	NTSTATUS			NTStatus = STATUS_UNSUCCESSFUL;
	UCHAR				IOData[ MAX_T1_BLOCK_SIZE ];
	ULONG				IOBytes;

	//	select ICC status file if it's not the active file
	if( ReaderExtension->StatusFileSelected == FALSE )
	{

		//	select master file on reader
		NTStatus = CmdSelectFile( ReaderExtension, FILE_MASTER );

		//	select ICC directory
		if( NT_SUCCESS( NTStatus ))
		{
			if( Device != DEVICE_ICC1 )
			{
				NTStatus = STATUS_UNSUCCESSFUL;
			}
			else
			{
				NTStatus = CmdSelectFile( 
					ReaderExtension, 
					FILE_ICC1_DIR
					);

				//	select status file
				if( NT_SUCCESS( NTStatus ))
				{
					NTStatus = CmdSelectFile( 
						ReaderExtension, 
						FILE_ICC1_DIR_STATUS
						);
					if( NT_SUCCESS( NTStatus ))
					{
						ReaderExtension->StatusFileSelected = TRUE;
					}
				}
			}
		}
	}

	//	read status file if successful selected
	if( ReaderExtension->StatusFileSelected == TRUE )
	{
		IOBytes = MAX_T1_BLOCK_SIZE;
		NTStatus = CmdReadBinary(
			ReaderExtension,
			0,
			IOData,
			&IOBytes
			);

		//	copy data to user buffer
		if( NT_SUCCESS( NTStatus ))
		{
			if(( pTLVList != NULL ) && ( IOBytes < *pTLVListLen ))
			{
				*pTLVListLen = IOBytes;
				SysCopyMemory( pTLVList, IOData, IOBytes );
			}
			else
			{
				NTStatus = STATUS_BUFFER_TOO_SMALL;
			}
		}
	}
	return( NTStatus );
}


NTSTATUS
CmdGetFirmwareRevision (
	PREADER_EXTENSION	ReaderExtension
	)
/*++
CmdGetFirmwareRevision:
	get the firmware revision of the reader. Ther firmware revision is found
	in the PSCR configuration file (ID 0x0020) in the master directory.
	The tag of the revision is 0x0F, and the value is coded as an ASCII string,
	p.E. "2.20"

Arguments:
	ReaderExtension		context of call

Return Value:
	STATUS_SUCCESS
	error values from PscrRead / PscrWrite

--*/
{
	NTSTATUS			NTStatus = STATUS_SUCCESS;
	UCHAR				TLVList[ MAX_T1_BLOCK_SIZE ],
						Len;
	char				Revision[ 0x10 ],
						UpdateKey[ 0x10 ];
	ULONG				IOBytes;
	//
	//	select master file on reader
	//
	NTStatus = CmdSelectFile( ReaderExtension, FILE_MASTER );
	//
	//	select pscr configuration file
	//
	if( NT_SUCCESS( NTStatus ))
	{
		NTStatus = CmdSelectFile( ReaderExtension, FILE_PSCR_CONFIG );
		//
		//	read confiuration file
		//
		if( NT_SUCCESS( NTStatus ))
		{
			IOBytes = MAX_T1_BLOCK_SIZE;
			NTStatus = CmdReadBinary(
				ReaderExtension,
				0,
				TLVList,
				&IOBytes
				);
			//
			//	get the value of revison
			//
			if( NT_SUCCESS( NTStatus ))
			{
				CmdGetTagValue( 
					TAG_SOFTWARE_REV, 
					TLVList, 
					IOBytes, 
					&Len, 
					Revision 
					);
				//
				//	the coding is always X.YY (in ASCII), so we can get the numeric
				//	values hardcoded by taking the low nibbles of the char's.
				//
				ReaderExtension->FirmwareMajor =   Revision[0] & 0x0F;
				ReaderExtension->FirmwareMinor = ( Revision[2] & 0x0F ) << 4;
				ReaderExtension->FirmwareMinor |=  Revision[3] & 0x0F;
				//
				//	get value of update key
				//
				CmdGetTagValue( 
					TAG_UPDATE_KEY, 
					TLVList, 
					IOBytes, 
					&Len, 
					UpdateKey
					);

				ReaderExtension->UpdateKey = UpdateKey[0];
			}
		}
	}
	ReaderExtension->StatusFileSelected = FALSE;
	return( NTStatus );
}

NTSTATUS
CmdPscrCommand (
	PREADER_EXTENSION	ReaderExtension,
	PUCHAR				pInData,
	ULONG				InDataLen,
	PUCHAR				pOutData,
	ULONG				OutDataLen,
	PULONG				pNBytes
	)
/*++
CmdPscrCommand:
	send a command transparent to the reader

Arguments:
	ReaderExtension		context of call
	pInData,			ptr to input buffer
	InDataLen,			len of input buffer
	pOutData,			ptr to output buffer
	OutDataLen,			len of output buffer
	pNBytes				number of bytes transferred

Return Value:
	STATUS_SUCCESS
	STATUS_INVALID_PARAMETER
	error values from PscrRead / PscrWrite

--*/
{
	NTSTATUS			NTStatus = STATUS_SUCCESS;
	UCHAR				IOData[ MAX_T1_BLOCK_SIZE ];
	ULONG				IOBytes;
	//
	//	the function is used for generic ioctl's, so carful ALL
	//	parametes will be checked!
	//
	if( ( pInData == NULL ) || 
		( pOutData == NULL ) || 
		( pNBytes == NULL ) ||
		(InDataLen == 0 ) ||
		(OutDataLen == 0 )
		)
	{
		NTStatus = STATUS_INVALID_PARAMETER;
	}
	else
	{
		IOBytes = 0;
		NTStatus = PscrWriteDirect(
			ReaderExtension,
			pInData,
			InDataLen,
			&IOBytes
			);

		if( NT_SUCCESS( NTStatus ))
		{
			//
			//	get result. ignore all reader errors
			//
			IOBytes = 0;
			NTStatus = PscrRead(
				ReaderExtension,
				IOData,
				MAX_T1_BLOCK_SIZE,
				&IOBytes
				);
			//
			//	tranfer data
			//
			if( IOBytes > OutDataLen )
			{
				NTStatus = STATUS_BUFFER_TOO_SMALL;
			}
			else
			{
				*pNBytes = IOBytes;
				SysCopyMemory( pOutData, IOData, IOBytes );
			}
		}
	}
	return( NTStatus );
}

NTSTATUS
CmdGetTagValue (
	UCHAR	Tag,
	PUCHAR	pTLVList,
	ULONG	TLVListLen,
	PUCHAR	pTagLen,
	PVOID	pTagVal
	)
/*++
CmdGetTagValue:
	scans a TLV list for the value of a user specified tag
	it is assumed, the caller knows the kind of the requested
	field, so only the ptr to the buffer will be checked

Arguments:
	Tag				requested Tag
	pTLVList		ptr to list
	TLVListLen		length of list
	pTagLen			ptr to length
	pTagVal			ptr to value

Return Value:
	STATUS_SUCCESS
	STATUS_UNSUCCESSFUL
	STATUS_INVALID_PARAMETER

--*/
{
	NTSTATUS	NTStatus = STATUS_SUCCESS;
	ULONG		Idx;
	//
	//	step through the given list
	//
	if(( pTLVList != NULL ) && ( pTagVal != NULL ) && ( pTagLen != NULL ))
	{
		//
		//	look for requested tag
		//
		Idx = 0;
		while( Idx < TLVListLen )
		{
			if( pTLVList[ Idx ] == Tag )
			{
				//
				//	ASSUMED THE CALLER KNOWS KIND OF FIELD!!!
				//
				*pTagLen = pTLVList[ Idx + 1 ];
				SysCopyMemory( 
					pTagVal, 
					&pTLVList[ Idx+2 ], 
					pTLVList[ Idx+1 ] 
					);

				break;
			}
			Idx += pTLVList[ Idx+1 ] + 2;
		}
		if( Idx >= TLVListLen )
		{
			NTStatus = STATUS_UNSUCCESSFUL;
		}
	}
	else
	{
		NTStatus = STATUS_INVALID_PARAMETER;
	}
	return( NTStatus );
}

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

