Windows2003-3790/drivers/smartcrd/pscr/pscrcb.c
2020-09-30 16:53:55 +02:00

916 lines
28 KiB
C

/*++
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;
KIRQL irql;
#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
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
if ( SmartcardExtension->ReaderCapabilities.CurrentState >
SCARD_SWALLOWED ) {
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
// reset the card
ATRLength = ATR_SIZE;
NTStatus = CmdReset(
ReaderExtension,
ReaderExtension->Device,
TRUE, // warm reset
ATRBuffer,
&ATRLength
);
break;
} else {
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
}
// 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;
KIRQL irql;
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC) {
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
return STATUS_SUCCESS;
} else {
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
}
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 )) {
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
SCLibProtocol = (SCLibProtocol & SCARD_PROTOCOL_T1 &
SmartcardExtension->CardCapabilities.Protocol.Supported) ?
SCARD_PROTOCOL_T1 :
SCARD_PROTOCOL_T0;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
} 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 )) {
Len = sizeof(Val);
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 (IOBytes < 4) {
NTStatus = STATUS_DEVICE_PROTOCOL_ERROR;
break;
}
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 {
if ((IOData[PSCR_LEN] > SmartcardExtension->SmartcardReply.BufferSize) ||
(IOData[PSCR_LEN] > MAX_T1_BLOCK_SIZE + PSCR_APDU)) {
NTStatus = STATUS_DEVICE_PROTOCOL_ERROR;
break;
}
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
Len = sizeof(Val);
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 ------------------------------