1048 lines
25 KiB
C
1048 lines
25 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1997 SCM Microsystems, Inc.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
usbcom.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Hardware access functions for USB smartcard reader
|
||
|
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
WDM
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
PP 01/19/1999 1.01
|
||
|
PP 12/18/1998 1.00 Initial Version
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "stcCmd.h"
|
||
|
#include "usbcom.h"
|
||
|
#include "stcusbnt.h"
|
||
|
|
||
|
#pragma optimize( "", off )
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS STCtoNT(
|
||
|
UCHAR ucData[])
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Error code translation routine
|
||
|
|
||
|
Arguments:
|
||
|
ucData Error code returned by the STC
|
||
|
|
||
|
Return Value:
|
||
|
Corresponding NT error code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
USHORT usCode = ucData[0]*0x100 +ucData[1];
|
||
|
NTSTATUS NtStatus;
|
||
|
|
||
|
|
||
|
switch (usCode)
|
||
|
{
|
||
|
case 0x9000:
|
||
|
NtStatus = STATUS_SUCCESS;
|
||
|
break;
|
||
|
case 0x5800:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x2000:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x4000:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x64A1:
|
||
|
NtStatus = STATUS_NO_MEDIA;
|
||
|
break;
|
||
|
case 0x64A0:
|
||
|
NtStatus = STATUS_MEDIA_CHANGED;
|
||
|
break;
|
||
|
case 0x6203:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x6300:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x6500:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x6A00:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
case 0x6A80:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
default:
|
||
|
NtStatus = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
return(NtStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
//******************************************************************************
|
||
|
//
|
||
|
// UsbSyncCompletionRoutine()
|
||
|
//
|
||
|
// Completion routine used by UsbCallUSBD.
|
||
|
//
|
||
|
// Signals an Irp completion event and then returns MORE_PROCESSING_REQUIRED
|
||
|
// to stop further completion of the Irp.
|
||
|
//
|
||
|
// If the Irp is one we allocated ourself, DeviceObject is NULL.
|
||
|
//
|
||
|
//******************************************************************************
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbSyncCompletionRoutine (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
{
|
||
|
PKEVENT kevent;
|
||
|
|
||
|
kevent = (PKEVENT)Context;
|
||
|
|
||
|
KeSetEvent(kevent,
|
||
|
IO_NO_INCREMENT,
|
||
|
FALSE);
|
||
|
|
||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
//
|
||
|
// UsbCallUSBD()
|
||
|
//
|
||
|
// Synchronously sends a URB down the device stack. Blocks until the request
|
||
|
// completes normally or until the request is timed out and cancelled.
|
||
|
//
|
||
|
// Must be called at IRQL PASSIVE_LEVEL
|
||
|
//
|
||
|
//******************************************************************************
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbCallUSBD (
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PURB Urb
|
||
|
)
|
||
|
{
|
||
|
PDEVICE_EXTENSION deviceExtension;
|
||
|
KEVENT localevent;
|
||
|
PIRP irp;
|
||
|
PIO_STACK_LOCATION nextStack;
|
||
|
NTSTATUS ntStatus;
|
||
|
|
||
|
deviceExtension = DeviceObject->DeviceExtension;
|
||
|
|
||
|
// Initialize the event we'll wait on
|
||
|
//
|
||
|
KeInitializeEvent(&localevent,
|
||
|
SynchronizationEvent,
|
||
|
FALSE);
|
||
|
|
||
|
// Allocate the Irp
|
||
|
//
|
||
|
irp = IoAllocateIrp(deviceExtension->AttachedPDO->StackSize,
|
||
|
FALSE);
|
||
|
|
||
|
if (irp == NULL)
|
||
|
{
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
// Set the Irp parameters
|
||
|
//
|
||
|
nextStack = IoGetNextIrpStackLocation(irp);
|
||
|
|
||
|
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
|
|
||
|
nextStack->Parameters.DeviceIoControl.IoControlCode =
|
||
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
|
|
||
|
nextStack->Parameters.Others.Argument1 = Urb;
|
||
|
|
||
|
// Set the completion routine, which will signal the event
|
||
|
//
|
||
|
IoSetCompletionRoutine(irp,
|
||
|
UsbSyncCompletionRoutine,
|
||
|
&localevent,
|
||
|
TRUE, // InvokeOnSuccess
|
||
|
TRUE, // InvokeOnError
|
||
|
TRUE); // InvokeOnCancel
|
||
|
|
||
|
// Pass the Irp & Urb down the stack
|
||
|
//
|
||
|
ntStatus = IoCallDriver(deviceExtension->AttachedPDO,
|
||
|
irp);
|
||
|
|
||
|
// If the request is pending, block until it completes
|
||
|
//
|
||
|
if (ntStatus == STATUS_PENDING)
|
||
|
{
|
||
|
LARGE_INTEGER timeout;
|
||
|
|
||
|
// We used to wait for 1 second, but that made this timeout longer
|
||
|
// than the polling period of 500ms. So, if this read failed (e.g.,
|
||
|
// because of device or USB failure) and timeout, two more worker items
|
||
|
// would get queued and eventually hundreds of working items would be
|
||
|
// backed up. By reducing this timeout we have a good chance that this
|
||
|
// will finish before the next item is queued. 450ms seems a good value.
|
||
|
//
|
||
|
timeout.QuadPart = -4500000; // 450ms
|
||
|
|
||
|
ntStatus = KeWaitForSingleObject(&localevent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
&timeout);
|
||
|
|
||
|
if (ntStatus == STATUS_TIMEOUT)
|
||
|
{
|
||
|
ntStatus = STATUS_IO_TIMEOUT;
|
||
|
|
||
|
// Cancel the Irp we just sent.
|
||
|
//
|
||
|
IoCancelIrp(irp);
|
||
|
|
||
|
// And wait until the cancel completes
|
||
|
//
|
||
|
KeWaitForSingleObject(&localevent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ntStatus = irp->IoStatus.Status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Done with the Irp, now free it.
|
||
|
//
|
||
|
IoFreeIrp(irp);
|
||
|
|
||
|
// If the request was not sucessful, delay for 1 second. (Why?)
|
||
|
//
|
||
|
if (ntStatus != STATUS_SUCCESS)
|
||
|
{
|
||
|
SysDelay(1000);
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbSelectInterfaces(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Initializes an USB reader with (possibly) multiple interfaces;
|
||
|
This driver only supports one interface (with multiple endpoints).
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
DeviceObject - pointer to the device object for this instance of the device.
|
||
|
|
||
|
ConfigurationDescriptor - pointer to the USB configuration
|
||
|
descriptor containing the interface and endpoint
|
||
|
descriptors.
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDEVICE_EXTENSION DeviceExtension= DeviceObject->DeviceExtension;
|
||
|
NTSTATUS NtStatus;
|
||
|
PURB pUrb = NULL;
|
||
|
USHORT usSize;
|
||
|
ULONG ulNumberOfInterfaces, i;
|
||
|
UCHAR ucNumberOfPipes, ucAlternateSetting, ucMyInterfaceNumber;
|
||
|
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
|
||
|
PUSBD_INTERFACE_INFORMATION InterfaceObject;
|
||
|
|
||
|
// This driver only supports one interface, we must parse
|
||
|
// the configuration descriptor for the interface
|
||
|
// and remember the pipes.
|
||
|
//
|
||
|
|
||
|
pUrb = USBD_CreateConfigurationRequest(ConfigurationDescriptor, &usSize);
|
||
|
|
||
|
if (pUrb)
|
||
|
{
|
||
|
//
|
||
|
// USBD_ParseConfigurationDescriptorEx searches a given configuration
|
||
|
// descriptor and returns a pointer to an interface that matches the
|
||
|
// given search criteria. We only support one interface on this device
|
||
|
//
|
||
|
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
|
||
|
ConfigurationDescriptor,
|
||
|
ConfigurationDescriptor, //search from start of config descriptro
|
||
|
-1, // interface number not a criteria; we only support one interface
|
||
|
-1, // not interested in alternate setting here either
|
||
|
-1, // interface class not a criteria
|
||
|
-1, // interface subclass not a criteria
|
||
|
-1); // interface protocol not a criteria
|
||
|
|
||
|
ASSERT( InterfaceDescriptor != NULL );
|
||
|
|
||
|
InterfaceObject = &pUrb->UrbSelectConfiguration.Interface;
|
||
|
|
||
|
for (i = 0; i < InterfaceObject->NumberOfPipes; i++)
|
||
|
{
|
||
|
InterfaceObject->Pipes[i].PipeFlags = 0;
|
||
|
}
|
||
|
|
||
|
UsbBuildSelectConfigurationRequest(
|
||
|
pUrb,
|
||
|
usSize,
|
||
|
ConfigurationDescriptor);
|
||
|
|
||
|
NtStatus = UsbCallUSBD(DeviceObject, pUrb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
if(NtStatus == STATUS_SUCCESS)
|
||
|
{
|
||
|
|
||
|
// Save the configuration handle for this device
|
||
|
DeviceExtension->ConfigurationHandle =
|
||
|
pUrb->UrbSelectConfiguration.ConfigurationHandle;
|
||
|
|
||
|
ASSERT(DeviceExtension->Interface == NULL);
|
||
|
|
||
|
DeviceExtension->Interface = ExAllocatePool(
|
||
|
NonPagedPool,
|
||
|
InterfaceObject->Length
|
||
|
);
|
||
|
|
||
|
if (DeviceExtension->Interface)
|
||
|
{
|
||
|
// save a copy of the interface information returned
|
||
|
RtlCopyMemory(
|
||
|
DeviceExtension->Interface,
|
||
|
InterfaceObject,
|
||
|
InterfaceObject->Length);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NtStatus = STATUS_NO_MEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pUrb)
|
||
|
{
|
||
|
ExFreePool(pUrb);
|
||
|
}
|
||
|
|
||
|
return NtStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbConfigureDevice(
|
||
|
IN PDEVICE_OBJECT DeviceObject)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Initializes a given instance of the device on the USB and
|
||
|
selects and saves the configuration.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject - pointer to the physical device object for this instance of the device.
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDEVICE_EXTENSION DeviceExtension= DeviceObject->DeviceExtension;
|
||
|
NTSTATUS NtStatus;
|
||
|
PURB pUrb = NULL;
|
||
|
ULONG ulSize;
|
||
|
PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor = NULL;
|
||
|
|
||
|
__try {
|
||
|
|
||
|
pUrb = ExAllocatePool(
|
||
|
NonPagedPool,
|
||
|
sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST )
|
||
|
);
|
||
|
|
||
|
if( pUrb == NULL)
|
||
|
{
|
||
|
NtStatus = STATUS_NO_MEMORY;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
// When USB_CONFIGURATION_DESCRIPTOR_TYPE is specified for DescriptorType
|
||
|
// in a call to UsbBuildGetDescriptorRequest(),
|
||
|
// all interface, endpoint, class-specific, and vendor-specific descriptors
|
||
|
// for the configuration also are retrieved.
|
||
|
// The caller must allocate a buffer large enough to hold all of this
|
||
|
// information or the data is truncated without error.
|
||
|
// Therefore the 'siz' set below is just a 'good guess', and we may have to retry
|
||
|
ulSize = sizeof( USB_CONFIGURATION_DESCRIPTOR ) + 16;
|
||
|
|
||
|
// We will break out of this 'retry loop' when UsbBuildGetDescriptorRequest()
|
||
|
// has a big enough deviceExtension->UsbConfigurationDescriptor buffer not to truncate
|
||
|
while( 1 )
|
||
|
{
|
||
|
ConfigurationDescriptor = ExAllocatePool( NonPagedPool, ulSize );
|
||
|
|
||
|
if(ConfigurationDescriptor == NULL)
|
||
|
{
|
||
|
NtStatus = STATUS_NO_MEMORY;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
UsbBuildGetDescriptorRequest(
|
||
|
pUrb,
|
||
|
sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
|
||
|
USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
||
|
0,
|
||
|
0,
|
||
|
ConfigurationDescriptor,
|
||
|
NULL,
|
||
|
ulSize,
|
||
|
NULL );
|
||
|
|
||
|
NtStatus = UsbCallUSBD( DeviceObject, pUrb );
|
||
|
|
||
|
// if we got some data see if it was enough.
|
||
|
// NOTE: we may get an error in URB because of buffer overrun
|
||
|
if (pUrb->UrbControlDescriptorRequest.TransferBufferLength == 0 ||
|
||
|
ConfigurationDescriptor->wTotalLength <= ulSize)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ulSize = ConfigurationDescriptor->wTotalLength;
|
||
|
ExFreePool(ConfigurationDescriptor);
|
||
|
ConfigurationDescriptor = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We have the configuration descriptor for the configuration we want.
|
||
|
// Now we issue the select configuration command to get
|
||
|
// the pipes associated with this configuration.
|
||
|
//
|
||
|
if(NT_SUCCESS(NtStatus))
|
||
|
{
|
||
|
NtStatus = UsbSelectInterfaces(
|
||
|
DeviceObject,
|
||
|
ConfigurationDescriptor);
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
|
||
|
if( pUrb )
|
||
|
{
|
||
|
ExFreePool( pUrb );
|
||
|
}
|
||
|
if( ConfigurationDescriptor )
|
||
|
{
|
||
|
ExFreePool( ConfigurationDescriptor );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NtStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbWriteSTCData(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
PUCHAR pucData,
|
||
|
ULONG ulSize)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Write data in the STC
|
||
|
|
||
|
Arguments:
|
||
|
ReaderExtension Context of the call
|
||
|
APDU Buffer to write
|
||
|
ulAPDULen Length of the buffer to write
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
||
|
PUCHAR pucCmd;
|
||
|
UCHAR ucResponse[3];
|
||
|
BOOLEAN resend = TRUE;
|
||
|
LONG Len;
|
||
|
ULONG Index;
|
||
|
LONG refLen = (LONG) ulSize;
|
||
|
ULONG Retries;
|
||
|
|
||
|
pucCmd = ExAllocatePool( NonPagedPool, MIN_BUFFER_SIZE);
|
||
|
|
||
|
if(pucCmd == NULL)
|
||
|
{
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
|
||
|
ReaderExtension->ulReadBufferLen = 0;
|
||
|
|
||
|
// Build the write data command
|
||
|
Len = refLen;
|
||
|
Index = 0;
|
||
|
|
||
|
while (resend == TRUE)
|
||
|
{
|
||
|
if(Len > 62)
|
||
|
{
|
||
|
Len = 62;
|
||
|
resend = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resend = FALSE;
|
||
|
}
|
||
|
|
||
|
*pucCmd = 0xA0;
|
||
|
*(pucCmd+1) = (UCHAR) Len;
|
||
|
memcpy( pucCmd + 2, pucData+Index, Len );
|
||
|
|
||
|
Retries = USB_WRITE_RETRIES;
|
||
|
do
|
||
|
{
|
||
|
// Send the Write data command
|
||
|
NTStatus = UsbWrite( ReaderExtension, pucCmd, 2 + Len);
|
||
|
if (NTStatus != STATUS_SUCCESS)
|
||
|
{
|
||
|
SmartcardDebug(
|
||
|
DEBUG_DRIVER,
|
||
|
("%s!UsbWriteSTCData: write error %X \n",
|
||
|
DRIVER_NAME,
|
||
|
NTStatus)
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
// Read the response
|
||
|
NTStatus = UsbRead( ReaderExtension, ucResponse, 3);
|
||
|
if (NTStatus != STATUS_SUCCESS)
|
||
|
{
|
||
|
SmartcardDebug(
|
||
|
DEBUG_DRIVER,
|
||
|
("%s!UsbWriteSTCData: read error %X \n",
|
||
|
DRIVER_NAME,
|
||
|
NTStatus)
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Test if what we read is really a response to a write
|
||
|
if(ucResponse[0] != 0xA0)
|
||
|
{
|
||
|
NTStatus = STCtoNT(ucResponse);
|
||
|
}
|
||
|
}
|
||
|
} while(( NTStatus != STATUS_SUCCESS ) && --Retries );
|
||
|
|
||
|
if( NTStatus != STATUS_SUCCESS )
|
||
|
break;
|
||
|
|
||
|
Index += 62;
|
||
|
Len = refLen - 62;
|
||
|
refLen = refLen - 62;
|
||
|
}
|
||
|
|
||
|
ExFreePool( pucCmd );
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbReadSTCData(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
PUCHAR pucData,
|
||
|
ULONG ulDataLen)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Read data from the STC
|
||
|
|
||
|
Arguments:
|
||
|
ReaderExtension Context of the call
|
||
|
ulAPDULen Length of the buffer to write
|
||
|
pucData Output Buffer
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
||
|
UCHAR ucCmd[1];
|
||
|
PUCHAR pucResponse;
|
||
|
int i;
|
||
|
ULONG ulLenExpected = ulDataLen;
|
||
|
ULONG Index=0;
|
||
|
BOOLEAN SendReadCommand = TRUE;
|
||
|
LARGE_INTEGER Begin;
|
||
|
LARGE_INTEGER End;
|
||
|
|
||
|
pucResponse = ExAllocatePool( NonPagedPool, MIN_BUFFER_SIZE);
|
||
|
if(pucResponse == NULL)
|
||
|
{
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
|
||
|
KeQuerySystemTime( &Begin );
|
||
|
End = Begin;
|
||
|
End.QuadPart = End.QuadPart + (LONGLONG)10 * 1000 * ReaderExtension->ReadTimeout;
|
||
|
|
||
|
// First let see if we have not already read the data that
|
||
|
// we need
|
||
|
if(ReaderExtension->ulReadBufferLen != 0)
|
||
|
{
|
||
|
if(ReaderExtension->ulReadBufferLen >= ulLenExpected)
|
||
|
{
|
||
|
// all the data that we need are available
|
||
|
memcpy(pucData,ReaderExtension->ucReadBuffer,ulLenExpected);
|
||
|
ReaderExtension->ulReadBufferLen = ReaderExtension->ulReadBufferLen - ulLenExpected;
|
||
|
if(ReaderExtension->ulReadBufferLen != 0)
|
||
|
{
|
||
|
memcpy(
|
||
|
ReaderExtension->ucReadBuffer,
|
||
|
ReaderExtension->ucReadBuffer+ulLenExpected,
|
||
|
ReaderExtension->ulReadBufferLen);
|
||
|
}
|
||
|
SendReadCommand = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// all the data that we need are not available
|
||
|
memcpy(pucData,ReaderExtension->ucReadBuffer,ReaderExtension->ulReadBufferLen);
|
||
|
ulLenExpected = ulLenExpected - ReaderExtension->ulReadBufferLen;
|
||
|
Index = ReaderExtension->ulReadBufferLen;
|
||
|
ReaderExtension->ulReadBufferLen = 0;
|
||
|
SendReadCommand = TRUE;
|
||
|
}
|
||
|
}
|
||
|
while( SendReadCommand == TRUE)
|
||
|
{
|
||
|
// Build the Read Register command
|
||
|
ucCmd[0] = 0xE0;
|
||
|
|
||
|
NTStatus = UsbWrite( ReaderExtension, ucCmd, 1);
|
||
|
if (NTStatus != STATUS_SUCCESS)
|
||
|
{
|
||
|
SmartcardDebug(
|
||
|
DEBUG_DRIVER,
|
||
|
("%s!UsbReadSTCData: write error %X \n",
|
||
|
DRIVER_NAME,
|
||
|
NTStatus)
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NTStatus = UsbRead( ReaderExtension, pucResponse, 64);
|
||
|
if (NTStatus != STATUS_SUCCESS)
|
||
|
{
|
||
|
SmartcardDebug(
|
||
|
DEBUG_DRIVER,
|
||
|
("%s!UsbReadSTCData: read error %X \n",
|
||
|
DRIVER_NAME,
|
||
|
NTStatus)
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
// Test if what we read is really a READ DATA frame
|
||
|
if(*pucResponse != 0xE0)
|
||
|
{
|
||
|
if(*pucResponse == 0x64 && *(pucResponse + 1) == 0xA0)
|
||
|
{
|
||
|
NTStatus = STATUS_NO_MEDIA;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NTStatus = STCtoNT(pucResponse);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// If there is no data available
|
||
|
if (*(pucResponse + 1) == 0)
|
||
|
{
|
||
|
KeQuerySystemTime( &Begin );
|
||
|
if(RtlLargeIntegerGreaterThan(End, Begin))
|
||
|
{
|
||
|
SendReadCommand = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReaderExtension->ulReadBufferLen = 0;
|
||
|
SmartcardDebug(
|
||
|
DEBUG_DRIVER,
|
||
|
("%s!UsbReadSTCData: Timeout %X \n",
|
||
|
DRIVER_NAME,
|
||
|
STATUS_IO_TIMEOUT));
|
||
|
NTStatus =STATUS_IO_TIMEOUT;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ((ULONG) *(pucResponse+1) < ulLenExpected)
|
||
|
{
|
||
|
memcpy(pucData+Index,pucResponse+2,(ULONG) *(pucResponse+1));
|
||
|
Index = Index + (ULONG) *(pucResponse+1);
|
||
|
ulLenExpected = ulLenExpected - (ULONG) *(pucResponse+1);
|
||
|
SendReadCommand = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SendReadCommand = FALSE;
|
||
|
memcpy(pucData+Index,pucResponse+2,ulLenExpected);
|
||
|
|
||
|
if((ULONG) *(pucResponse+1) > ulLenExpected)
|
||
|
{
|
||
|
memcpy(
|
||
|
ReaderExtension->ucReadBuffer,
|
||
|
pucResponse+ulLenExpected+2,
|
||
|
(ULONG) *(pucResponse+1) - ulLenExpected);
|
||
|
|
||
|
ReaderExtension->ulReadBufferLen =
|
||
|
(ULONG) *(pucResponse+1) - ulLenExpected;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReaderExtension->ulReadBufferLen = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExFreePool( pucResponse );
|
||
|
return NTStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbWriteSTCRegister(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
UCHAR ucAddress,
|
||
|
ULONG ulSize,
|
||
|
PUCHAR pucValue)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
||
|
PUCHAR pucCmd;
|
||
|
UCHAR ucResponse[2];
|
||
|
|
||
|
if(ulSize > 16)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
pucCmd = ExAllocatePool( NonPagedPool, MAX_READ_REGISTER_BUFFER_SIZE);
|
||
|
if(pucCmd == NULL)
|
||
|
{
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
ReaderExtension->ulReadBufferLen = 0;
|
||
|
|
||
|
// Build the write register command
|
||
|
*pucCmd = 0x80 | ucAddress;
|
||
|
*(pucCmd+1) = (UCHAR) ulSize;
|
||
|
memcpy( pucCmd + 2, pucValue, ulSize );
|
||
|
|
||
|
// Send the Write Register command
|
||
|
NTStatus = UsbWrite( ReaderExtension, pucCmd, 2 + ulSize);
|
||
|
if (NTStatus == STATUS_SUCCESS)
|
||
|
{
|
||
|
// Read the acknowledge
|
||
|
NTStatus = UsbRead( ReaderExtension, ucResponse, 2);
|
||
|
if (NTStatus == STATUS_SUCCESS)
|
||
|
{
|
||
|
NTStatus = STCtoNT(ucResponse);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExFreePool( pucCmd );
|
||
|
return NTStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbReadSTCRegister(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
UCHAR ucAddress,
|
||
|
ULONG ulSize,
|
||
|
PUCHAR pucValue)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
||
|
UCHAR ucCmd[2];
|
||
|
PUCHAR pucResponse;
|
||
|
|
||
|
if(ulSize > 16)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
pucResponse = ExAllocatePool(
|
||
|
NonPagedPool,
|
||
|
MAX_READ_REGISTER_BUFFER_SIZE
|
||
|
);
|
||
|
|
||
|
if(pucResponse == NULL)
|
||
|
{
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
|
||
|
// Build the Read Register command
|
||
|
ucCmd[0] = 0xC0 | ucAddress;
|
||
|
ucCmd[1] = (UCHAR) ulSize;
|
||
|
|
||
|
// Send the Read Register command
|
||
|
NTStatus = UsbWrite( ReaderExtension, ucCmd, 2);
|
||
|
if (NTStatus == STATUS_SUCCESS)
|
||
|
{
|
||
|
// Read the response from the reader
|
||
|
NTStatus = UsbRead(
|
||
|
ReaderExtension,
|
||
|
pucResponse,
|
||
|
6
|
||
|
);
|
||
|
|
||
|
if (NTStatus == STATUS_SUCCESS)
|
||
|
{
|
||
|
// Test if what we read is really a READ frame
|
||
|
if(*pucResponse == 0x21)
|
||
|
{
|
||
|
if(*(pucResponse + 1) > 16)
|
||
|
{
|
||
|
NTStatus = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy(
|
||
|
pucValue,
|
||
|
pucResponse + 2,
|
||
|
(ULONG) *(pucResponse + 1)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NTStatus = STCtoNT(pucResponse);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExFreePool( pucResponse );
|
||
|
return NTStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbGetFirmwareRevision(
|
||
|
PREADER_EXTENSION ReaderExtension)
|
||
|
/*++
|
||
|
Description:
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NTStatus = STATUS_SUCCESS;
|
||
|
UCHAR ucCmd[1];
|
||
|
UCHAR ucResponse[4];
|
||
|
|
||
|
ucCmd[0] = 0xE1;
|
||
|
NTStatus = UsbWrite( ReaderExtension, ucCmd, 2 );
|
||
|
|
||
|
if( NTStatus == STATUS_SUCCESS )
|
||
|
{
|
||
|
ReaderExtension->ReadTimeout = 1000;
|
||
|
NTStatus = UsbRead( ReaderExtension, ucResponse, 4 );
|
||
|
|
||
|
if( NTStatus == STATUS_SUCCESS )
|
||
|
{
|
||
|
ReaderExtension->FirmwareMajor = ucResponse[ 2 ];
|
||
|
ReaderExtension->FirmwareMinor = ucResponse[ 3 ];
|
||
|
}
|
||
|
}
|
||
|
return NTStatus ;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbRead(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
PUCHAR pData,
|
||
|
ULONG DataLen )
|
||
|
/*++
|
||
|
Description:
|
||
|
Read data on the USB bus
|
||
|
|
||
|
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_SUCCESS;
|
||
|
PURB pUrb;
|
||
|
USBD_INTERFACE_INFORMATION* pInterfaceInfo;
|
||
|
USBD_PIPE_INFORMATION* pPipeInfo;
|
||
|
PDEVICE_OBJECT DeviceObject = ReaderExtension->DeviceObject;
|
||
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
||
|
ULONG ulSize;
|
||
|
|
||
|
pInterfaceInfo = DeviceExtension->Interface;
|
||
|
|
||
|
ASSERT(pInterfaceInfo != NULL);
|
||
|
|
||
|
if (pInterfaceInfo == NULL) {
|
||
|
|
||
|
// The device has likely been disconnected during hibernate / stand by
|
||
|
return STATUS_DEVICE_NOT_CONNECTED;
|
||
|
}
|
||
|
|
||
|
// Read pipe number is 0 on this device
|
||
|
pPipeInfo = &( pInterfaceInfo->Pipes[ 0 ] );
|
||
|
|
||
|
ulSize = sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER );
|
||
|
pUrb = ExAllocatePool( NonPagedPool, ulSize );
|
||
|
|
||
|
if(pUrb == NULL)
|
||
|
{
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UsbBuildInterruptOrBulkTransferRequest(
|
||
|
pUrb,
|
||
|
(USHORT)ulSize,
|
||
|
pPipeInfo->PipeHandle,
|
||
|
pData,
|
||
|
NULL,
|
||
|
DataLen,
|
||
|
USBD_SHORT_TRANSFER_OK,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
NtStatus = UsbCallUSBD( DeviceObject, pUrb );
|
||
|
ExFreePool( pUrb );
|
||
|
}
|
||
|
|
||
|
return NtStatus;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
UsbWrite(
|
||
|
PREADER_EXTENSION ReaderExtension,
|
||
|
PUCHAR pData,
|
||
|
ULONG DataLen)
|
||
|
/*++
|
||
|
Description:
|
||
|
Write data on the usb port
|
||
|
|
||
|
Arguments:
|
||
|
ReaderExtension context of call
|
||
|
pData ptr to data buffer
|
||
|
DataLen length of data buffer (exclusive LRC!)
|
||
|
|
||
|
Return Value:
|
||
|
return value of
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
|
PURB pUrb;
|
||
|
USBD_INTERFACE_INFORMATION* pInterfaceInfo;
|
||
|
USBD_PIPE_INFORMATION* pPipeInfo;
|
||
|
PDEVICE_OBJECT DeviceObject = ReaderExtension->DeviceObject;
|
||
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
||
|
ULONG ulSize;
|
||
|
|
||
|
pInterfaceInfo = DeviceExtension->Interface;
|
||
|
|
||
|
ASSERT(pInterfaceInfo != NULL);
|
||
|
|
||
|
if (pInterfaceInfo == NULL) {
|
||
|
|
||
|
// The device has likely been disconnected during hibernate / stand by
|
||
|
return STATUS_DEVICE_NOT_CONNECTED;
|
||
|
}
|
||
|
|
||
|
// Write pipe number is 1 on this device
|
||
|
pPipeInfo = &( pInterfaceInfo->Pipes[ 1 ] );
|
||
|
|
||
|
ulSize = sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER );
|
||
|
pUrb = ExAllocatePool( NonPagedPool, ulSize );
|
||
|
if(pUrb == NULL)
|
||
|
{
|
||
|
NtStatus = STATUS_NO_MEMORY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UsbBuildInterruptOrBulkTransferRequest(
|
||
|
pUrb,
|
||
|
(USHORT)ulSize,
|
||
|
pPipeInfo->PipeHandle,
|
||
|
pData,
|
||
|
NULL,
|
||
|
DataLen,
|
||
|
USBD_SHORT_TRANSFER_OK,
|
||
|
NULL );
|
||
|
|
||
|
NtStatus = UsbCallUSBD( DeviceObject, pUrb );
|
||
|
ExFreePool( pUrb );
|
||
|
}
|
||
|
return NtStatus;
|
||
|
}
|