430 lines
8.5 KiB
C
430 lines
8.5 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 - 1999 SCM Microsystems, Inc.
|
|
|
|
Module Name:
|
|
|
|
PscrRdWr.c
|
|
|
|
Abstract:
|
|
|
|
Hardware access 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 7/16/1997 1.00 Initial Version
|
|
Klaus Schuetz 9/20/1997 1.01 Timing changed
|
|
Andreas Straub 9/24/1997 1.02 Low Level error handling,
|
|
minor bugfixes, clanup
|
|
|
|
--*/
|
|
|
|
#if defined( SMCLIB_VXD )
|
|
|
|
#include <Pscr95.h>
|
|
|
|
#else // SMCLIB_VXD
|
|
|
|
#include <PscrNT.h>
|
|
|
|
#endif // SMCLIB_VXD
|
|
|
|
#include <PscrCmd.h>
|
|
#include <PscrRdWr.h>
|
|
|
|
#pragma optimize( "", off )
|
|
VOID
|
|
PscrFlushInterface( PREADER_EXTENSION ReaderExtension )
|
|
/*++
|
|
|
|
PscrFlushInterface:
|
|
Read & discard data from the pcmcia interface
|
|
|
|
Arguments:
|
|
ReaderExtension context of call
|
|
|
|
Return Value:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
UCHAR Status;
|
|
ULONG Length;
|
|
PPSCR_REGISTERS IOBase;
|
|
|
|
IOBase = ReaderExtension->IOBase;
|
|
|
|
Status = READ_PORT_UCHAR( &IOBase->CmdStatusReg );
|
|
if(( Status & PSCR_DATA_AVAIL_BIT ) && ( Status & PSCR_FREE_BIT ))
|
|
{
|
|
|
|
// take control over
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
|
|
|
|
// get number of available bytes
|
|
Length = ((ULONG)READ_PORT_UCHAR( &IOBase->SizeMSReg )) << 8;
|
|
Length |= READ_PORT_UCHAR( &IOBase->SizeLSReg );
|
|
|
|
// perform a dummy read
|
|
while( Length-- )
|
|
{
|
|
READ_PORT_UCHAR( &IOBase->DataReg );
|
|
}
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
|
|
}
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
PscrRead(
|
|
PREADER_EXTENSION ReaderExtension,
|
|
PUCHAR pData,
|
|
ULONG DataLen,
|
|
PULONG pNBytes
|
|
)
|
|
/*++
|
|
PscrRead:
|
|
wait until data available & transfer data from reader to host
|
|
|
|
Arguments:
|
|
ReaderExtension context of call
|
|
pData ptr to data buffer
|
|
DataLen length of data buffer
|
|
pNBytes number of bytes returned
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS
|
|
STATUS_BUFFER_TOO_SMALL
|
|
STATUS_UNSUCCESSFUL
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NTStatus = STATUS_UNSUCCESSFUL;
|
|
USHORT ReaderStatus;
|
|
PPSCR_REGISTERS IOBase;
|
|
USHORT InDataLen;
|
|
|
|
IOBase = ReaderExtension->IOBase;
|
|
|
|
// wait until interface is ready to transfer
|
|
InDataLen = 0;
|
|
|
|
if( NT_SUCCESS( NTStatus = PscrWait( ReaderExtension, PSCR_DATA_AVAIL_BIT | PSCR_FREE_BIT )))
|
|
{
|
|
// take control over
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
|
|
|
|
// get number of available bytes
|
|
InDataLen = ( READ_PORT_UCHAR( &IOBase->SizeMSReg ) << 8 );
|
|
InDataLen |= READ_PORT_UCHAR( &IOBase->SizeLSReg );
|
|
|
|
if (InDataLen <= PSCR_PROLOGUE_LENGTH) {
|
|
|
|
// the buffer does not contain the minimum packet length
|
|
NTStatus = STATUS_IO_TIMEOUT;
|
|
|
|
} else if( ( ULONG )InDataLen <= DataLen ) {
|
|
|
|
// check buffer size. if buffer to small, the data will be discarded
|
|
|
|
// read data
|
|
ULONG Idx;
|
|
for (Idx = 0; Idx < InDataLen; Idx++) {
|
|
|
|
pData[ Idx ] = READ_PORT_UCHAR( &IOBase->DataReg );
|
|
}
|
|
|
|
// error check
|
|
if( pData[ InDataLen - 1 ] !=
|
|
PscrCalculateLRC( pData, (USHORT)( InDataLen - 1 )))
|
|
{
|
|
NTStatus = STATUS_CRC_ERROR;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Evaluation of reader errors. A reader error is indicated
|
|
// if the T1 length is 2 and the Nad indicates that this
|
|
// packet came from the reader
|
|
//
|
|
if( ( ( pData[ PSCR_NAD ] & 0x0F ) == 0x01 ) &&
|
|
( pData[ PSCR_LEN ] == 0x02 )
|
|
)
|
|
{
|
|
ReaderStatus = (( USHORT ) pData[3] ) << 8;
|
|
ReaderStatus |= (( USHORT ) pData[4] );
|
|
|
|
if( ( ReaderStatus != 0x9000 ) &&
|
|
( ReaderStatus != 0x9001 )
|
|
)
|
|
{
|
|
SmartcardDebug(
|
|
DEBUG_TRACE,
|
|
( "PSCR!PscrRead: ReaderStatus = %lx\n", ReaderStatus )
|
|
);
|
|
|
|
InDataLen = 0;
|
|
|
|
if (ReaderStatus == PSCR_SW_PROTOCOL_ERROR) {
|
|
|
|
NTStatus = STATUS_IO_TIMEOUT;
|
|
|
|
} else {
|
|
|
|
NTStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// flush interface in case of wrong buffer size
|
|
do
|
|
{
|
|
READ_PORT_UCHAR( &IOBase->DataReg );
|
|
|
|
} while( --InDataLen );
|
|
|
|
NTStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// clean up
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
|
|
}
|
|
|
|
// write number of bytes received
|
|
if( InDataLen )
|
|
{
|
|
if( pNBytes != NULL )
|
|
{
|
|
( *pNBytes ) = ( ULONG ) InDataLen;
|
|
}
|
|
NTStatus = STATUS_SUCCESS;
|
|
}
|
|
return ( NTStatus );
|
|
}
|
|
|
|
NTSTATUS
|
|
PscrWrite(
|
|
PREADER_EXTENSION ReaderExtension,
|
|
PUCHAR pData,
|
|
ULONG DataLen,
|
|
PULONG pNBytes
|
|
)
|
|
/*++
|
|
PscrWrite:
|
|
calculates the LRC of the buffer & sends command to the reader
|
|
|
|
Arguments:
|
|
ReaderExtension context of call
|
|
pData ptr to data buffer
|
|
DataLen length of data buffer (exclusive LRC!)
|
|
pNBytes number of bytes written
|
|
|
|
Return Value:
|
|
return value of PscrWriteDirect
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NTStatus;
|
|
|
|
// Add the EDC field to the end of the data
|
|
pData[ DataLen ] = PscrCalculateLRC( pData, ( USHORT ) DataLen );
|
|
|
|
// Send buffer
|
|
NTStatus = PscrWriteDirect(
|
|
ReaderExtension,
|
|
pData,
|
|
DataLen + PSCR_EPILOGUE_LENGTH,
|
|
pNBytes
|
|
);
|
|
|
|
return( NTStatus );
|
|
}
|
|
|
|
NTSTATUS
|
|
PscrWriteDirect(
|
|
PREADER_EXTENSION ReaderExtension,
|
|
PUCHAR pData,
|
|
ULONG DataLen,
|
|
PULONG pNBytes
|
|
)
|
|
|
|
/*++
|
|
PscrWriteDirect:
|
|
sends command to the reader. The LRC / CRC must be calculated by caller!
|
|
|
|
Arguments:
|
|
ReaderExtension context of call
|
|
pData ptr to data buffer
|
|
DataLen length of data buffer (exclusive LRC!)
|
|
pNBytes number of bytes written
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS
|
|
STATUS_DEVICE_BUSY
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
|
UCHAR Status;
|
|
PPSCR_REGISTERS IOBase;
|
|
|
|
IOBase = ReaderExtension->IOBase;
|
|
|
|
// in case of card change, there may be data available
|
|
Status = READ_PORT_UCHAR( &IOBase->CmdStatusReg );
|
|
if( Status & PSCR_DATA_AVAIL_BIT )
|
|
{
|
|
NTStatus = STATUS_DEVICE_BUSY;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// wait until reader is ready
|
|
//
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
|
|
NTStatus = PscrWait( ReaderExtension, PSCR_FREE_BIT );
|
|
|
|
if( NT_SUCCESS( NTStatus ))
|
|
{
|
|
ULONG Idx;
|
|
|
|
// take control over
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, PSCR_HOST_CONTROL_BIT );
|
|
|
|
// write the buffer size
|
|
WRITE_PORT_UCHAR( &IOBase->SizeMSReg, ( UCHAR )( DataLen >> 8 ));
|
|
SysDelay( DELAY_WRITE_PSCR_REG );
|
|
WRITE_PORT_UCHAR( &IOBase->SizeLSReg, ( UCHAR )( DataLen & 0x00FF ));
|
|
SysDelay( DELAY_WRITE_PSCR_REG );
|
|
|
|
// write data
|
|
for (Idx = 0; Idx < DataLen; Idx++) {
|
|
|
|
WRITE_PORT_UCHAR( &IOBase->DataReg, pData[ Idx ] );
|
|
}
|
|
|
|
if( pNBytes != NULL )
|
|
{
|
|
*pNBytes = DataLen;
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
WRITE_PORT_UCHAR( &IOBase->CmdStatusReg, CLEAR_BIT );
|
|
}
|
|
return( NTStatus );
|
|
}
|
|
|
|
UCHAR
|
|
PscrCalculateLRC(
|
|
PUCHAR pData,
|
|
USHORT DataLen
|
|
)
|
|
/*++
|
|
|
|
PscrCalculateLRC:
|
|
calculates the XOR LRC of a buffer.
|
|
|
|
Arguments:
|
|
pData ptr to data buffer
|
|
DataLen length of range
|
|
|
|
Return Value:
|
|
LRC
|
|
|
|
--*/
|
|
{
|
|
UCHAR Lrc;
|
|
USHORT Idx;
|
|
|
|
//
|
|
// Calculate LRC by XORing all the bytes.
|
|
//
|
|
Lrc = pData[ 0 ];
|
|
for ( Idx = 1 ; Idx < DataLen; Idx++ )
|
|
{
|
|
Lrc ^= pData[ Idx ];
|
|
}
|
|
return ( Lrc );
|
|
}
|
|
|
|
NTSTATUS
|
|
PscrWait(
|
|
PREADER_EXTENSION ReaderExtension,
|
|
UCHAR Mask
|
|
)
|
|
/*++
|
|
PscrWait:
|
|
Test the status port of the reader until ALL bits in the mask are set.
|
|
The maximum of time until DEVICE_BUSY is returned is approx.
|
|
MaxRetries * DELAY_PSCR_WAIT if MaxRetries != 0.
|
|
If MaxRetries = 0 the driver waits until the requested status is reported or the
|
|
user defines a timeout.
|
|
|
|
Arguments:
|
|
ReaderExtension context of call
|
|
Mask mask of bits to test the status register
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS
|
|
STATUS_DEVICE_BUSY
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NTStatus;
|
|
PPSCR_REGISTERS IOBase;
|
|
ULONG Retries;
|
|
UCHAR Status;
|
|
|
|
IOBase = ReaderExtension->IOBase;
|
|
NTStatus = STATUS_DEVICE_BUSY;
|
|
|
|
// wait until condition fulfilled or specified timeout expired
|
|
for ( Retries = 0; Retries < ReaderExtension->MaxRetries; Retries++)
|
|
{
|
|
// canceled?
|
|
if( ReaderExtension->RequestCancelled == TRUE )
|
|
{
|
|
NTStatus = STATUS_CANCELLED;
|
|
break;
|
|
}
|
|
|
|
if (( (READ_PORT_UCHAR( &IOBase->CmdStatusReg )) == 0x01) &&
|
|
ReaderExtension->InvalidStatus)
|
|
|
|
{
|
|
NTStatus = STATUS_CANCELLED;
|
|
break;
|
|
}
|
|
|
|
// test requested bits
|
|
if(( (READ_PORT_UCHAR( &IOBase->CmdStatusReg )) & Mask ) == Mask )
|
|
{
|
|
NTStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
SysDelay( DELAY_PSCR_WAIT );
|
|
}
|
|
|
|
Status = READ_PORT_UCHAR( &IOBase->CmdStatusReg );
|
|
return NTStatus;
|
|
}
|
|
|
|
#pragma optimize( "", on )
|
|
|