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

2485 lines
74 KiB
C

#include "usbsc.h"
#include "usbsccb.h"
#include "usbcom.h"
#include "usbutil.h"
#include "usbscnt.h"
#pragma warning( disable : 4242 4244)
CHAR* InMessages[] = {
"RDR_to_PC_DataBlock",
"RDR_to_PC_SlotStatus",
"RDR_to_PC_Parameters",
"RDR_to_PC_Escape",
"RDR_to_PC_DataRateAndClockFrequency",
"INVALID MESSAGE"
};
CHAR* OutMessages[] = {
"INVALID MESSAGE", // 0x60
"PC_to_RDR_SetParameters", // 0x61
"PC_to_RDR_IccPowerOn", // 0x62
"PC_to_RDR_IccPowerOff", // 0x63
"INVALID MESSAGE", // 0x64
"PC_to_RDR_GetSlotStatus", // 0x65
"INVALID MESSAGE", // 0x66
"INVALID MESSAGE", // 0x67
"INVALID MESSAGE", // 0x68
"INVALID MESSAGE", // 0x69
"PC_to_RDR_Secure", // 0x6a
"PC_to_RDR_Escape", // 0x6b
"PC_to_RDR_GetParameters", // 0x6c
"PC_to_RDR_ResetParameters", // 0x6d
"PC_to_RDR_IccClock", // 0x6e
"PC_to_RDR_XfrBlock", // 0x6f
"INVALID MESSAGE", // 0x70
"PC_to_RDR_Mechanical", // 0x71
"PC_to_RDR_Abort", // 0x72
"PC_to_RDR_SetDataRateAndClockFrequency" // 0x73
};
CHAR*
GetMessageName(
BYTE MessageType
)
{
CHAR* name;
if (MessageType & 0x80) {
// IN message
MessageType -= 0x80;
if (MessageType > 5) {
MessageType = 5;
}
name = InMessages[MessageType];
} else {
MessageType -= 0x60;
if (MessageType >= 20) {
MessageType = 0;
}
name = OutMessages[MessageType];
}
return name;
}
NTSTATUS
UsbScTransmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_TRANSMIT Callback function
Called from smclib
Arguments:
SmartcardExtension
Return Value:
NT Status value
--*/
{
NTSTATUS status = STATUS_SUCCESS;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScTransmit Enter\n",DRIVER_NAME ));
switch( SmartcardExtension->CardCapabilities.Protocol.Selected ) {
case SCARD_PROTOCOL_T0:
status = UsbScT0Transmit( SmartcardExtension );
break;
case SCARD_PROTOCOL_T1:
status = UsbScT1Transmit( SmartcardExtension );
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScTransmit Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScSetProtocol(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_SET_PROTOCOL callback
called from smclib
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UINT protocolMask;
UINT protocol;
PUSBSC_OUT_MESSAGE_HEADER message = NULL;
PROTOCOL_DATA_T0 *dataT0;
PROTOCOL_DATA_T1 *dataT1;
PSCARD_CARD_CAPABILITIES cardCaps = NULL;
PCLOCK_AND_DATA_RATE cadData = NULL;
PUCHAR response = NULL;
PPPS_REQUEST pPPS = NULL;
PPS_REQUEST pps;
UINT bytesToRead;
KIRQL irql;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScSetProtocol Enter\n",DRIVER_NAME ));
protocolMask = SmartcardExtension->MinorIoControlCode;
cardCaps = &SmartcardExtension->CardCapabilities;
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Setting Protocol\n",DRIVER_NAME ));
//
// If the reader supports automatic parameter configuration, then just
// check for the selected protocol
//
if (SmartcardExtension->ReaderExtension->ClassDescriptor.dwFeatures & AUTO_PARAMETER_NEGOTIATION) {
message = ExAllocatePool(NonPagedPool,
sizeof(USBSC_OUT_MESSAGE_HEADER));
if (!message) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(message,
sizeof( USBSC_OUT_MESSAGE_HEADER ));
response = ExAllocatePool(NonPagedPool,
sizeof(USBSC_IN_MESSAGE_HEADER)
+ sizeof(PROTOCOL_DATA_T1));
if (!response) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
message->bMessageType = PC_to_RDR_GetParameters;
message->dwLength = 0;
message->bSlot = 0;
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Reader supports auto parameter negotiation\n",DRIVER_NAME ));
status = UsbScReadWrite(SmartcardExtension,
message,
response,
sizeof(PROTOCOL_DATA_T1),
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
if ((((PUSBSC_IN_MESSAGE_HEADER) response)->bProtocolNum+1) & protocolMask) {
*SmartcardExtension->IoRequest.ReplyBuffer = ((PUSBSC_IN_MESSAGE_HEADER) response)->bProtocolNum + 1;
*SmartcardExtension->IoRequest.Information = sizeof(ULONG);
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
SmartcardExtension->CardCapabilities.Protocol.Selected = *SmartcardExtension->IoRequest.ReplyBuffer;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
} else {
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Reader has not selected desired protocol\n",DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
}
__leave;
}
//
// Check if the card has already selected the right protocol
//
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC &&
(SmartcardExtension->CardCapabilities.Protocol.Selected &
SmartcardExtension->MinorIoControlCode)) {
protocolMask = SmartcardExtension->CardCapabilities.Protocol.Selected;
status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
//
// Check if PPS request needs to be sent
//
if (!(NT_SUCCESS(status)) && !(SmartcardExtension->ReaderExtension->ClassDescriptor.dwFeatures & (AUTO_PARAMETER_NEGOTIATION | AUTO_PPS))) {
PUCHAR replyPos;
//
// Must send PPS request
//
message = ExAllocatePool(NonPagedPool,
sizeof(USBSC_OUT_MESSAGE_HEADER)
+ sizeof(PPS_REQUEST));
if (!message) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(message,
sizeof( USBSC_OUT_MESSAGE_HEADER )
+ sizeof(PPS_REQUEST));
response = ExAllocatePool(NonPagedPool,
sizeof(USBSC_IN_MESSAGE_HEADER)
+ sizeof(PPS_REQUEST));
if (!response) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
while (TRUE) {
pPPS = (PPPS_REQUEST) ((PUCHAR) message + sizeof(USBSC_OUT_MESSAGE_HEADER));
pPPS->bPPSS = 0xff;
if (protocolMask & SCARD_PROTOCOL_T1) {
pPPS->bPPS0 = 0x11;
} else if (protocolMask & SCARD_PROTOCOL_T0) {
pPPS->bPPS0 = 0x10;
} else {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScSetProtocol Invalid protocol\n",DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
__leave;
}
pPPS->bPPS1 = (cardCaps->PtsData.Fl << 4)
| cardCaps->PtsData.Dl;
pPPS->bPCK = (pPPS->bPPSS ^ pPPS->bPPS0 ^ pPPS->bPPS1);
SmartcardDebug( DEBUG_PROTOCOL,
("%s : Sending PPS request (0x%x 0x%x 0x%x 0x%x)\n",
DRIVER_NAME,
pPPS->bPPSS,
pPPS->bPPS0,
pPPS->bPPS1,
pPPS->bPCK ));
message->bMessageType = PC_to_RDR_XfrBlock;
message->bSlot = 0;
message->dwLength = sizeof(PPS_REQUEST);
switch (SmartcardExtension->ReaderExtension->ExchangeLevel) {
case TPDU_LEVEL:
message->wLevelParameter = 0;
bytesToRead = sizeof(PPS_REQUEST);
status = UsbScReadWrite(SmartcardExtension,
message,
response,
bytesToRead,
&pps,
FALSE);
break;
case CHARACTER_LEVEL:
replyPos = (PUCHAR) &pps;
bytesToRead = 4;
message->wLevelParameter = 2;
while (bytesToRead) {
status = UsbScReadWrite(SmartcardExtension,
message,
response,
message->wLevelParameter,
replyPos,
FALSE);
bytesToRead -= 2;
replyPos += 2;
message->dwLength = 0;
}
break;
}
if (!NT_SUCCESS(status) || (memcmp(&pps, pPPS, sizeof(PPS_REQUEST)) != 0)) {
// The card failed with the current settings
// If PtsData.Type is not PTS_TYPE_DEFAULT, then try that.
if (cardCaps->PtsData.Type == PTS_TYPE_DEFAULT) {
SmartcardDebug( DEBUG_PROTOCOL, ("%s : The card failed PPS request\n",DRIVER_NAME ));
ASSERT(FALSE);
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
SmartcardDebug( DEBUG_PROTOCOL, ("%s : The card failed PPS request, trying default PPS\n",DRIVER_NAME ));
cardCaps->PtsData.Type = PTS_TYPE_DEFAULT;
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
status = UsbScCardPower(SmartcardExtension);
} else {
break;
}
}
if (message != NULL) {
ExFreePool(message);
message = NULL;
}
if (response != NULL) {
ExFreePool(response);
response = NULL;
}
}
//
// Send set protocol request to the reader
//
if (protocolMask & SCARD_PROTOCOL_T1) { // T=1
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Setting protocol T=1\n",DRIVER_NAME ));
protocol = 1;
message = ExAllocatePool(NonPagedPool,
sizeof( USBSC_OUT_MESSAGE_HEADER )
+ sizeof(PROTOCOL_DATA_T1));
if (!message) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(message,
sizeof( USBSC_OUT_MESSAGE_HEADER )
+ sizeof(PROTOCOL_DATA_T1));
dataT1 = (PPROTOCOL_DATA_T1) ((PUCHAR) message + sizeof(USBSC_OUT_MESSAGE_HEADER));
//
// 7 4 3 0
// ----------------------------
// | FI | DI |
// ----------------------------
//
dataT1->bmFindexDindex = (cardCaps->PtsData.Fl << 4)
| cardCaps->PtsData.Dl;
//
// B7-2 = 000100b
// B0 = Checksum type (0=LRC, 1=CRC)
// B1 = Convention used (0=direct, 1=inverse)
//
dataT1->bmTCCKST1 = 0x10 | (cardCaps->T1.EDC & 0x01);
if (cardCaps->InversConvention) {
dataT1->bmTCCKST1 |= 2;
}
dataT1->bGuardTimeT1 = cardCaps->N;
//
// 7 4 3 0
// ----------------------------
// | BWI | CWI |
// ----------------------------
//
dataT1->bmWaitingIntegersT1 = (cardCaps->T1.BWI << 4)
| (cardCaps->T1.CWI & 0xf);
dataT1->bClockStop = 0;
dataT1->bIFSC = cardCaps->T1.IFSC;
dataT1->bNadValue = 0;
message->dwLength = sizeof(PROTOCOL_DATA_T1);
} else if (protocolMask & SCARD_PROTOCOL_T0) {
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Setting protocol T=0\n",DRIVER_NAME ));
protocol = 0;
message = ExAllocatePool(NonPagedPool,
sizeof( USBSC_OUT_MESSAGE_HEADER )
+ sizeof(PROTOCOL_DATA_T0));
if (!message) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(message,
sizeof( USBSC_OUT_MESSAGE_HEADER )
+ sizeof(PROTOCOL_DATA_T0));
dataT0 = (PROTOCOL_DATA_T0 *) (((UCHAR *) message) + sizeof(USBSC_OUT_MESSAGE_HEADER));
//
// 7 4 3 0
// ----------------------------
// | FI | DI |
// ----------------------------
//
dataT0->bmFindexDindex = (cardCaps->PtsData.Fl << 4)
| cardCaps->PtsData.Dl;
//
// B7-2 = 000000b
// B0 = 0
// B1 = Convention used (0=direct, 1=inverse)
//
dataT0->bmTCCKST0 = 0;
if (cardCaps->InversConvention) {
dataT0->bmTCCKST0 |= 2;
}
dataT0->bGuardTimeT0 = cardCaps->N;
dataT0->bWaitingIntegerT0 = cardCaps->T0.WI;
dataT0->bClockStop = 0;
message->dwLength = sizeof(PROTOCOL_DATA_T0);
} else {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScSetProtocol Invalid protocol\n",DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
__leave;
}
message->bMessageType = PC_to_RDR_SetParameters;
message->bSlot = 0;
message->bProtocolNum = protocol;
status = UsbScReadWrite(SmartcardExtension,
message,
(PVOID) message,
message->dwLength,
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
//
// See if reader supports auto clock/baud rate configuration
//
if ((SmartcardExtension->ReaderExtension->ClassDescriptor.dwFeatures & (AUTO_BAUD_RATE | AUTO_CLOCK_FREQ)) != (AUTO_BAUD_RATE | AUTO_CLOCK_FREQ)) {
// Set ClockFrequency and DataRate
RtlZeroMemory(message,
sizeof(USBSC_OUT_MESSAGE_HEADER)
+ sizeof(CLOCK_AND_DATA_RATE));
message->bMessageType = PC_to_RDR_SetDataRateAndClockFrequency;
message->dwLength = 8;
message->bSlot = 0;
cadData = (PCLOCK_AND_DATA_RATE) ((PUCHAR)message+sizeof(USBSC_OUT_MESSAGE_HEADER));
cadData->dwClockFrequency = SmartcardExtension->CardCapabilities.PtsData.CLKFrequency;
cadData->dwDataRate = SmartcardExtension->CardCapabilities.PtsData.DataRate;
status = UsbScReadWrite(SmartcardExtension,
message,
(PVOID) message,
sizeof(CLOCK_AND_DATA_RATE),
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
}
*SmartcardExtension->IoRequest.ReplyBuffer = protocol + 1;
*SmartcardExtension->IoRequest.Information = sizeof(ULONG);
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
SmartcardExtension->CardCapabilities.Protocol.Selected = protocol + 1;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
}
__finally
{
if (message != NULL) {
ExFreePool(message);
message = NULL;
}
if (response != NULL) {
ExFreePool(response);
response = NULL;
}
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScSetProtocol Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScCardPower(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_CARD_POWER callback
called from smclib
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PREADER_EXTENSION readerExtension;
UCHAR atr[sizeof(USBSC_IN_MESSAGE_HEADER) + ATR_SIZE];
USBSC_OUT_MESSAGE_HEADER powerMessage;
PUSBSC_IN_MESSAGE_HEADER replyHeader;
BYTE voltage;
LARGE_INTEGER waitTime;
KIRQL irql;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardPower Enter\n",DRIVER_NAME ));
replyHeader = (PUSBSC_IN_MESSAGE_HEADER) atr;
readerExtension = SmartcardExtension->ReaderExtension;
*SmartcardExtension->IoRequest.Information = 0;
switch (SmartcardExtension->MinorIoControlCode) {
case SCARD_WARM_RESET:
RtlZeroMemory(&powerMessage, sizeof(USBSC_OUT_MESSAGE_HEADER));
powerMessage.bMessageType = PC_to_RDR_IccPowerOn;
powerMessage.bPowerSelect = readerExtension->CurrentVoltage;
powerMessage.dwLength = 0;
powerMessage.bSlot = 0;
SmartcardDebug( DEBUG_PROTOCOL, ("%s : SCARD_WARM_RESET (bPowerSelect = 0x%x)\n",DRIVER_NAME, readerExtension->CurrentVoltage ));
status = UsbScReadWrite(SmartcardExtension,
&powerMessage,
atr,
ATR_SIZE,
SmartcardExtension->IoRequest.ReplyBuffer,
FALSE);
if (!NT_SUCCESS(status)) {
SmartcardDebug( DEBUG_ERROR, ("%s : PC_to_RDR_IccPowerOn failed with status = 0x%x\n",DRIVER_NAME, status ));
__leave;
}
if (replyHeader->dwLength > ATR_SIZE) {
SmartcardDebug( DEBUG_ERROR, ("%s :Invalid ATR size returned\n",DRIVER_NAME, status ));
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
*SmartcardExtension->IoRequest.Information = replyHeader->dwLength;
RtlCopyMemory(SmartcardExtension->CardCapabilities.ATR.Buffer,
SmartcardExtension->IoRequest.ReplyBuffer,
*SmartcardExtension->IoRequest.Information);
SmartcardExtension->CardCapabilities.ATR.Length = *SmartcardExtension->IoRequest.Information;
status = SmartcardUpdateCardCapabilities(SmartcardExtension);
break;
case SCARD_COLD_RESET:
SmartcardDebug( DEBUG_PROTOCOL, ("%s : SCARD_COLD_RESET\n",DRIVER_NAME ));
RtlZeroMemory(&powerMessage, sizeof(USBSC_OUT_MESSAGE_HEADER));
powerMessage.dwLength = 0;
powerMessage.bSlot = 0;
// Need to first power off card before selecting another
// voltage.
powerMessage.bMessageType = PC_to_RDR_IccPowerOff;
status = UsbScReadWrite(SmartcardExtension,
&powerMessage,
atr,
0,
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
SmartcardDebug( DEBUG_ERROR, ("%s : PC_to_RDR_IccPowerOff failed with status = 0x%x\n",DRIVER_NAME, status ));
__leave;
}
// We need to iterate through the possible voltages, moving from low voltage to high
// until we find one that works
for (voltage = 3; voltage > 0 && voltage <= 3; voltage--) {
if (readerExtension->ClassDescriptor.dwFeatures & AUTO_VOLTAGE_SELECTION) {
// reader supports auto voltage selection
voltage = 0;
}
// Wait 10ms per spec
waitTime.HighPart = -1;
waitTime.LowPart = -100; // 10ms
KeDelayExecutionThread(KernelMode,
FALSE,
&waitTime);
// Now we can power back on
RtlZeroMemory(&powerMessage, sizeof(USBSC_OUT_MESSAGE_HEADER));
powerMessage.bMessageType = PC_to_RDR_IccPowerOn;
powerMessage.bPowerSelect = voltage;
powerMessage.dwLength = 0;
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Selecting voltage = 0x%x\n",DRIVER_NAME, voltage ));
status = UsbScReadWrite(SmartcardExtension,
&powerMessage,
atr,
ATR_SIZE,
SmartcardExtension->IoRequest.ReplyBuffer,
FALSE);
if (NT_SUCCESS(status)) {
// everything worked. We found the correct voltage
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Voltage set to 0x%x\n",DRIVER_NAME, voltage ));
*SmartcardExtension->IoRequest.Information = replyHeader->dwLength;
readerExtension->CurrentVoltage = voltage;
break;
}
//
// If card or reader doesn't support selected voltage, it will
// either timeout, return ICC_CLASS_NOT_SUPPORTED or
// report that parameter 7 (bPowerselect) is not supported
//
if (((replyHeader->bStatus == 0x41) && (replyHeader->bError == ICC_MUTE))
|| (replyHeader->bError == ICC_CLASS_NOT_SUPPORTED)
|| (replyHeader->bError == 7)) {
SmartcardDebug( DEBUG_PROTOCOL, ("%s : Reader did not support voltage = 0x%x\n",DRIVER_NAME, voltage ));
// We are going to try another voltage
} else {
//
// We have a bigger problem that we can't handle
// Let's just ignore it for now and see if it is
// due to insufficient voltage
//
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScCardPower Unhandled error (probably due to insufficient voltage)\n",DRIVER_NAME ));
}
}
// Save the ATR so smclib can parse it
if (*SmartcardExtension->IoRequest.Information > ATR_SIZE) {
SmartcardDebug( DEBUG_ERROR, ("%s : Invalid ATR size returned\n",DRIVER_NAME ));
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
RtlCopyMemory(SmartcardExtension->CardCapabilities.ATR.Buffer,
SmartcardExtension->IoRequest.ReplyBuffer,
*SmartcardExtension->IoRequest.Information);
SmartcardExtension->CardCapabilities.ATR.Length = *SmartcardExtension->IoRequest.Information;
status = SmartcardUpdateCardCapabilities(SmartcardExtension);
break;
case SCARD_POWER_DOWN:
SmartcardDebug( DEBUG_PROTOCOL, ("%s : SCARD_POWER_DOWN\n",DRIVER_NAME ));
RtlZeroMemory(&powerMessage, sizeof(USBSC_OUT_MESSAGE_HEADER));
powerMessage.bSlot = 0;
powerMessage.bMessageType = PC_to_RDR_IccPowerOff;
powerMessage.dwLength = 0;
status = UsbScReadWrite(SmartcardExtension,
&powerMessage,
atr,
0,
NULL,
FALSE);
if (!NT_SUCCESS(status)) {
SmartcardDebug( DEBUG_ERROR, ("%s : PC_to_RDR_IccPowerOff failed with status = 0x%x\n",DRIVER_NAME, status ));
__leave;
}
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
if (SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_PRESENT) {
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_PRESENT;
}
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
SmartcardExtension->CardCapabilities.ATR.Length = 0;
break;
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardPower Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScCardTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_CARD_TRACKING callback
called from smclib
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_SUCCESS;
KIRQL ioIrql, keIrql;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardTracking Enter\n",DRIVER_NAME ));
// set cancel routine
IoAcquireCancelSpinLock( &ioIrql );
IoSetCancelRoutine(
SmartcardExtension->OsData->NotificationIrp,
ScUtil_Cancel);
IoReleaseCancelSpinLock( ioIrql );
status = STATUS_PENDING;
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardTracking Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScCardSwallow(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_READER_SWALLOW callback
called from smclib
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_SUCCESS;
__try
{
USBSC_OUT_MESSAGE_HEADER header;
USBSC_IN_MESSAGE_HEADER reply;
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardSwallow Enter\n",DRIVER_NAME ));
header.bMessageType = PC_to_RDR_Mechanical;
header.dwLength = 0;
header.bSlot = 0;
header.bFunction = 4; // lock
status = UsbScReadWrite(SmartcardExtension,
&header,
(PUCHAR) &reply,
0,
NULL,
FALSE);
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardSwallow Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScCardEject(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
RDF_CARD_EJECT callback
called from smclib
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_SUCCESS;
__try
{
USBSC_OUT_MESSAGE_HEADER header;
USBSC_IN_MESSAGE_HEADER reply;
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardEject Enter\n",DRIVER_NAME ));
header.bMessageType = PC_to_RDR_Mechanical;
header.dwLength = 0;
header.bSlot = 0;
header.bFunction = 5; // unlock
status = UsbScReadWrite(SmartcardExtension,
&header,
(PUCHAR) &reply,
0,
NULL,
FALSE);
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCardEject Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScT0Transmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
Handles transmitting data to/from reader using the T=0 protocol
Arguments:
SmartcardExtension
Return Value:
NT status value
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
ULONG bytesToSend;
ULONG requestLength;
ULONG bytesToRead;
PUCHAR currentHeaderPos;
PUCHAR currentData;
ULONG maxDataLen = 0;
USBSC_OUT_MESSAGE_HEADER header;
PUSBSC_IN_MESSAGE_HEADER replyHeader;
PUCHAR responseBuffer = NULL;
PUCHAR replyPos;
LONG timeout = 0;
UCHAR ins;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit Enter\n",DRIVER_NAME ));
// Tell the lib to allocate space for the header
SmartcardExtension->SmartcardRequest.BufferLength = sizeof(USBSC_OUT_MESSAGE_HEADER);
SmartcardExtension->SmartcardReply.BufferLength = 0;
status = SmartcardT0Request(SmartcardExtension);
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToSend = SmartcardExtension->SmartcardRequest.BufferLength - sizeof(USBSC_OUT_MESSAGE_HEADER);
bytesToRead = SmartcardExtension->T0.Le + 2; // Le + SW2 and SW2
replyPos = SmartcardExtension->SmartcardReply.Buffer;
// allocate buffer to hold message header and data.
responseBuffer = ExAllocatePool(NonPagedPool,
sizeof( USBSC_OUT_MESSAGE_HEADER ) + bytesToRead);
if (!responseBuffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
replyHeader = (PUSBSC_IN_MESSAGE_HEADER) responseBuffer;
currentHeaderPos = SmartcardExtension->SmartcardRequest.Buffer;
// Set parameters that are common to all ExchangeLevels.
header.bMessageType = PC_to_RDR_XfrBlock;
header.bSlot = 0;
header.bBWI = 0;
switch (ReaderExtension->ExchangeLevel) {
case SHORT_APDU_LEVEL:
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit SHORT_APDU_LEVEL\n",DRIVER_NAME ));
// Fall through since short APDU is an extended APDU with bytesToSend <= MaxMessageLength-10
case EXTENDED_APDU_LEVEL:
if (ReaderExtension->ExchangeLevel == EXTENDED_APDU_LEVEL) {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit EXTENDED_APDU_LEVEL\n",DRIVER_NAME ));
}
if (bytesToSend <= (ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER))) {
// this is basically just like a short APDU
header.wLevelParameter = 0;
requestLength = bytesToSend;
} else {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit multi-packet message\n",DRIVER_NAME ));
requestLength = ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER);
header.wLevelParameter = 1;
}
while (bytesToSend || ((replyHeader->bChainParameter & 0x01) == 0x01)) {
header.dwLength = requestLength;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
bytesToRead,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
SmartcardDebug( DEBUG_PROTOCOL, ("%s!UsbScT0Transmit SW1=0x%x, SW2=0x%x\n",DRIVER_NAME, replyPos[replyHeader->dwLength-2], replyPos[replyHeader->dwLength-1] ));
if (bytesToSend < requestLength) {
// this should NEVER happen
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
bytesToSend -= requestLength;
currentHeaderPos += requestLength;
if ((bytesToSend <= (ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER))) && (bytesToSend > 0)) {
// Last part of APDU
requestLength = bytesToSend;
header.wLevelParameter = 2;
} else if (bytesToSend > 0) {
// Still more APDU to come
header.wLevelParameter = 3;
} else {
// Expect more data
header.wLevelParameter = 0x10;
}
if (bytesToRead < replyHeader->dwLength) {
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
bytesToRead -= replyHeader->dwLength;
replyPos += replyHeader->dwLength;
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
}
break;
case TPDU_LEVEL:
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit TPDU_LEVEL\n",DRIVER_NAME ));
header.wLevelParameter = 0;
header.dwLength = bytesToSend;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
bytesToRead,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToSend = 0;
bytesToRead -= replyHeader->dwLength;
replyPos += replyHeader->dwLength;
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
break;
case CHARACTER_LEVEL:
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit CHARACTER_LEVEL\n",DRIVER_NAME ));
//
// Send T0 command header
//
requestLength = 5;
currentHeaderPos = SmartcardExtension->SmartcardRequest.Buffer;
ins = currentHeaderPos[sizeof(USBSC_OUT_MESSAGE_HEADER)+1];
header.dwLength = requestLength;
while (bytesToSend || bytesToRead) {
BOOL restartWorkingTime = TRUE;
header.wLevelParameter = 1;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
header.wLevelParameter,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToSend -= header.dwLength;
currentHeaderPos += header.dwLength;
currentData = responseBuffer + sizeof( USBSC_IN_MESSAGE_HEADER );
if ((*currentData) == 0x60) {
// this is a NULL byte.
header.wLevelParameter = 1;
header.dwLength = 0;
continue;
} else if (((*currentData & 0xF0) == 0x60) || ((*currentData & 0xF0) == 0x90)) {
// Got SW1
//
// data has already been coppied to request buffer
// just increment replyPos to prevent overwriting
//
replyPos++;
SmartcardExtension->SmartcardReply.BufferLength++;
//Get SW2
header.dwLength = 0;
header.wLevelParameter = 1;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
header.wLevelParameter,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToRead = 0;
bytesToSend = 0;
replyPos++;
SmartcardExtension->SmartcardReply.BufferLength++;
continue;
}
if ((*currentData & 0xFE) == (ins & 0xFE)) {
//
//Transfer all bytes
//
header.dwLength = bytesToSend;
header.wLevelParameter = bytesToRead;
if (bytesToSend) {
continue;
}
} else if ((*currentData & 0xFE) == ((~ins) & 0xFE)) {
//
// Transfer next byte
//
header.dwLength = bytesToSend ? 1 : 0;
header.wLevelParameter = bytesToRead ? 1 : 0;
if (bytesToSend) {
continue;
}
} else {
status = STATUS_UNSUCCESSFUL;
__leave;
}
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
header.wLevelParameter,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
if (bytesToRead < replyHeader->dwLength) {
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
bytesToSend -= header.dwLength;
currentHeaderPos += header.dwLength;
bytesToRead -= replyHeader->dwLength;
replyPos += replyHeader->dwLength;
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
}
break;
}
status = SmartcardT0Reply(SmartcardExtension);
}
__finally
{
if (responseBuffer) {
ExFreePool(responseBuffer);
responseBuffer = NULL;
}
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT0Transmit Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScT1Transmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
Handles transmitting data to/from reader using the T=1 protocol
Arguments:
SmartcardExtension
Return Value:
NT status value
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
USBSC_OUT_MESSAGE_HEADER header;
PUSBSC_IN_MESSAGE_HEADER replyHeader;
PUCHAR currentHeaderPos;
PUCHAR responseBuffer = NULL;
PUCHAR requestBuffer = NULL;
PUCHAR replyPos;
ULONG bytesToSend;
ULONG requestLength;
ULONG bytesToRead;
LONG timeout = 0;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT1Transmit Enter\n",DRIVER_NAME ));
// allocate buffer to hold message header and data.
responseBuffer = ExAllocatePool(NonPagedPool,
SmartcardExtension->IoRequest.ReplyBufferLength + sizeof(USBSC_OUT_MESSAGE_HEADER));
if (!responseBuffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
replyPos = SmartcardExtension->SmartcardReply.Buffer;
replyHeader = (PUSBSC_IN_MESSAGE_HEADER) responseBuffer;
currentHeaderPos = SmartcardExtension->SmartcardRequest.Buffer;
SmartcardExtension->SmartcardReply.BufferLength = 0;
bytesToRead = SmartcardExtension->ReaderCapabilities.MaxIFSD + 5; // Set to MAX possible so we allocate enough
// With the APDU exchange levels, we don't use smclib to manage the
// protocol. We just shovel the data and the reader handles the details
if ((ReaderExtension->ExchangeLevel == SHORT_APDU_LEVEL)
|| (ReaderExtension->ExchangeLevel == EXTENDED_APDU_LEVEL)) {
PIO_HEADER IoHeader = (PIO_HEADER) SmartcardExtension->IoRequest.RequestBuffer;
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT1Transmit APDU_LEVEL\n",DRIVER_NAME ));
if (SmartcardExtension->IoRequest.ReplyBufferLength <
IoHeader->ScardIoRequest.cbPciLength + 2) {
//
// We should at least be able to store
// the io-header plus SW1 and SW2
//
status = STATUS_BUFFER_TOO_SMALL;
__leave;
}
bytesToSend = SmartcardExtension->IoRequest.RequestBufferLength -
IoHeader->ScardIoRequest.cbPciLength;
// Need to allocate buffer for write data
requestBuffer = ExAllocatePool(NonPagedPool,
bytesToSend + sizeof(USBSC_OUT_MESSAGE_HEADER));
if (requestBuffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
replyPos = SmartcardExtension->IoRequest.ReplyBuffer;
currentHeaderPos = requestBuffer;
*SmartcardExtension->IoRequest.Information = 0;
// Copy over the data to write to the reader so we have room for the message header
RtlCopyMemory(&requestBuffer[sizeof(USBSC_OUT_MESSAGE_HEADER)],
&SmartcardExtension->IoRequest.RequestBuffer[sizeof(SCARD_IO_REQUEST)],
SmartcardExtension->IoRequest.RequestBufferLength - sizeof(SCARD_IO_REQUEST));
// Copy over the SCARD_IO)REQUEST structure from the request buffer to the
// reply buffer
RtlCopyMemory(replyPos,
IoHeader,
sizeof(SCARD_IO_REQUEST ));
replyPos += sizeof(SCARD_IO_REQUEST);
header.bMessageType = PC_to_RDR_XfrBlock;
header.bSlot = 0;
header.bBWI = 0;
if (bytesToSend <= (ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER))) {
// this is basically just like a short APDU
header.wLevelParameter = 0;
requestLength = bytesToSend;
} else {
requestLength = ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER);
header.wLevelParameter = 1;
}
while (bytesToSend || ((replyHeader->bChainParameter & 0x01) == 0x01)) {
header.dwLength = requestLength;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
bytesToRead,
replyPos,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToSend -= requestLength;
currentHeaderPos += requestLength;
if ((bytesToSend <= (ReaderExtension->MaxMessageLength - sizeof(USBSC_OUT_MESSAGE_HEADER))) && (bytesToSend > 0)) {
// Last part of APDU
requestLength = bytesToSend;
header.wLevelParameter = 2;
} else if (bytesToSend > 0) {
// Still more APDU to come
header.wLevelParameter = 3;
} else {
// Expect more data
header.wLevelParameter = 0x10;
}
replyPos += replyHeader->dwLength;
*SmartcardExtension->IoRequest.Information += replyHeader->dwLength;
}
*SmartcardExtension->IoRequest.Information += IoHeader->ScardIoRequest.cbPciLength;
__leave; // Finished transmitting data
}
// TPDU and Character levels
// Run this loop as long as he protocol requires to send data
do {
// Tell the lib to allocate space for the header
SmartcardExtension->SmartcardRequest.BufferLength = sizeof(USBSC_OUT_MESSAGE_HEADER);
SmartcardExtension->SmartcardReply.BufferLength = 0;
status = SmartcardT1Request(SmartcardExtension);
if (!NT_SUCCESS(status)) {
__leave;
}
replyPos = SmartcardExtension->SmartcardReply.Buffer;
bytesToSend = SmartcardExtension->SmartcardRequest.BufferLength - sizeof(USBSC_OUT_MESSAGE_HEADER);
// Set parameters that are common to all ExchangeLevels.
header.bMessageType = PC_to_RDR_XfrBlock;
header.bSlot = 0;
switch (ReaderExtension->ExchangeLevel) {
case TPDU_LEVEL:
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT1Transmit TPDU_LEVEL\n",DRIVER_NAME ));
header.wLevelParameter = 0;
header.dwLength = bytesToSend;
header.bBWI = SmartcardExtension->T1.Wtx;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
bytesToRead,
replyPos,
FALSE);
//
// smclib will detect timeout errors, so we set status
// to success
//
if (status == STATUS_IO_TIMEOUT) {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
__leave;
}
bytesToSend = 0;
SmartcardExtension->SmartcardReply.BufferLength = replyHeader->dwLength;
break;
case CHARACTER_LEVEL:
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT1Transmit CHARACTER_LEVEL\n",DRIVER_NAME ));
currentHeaderPos = SmartcardExtension->SmartcardRequest.Buffer;
header.dwLength = bytesToSend;
header.wLevelParameter = 3; // response is just the prologue field
header.bBWI = SmartcardExtension->T1.Wtx;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
header.wLevelParameter,
replyPos,
FALSE);
//
// smclib will detect timeout errors, so we set status to
// success
//
if (status == STATUS_IO_TIMEOUT) {
status = STATUS_SUCCESS;
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
break;
}
if (!NT_SUCCESS(status)) {
ASSERT(FALSE);
__leave;
}
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
bytesToSend = 0;
bytesToRead = replyPos[2] +
(SmartcardExtension->CardCapabilities.T1.EDC & 0x01 ? 2 : 1);
header.dwLength = 0;
header.wLevelParameter = bytesToRead;
*(PUSBSC_OUT_MESSAGE_HEADER)currentHeaderPos = header;
replyPos += replyHeader->dwLength;
status = UsbScReadWrite(SmartcardExtension,
currentHeaderPos,
responseBuffer,
header.wLevelParameter,
replyPos,
FALSE);
//
// smclib will detect timeout errors, so we set status to
// success
//
if (status == STATUS_IO_TIMEOUT) {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
__leave;
}
SmartcardExtension->SmartcardReply.BufferLength += replyHeader->dwLength;
break;
}
status = SmartcardT1Reply(SmartcardExtension);
} while (status == STATUS_MORE_PROCESSING_REQUIRED);
}
__finally
{
if (requestBuffer) {
ExFreePool(requestBuffer);
requestBuffer = NULL;
}
if (responseBuffer) {
ExFreePool(responseBuffer);
responseBuffer = NULL;
}
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScT1Transmit Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScReadWrite(
PSMARTCARD_EXTENSION SmartcardExtension,
PVOID WriteBuffer,
PUCHAR ReadBuffer,
WORD ReadLength,
PVOID ResponseBuffer,
BOOL NullByte)
/*++
Routine Description:
Writes data to the reader, and then reads response from reader.
Handles the bSeq value, checking the slot number, any time extension requests
Also converts errors into NTSTATUS codes
Arguments:
SmartcardExtension -
WriteBuffer - Data to be written to the reader
ReaderBuffer - Caller allocated buffer to hold the response
ReadLength - Number of bytes expected (excluding header)
ResponseBuffer - optional buffer to copy response data only (no header)
NullByte - Look for NULL byte (used in T=0 protocol)
Return Value:
NTSTATUS value
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PUSBSC_OUT_MESSAGE_HEADER header;
PUSBSC_IN_MESSAGE_HEADER replyHeader;
WORD writeLength;
ULONG timeout = 0;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScReadWrite Enter\n",DRIVER_NAME ));
header = (PUSBSC_OUT_MESSAGE_HEADER) WriteBuffer;
replyHeader = (PUSBSC_IN_MESSAGE_HEADER) ReadBuffer;
writeLength = header->dwLength + sizeof( USBSC_OUT_MESSAGE_HEADER);
ReadLength += sizeof( USBSC_IN_MESSAGE_HEADER );
header->bSeq = InterlockedIncrement(&SmartcardExtension->ReaderExtension->SequenceNumber);
//
// Send the data to the device
//
status = UsbWrite(SmartcardExtension->ReaderExtension,
WriteBuffer,
writeLength,
timeout);
if (!NT_SUCCESS(status)) {
__leave;
}
SmartcardDebug( DEBUG_PROTOCOL,
("%s!UsbScReadWrite Wrote %s, 0x%x bytes (header + 0x%x)\n",
DRIVER_NAME,
GetMessageName(header->bMessageType),
writeLength, header->dwLength ));
if (SmartcardExtension->CardCapabilities.Protocol.Selected == SCARD_PROTOCOL_T1) {
if (SmartcardExtension->T1.Wtx) {
SmartcardDebug( DEBUG_PROTOCOL, ("%s!UsbScReadWrite Wait time extension. Wtx=0x%x, bBWI=0x%x\n",DRIVER_NAME, SmartcardExtension->T1.Wtx, header->bBWI));
}
}
// We want to loop here if the reader requests a time extension
while (1) {
status = UsbRead(SmartcardExtension->ReaderExtension,
ReadBuffer,
ReadLength,
timeout);
if (!NT_SUCCESS(status)) {
__leave;
}
SmartcardDebug( DEBUG_PROTOCOL,
("%s!UsbScReadWrite Read %s, 0x%x bytes (header + 0x%x)\n",
DRIVER_NAME,
GetMessageName(replyHeader->bMessageType),
ReadLength, replyHeader->dwLength ));
if ((replyHeader->bSlot != header->bSlot) || (replyHeader->bSeq != header->bSeq)) {
// This is not ours. Who knows where this came from (probably a different slot that we don't support)
SmartcardDebug( DEBUG_PROTOCOL, ("%s!UsbScReadWrite Someone else's message received\n\t\tSlot %x (%x), Seq %x (%x)\n",
DRIVER_NAME,
replyHeader->bSlot,
header->bSlot,
replyHeader->bSeq,
header->bSeq ));
continue;
} else if (replyHeader->bStatus & COMMAND_STATUS_FAILED) {
// Doh we have a problem
SmartcardDebug( DEBUG_PROTOCOL,
("%s!UsbScReadWrite COMMAND_STATUS_FAILED\n\tbmICCStatus = 0x%x\n\tbmCommandStatus = 0x%x\n\tbError = 0x%x\n",
DRIVER_NAME,
replyHeader->bStatus & ICC_STATUS_MASK,
(replyHeader->bStatus & COMMAND_STATUS_MASK) >> 6,
replyHeader->bError));
status = UsbScErrorConvert(replyHeader);
} else if (replyHeader->bStatus & COMMAND_STATUS_TIME_EXT) {
// Time extension requested
// We should sleep for a bit while the reader prepares the data
UINT wi = replyHeader->bError;
LARGE_INTEGER delayTime;
SmartcardDebug( DEBUG_PROTOCOL, ("%s!UsbScReadWrite Time extension requested\n",DRIVER_NAME ));
delayTime.HighPart = -1;
if (SmartcardExtension->CardCapabilities.Protocol.Selected == SCARD_PROTOCOL_T1) {
delayTime.LowPart =
(-1) *
SmartcardExtension->CardCapabilities.T1.BWT *
wi *
10;
} else {
delayTime.LowPart =
(-1) * // relative
SmartcardExtension->CardCapabilities.T0.WT *
wi *
10; // 100 nanosecond units
}
// KeDelayExecutionThread(KernelMode,
// FALSE,
// &delayTime);
continue;
} else if (NullByte && (ReadBuffer[sizeof(USBSC_IN_MESSAGE_HEADER)] == 0x60)) {
// NULL byte, wait for another response
SmartcardDebug( DEBUG_PROTOCOL, ("%s!UsbScReadWrite Received NULL byte, waiting for next response\n",DRIVER_NAME ));
continue;
} else {
// SUCCESS!!
SmartcardDebug( DEBUG_PROTOCOL,
("%s!UsbScReadWrite Read %s, 0x%x bytes (header + 0x%x)\n",
DRIVER_NAME,
GetMessageName(replyHeader->bMessageType),
ReadLength, replyHeader->dwLength ));
}
break;
}
//
// copy data to request buffer
//
if (ResponseBuffer) {
RtlCopyMemory(ResponseBuffer,
ReadBuffer + sizeof(USBSC_IN_MESSAGE_HEADER),
replyHeader->dwLength);
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScReadWrite Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScErrorConvert(
PUSBSC_IN_MESSAGE_HEADER ReplyHeader
)
/*++
Routine Description:
Converts the errors returned by the reader into
valid NTSTATUS codes
Arguments:
ReplyHeader - reply header recieved from reader
Return Value:
NTSTATUS code corresponding to the reader error
--*/
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScErrorConvert Enter\n",DRIVER_NAME ));
SmartcardDebug( DEBUG_TRACE, ("bmICCStatus = 0x%x\n",ReplyHeader->bStatus & ICC_STATUS_MASK ));
SmartcardDebug( DEBUG_TRACE, ("bmCommandStatus = 0x%x\n", (ReplyHeader->bStatus & COMMAND_STATUS_MASK) >> 6 ));
SmartcardDebug( DEBUG_TRACE, ("bError = 0x%x\n",ReplyHeader->bError ));
switch (ReplyHeader->bError) {
case CMD_ABORTED:
status = STATUS_CANCELLED;
break;
case ICC_MUTE:
if ((ReplyHeader->bStatus & ICC_STATUS_MASK) == 2) {
status = STATUS_NO_MEDIA;
} else {
status = STATUS_IO_TIMEOUT;
}
break;
case XFR_PARITY_ERROR:
status = STATUS_PARITY_ERROR;
break;
case XFR_OVERRUN:
status = STATUS_DATA_OVERRUN;
break;
case HW_ERROR:
status = STATUS_IO_DEVICE_ERROR;
break;
case BAD_ATR_TS:
status = STATUS_DEVICE_PROTOCOL_ERROR;
break;
case BAD_ATR_TCK:
status = STATUS_DEVICE_PROTOCOL_ERROR;
break;
case ICC_PROTOCOL_NOT_SUPPORTED:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
case ICC_CLASS_NOT_SUPPORTED:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
case PROCEDURE_BYTE_CONFLICT:
status = STATUS_DEVICE_PROTOCOL_ERROR;
break;
case DEACTIVATED_PROTOCOL:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
case BUSY_WITH_AUTO_SEQUENCE:
status = STATUS_DEVICE_BUSY;
break;
case PIN_TIMEOUT:
break;
case PIN_CANCELLED:
break;
case CMD_SLOT_BUSY:
status = STATUS_DEVICE_BUSY;
break;
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScErrorConvert Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
NTSTATUS
UsbScTrackingISR(
PVOID Context,
PVOID Buffer,
ULONG BufferLength,
ULONG NotificationType,
PBOOLEAN QueueData)
/*++
Routine Description:
Callback function that is called when there is a slot change notification or
a reader error. This handles completing card tracking irps.
Arguments:
Return Value:
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PSMARTCARD_EXTENSION SmartcardExtension = (PSMARTCARD_EXTENSION) Context;
PUSBSC_SLOT_CHANGE_HEADER header;
PUSBSC_HWERROR_HEADER errorHeader;
ULONG oldState;
UCHAR slotState;
KIRQL irql;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScTrackingISR Enter\n",DRIVER_NAME ));
// We don't need this data to be saved
*QueueData = FALSE;
// Make sure that we got enough data
if (BufferLength < sizeof(USBSC_SLOT_CHANGE_HEADER)) {
status = STATUS_INVALID_PARAMETER;
__leave;
}
header = (PUSBSC_SLOT_CHANGE_HEADER) Buffer;
switch (header->bMessageType) {
case RDR_to_PC_NotifySlotChange:
slotState = header->bmSlotICCState & SLOT0_MASK;
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&irql);
oldState = SmartcardExtension->ReaderCapabilities.CurrentState;
if (slotState & 0x2) {
// State changed
if (slotState & 0x1) {
SmartcardDebug( DEBUG_PROTOCOL, ("%s: RDR_to_PC_NotifySlotChange - Card Inserted (0x%x)\n",DRIVER_NAME, slotState));
// Card is inserted
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
if (SmartcardExtension->OsData->NotificationIrp && (oldState <= SCARD_ABSENT)) {
UsbScCompleteCardTracking(SmartcardExtension);
}
} else {
SmartcardDebug( DEBUG_PROTOCOL, ("%s: RDR_to_PC_NotifySlotChange - Card Removed (0x%x)\n",DRIVER_NAME, slotState));
// Card is removed
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
if (SmartcardExtension->OsData->NotificationIrp && (oldState > SCARD_ABSENT)) {
UsbScCompleteCardTracking(SmartcardExtension);
}
}
} else {
SmartcardDebug( DEBUG_PROTOCOL, ("%s: RDR_to_PC_NotifySlotChange - No change (0x%x)\n",DRIVER_NAME, slotState));
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
irql);
}
break;
case RDR_to_PC_HardwareError:
errorHeader = (PUSBSC_HWERROR_HEADER) Buffer;
SmartcardDebug( DEBUG_PROTOCOL, ("%s: RDR_to_PC_HardwareError - 0x%x\n",DRIVER_NAME, errorHeader->bHardwareErrorCode));
// Lets just ignore hardware errors for now and see what happens.
break;
default:
ASSERT(FALSE);
break;
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScTrackingISR Exit : 0x%x\n",DRIVER_NAME, status ));
}
return status;
}
VOID
UsbScCompleteCardTracking(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
Checks to see if there is a pending card tracking irp, and completes it if
necessary.
Arguments:
Return Value:
--*/
{
KIRQL ioIrql,
keIrql;
PIRP notificationIrp;
KIRQL cancelIrql;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCompleteCardTracking Enter\n",DRIVER_NAME ));
// Grab the NotificationIrp
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
&keIrql);
notificationIrp = SmartcardExtension->OsData->NotificationIrp;
SmartcardExtension->OsData->NotificationIrp = NULL;
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
keIrql);
if (notificationIrp) {
// Complete the irp
if (notificationIrp->Cancel) {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCompleteCardTracking Irp CANCELLED\n",DRIVER_NAME ));
notificationIrp->IoStatus.Status = STATUS_CANCELLED;
} else {
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScCompleteCardTracking Completing Irp\n",DRIVER_NAME ));
notificationIrp->IoStatus.Status = STATUS_SUCCESS;
}
notificationIrp->IoStatus.Information = 0;
IoAcquireCancelSpinLock(&cancelIrql);
// reset the cancel function so that it won't be called anymore
IoSetCancelRoutine(notificationIrp,
NULL);
IoReleaseCancelSpinLock(cancelIrql);
IoCompleteRequest(notificationIrp,
IO_NO_INCREMENT);
}
}
__finally
{
SmartcardDebug( DEBUG_TRACE, ("%s!TLP3CompleteCardTracking Exit\n",DRIVER_NAME ));
}
}
NTSTATUS
UsbScVendorIoctl(
PSMARTCARD_EXTENSION SmartcardExtension
)
{
/*++
Routine Description:
Handles vendor specific ioctls, specifically the support for PC_to_RDR_Escape
Which allows reader vendors to implement their own features and still
utilize this driver. There is no parameter checking since this is a
generic pass through, so it is up to the vendor to have a rock-solid
design.
Arguments:
Return Value:
--*/
NTSTATUS status = STATUS_UNSUCCESSFUL;
PUSBSC_OUT_MESSAGE_HEADER header = NULL;
PUSBSC_IN_MESSAGE_HEADER replyHeader = NULL;
ULONG replySize;
ULONG requestSize;
__try
{
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScVendorIoclt Enter\n", DRIVER_NAME ));
switch (SmartcardExtension->MajorIoControlCode) {
case IOCTL_CCID_ESCAPE:
if (!SmartcardExtension->ReaderExtension->EscapeCommandEnabled) {
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScVendorIoclt Escape Command Not Enabled\n", DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
__leave;
}
if ((MAXULONG - sizeof(USBSC_OUT_MESSAGE_HEADER)) < SmartcardExtension->IoRequest.RequestBufferLength) {
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScVendorIoclt Request Buffer Length is too large\n", DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
__leave;
}
if ((MAXULONG - sizeof(USBSC_IN_MESSAGE_HEADER)) < SmartcardExtension->IoRequest.ReplyBufferLength) {
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScVendorIoclt Reply Buffer Length is too large\n", DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
__leave;
}
requestSize = SmartcardExtension->IoRequest.RequestBufferLength + sizeof(USBSC_OUT_MESSAGE_HEADER);
replySize = SmartcardExtension->IoRequest.ReplyBufferLength + sizeof(USBSC_IN_MESSAGE_HEADER);
header = ExAllocatePool(NonPagedPool,
requestSize);
if (!header) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(header,
sizeof(USBSC_OUT_MESSAGE_HEADER));
replyHeader = ExAllocatePool(NonPagedPool,
replySize);
if (!replyHeader) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
header->bMessageType = PC_to_RDR_Escape;
header->dwLength = SmartcardExtension->IoRequest.RequestBufferLength;
header->bSlot = 0;
RtlCopyMemory(&header[1],
SmartcardExtension->IoRequest.RequestBuffer,
SmartcardExtension->IoRequest.RequestBufferLength);
status = UsbScReadWrite(SmartcardExtension,
header,
(PUCHAR) replyHeader,
replySize,
SmartcardExtension->IoRequest.ReplyBuffer,
FALSE);
if (!NT_SUCCESS(status)) {
__leave;
}
if (replyHeader->bMessageType != RDR_to_PC_Escape) {
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScVendorIoclt reader returned the wrong message type\n", DRIVER_NAME ));
status = STATUS_DEVICE_PROTOCOL_ERROR;
__leave;
}
*SmartcardExtension->IoRequest.Information = replyHeader->dwLength;
break;
default:
SmartcardDebug( DEBUG_ERROR, ("%s!UsbScVendorIoclt Unsupported Vendor IOCTL\n", DRIVER_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
__finally
{
if (header) {
ExFreePool(header);
}
if (replyHeader) {
ExFreePool(replyHeader);
}
SmartcardDebug( DEBUG_TRACE, ("%s!UsbScVendorIoclt Exit (0x%x)\n", DRIVER_NAME, status ));
}
return status;
}