Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1002 lines
25 KiB

/*++
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);
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 = ReaderExtension->pExtBuffer;
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;
}
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 = ReaderExtension->pExtBuffer;
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;
}
}
}
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 = ReaderExtension->pExtBuffer;
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);
}
}
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 = ReaderExtension->pExtBuffer;
// 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) > 4)
{
NTStatus = STATUS_BUFFER_TOO_SMALL;
}
else
{
memcpy(
pucValue,
pucResponse + 2,
(ULONG) *(pucResponse + 1)
);
}
}
else
{
NTStatus = STCtoNT(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 = ReaderExtension->pUrb;
UsbBuildInterruptOrBulkTransferRequest(
pUrb,
(USHORT)ulSize,
pPipeInfo->PipeHandle,
pData,
NULL,
DataLen,
USBD_SHORT_TRANSFER_OK,
NULL);
NtStatus = UsbCallUSBD( DeviceObject, 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 = ReaderExtension->pUrb;
UsbBuildInterruptOrBulkTransferRequest(
pUrb,
(USHORT)ulSize,
pPipeInfo->PipeHandle,
pData,
NULL,
DataLen,
USBD_SHORT_TRANSFER_OK,
NULL );
NtStatus = UsbCallUSBD( DeviceObject, pUrb );
return NtStatus;
}