/*++ 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 #else // SMCLIB_VXD #include #endif // SMCLIB_VXD #include #include #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++) { 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 )