/*++ 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 #else // SMCLIB_VXD #include #endif // SMCLIB_VXD #include #include #include 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 (NT_SUCCESS(NTStatus)) { // // 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(NT_SUCCESS(NTStatus)); // 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( ¤tIrql ); 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 Val = PSCR_ICC_ABSENT; } return Val; } // -------------------------------- END OF FILE ------------------------------