windows-nt/Source/XPSP1/NT/drivers/smartcrd/rnb3531/rnbo3531.c
2020-09-26 16:20:57 +08:00

4239 lines
114 KiB
C

/*++
Copyright (c) 1997 Rainbow Technologies Inc.
Module Name:
RNBO3531.c
Abstract:
Author:
Mehdi Sotoodeh mehdi@rainbow.com
Environment:
Kernel mode
Revision History :
Jan 98 - Started from the DDK sources from Klaus Schutz
Mar 98 - PnP support added
Apr 98 - Changes on card timings and PnP
- Added power management
May 98 - RemoveLock changes
- RBSCT1Transmit timeout bug fixed
- Sent to MicroSoft
May 98 - Added variable baudrate support
- Reduced ATR response time
- Sent to MicroSoft
- Data transmission size of 256 bug fixed.
Jun 98 - Power management update
- RBSCReaderPower and RBSCCardTracking moved to non-paged
code (use of KeAcquireSpinLock/IoAcquireCancelSpinLock)
- fixed: completeion function when IoSkipCurrentIrpStackLocation
is called.
- turned off PowerRequest flag on exit from RBSCReaderPower.
- Sent to MicroSoft
Sep 98 - Power management update
- Sent to MicroSoft
Oct 98 - warning fixes for win64 compatibility.
Nov 98 - Set to default when PTS fails.
- Mondex fix
- Sent to MicroSoft
Dec 98 - Reties removed from the reset operations
- Sent to MicroSoft
Apr 99 - Misc. changes
--*/
#include <stdio.h>
#include "RNBO3531.h"
//
// We do not need these functions after init anymore
//
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGEABLE, RBSCAddDevice)
#pragma alloc_text(PAGEABLE, RBSCUnloadDriver)
#pragma alloc_text(PAGEABLE, RBSCRemoveDevice)
#pragma alloc_text(PAGEABLE, RBSCTransmit)
#pragma alloc_text(PAGEABLE, RBSCSetProtocol)
#pragma alloc_text(PAGEABLE, RBSCT0Transmit)
#pragma alloc_text(PAGEABLE, RBSCT1Transmit)
#if DBG
#pragma optimize ("", off)
#endif
UCHAR GetReaderType[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_GET_TYPE};
UCHAR CardPowerDown[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_PWR_OFF};
UCHAR CardPowerUp[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_PWR_ON};
#ifdef NT4
#define SmartcardAcquireRemoveLockWithTag(x,y) SmartcardAcquireRemoveLock(x)
#define SmartcardReleaseRemoveLockWithTag(x,y) SmartcardReleaseRemoveLock(x)
#endif
#ifndef NT5
DWORD UsedPortsMask = 0; // Bit mask for used ports
#endif
//
// Table to convert inverse convention data
//
static UCHAR InverseCharTable[256] =
{
0xFF,0x7F,0xBF,0x3F,0xDF,0x5F,0x9F,0x1F,
0xEF,0x6F,0xAF,0x2F,0xCF,0x4F,0x8F,0x0F,
0xF7,0x77,0xB7,0x37,0xD7,0x57,0x97,0x17,
0xE7,0x67,0xA7,0x27,0xC7,0x47,0x87,0x07,
0xFB,0x7B,0xBB,0x3B,0xDB,0x5B,0x9B,0x1B,
0xEB,0x6B,0xAB,0x2B,0xCB,0x4B,0x8B,0x0B,
0xF3,0x73,0xB3,0x33,0xD3,0x53,0x93,0x13,
0xE3,0x63,0xA3,0x23,0xC3,0x43,0x83,0x03,
0xFD,0x7D,0xBD,0x3D,0xDD,0x5D,0x9D,0x1D,
0xED,0x6D,0xAD,0x2D,0xCD,0x4D,0x8D,0x0D,
0xF5,0x75,0xB5,0x35,0xD5,0x55,0x95,0x15,
0xE5,0x65,0xA5,0x25,0xC5,0x45,0x85,0x05,
0xF9,0x79,0xB9,0x39,0xD9,0x59,0x99,0x19,
0xE9,0x69,0xA9,0x29,0xC9,0x49,0x89,0x09,
0xF1,0x71,0xB1,0x31,0xD1,0x51,0x91,0x11,
0xE1,0x61,0xA1,0x21,0xC1,0x41,0x81,0x01,
0xFE,0x7E,0xBE,0x3E,0xDE,0x5E,0x9E,0x1E,
0xEE,0x6E,0xAE,0x2E,0xCE,0x4E,0x8E,0x0E,
0xF6,0x76,0xB6,0x36,0xD6,0x56,0x96,0x16,
0xE6,0x66,0xA6,0x26,0xC6,0x46,0x86,0x06,
0xFA,0x7A,0xBA,0x3A,0xDA,0x5A,0x9A,0x1A,
0xEA,0x6A,0xAA,0x2A,0xCA,0x4A,0x8A,0x0A,
0xF2,0x72,0xB2,0x32,0xD2,0x52,0x92,0x12,
0xE2,0x62,0xA2,0x22,0xC2,0x42,0x82,0x02,
0xFC,0x7C,0xBC,0x3C,0xDC,0x5C,0x9C,0x1C,
0xEC,0x6C,0xAC,0x2C,0xCC,0x4C,0x8C,0x0C,
0xF4,0x74,0xB4,0x34,0xD4,0x54,0x94,0x14,
0xE4,0x64,0xA4,0x24,0xC4,0x44,0x84,0x04,
0xF8,0x78,0xB8,0x38,0xD8,0x58,0x98,0x18,
0xE8,0x68,0xA8,0x28,0xC8,0x48,0x88,0x08,
0xF0,0x70,0xB0,0x30,0xD0,0x50,0x90,0x10,
0xE0,0x60,0xA0,0x20,0xC0,0x40,0x80,0x00
};
#if DBG
void
_stdcall
DumpData(
PUCHAR Msg,
PUCHAR Buffer,
ULONG Length
)
/*++
Routine Description:
This function formats the debug output and
shows it on the debug screen
Arguments:
Buffer - pointer to the buffer which contains the
debug data
Length - length of the datablock
Return Value:
Note:
Output Format:
0000 : FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0010 : FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0020 : .....
--*/
{
ULONG i, n;
char buff[80];
n = sprintf( buff, "RNBO3531!%s = %ld", Msg, Length );
for( i = 0; i < Length; i++ )
{
if( (i & 15) == 0 )
{
SmartcardDebug( DEBUG_PROTOCOL, ("%s\n", buff) );
n = sprintf( buff, "%04X :", i );
}
n += sprintf( buff+n, " %02X", Buffer[i] );
if( ((i + 1) & 7) == 0 )
{
n += sprintf( buff+n, " " );
}
}
SmartcardDebug( DEBUG_PROTOCOL, ("%s\n", buff) );
}
#else
#define DumpData(Msg,Buffer,Length)
#endif
VOID
RBSCDelay(
ULONG waitTime
)
/*++
Routine Description:
Delay function.
Arguments:
waitTime - Waiting time in micro-seconds
Return Value:
None
--*/
{
if( waitTime )
{
LARGE_INTEGER delayPeriod;
delayPeriod.HighPart = -1;
delayPeriod.LowPart = waitTime * (-10); // units of 100nsec
KeDelayExecutionThread( KernelMode,
FALSE,
&delayPeriod );
}
}
NTSTATUS
DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called at system initialization time to initialize
this driver.
Arguments:
DriverObject - Supplies the driver object.
RegistryPath - Supplies the registry path for this driver.
Return Value:
STATUS_SUCCESS - We could initialize at least one device.
STATUS_NO_SUCH_DEVICE - We could not initialize even one device.
--*/
{
#ifndef NT5
RTL_QUERY_REGISTRY_TABLE paramTable[2];
ULONG i, noOfDevices = 0;
DWORD UsedPorts = 1; // COM1 as default
NTSTATUS status;
#endif
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!DriverEntry: Enter - %s %s\n",
__DATE__,
__TIME__)
);
//
// Initialize the Driver Object with driver's entry points
//
DriverObject->DriverUnload = RBSCUnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = RBSCCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = RBSCCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = RBSCCleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RBSCDeviceControl;
#ifdef NT5
DriverObject->DriverExtension->AddDevice = RBSCAddDevice;
DriverObject->MajorFunction[IRP_MJ_POWER] = RBSCPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = RBSCPnPDeviceControl;
#else
RtlZeroMemory( paramTable, sizeof(paramTable) );
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"ComPorts";
paramTable[0].EntryContext = &UsedPorts;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &UsedPorts;
paramTable[0].DefaultLength = 0;
//
// now try to find the entry in the registry
//
RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE,
RegistryPath->Buffer,
&paramTable[0],
NULL,
NULL );
//
// We now search the registry for DeviceN value
//
for( i = 0;
noOfDevices < MAXIMUM_SERIAL_READERS && UsedPorts != 0;
i++, UsedPorts >>= 1 )
{
PFILE_OBJECT SerialFileObject;
PDEVICE_OBJECT SerialDeviceObject;
UNICODE_STRING SerialDeviceName;
while( (UsedPorts & 1) == 0 )
{
UsedPorts >>= 1;
i++;
}
RtlInitUnicodeString( &SerialDeviceName, L"\\Device\\Serial0" );
//
// create string like \Device\SerialN
//
SerialDeviceName.Buffer[14] = L'0' + (WCHAR) i;
//
// Try to create a device with the just created device name
// It is possible that a smart card device with this name
// already exists from a previous call.
// It is possible to have up to 4 devices in a system
//
status = IoGetDeviceObjectPointer( &SerialDeviceName,
FILE_ALL_ACCESS,
&SerialFileObject,
&SerialDeviceObject );
if( !NT_SUCCESS(status) )
{
UNICODE_STRING comPortNumber;
WCHAR buffer[2];
//
// Extract the com-port-number
//
comPortNumber.Buffer = buffer;
comPortNumber.Length = sizeof(WCHAR);
comPortNumber.MaximumLength = sizeof(buffer);
comPortNumber.Buffer[0] = SerialDeviceName.Buffer[14] + (WCHAR)1;
//
// Write an event-log msg that this com port is not available
//
SmartcardLogError( DriverObject,
RBSC_CANT_CONNECT_TO_SERIAL_PORT,
&comPortNumber,
status );
continue;
}
//
// Now try to create a device and connect to the serial port
//
status = RBSCAddDevice( DriverObject, SerialDeviceObject );
if( status == STATUS_SUCCESS )
{
//
// We have successfully created a device
//
PREADER_EXTENSION ReaderExtension =
DriverObject->DeviceObject->DeviceExtension;
//
// Save the serial port number this device is connected to
//
ReaderExtension->SmartcardExtension.ReaderCapabilities.Channel =
i + 1;
//
// The fileObject is used in the unload function for
// dereferencing
//
ReaderExtension->SerialFileObject = SerialFileObject;
noOfDevices++;
} else {
//
// We were unable to get the reader type.
// So remove the device.
//
ObDereferenceObject( SerialFileObject );
SmartcardLogError( DriverObject,
RBSC_CANNOT_OPEN_DEVICE,
&SerialDeviceName,
status );
}
}
if( noOfDevices == 0 )
{
SmartcardLogError( DriverObject,
RBSC_NO_SUCH_DEVICE,
NULL,
status );
return STATUS_NO_SUCH_DEVICE;
}
#endif
return STATUS_SUCCESS;
}
VOID
RBSCUnloadDriver(
PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
The driver unload routine. This is called by the I/O system
when the device is unloaded from memory.
Arguments:
DriverObject - Pointer to driver object created by system.
Return Value:
STATUS_SUCCESS.
--*/
{
#ifdef NT5
//
// We do not need to do anything since all the cleanups are done by
// RBSCRemoveDevice().
//
#else
while( DriverObject->DeviceObject )
{
RBSCRemoveDevice( DriverObject->DeviceObject );
}
#endif
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!UnloadDriver\n")
);
}
#ifdef NT5
NTSTATUS
RBSCIoCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PKEVENT Event
)
/*++
Routine Description:
Completion routine for an Irp sent to the serial driver. The event will
be set to notify that the serial driver is done. The routine will not
'complete' the Irp, so the caller of RBSCCallSerialDriver can continue.
Arguments:
DeviceObject - Pointer to device object
Irp - Irp to complete
Event - Used for process synchronization
Return Value:
STATUS_CANCELLED Irp was cancelled by the IO manager
STATUS_MORE_PROCESSING_REQUIRED Irp will be finished by caller of
RBSCCallSerialDriver
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
Irp->IoStatus.Status =
Irp->Cancel ? STATUS_CANCELLED : STATUS_MORE_PROCESSING_REQUIRED;
KeSetEvent( Event, 0, FALSE );
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
RBSCCallSerialDriver(
PDEVICE_OBJECT AttachedSerialPort,
PIRP Irp
)
/*++
Routine Description:
Send an Irp to the serial driver and wait until the serial driver has
finished the request.
To make sure that the serial driver will not complete the Irp we first
initialize an event and set our own completion routine for the Irp.
When the serial driver has processed the Irp the completion routine will
set the event and tell the IO manager that more processing is required.
By waiting for the event we make sure that we continue only if the serial
driver has processed the Irp completely.
Arguments:
AttachedSerialPort - The PDO
Irp - Irp to send to the serial driver
Return Value:
status returned by the serial driver
Note:
Since this function can be called after our device is removed, do not
use any of the reader extension data here.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
KEVENT Event;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!CallSerialDriver: Enter\n")
);
//
// Prepare everything to call the underlying driver
//
//
// Copy our stack to the next stack location.
//
IoCopyCurrentIrpStackLocationToNext( Irp);
//
// initialize an event for process synchronization. the event is passed
// to our completion routine and will be set if the serial driver is done
//
KeInitializeEvent( &Event,
NotificationEvent,
FALSE );
//
// Our IoCompletionRoutine sets only our event
//
IoSetCompletionRoutine( Irp,
RBSCIoCompletion,
&Event,
TRUE,
TRUE,
TRUE );
//
// Call the serial driver
//
status = (IoGetCurrentIrpStackLocation( Irp )->MajorFunction ==
IRP_MJ_POWER) ?
PoCallDriver( AttachedSerialPort, Irp ) :
IoCallDriver( AttachedSerialPort, Irp );
//
// Wait until the serial driver has processed the Irp
//
if( status == STATUS_PENDING )
{
status = KeWaitForSingleObject( &Event,
Executive,
KernelMode,
FALSE, // No alert
NULL ); // No timeout
ASSERT( STATUS_SUCCESS == status );
status = Irp->IoStatus.Status;
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!CallSerialDriver: Exit %x\n",status)
);
return status;
}
#endif
NTSTATUS
RBSCStartDevice(
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Open the underlying serial driver and initialize it.
Then initialize the reader hardware.
Arguments:
DeviceObject - Pointer to device object
Return Value:
STATUS_SUCCESS
STATUS_NO_MEMORY
status returned by LowLevel routines
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
PDEVICE_OBJECT AttachedSerialPort = ReaderExtension->AttachedSerialPort;
NTSTATUS status;
#ifdef NT5
PIO_STACK_LOCATION irpSp;
PIRP irp = IoAllocateIrp( (CCHAR)(DeviceObject->StackSize + 1), FALSE );
ASSERT( irp != NULL );
if( irp == NULL )
{
return STATUS_NO_MEMORY;
}
#endif
_try {
#ifdef NT5
PIO_STACK_LOCATION irpStack;
IO_STATUS_BLOCK ioStatusBlock;
//
// Open the underlying serial driver.
// This is necessary for two reasons:
// a) The serial driver can't be used without opening it
// b) The call will go through serenum first which informs
// it to stop looking/polling for new devices.
//
irp->UserIosb = &ioStatusBlock;
IoSetNextIrpStackLocation(irp);
irpStack = IoGetCurrentIrpStackLocation( irp );
irpStack->MajorFunction = IRP_MJ_CREATE;
irpStack->Parameters.Create.Options = 0;
irpStack->Parameters.Create.ShareAccess = 0;
irpStack->Parameters.Create.FileAttributes = 0;
irpStack->Parameters.Create.EaLength = 0;
status = RBSCCallSerialDriver( AttachedSerialPort, irp );
if( status != STATUS_SUCCESS )
{
if( status == STATUS_SHARED_IRQ_BUSY )
{
// Port is in use by another device
SmartcardLogError( DeviceObject,
RBSC_CANT_SHARE_IRQ,
NULL,
0 );
}
leave;
}
#endif
KeClearEvent( &ReaderExtension->SerialCloseDone );
ReaderExtension->SmartcardExtension.ReaderCapabilities.CurrentState =
(ULONG) SCARD_UNKNOWN;
status = RBSCConfigureSerialPort( ReaderExtension );
if( status != STATUS_SUCCESS )
{
leave;
}
//
// Send a wait mask to the serial driver.
// This call only sets the wait mask.
// We want to be informed when CTS or DSR changes its state
//
#ifdef NT5
ReaderExtension->WaitMask = SERIAL_EV_CTS | SERIAL_EV_DSR;
#else
ReaderExtension->WaitMask = SERIAL_EV_CTS;
#endif
status = RBSCSerialIo( &ReaderExtension->SmartcardExtension,
IOCTL_SERIAL_SET_WAIT_MASK,
(PUCHAR) &ReaderExtension->WaitMask,
sizeof(ReaderExtension->WaitMask),
0 );
if( status != STATUS_SUCCESS)
{
leave;
}
//
// Now tell the serial driver that we want to be informed
// when CTS or DSR changes its state.
// Changing the lowest two bits tells IoBuildDeviceIoControlRequest
// NOT to allocate a system buffer for the I/O operation.
// We don't need a system buffer since we use our own buffers.
//
ReaderExtension->SerialStatusIrp = IoAllocateIrp(
(CCHAR) (DeviceObject->StackSize + 1),
FALSE
);
if (ReaderExtension->SerialStatusIrp == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
irpSp = IoGetNextIrpStackLocation( ReaderExtension->SerialStatusIrp );
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
irpSp->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(ReaderExtension->WaitMask);
irpSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&ReaderExtension->WaitMask;
//
// this artificial delay is necessary to make this driver work
// with digi board cards
//
RBSCDelay( 100000 );
//
// We simulate a callback now that triggers the card supervision
//
RBSCSerialEvents(
ReaderExtension->SmartcardExtension.OsData->DeviceObject,
ReaderExtension->SerialStatusIrp,
ReaderExtension
);
#ifdef NT5
status = IoSetDeviceInterfaceState(
&ReaderExtension->PnPDeviceName,
TRUE
);
if( status != STATUS_SUCCESS )
{
leave;
}
#endif
KeSetEvent( &ReaderExtension->ReaderStarted, 0, FALSE );
} _finally {
if (status == STATUS_SHARED_IRQ_BUSY)
{
SmartcardLogError( DeviceObject,
RBSC_IRQ_BUSY,
NULL,
status );
}
#ifdef NT5
IoFreeIrp( irp );
#endif
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!StartDevice: Exit (%x)\n",status)
);
return status;
}
VOID
RBSCStopDevice(
PREADER_EXTENSION ReaderExtension
)
/*++
Routine Description:
Waits for the device to be closed
Arguments:
ReaderExtension - Pointer to our device data
Return Value:
void
--*/
{
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!StopDevice: Enter\n")
);
if( !KeReadStateEvent( &ReaderExtension->SerialCloseDone ) )
{
NTSTATUS status;
// test if we ever started event tracking
if( ReaderExtension->WaitMask == 0 )
{
// no, we did not
// We 'only' need to close the serial port
RBSCCloseSerialPort(
ReaderExtension->SmartcardExtension.OsData->DeviceObject,
NULL );
}
else
{
//
// We now inform the serial driver that we're no longer
// interested in serial events. This will also free the irp
// we use for those io-completions
//
ReaderExtension->WaitMask = 0;
status = RBSCSerialIo(
&ReaderExtension->SmartcardExtension,
IOCTL_SERIAL_SET_WAIT_MASK,
(PUCHAR)&ReaderExtension->WaitMask,
sizeof(ULONG),
0
);
ASSERT( status == STATUS_SUCCESS );
// now wait until the connetion to serial is closed
status = KeWaitForSingleObject(
&ReaderExtension->SerialCloseDone,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT( status == STATUS_SUCCESS );
}
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!StopDevice\n")
);
}
VOID
RBSCRemoveDevice(
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Stops the device then releases all the resources used.
Arguments:
DeviceObject - Pointer to device object
Return Value:
void
--*/
{
NTSTATUS status;
PREADER_EXTENSION ReaderExtension;
PSMARTCARD_EXTENSION SmartcardExtension;
PAGED_CODE();
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!RemoveDevice: Enter\n")
);
ReaderExtension = DeviceObject->DeviceExtension;
SmartcardExtension = &ReaderExtension->SmartcardExtension;
if( SmartcardExtension->OsData )
{
// complete pending card tracking requests (if any)
RBSCCompleteCardTracking( SmartcardExtension );
ASSERT( SmartcardExtension->OsData->NotificationIrp == NULL );
#ifdef NT5
SmartcardReleaseRemoveLockAndWait( SmartcardExtension );
#endif
}
#ifndef NT5
//
// Free the device slot (if in use)
//
UsedPortsMask &= ~(1<<SmartcardExtension->VendorAttr.UnitNo);
#endif
RBSCStopDevice( ReaderExtension );
#ifdef NT5
if( ReaderExtension->AttachedSerialPort )
{
IoDetachDevice( ReaderExtension->AttachedSerialPort );
}
if( ReaderExtension->PnPDeviceName.Buffer != NULL )
{
RtlFreeUnicodeString( &ReaderExtension->PnPDeviceName );
}
if( ReaderExtension->CloseSerial != NULL )
{
IoFreeWorkItem(ReaderExtension->CloseSerial);
}
#else
if( ReaderExtension->SerialFileObject )
{
ObDereferenceObject( ReaderExtension->SerialFileObject );
}
if( ReaderExtension->DosDeviceName.Buffer != NULL )
{
//
// Delete the symbolic link of the smart card reader
//
IoDeleteSymbolicLink( &ReaderExtension->DosDeviceName );
RtlFreeUnicodeString( &ReaderExtension->DosDeviceName );
}
#endif
if( SmartcardExtension->OsData != NULL )
{
SmartcardExit( SmartcardExtension );
}
IoDeleteDevice( DeviceObject );
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!RemoveDevice: Exit\n")
);
}
#ifdef NT5
NTSTATUS
RBSCPnPDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
driver callback for pnp manager
Request: Action:
IRP_MN_START_DEVICE Notify the driver about the new device
and start the device
IRP_MN_STOP_DEVICE Free all resources used by the device and
tell the driver that the device was stopped
IRP_MN_QUERY_REMOVE_DEVICE If the device is opened (i.e. in use) an
error will be returned to prevent the PnP
manager to stop the driver
IRP_MN_CANCEL_REMOVE_DEVICE just notify that we can continue without any
restrictions
IRP_MN_REMOVE_DEVICE notify the driver that the device was
removed, stop & unload the device
All other requests will be passed to the driver to ensure correct
processing.
Arguments:
DeviceObject - Pointer to device object
Irp - Irp from the PnP manager
Return Value:
STATUS_SUCCESS
STATUS_UNSUCCESSFUL
status returned by serial driver
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
PDEVICE_OBJECT AttachedSerialPort = ReaderExtension->AttachedSerialPort;
NTSTATUS status;
PIO_STACK_LOCATION stack;
BOOLEAN deviceRemoved = FALSE, irpSkipped = FALSE;
KEVENT Event;
KIRQL irql;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!PnPDeviceControl: Enter (%08x)\n",
IoGetCurrentIrpStackLocation( Irp )->MinorFunction)
);
status = SmartcardAcquireRemoveLockWithTag(
&ReaderExtension->SmartcardExtension, ' PnP' );
ASSERT( status == STATUS_SUCCESS );
Irp->IoStatus.Information = 0;
if( status != STATUS_SUCCESS )
{
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
}
stack = IoGetCurrentIrpStackLocation( Irp );
//
// Now look what the PnP manager wants...
//
switch( stack->MinorFunction )
{
case IRP_MN_START_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_START_DEVICE\n")
);
// We have to call the underlying driver first
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
ASSERT( status == STATUS_SUCCESS );
if( status == STATUS_SUCCESS)
{
status = RBSCStartDevice( DeviceObject );
}
break;
case IRP_MN_QUERY_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_QUERY_STOP_DEVICE\n")
);
KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql );
if( ReaderExtension->IoCount > 0 )
{
// we refuse to stop if we have pending io
KeReleaseSpinLock( &ReaderExtension->SpinLock, irql );
status = STATUS_DEVICE_BUSY;
break;
}
// stop processing requests
KeClearEvent( &ReaderExtension->ReaderStarted );
KeReleaseSpinLock( &ReaderExtension->SpinLock, irql );
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
break;
case IRP_MN_CANCEL_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_CANCEL_STOP_DEVICE\n")
);
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
if( status == STATUS_SUCCESS )
{
// we can continue to process requests
KeSetEvent( &ReaderExtension->ReaderStarted, 0, FALSE );
}
break;
case IRP_MN_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_STOP_DEVICE\n")
);
RBSCStopDevice( ReaderExtension );
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_QUERY_REMOVE_DEVICE\n")
);
// disable the interface (and ignore possible errors)
IoSetDeviceInterfaceState(
&ReaderExtension->PnPDeviceName,
FALSE
);
// now look if someone is currently connected to us
if (ReaderExtension->ReaderOpen)
{
//
// someone is connected, fail the call
// we will enable the device interface in
// IRP_MN_CANCEL_REMOVE_DEVICE again
//
status = STATUS_UNSUCCESSFUL;
break;
}
// pass the call to the next driver in the stack
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_CANCEL_REMOVE_DEVICE\n")
);
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
//
// reenable the interface only in case that the reader is
// still connected. This covers the following case:
// hibernate machine, disconnect reader, wake up, stop device
// (from task bar) and stop fails since an app. holds the device open
//
if( status == STATUS_SUCCESS &&
ReaderExtension->WaitMask != 0 )
{
status = IoSetDeviceInterfaceState(
&ReaderExtension->PnPDeviceName,
TRUE
);
ASSERT(status == STATUS_SUCCESS);
}
break;
case IRP_MN_REMOVE_DEVICE:
// Remove our device
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!PnPDeviceControl: IRP_MN_REMOVE_DEVICE\n")
);
RBSCRemoveDevice( DeviceObject );
// DeviceObject is freed, do not reference it again.
status = RBSCCallSerialDriver( AttachedSerialPort, Irp );
deviceRemoved = TRUE;
break;
default:
// This is an Irp that is only useful for underlying drivers
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver( AttachedSerialPort, Irp );
irpSkipped = TRUE;
break;
}
if( !irpSkipped )
{
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
if( !deviceRemoved )
{
SmartcardReleaseRemoveLockWithTag(
&ReaderExtension->SmartcardExtension, ' PnP' );
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!PnPDeviceControl: Exit %x\n",status)
);
return status;
}
#endif
NTSTATUS
RBSCAddDevice(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This is the add-device routine for the miniport.
Find a device slot then, create device object and attach it to the
given PhysicalDeviceObject.
Initialize the device extension and its smart card data.
Arguments:
DriverObject - a pointer to the driver object for this device
PhysicalDeviceObject- The device object of the serial port to attach to
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
PSMARTCARD_EXTENSION SmartcardExtension = NULL;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!AddDevice: Enter\n")
);
try {
ULONG DeviceNo;
PREADER_EXTENSION ReaderExtension;
#ifndef NT5
UNICODE_STRING SmartcardDeviceName;
for( DeviceNo = 0;
DeviceNo < MAXIMUM_SERIAL_READERS;
DeviceNo++ )
{
if( (UsedPortsMask & (1<<DeviceNo)) == 0 )
{
break;
}
}
if( DeviceNo == MAXIMUM_SERIAL_READERS )
{
SmartcardLogError(
DriverObject,
RBSC_CANT_CREATE_MORE_DEVICES,
NULL,
0
);
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// construct the device name
//
RtlInitUnicodeString( &SmartcardDeviceName, L"\\Device\\RNBO3531?" );
SmartcardDeviceName.Buffer[16] = L'0' + (WCHAR) DeviceNo;
#endif
//
// Create the functional device object
//
status = IoCreateDevice(
DriverObject,
sizeof(READER_EXTENSION),
#ifdef NT5
NULL, // No name
#else
&SmartcardDeviceName,
#endif
FILE_DEVICE_SMARTCARD,
0,
TRUE, // Exclusive
&DeviceObject
);
if( status != STATUS_SUCCESS )
{
SmartcardLogError( DriverObject,
RBSC_CANT_CREATE_DEVICE,
NULL,
0 );
leave;
}
//
// set up the device extension and initialize it.
//
ReaderExtension = DeviceObject->DeviceExtension;
SmartcardExtension = &ReaderExtension->SmartcardExtension;
SmartcardExtension->ReaderExtension = ReaderExtension;
#ifdef NT5
ReaderExtension->CloseSerial = IoAllocateWorkItem(
DeviceObject
);
if (ReaderExtension->CloseSerial == NULL) {
SmartcardLogError(
DriverObject,
RBSC_NO_MEMORY,
NULL,
0
);
leave;
}
#endif
// Used for stop / start notification
KeInitializeEvent( &ReaderExtension->ReaderStarted,
NotificationEvent,
FALSE );
KeInitializeEvent( &ReaderExtension->SerialCloseDone,
NotificationEvent,
TRUE );
// Used to keep track of open close calls
ReaderExtension->ReaderOpen = FALSE;
KeInitializeSpinLock( &ReaderExtension->SpinLock );
//
// enter correct version of the lib
//
SmartcardExtension->Version = SMCLIB_VERSION;
SmartcardExtension->SmartcardRequest.BufferSize =
SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
status = SmartcardInitialize( SmartcardExtension );
if( status != STATUS_SUCCESS )
{
SmartcardLogError(
DriverObject,
(SmartcardExtension->OsData ?
RBSC_WRONG_LIB_VESION : RBSC_NO_MEMORY),
NULL,
0
);
leave;
}
//
// Set up call back functions
//
SmartcardExtension->ReaderFunction[ RDF_TRANSMIT ] =
RBSCTransmit;
SmartcardExtension->ReaderFunction[ RDF_SET_PROTOCOL ] =
RBSCSetProtocol;
SmartcardExtension->ReaderFunction[ RDF_CARD_POWER ] =
RBSCReaderPower;
SmartcardExtension->ReaderFunction[ RDF_CARD_TRACKING ] =
RBSCCardTracking;
//
// setup smartcard extension
//
//
// Initialize the vendor information
//
strcpy( SmartcardExtension->VendorAttr.VendorName.Buffer,
VENDOR_NAME );
SmartcardExtension->VendorAttr.VendorName.Length =
sizeof(VENDOR_NAME);
strcpy( SmartcardExtension->VendorAttr.IfdType.Buffer,
READER_NAME );
SmartcardExtension->VendorAttr.IfdType.Length =
sizeof(READER_NAME);
SmartcardExtension->VendorAttr.UnitNo = MAXULONG;
for (DeviceNo = 0; DeviceNo < MAXULONG; DeviceNo++)
{
PDEVICE_OBJECT devObj;
for (devObj = DeviceObject;
devObj != NULL;
devObj = devObj->NextDevice)
{
PREADER_EXTENSION devExt = devObj->DeviceExtension;
if (DeviceNo ==
devExt->SmartcardExtension.VendorAttr.UnitNo)
{
break;
}
}
if (devObj == NULL)
{
SmartcardExtension->VendorAttr.UnitNo = DeviceNo;
break;
}
}
//
// tell the lib our device object
//
SmartcardExtension->OsData->DeviceObject = DeviceObject;
#ifdef NT5
ReaderExtension->AttachedSerialPort = IoAttachDeviceToDeviceStack(
DeviceObject,
PhysicalDeviceObject
);
if( ReaderExtension->AttachedSerialPort == NULL )
{
SmartcardLogError( DriverObject,
RBSC_CANT_ATTACH_TO_SERIAL_PORT,
&DriverObject->DriverName,
0 );
status = STATUS_UNSUCCESSFUL;
leave;
}
// register our new device
status = IoRegisterDeviceInterface( PhysicalDeviceObject,
&SmartCardReaderGuid,
NULL,
&ReaderExtension->PnPDeviceName );
ASSERT( status == STATUS_SUCCESS );
ReaderExtension->ReaderPowerState = PowerReaderWorking;
#else
//
// Save the deviceObject for the connected serial port
//
ReaderExtension->AttachedSerialPort = PhysicalDeviceObject;
status = RBSCStartDevice( DeviceObject );
if( status != STATUS_SUCCESS )
{
leave;
}
//
// Create a symbolic link
//
status = SmartcardCreateLink( &ReaderExtension->DosDeviceName,
&SmartcardDeviceName );
#endif
if( status != STATUS_SUCCESS )
{
leave;
}
//
// tell the OS that we supposed to do buffered io
//
#ifdef NT5
DeviceObject->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
#else
DeviceObject->Flags |= DO_BUFFERED_IO;
UsedPortsMask |= (1<<DeviceNo);
#endif
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!AddDevice: Device #%d created Ext=%08x.\n",
DeviceNo,ReaderExtension)
);
}
finally
{
if( status != STATUS_SUCCESS && DeviceObject != NULL )
{
RBSCRemoveDevice( DeviceObject );
}
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!AddDevice: Exit %x\n",status)
);
return status;
}
NTSTATUS
RBSCConfigureSerialPort(
PREADER_EXTENSION ReaderExtension
)
/*++
Routine Description:
This routine reads the default configuraton values for this device.
Arguments:
ReaderExtension - Pointer to our device data
Return Value:
none
--*/
{
PSMARTCARD_EXTENSION SmartcardExtension =
&ReaderExtension->SmartcardExtension;
PUCHAR RxBuffer = SmartcardExtension->SmartcardReply.Buffer;
NTSTATUS status;
{
//
// Set up baudrate
//
SERIAL_BAUD_RATE BaudRate;
BaudRate.BaudRate = DATARATE_DEFAULT;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_BAUD_RATE,
(PUCHAR) &BaudRate,
sizeof(SERIAL_BAUD_RATE),
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Set up line control parameters
//
ReaderExtension->LineControl.Parity = ODD_PARITY;
ReaderExtension->LineControl.StopBits = STOP_BIT_1;
ReaderExtension->LineControl.WordLength = SERIAL_DATABITS_8;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_LINE_CONTROL,
(PUCHAR)&ReaderExtension->LineControl,
sizeof(SERIAL_LINE_CONTROL),
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Set serial special characters
//
SERIAL_CHARS SerialChars;
SerialChars.ErrorChar = 0;
SerialChars.EofChar = 0;
SerialChars.EventChar = 0;
SerialChars.XonChar = 0;
SerialChars.XoffChar = 0;
SerialChars.BreakChar = 0xFF;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_CHARS,
(PUCHAR) &SerialChars,
sizeof(SERIAL_CHARS),
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Set up timeouts (all in msec)
//
ReaderExtension->SerialTimeouts.ReadTotalTimeoutMultiplier = 1;
ReaderExtension->SerialTimeouts.ReadIntervalTimeout = 2*
ATR_CHAR_TIMEOUT/1000;
ReaderExtension->SerialTimeouts.ReadTotalTimeoutConstant = 2*
ATR_BLOCK_TIMEOUT/1000;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_TIMEOUTS,
(PUCHAR)&ReaderExtension->SerialTimeouts,
sizeof(SERIAL_TIMEOUTS),
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Set flowcontrol and handshaking
//
SERIAL_HANDFLOW HandFlow;
HandFlow.XonLimit = 0;
HandFlow.XoffLimit = 0;
HandFlow.FlowReplace = SERIAL_XOFF_CONTINUE ;
HandFlow.ControlHandShake = 0;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_HANDFLOW,
(PUCHAR) &HandFlow,
sizeof(SERIAL_HANDFLOW),
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Set break off
//
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_BREAK_OFF,
NULL,
0,
0 );
}
if( status == STATUS_SUCCESS )
{
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_RTS,
NULL,
0,
0 );
}
if( status == STATUS_SUCCESS )
{
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_DTR,
NULL,
0,
0 );
}
if( status == STATUS_SUCCESS )
{
//
// Toggle DTR line to wake-up the reader if it is
// in SLEEP mode
//
RBSCDelay( 10000 );
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_CLR_DTR,
NULL,
0,
0 );
}
if( status == STATUS_SUCCESS )
{
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_DTR,
NULL,
0,
0 );
}
if( status == STATUS_SUCCESS )
{
RBSCDelay( 10000 );
status = RBSCPacketExchange( SmartcardExtension,
GetReaderType,
sizeof(GetReaderType),
WAIT_TIME_READER_TYPE,
TRUE );
}
if( status != STATUS_SUCCESS )
{
return status;
}
if( SmartcardExtension->SmartcardReply.BufferLength <
SIZE_READER_TYPE ||
RxBuffer[0] != 'R' || RxBuffer[1] != 'N' ||
RxBuffer[2] != 'B' || RxBuffer[3] != 'O' ||
RxBuffer[15] != READER_TYPE_1 )
{
return STATUS_DEVICE_DATA_ERROR;
}
//
// Set reader info
//
SmartcardExtension->VendorAttr.IfdVersion.VersionMajor =
(UCHAR)RxBuffer[6];
SmartcardExtension->VendorAttr.IfdVersion.VersionMinor =
(UCHAR)RxBuffer[7];
SmartcardExtension->VendorAttr.IfdVersion.BuildNumber =
((USHORT)RxBuffer[8]<<8) + ((USHORT)RxBuffer[9]);
//
// Clk frequency in KHz encoded as little endian integer
//
SmartcardExtension->ReaderCapabilities.CLKFrequency.Default =
SmartcardExtension->ReaderCapabilities.CLKFrequency.Max =
((USHORT)RxBuffer[10]<<8) + ((USHORT)RxBuffer[11]);
SmartcardExtension->ReaderCapabilities.DataRate.Default =
SmartcardExtension->ReaderCapabilities.DataRate.Max = DATARATE_DEFAULT;
if( RxBuffer[13] != 0 )
{
// reader is supposed to support higher data rates
ULONG NumRates = 1;
if( RxBuffer[13] & DATARATE_14400 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 14400;
}
if( RxBuffer[13] & DATARATE_19200 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 19200;
}
if( RxBuffer[13] & DATARATE_28800 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 28800;
}
if( RxBuffer[13] & DATARATE_38400 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 38400;
}
if( RxBuffer[13] & DATARATE_57600 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 57600;
}
if( RxBuffer[13] & DATARATE_115200 )
{
SmartcardExtension->ReaderCapabilities.DataRate.Max =
ReaderExtension->DataRatesSupported[ NumRates++ ] = 115200;
}
if( NumRates > 1 )
{
ReaderExtension->DataRatesSupported[0] = 9600; // always supported
SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries =
(UCHAR) NumRates;
SmartcardExtension->ReaderCapabilities.DataRatesSupported.List =
ReaderExtension->DataRatesSupported;
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!ConfigureSerialPort: %ld DataRates, Max=%ld\n",
NumRates,
SmartcardExtension->ReaderCapabilities.DataRate.Max)
);
}
}
SmartcardExtension->ReaderCapabilities.MaxIFSD = MAX_IFSD;
//
// setup smartcard extension - reader capabilities
//
SmartcardExtension->ReaderCapabilities.SupportedProtocols =
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
SmartcardExtension->ReaderCapabilities.ReaderType =
SCARD_READER_TYPE_SERIAL;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_GET_MODEMSTATUS,
NULL,
0,
sizeof(ReaderExtension->ModemStatus) );
if( status == STATUS_SUCCESS )
{
ReaderExtension->ModemStatus =
*(PULONG)SmartcardExtension->SmartcardReply.Buffer;
SmartcardExtension->ReaderCapabilities.CurrentState =
(ReaderExtension->ModemStatus & SERIAL_CTS_STATE) ?
SCARD_SWALLOWED : SCARD_ABSENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
RBSCCreateClose(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system when the device is opened
or closed.
Arguments:
DeviceObject - Pointer to device object
Irp - IRP involved.
Return Value:
STATUS_SUCCESS.
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION SmartcardExtension =
&ReaderExtension->SmartcardExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
__try
{
if( irpStack->MajorFunction == IRP_MJ_CREATE )
{
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Create\n")
);
#ifdef NT5
if( SmartcardAcquireRemoveLockWithTag(
SmartcardExtension, 'lCrC' ) != STATUS_SUCCESS )
{
status = STATUS_DEVICE_REMOVED;
__leave;
}
// test if the device has been opened already
if (InterlockedCompareExchange(
&ReaderExtension->ReaderOpen,
TRUE,
FALSE) == FALSE)
{
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!CreateClose: Open\n")
);
}
else
{
// the device is already in use
status = STATUS_UNSUCCESSFUL;
// release the lock
SmartcardReleaseRemoveLockWithTag( SmartcardExtension,
'lCrC' );
}
#endif
} else {
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Close\n")
);
#ifdef NT5
SmartcardReleaseRemoveLockWithTag( SmartcardExtension,
'lCrC' );
#endif
ReaderExtension->ReaderOpen = FALSE;
}
}
__finally
{
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS
RBSCCancel(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system
when the irp should be cancelled
Arguments:
DeviceObject - Pointer to device object
Irp - IRP involved.
Return Value:
STATUS_CANCELLED
--*/
{
PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION SmartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
("%s!RBSCCancel: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp == SmartcardExtension->OsData->NotificationIrp);
IoReleaseCancelSpinLock(
Irp->CancelIrql
);
RBSCCompleteCardTracking(SmartcardExtension);
SmartcardDebug(
DEBUG_TRACE,
("%s!RBSCCancel: Exit\n",
DRIVER_NAME)
);
return STATUS_CANCELLED;
}
NTSTATUS
RBSCCleanup(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system when the calling thread
terminates
Arguments:
DeviceObject - Pointer to device object
Irp - IRP involved.
Return Value:
STATUS_CANCELLED
--*/
{
PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION SmartcardExtension = &deviceExtension->SmartcardExtension;
NTSTATUS status = STATUS_SUCCESS;
SmartcardDebug(
DEBUG_TRACE,
("%s!RBSCCleanup: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp != SmartcardExtension->OsData->NotificationIrp);
// We need to complete the notification irp
RBSCCompleteCardTracking(SmartcardExtension);
SmartcardDebug(
DEBUG_DRIVER,
("%s!RBSCCleanup: Completing IRP %lx\n",
DRIVER_NAME,
Irp)
);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
SmartcardDebug(
DEBUG_TRACE,
("%s!RBSCCleanup: Exit\n",
DRIVER_NAME)
);
return STATUS_SUCCESS;
}
NTSTATUS
RBSCSerialIo(
PSMARTCARD_EXTENSION SmartcardExtension,
ULONG SerialIoControlCode,
PUCHAR TxBuffer,
ULONG TxSize,
ULONG RxSize
)
/*++
Routine Description:
This routine sends IOCTL's to the serial driver. It waits on for their
completion, and then returns.
Arguments:
SmartcardExtension - Pointer to the smart card data
SerialIoControlCode - IOCTL code to be sent to the serial driver
TxBuffer - Ponter to the data to send
TxSize - Size of data to send
RxSize - Number of bytes expected to receive
Return Value:
NTSTATUS
--*/
{
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PIRP irp;
if (KeReadStateEvent(&ReaderExtension->SerialCloseDone))
{
//
// we have no connection to serial, fail the call
// this could be the case if the reader was removed
// during stand by / hibernation
//
return STATUS_UNSUCCESSFUL;
}
//
// Check if the buffers are large enough
//
ASSERT( RxSize <= SmartcardExtension->SmartcardReply.BufferSize );
if( RxSize > SmartcardExtension->SmartcardReply.BufferSize )
{
SmartcardLogError( SmartcardExtension->OsData->DeviceObject,
RBSC_BUFFER_TOO_SMALL,
NULL,
0 );
return STATUS_BUFFER_TOO_SMALL;
}
KeInitializeEvent( &event, NotificationEvent, FALSE );
//
// Build irp to be sent to serial driver
//
irp = IoBuildDeviceIoControlRequest(
SerialIoControlCode,
ReaderExtension->AttachedSerialPort,
(TxSize ? TxBuffer : NULL),
TxSize,
(RxSize ? SmartcardExtension->SmartcardReply.Buffer : NULL),
RxSize,
FALSE,
&event,
&ioStatus
);
ASSERT( irp != NULL );
if( irp == NULL )
{
return STATUS_INSUFFICIENT_RESOURCES;
}
switch( SerialIoControlCode )
{
PIO_STACK_LOCATION nextsp;
//
// The serial driver trasfers data from/to
// irp->AssociatedIrp.SystemBuffer
//
case SMARTCARD_WRITE:
nextsp = IoGetNextIrpStackLocation( irp );
nextsp->MajorFunction = IRP_MJ_WRITE;
nextsp->Parameters.Write.Length = TxSize;
nextsp->Parameters.Write.ByteOffset.QuadPart = 0;
break;
case SMARTCARD_READ:
nextsp = IoGetNextIrpStackLocation( irp );
nextsp->MajorFunction = IRP_MJ_READ;
nextsp->Parameters.Read.Length = RxSize;
nextsp->Parameters.Read.ByteOffset.QuadPart = 0;
break;
}
status = IoCallDriver( ReaderExtension->AttachedSerialPort, irp );
if( status == STATUS_PENDING )
{
KeWaitForSingleObject( &event,
Suspended,
KernelMode,
FALSE,
NULL );
status = ioStatus.Status;
if( status == STATUS_TIMEOUT &&
ioStatus.Information != 0 &&
ioStatus.Information < RxSize )
{
// Ignore time-out if some data returned
status = STATUS_SUCCESS;
}
// save the number of bytes received
SmartcardExtension->SmartcardReply.BufferLength =
(RxSize != 0 && status == STATUS_SUCCESS) ?
(ULONG) ioStatus.Information : 0;
} else {
SmartcardExtension->SmartcardReply.BufferLength =
(status == STATUS_SUCCESS) ? RxSize : 0;
}
#if 0
if( SerialIoControlCode == SMARTCARD_WRITE )
{
// TDB: Wait for Tx buffer empty
}
#endif
//
// STATUS_TIMEOUT isn't correctly mapped
// to a WIN32 error, that's why we change it here
// to STATUS_IO_TIMEOUT
//
return( (status == STATUS_TIMEOUT) ? STATUS_IO_TIMEOUT : status );
}
NTSTATUS
RBSCWriteToCard(
PSMARTCARD_EXTENSION SmartcardExtension,
PUCHAR Buffer,
ULONG Length
)
/*++
Routine Description:
This routine sends data to the smart card. It handles the inverse
convention and gaurd time.
Arguments:
SmartcardExtension - Pointer to our smartcard data
Buffer - Points to the data to send
Length - Length of data to send (bytes)
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
UCHAR TempBuffer[MIN_BUFFER_SIZE];
ULONG StopBits = SmartcardExtension->CardCapabilities.PtsData.StopBits;
DumpData( "Data to the card", Buffer, Length );
if( SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F )
{
ULONG l;
if( Length > sizeof(TempBuffer) )
{
return STATUS_DEVICE_DATA_ERROR;
}
for( l = 0; l < Length; l++ )
{
TempBuffer[l] = InverseCharTable[ Buffer[l] ];
}
Buffer = TempBuffer;
}
if( StopBits <= 2 )
{
return RBSCSerialIo( SmartcardExtension,
SMARTCARD_WRITE,
Buffer,
Length,
0 );
}
while( Length != 0 )
{
status = RBSCSerialIo( SmartcardExtension,
SMARTCARD_WRITE,
Buffer,
1,
0 );
if( status != STATUS_SUCCESS )
{
return status;
}
Buffer++;
Length--;
if( Length != 0 )
KeStallExecutionProcessor(
(StopBits-1)*SmartcardExtension->CardCapabilities.etu );
}
return STATUS_SUCCESS;
}
NTSTATUS
RBSCSetCommParams(
PSMARTCARD_EXTENSION SmartcardExtension,
ULONG DataRate
)
/*++
Routine Description:
This routine sets the stop bits and read timeouts of the serial driver.
Arguments:
SmartcardExtension - Pointer to our smartcard data
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PSERIAL_LINE_CONTROL plc =
&SmartcardExtension->ReaderExtension->LineControl;
PSERIAL_TIMEOUTS timeout =
&SmartcardExtension->ReaderExtension->SerialTimeouts;
SmartcardDebug(
DEBUG_PROTOCOL,
("RNBO3531!SetCommParams: DataRate=%ld, StopBits=%d\n",
DataRate,SmartcardExtension->CardCapabilities.PtsData.StopBits)
);
plc->StopBits =
(SmartcardExtension->CardCapabilities.PtsData.StopBits == 2) ?
STOP_BITS_2 : STOP_BIT_1;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_LINE_CONTROL,
(PUCHAR) plc,
sizeof(SERIAL_LINE_CONTROL),
0 );
if( status != STATUS_SUCCESS )
{
return status;
}
if( SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries > 1 )
{
// Setup baudrate
SERIAL_BAUD_RATE BaudRate;
BaudRate.BaudRate = DataRate;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_BAUD_RATE,
(PUCHAR) &BaudRate,
sizeof(SERIAL_BAUD_RATE),
0 );
if( status != STATUS_SUCCESS )
{
return status;
}
}
if( SmartcardExtension->CardCapabilities.Protocol.Selected &
SCARD_PROTOCOL_T1 )
{
timeout->ReadTotalTimeoutConstant = 2* // for safty
(SmartcardExtension->CardCapabilities.T1.BWT +
WAIT_TIME_MINIMUM)/1000;
timeout->ReadIntervalTimeout = 2* // for safty
(SmartcardExtension->CardCapabilities.T1.CWT +
WAIT_TIME_MINIMUM)/1000;
} else {
timeout->ReadTotalTimeoutConstant =
timeout->ReadIntervalTimeout = 2* // for safty
(SmartcardExtension->CardCapabilities.T0.WT +
WAIT_TIME_MINIMUM)/1000;
}
//
// Set up timeout (in msec)
//
return RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_TIMEOUTS,
(PUCHAR) timeout,
sizeof(SERIAL_TIMEOUTS),
0 );
}
NTSTATUS
RBSCReadFromCard(
PSMARTCARD_EXTENSION SmartcardExtension,
ULONG Length
)
/*++
Routine Description:
Reads a block of data from the card.
On successful read, it converts the data if inverse conversion used.
Arguments:
SmartcardExtension - Pointer to smart card data.
Length - Number of bytes to read.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
SmartcardDebug(
DEBUG_PROTOCOL,
("RNBO3531!ReadFromCard(%ld)\n",Length)
);
status = RBSCSerialIo(
SmartcardExtension,
SMARTCARD_READ,
NULL,
0,
Length
);
if( status == STATUS_SUCCESS &&
SmartcardExtension->SmartcardReply.BufferLength != Length )
{
status = STATUS_IO_TIMEOUT;
}
if( SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F )
{
ULONG i;
for( i = 0; i < SmartcardExtension->SmartcardReply.BufferLength; i++ )
{
SmartcardExtension->SmartcardReply.Buffer[i] = InverseCharTable[
SmartcardExtension->SmartcardReply.Buffer[i] ];
}
}
#if 0
// The data dump here can time-out some cards
DumpData( "Data from card",
SmartcardExtension->SmartcardReply.Buffer,
SmartcardExtension->SmartcardReply.BufferLength );
SmartcardDebug(
DEBUG_PROTOCOL,
("RNBO3531!ReadFromCard Exit %08X\n", status)
);
#endif
return status;
}
NTSTATUS
RBSCSerialEvents(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PREADER_EXTENSION ReaderExtension
)
/*++
Routine Description:
This routine is called in three cases:
a) CTS changed (card inserted or removed) or
b) DSR changed (reader has been removed) or
c) The serial WaitMask is set to zero
For a) we update the card status and complete outstanding
card tracking requests.
For b) and c) we start to unload the driver
Arguments:
DeviceObject - Pointer to device object
Irp - The notofication Irp
ReaderExtension - Pointer to our device data
Return Value:
NTSTATUS
--*/
{
PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension;
PIO_STACK_LOCATION nextsp;
NTSTATUS status;
KIRQL irql;
UNREFERENCED_PARAMETER(DeviceObject);
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!SerialEvents: Enter\n")
);
KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock,
&irql );
if( ReaderExtension->GetModemStatus )
{
//
// This function requested the modem status previously.
// As part of the io-completion, this function is then
// called again. When we're here we can read the actual
// modem-status to figure out if the card is in the reader
//
#ifdef NT5
if( (ReaderExtension->ModemStatus & SERIAL_DSR_STATE) == 0 )
{
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!SerialEvent: Reader removed\n")
);
//
// We set the mask to zero to signal that we can
// release the irp that we use for the serial events
//
ReaderExtension->WaitMask = 0;
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_UNKNOWN;
} else
#endif
{
if( ReaderExtension->ModemStatus & SERIAL_CTS_STATE )
{
//
// Card is inserted
//
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_SWALLOWED;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!SerialEvents: Smart card inserted\n")
);
} else {
//
// Card is removed
//
SmartcardExtension->CardCapabilities.ATR.Length = 0;
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_ABSENT;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!SerialEvents: Smart card removed\n")
);
}
}
}
KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, irql );
if( SmartcardExtension->ReaderExtension->PowerRequest == FALSE )
{
// Inform the user of a card insertion/removal event
RBSCCompleteCardTracking( SmartcardExtension );
}
// The wait mask is set to 0 when the driver unloads
if( ReaderExtension->WaitMask == 0 )
{
#ifdef NT5
// schedule our remove thread
IoQueueWorkItem(
ReaderExtension->CloseSerial,
(PIO_WORKITEM_ROUTINE) RBSCCloseSerialPort,
DelayedWorkQueue,
NULL
);
#endif
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!SerialEvent: Exit (Release IRP)\n")
);
ReaderExtension->PowerRequest = FALSE;
//
// We don't need the IRP anymore, so free it and tell the
// io subsystem not to touch it anymore by returning the value below
//
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
nextsp = IoGetNextIrpStackLocation( ReaderExtension->SerialStatusIrp );
nextsp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
nextsp->MinorFunction = 0UL;
if( ReaderExtension->GetModemStatus )
{
nextsp->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(ReaderExtension->WaitMask);
nextsp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&ReaderExtension->WaitMask;
ReaderExtension->GetModemStatus = FALSE;
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!SerialEvents: IOCTL_SERIAL_WAIT_ON_MASK\n")
);
} else {
//
// Setup call for device control to get modem status.
// The CTS signal tells us if the card is inserted or removed.
// CTS is high if the card is inserted.
//
nextsp->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(ReaderExtension->ModemStatus);
nextsp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_GET_MODEMSTATUS;
ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&ReaderExtension->ModemStatus;
ReaderExtension->GetModemStatus = TRUE;
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!SerialEvents: IOCTL_SERIAL_GET_MODEMSTATUS\n")
);
}
IoSetCompletionRoutine( ReaderExtension->SerialStatusIrp,
RBSCSerialEvents,
ReaderExtension,
TRUE,
TRUE,
TRUE );
status = IoCallDriver( ReaderExtension->AttachedSerialPort,
ReaderExtension->SerialStatusIrp );
ReaderExtension->PowerRequest = FALSE;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!SerialEvents: Exit\n")
);
return STATUS_MORE_PROCESSING_REQUIRED;
}
#ifdef NT5
NTSTATUS
RBSCDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This is our IOCTL dispatch function
Arguments:
DeviceObject - Pointer to device object
Return Value:
NTSTATUS
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION SmartcardExtension =
&ReaderExtension->SmartcardExtension;
NTSTATUS status;
KIRQL irql;
if( ReaderExtension->WaitMask == 0 )
{
//
// the wait mask is set to 0 whenever the device was either
// surprise-removed or politely removed
//
status = STATUS_DEVICE_REMOVED;
}
else
{
KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql );
if( ReaderExtension->IoCount == 0 )
{
KeReleaseSpinLock( &ReaderExtension->SpinLock, irql );
status = KeWaitForSingleObject( &ReaderExtension->ReaderStarted,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT( status == STATUS_SUCCESS );
KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql );
}
ASSERT( ReaderExtension->IoCount >= 0 );
ReaderExtension->IoCount++;
KeReleaseSpinLock( &ReaderExtension->SpinLock, irql );
status = SmartcardAcquireRemoveLockWithTag( SmartcardExtension,
'tcoI' );
}
if (status != STATUS_SUCCESS)
{
// the device has been removed. Fail the call
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_DEVICE_REMOVED;
}
ASSERT(ReaderExtension->ReaderPowerState == PowerReaderWorking);
status = SmartcardDeviceControl( SmartcardExtension, Irp );
SmartcardReleaseRemoveLockWithTag( SmartcardExtension,'tcoI' );
KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql );
ReaderExtension->IoCount--;
ASSERT( ReaderExtension->IoCount >= 0 );
KeReleaseSpinLock( &ReaderExtension->SpinLock, irql );
return status;
}
#else
NTSTATUS
RBSCDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!DeviceControl: Enter (%lx)\n",
IoGetCurrentIrpStackLocation( Irp )->MinorFunction)
);
return SmartcardDeviceControl(
&((PREADER_EXTENSION)DeviceObject->DeviceExtension)->SmartcardExtension,
Irp
);
}
#endif
NTSTATUS
RBSCReaderPower(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
The smart card lib requires to have this function. It is called
for certain power requests to the card.
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Return Value:
NTSTATUS
--*/
{
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
KIRQL oldIrql;
ULONG step;
//PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ReaderPower: Enter (%lx)\n",
SmartcardExtension->MinorIoControlCode)
);
#define STEP_POWER_DOWN 0
#define STEP_COLD_RESET (STEP_POWER_DOWN+2)
#define STEP_WARM_RESET (STEP_COLD_RESET+10)
switch( SmartcardExtension->MinorIoControlCode )
{
case SCARD_WARM_RESET:
step = STEP_WARM_RESET;
break;
case SCARD_COLD_RESET:
step = STEP_COLD_RESET;
break;
case SCARD_POWER_DOWN:
step = STEP_POWER_DOWN;
break;
default :
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Since power down triggers the UpdateSerialStatus function, we have
// to inform it that we forced the change of the status and not the user
// (who might have removed and inserted a card)
//
ReaderExtension->PowerRequest = TRUE;
do
{
switch( step++ )
{
case STEP_COLD_RESET+0:
case STEP_POWER_DOWN+0:
status = RBSCPacketExchange( SmartcardExtension,
CardPowerDown,
sizeof(CardPowerDown),
WAIT_TIME_PWR_OFF,
TRUE );
break;
case STEP_POWER_DOWN+1:
if( SmartcardExtension->ReaderCapabilities.CurrentState >
SCARD_PRESENT )
{
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_PRESENT;
}
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
ReaderExtension->PowerRequest = FALSE;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ReaderPower: Exit PowerDown OK\n")
);
return STATUS_SUCCESS;
case STEP_COLD_RESET+1:
case STEP_WARM_RESET+0:
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_DTR,
NULL,
0,
0 );
//RBSCDelay( 15000 );
break;
case STEP_COLD_RESET+2:
case STEP_WARM_RESET+1:
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_RTS,
NULL,
0,
0 );
// RBSCDelay( 15000 );
break;
case STEP_WARM_RESET+2:
case STEP_COLD_RESET+3:
//
// Set up ATR timeouts (all in msec)
//
ReaderExtension->SerialTimeouts.ReadTotalTimeoutMultiplier = 1;
ReaderExtension->SerialTimeouts.ReadIntervalTimeout = 2*
ATR_CHAR_TIMEOUT/1000;
ReaderExtension->SerialTimeouts.ReadTotalTimeoutConstant = 2*
ATR_BLOCK_TIMEOUT/1000;
status = RBSCSerialIo(
SmartcardExtension,
IOCTL_SERIAL_SET_TIMEOUTS,
(PUCHAR) &ReaderExtension->SerialTimeouts,
sizeof(SERIAL_TIMEOUTS),
0
);
break;
case STEP_WARM_RESET+3:
case STEP_COLD_RESET+4:
if( SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries > 1 )
{
// Setup default baudrate
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ReaderPower: Set DataRate=%ld\n",DATARATE_DEFAULT)
);
{
SERIAL_BAUD_RATE BaudRate;
BaudRate.BaudRate = DATARATE_DEFAULT;
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_SET_BAUD_RATE,
(PUCHAR) &BaudRate,
sizeof(SERIAL_BAUD_RATE),
0 );
}
}
break;
case STEP_WARM_RESET+4:
case STEP_COLD_RESET+5:
RBSCDelay(15000);
status = RBSCPacketExchange( SmartcardExtension,
CardPowerUp,
sizeof(CardPowerUp),
0, //WAIT_TIME_PWR_ON,
FALSE );
break;
case STEP_WARM_RESET+5:
case STEP_COLD_RESET+6:
//
// Now read in part or all of the ATR
//
status = RBSCSerialIo( SmartcardExtension,
SMARTCARD_READ,
NULL,
0,
MAXIMUM_ATR_LENGTH );
break;
case STEP_WARM_RESET+6:
case STEP_COLD_RESET+7:
{
ULONG i;
if( SmartcardExtension->SmartcardReply.Buffer[0] == 0x03 )
{
//
// Inverse convention (Inv(3F) = 03)
//
SmartcardExtension->CardCapabilities.ATR.Buffer[0] = 0x3F;
SmartcardExtension->CardCapabilities.ATR.Length = 1;
}
else
if( SmartcardExtension->SmartcardReply.Buffer[0] == 0x3B )
{
//
// Direct convention
//
SmartcardExtension->CardCapabilities.ATR.Buffer[0] = 0x3B;
SmartcardExtension->CardCapabilities.ATR.Length = 1;
}
else
{
status = STATUS_UNRECOGNIZED_MEDIA;
}
for( i = 1; status == STATUS_SUCCESS; i = 0 )
{
while( i <
SmartcardExtension->SmartcardReply.BufferLength &&
SmartcardExtension->CardCapabilities.ATR.Length <
MAXIMUM_ATR_LENGTH )
{
SmartcardExtension->CardCapabilities.ATR.Buffer[
SmartcardExtension->CardCapabilities.ATR.Length++ ] =
(SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F) ?
InverseCharTable[
SmartcardExtension->SmartcardReply.Buffer[i] ] :
SmartcardExtension->SmartcardReply.Buffer[i];
i++;
}
//
// Check if ATR is complete
//
status = SmartcardUpdateCardCapabilities(
SmartcardExtension
);
if( status == STATUS_SUCCESS )
{
break;
}
//
// Read remaining bytes of ATR
//
status = RBSCSerialIo(
SmartcardExtension,
SMARTCARD_READ,
NULL,
0,
MAXIMUM_ATR_LENGTH -
SmartcardExtension->CardCapabilities.ATR.Length
);
}
break;
}
case STEP_WARM_RESET+7:
case STEP_COLD_RESET+8:
status = RBSCSetCommParams(
SmartcardExtension,
SmartcardExtension->CardCapabilities.PtsData.DataRate
);
break;
case STEP_WARM_RESET+8:
case STEP_COLD_RESET+9:
//RBSCDelay( 15000 );
//
// Make sure card is still in the reader.
//
if( (ReaderExtension->ModemStatus & SERIAL_CTS_STATE) == 0 )
{
status = STATUS_NO_MEDIA;
break;
}
KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock,
&oldIrql );
//
// Copy ATR to user space
//
if( SmartcardExtension->IoRequest.ReplyBuffer )
{
RtlCopyMemory(
SmartcardExtension->IoRequest.ReplyBuffer,
SmartcardExtension->CardCapabilities.ATR.Buffer,
SmartcardExtension->CardCapabilities.ATR.Length
);
//
// Tell user length of ATR
//
*SmartcardExtension->IoRequest.Information =
SmartcardExtension->CardCapabilities.ATR.Length;
}
KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock,
oldIrql );
ReaderExtension->PowerRequest = FALSE;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ReaderPower: Exit Reset OK\n")
);
return STATUS_SUCCESS;
}
} while( status == STATUS_SUCCESS );
//
// Reset failed, make sure card is powered OFF
//
RBSCPacketExchange( SmartcardExtension,
CardPowerDown,
sizeof(CardPowerDown),
WAIT_TIME_PWR_OFF,
TRUE );
SmartcardExtension->CardCapabilities.ATR.Length = 0;
status = (ReaderExtension->ModemStatus & SERIAL_CTS_STATE) ?
STATUS_UNRECOGNIZED_MEDIA :
STATUS_NO_MEDIA;
ReaderExtension->PowerRequest = FALSE;
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ReaderPower: Failed %08x\n",status)
);
return status;
}
NTSTATUS
RBSCSetProtocol(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
The smart card lib requires to have this function. It is called
to set a particular protocol.
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Return Value:
NTSTATUS
--*/
{
UCHAR PtsRequest[8];
NTSTATUS status;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!SetProtocol: Enter\n")
);
//
// Check if the card is already in specific state
// and if the caller wants to have the already selected protocol.
// We return success if this is the case.
//
if( SmartcardExtension->ReaderCapabilities.CurrentState ==
SCARD_SPECIFIC && (
SmartcardExtension->CardCapabilities.Protocol.Selected &
SmartcardExtension->MinorIoControlCode) )
{
status = STATUS_SUCCESS;
} else
while(TRUE)
{
PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer;
if( SmartcardExtension->CardCapabilities.Protocol.Supported &
SmartcardExtension->MinorIoControlCode &
SCARD_PROTOCOL_T1 )
{
PtsRequest[5] = 0x11;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_T1;
} else if(
SmartcardExtension->CardCapabilities.Protocol.Supported &
SmartcardExtension->MinorIoControlCode &
SCARD_PROTOCOL_T0 )
{
PtsRequest[5] = 0x10;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_T0;
} else {
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// pts
//
PtsRequest[0] = IFDCMD_HEADER1;
PtsRequest[1] = IFDCMD_HEADER2;
PtsRequest[2] = IFDCMD_SEND_0xx;
PtsRequest[3] = 4; // Length
PtsRequest[4] = 0xff; // PTS
PtsRequest[6] = // set pts1 which codes Fl and Dl
(SmartcardExtension->CardCapabilities.PtsData.Fl << 4) |
SmartcardExtension->CardCapabilities.PtsData.Dl;
//
// pck (check character)
//
PtsRequest[7] = PtsRequest[4] ^ PtsRequest[5] ^ PtsRequest[6];
status = RBSCPacketExchange( SmartcardExtension,
PtsRequest,
4,
0,
FALSE );
if( status == STATUS_SUCCESS )
{
status = RBSCWriteToCard( SmartcardExtension, PtsRequest+4, 4 );
if( status == STATUS_SUCCESS )
{
status = RBSCReadFromCard( SmartcardExtension, 4 );
}
}
if( status == STATUS_SUCCESS &&
reply[0] == PtsRequest[4] &&
((reply[1] ^ PtsRequest[5]) & 0x0f) == 0 )
{
//
// Update the current protocol
//
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_SPECIFIC;
status = RBSCSetCommParams(
SmartcardExtension,
SmartcardExtension->CardCapabilities.PtsData.DataRate
);
} else {
if (status == STATUS_IO_TIMEOUT &&
SmartcardExtension->CardCapabilities.PtsData.Type !=
PTS_TYPE_DEFAULT)
{
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!SetProtocol: PTS failed. Trying default parameters...\n")
);
//
// The card did either NOT reply or it replied incorrectly
// so try default values
//
SmartcardExtension->CardCapabilities.PtsData.Type =
PTS_TYPE_DEFAULT;
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
status = RBSCReaderPower( SmartcardExtension );
if( status == STATUS_SUCCESS )
continue;
}
status = STATUS_DEVICE_PROTOCOL_ERROR;
}
break;
}
if( status != STATUS_SUCCESS )
{
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
} else {
//
// return the selected protocol to the caller
//
*(PULONG) SmartcardExtension->IoRequest.ReplyBuffer =
SmartcardExtension->CardCapabilities.Protocol.Selected;
*SmartcardExtension->IoRequest.Information =
sizeof(SmartcardExtension->CardCapabilities.Protocol.Selected);
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!SetProtocol: Exit\n")
);
return status;
}
NTSTATUS
RBSCPacketExchange(
PSMARTCARD_EXTENSION SmartcardExtension,
PUCHAR Command,
ULONG Length,
ULONG WaitTime,
BOOLEAN IfdResponse
)
/*++
Routine Description:
This function is used to transmit a command to the reader (not card)
and return the response (if asked).
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Command - Points to the IFD command to send
Length - Length of the command in bytes
WaitTime - Waiting time expected (in microseconds)
IfdResponse - When TRUE, IFD response is expected (vs card)
Return Value:
NTSTATUS
--*/
{
PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer;
ULONG SerialRequest = SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXCLEAR;
ULONG ResponseSize;
NTSTATUS status;
int TrySend;
DumpData( "PacketExchange", Command, Length );
for( TrySend = 0; TrySend < 4; TrySend++ )
{
//
// start with clean buffers
//
status = RBSCSerialIo( SmartcardExtension,
IOCTL_SERIAL_PURGE,
(PUCHAR) &SerialRequest,
sizeof(ULONG),
0 );
if( status == STATUS_SUCCESS )
{
status = RBSCSerialIo( SmartcardExtension,
SMARTCARD_WRITE,
Command,
Length,
0 );
if( status == STATUS_SUCCESS && WaitTime != 0 )
RBSCDelay( WaitTime );
}
if( status == STATUS_SUCCESS && IfdResponse )
{
status = RBSCSerialIo( SmartcardExtension,
SMARTCARD_READ,
NULL,
0,
IFDRSP_HEADER_SIZE );
if( status != STATUS_SUCCESS ||
SmartcardExtension->SmartcardReply.BufferLength !=
IFDRSP_HEADER_SIZE )
{
continue;
}
DumpData( "PacketExchange: Received Header",
reply,
IFDRSP_HEADER_SIZE );
//
// We should have the marker plus the status/length byte
//
if( reply[0] != IFDRSP_MARKER )
{
status = STATUS_DEVICE_DATA_ERROR;
continue;
}
ResponseSize = (ULONG)reply[1];
if( ResponseSize > 0 && ResponseSize < IFDRSP_ACK )
{
status = RBSCSerialIo( SmartcardExtension,
SMARTCARD_READ,
NULL,
0,
ResponseSize );
if( status == STATUS_SUCCESS &&
SmartcardExtension->SmartcardReply.BufferLength !=
ResponseSize )
{
status = STATUS_DEVICE_DATA_ERROR;
}
DumpData( "PacketExchange: Received Data",
SmartcardExtension->SmartcardReply.Buffer,
SmartcardExtension->SmartcardReply.BufferLength );
}
else
{
//
// Map IFD errors
//
switch( ResponseSize )
{
case IFDRSP_ACK : return STATUS_SUCCESS;
case IFDRSP_NOCARD : return STATUS_NO_MEDIA;
case IFDRSP_BADCMD : return STATUS_INVALID_DEVICE_REQUEST;
case IFDRSP_PARITY :
default :
status = STATUS_DEVICE_DATA_ERROR;
break;
}
}
}
if( status == STATUS_SUCCESS)
return STATUS_SUCCESS;
}
SmartcardExtension->SmartcardReply.BufferLength = 0;
return status;
}
NTSTATUS
RBSCT0Transmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This function performs the T=0 transmission.
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Return Value:
NTSTATUS
--*/
{
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
PUCHAR RxBuffer = SmartcardExtension->SmartcardReply.Buffer;
PUCHAR TxBuffer = SmartcardExtension->SmartcardRequest.Buffer;
ULONG RxLength, TxLength, RequestSize;
UCHAR SendHeader[4], Ins, ProcByte;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!T0Transmit: Enter\n")
);
//
// Tell the lib function how many bytes I need for the prologue
//
SmartcardExtension->SmartcardRequest.BufferLength = 0;
//
// Let the lib build a T=0 packet
//
status = SmartcardT0Request( SmartcardExtension );
if( status != STATUS_SUCCESS )
return status;
TxLength = SmartcardExtension->SmartcardRequest.BufferLength;
if( TxLength < 5 || TxLength > 0x1ff )
{
return STATUS_DEVICE_DATA_ERROR;
}
//
// The number of bytes we expect from the card
// is Le + 2 status bytes
//
RxLength = SmartcardExtension->T0.Le + 2;
//
// Write command to the reader
//
Ins = TxBuffer[1]; // INS
RequestSize = 5;
for( ;; )
{
if( TxLength > 0 )
{
SendHeader[0] = IFDCMD_HEADER1;
SendHeader[1] = IFDCMD_HEADER2;
SendHeader[2] = (RequestSize > 256) ?
IFDCMD_SEND_1xx : IFDCMD_SEND_0xx;
SendHeader[3] = (UCHAR)RequestSize; // 0=256
status = RBSCPacketExchange(
SmartcardExtension,
SendHeader,
4,
0,
FALSE
);
if( status != STATUS_SUCCESS)
{
break;
}
status = RBSCWriteToCard(
SmartcardExtension,
TxBuffer,
RequestSize
);
if( status != STATUS_SUCCESS)
{
break;
}
TxLength -= RequestSize;
TxBuffer += RequestSize;
}
do
{
status = RBSCReadFromCard( SmartcardExtension, 1 ); // Proc. byte
if( status != STATUS_SUCCESS )
{
break;
}
ProcByte = SmartcardExtension->SmartcardReply.Buffer[0];
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!ProcByte=%02x Ins=%02x\n",ProcByte,Ins)
);
} while( ProcByte == 0x60);
if( status != STATUS_SUCCESS )
{
break;
}
if( ProcByte == Ins || ProcByte == (UCHAR)(Ins+1) )
{
//
// Send all remaining bytes
//
if( TxLength > 0 )
{
RequestSize = TxLength;
continue;
}
RequestSize = RxLength;
}
else if( ProcByte == (UCHAR)~Ins || ProcByte == (UCHAR)~(Ins+1) )
{
//
// Send 1 byte only
//
RequestSize = 1;
if( TxLength > 0 )
{
continue;
}
}
else
{
//
// Card returned status byte
//
SmartcardExtension->SmartcardReply.Buffer++;
TxLength = 0;
RxLength = 1; // Second byte of status (SW2)
RequestSize = 1;
}
status = RBSCReadFromCard( SmartcardExtension, RequestSize );
if( status != STATUS_SUCCESS )
{
break;
}
SmartcardExtension->SmartcardReply.Buffer += RequestSize;
RxLength -= RequestSize;
if( RxLength == 0 )
{
break;
}
}
//
// Restore reply buffer pointer
//
SmartcardExtension->SmartcardReply.BufferLength =
(ULONG) (SmartcardExtension->SmartcardReply.Buffer - RxBuffer);
SmartcardExtension->SmartcardReply.Buffer = RxBuffer;
if( status == STATUS_SUCCESS )
{
status = SmartcardT0Reply( SmartcardExtension );
}
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!T0Transmit: Exit (%lx)\n",status)
);
return status;
}
NTSTATUS
RBSCT1Transmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This function performs the T=1 transmission.
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
UCHAR SendHeader[4];
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!T1Transmit: Enter\n")
);
do
{
PUCHAR TxBuffer = SmartcardExtension->SmartcardRequest.Buffer;
PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer;
ULONG TxLength;
//
// Tell the lib function how many bytes I need for the prologue
//
SmartcardExtension->SmartcardRequest.BufferLength = 0;
status = SmartcardT1Request( SmartcardExtension );
if( status != STATUS_SUCCESS )
{
break;
}
TxLength = SmartcardExtension->SmartcardRequest.BufferLength;
//RBSCDelay( SmartcardExtension->CardCapabilities.T1.BGT );
//
// Write the command to the reader
//
SendHeader[0] = IFDCMD_HEADER1;
SendHeader[1] = IFDCMD_HEADER2;
SendHeader[2] = (UCHAR)(TxLength > 256) ?
IFDCMD_SEND_1xx : IFDCMD_SEND_0xx;
SendHeader[3] = (UCHAR)TxLength; // 0=256
status = RBSCPacketExchange( SmartcardExtension,
SendHeader,
4,
0,
FALSE );
if( status != STATUS_SUCCESS )
{
break;
}
status = RBSCWriteToCard( SmartcardExtension, TxBuffer, TxLength );
if( status != STATUS_SUCCESS )
{
break;
}
if( SmartcardExtension->T1.Wtx > 1 )
{
SmartcardDebug(
DEBUG_PROTOCOL,
("BWT=%ld, WTX=%d\n",
SmartcardExtension->CardCapabilities.T1.BWT,
SmartcardExtension->T1.Wtx)
);
RBSCDelay(
SmartcardExtension->CardCapabilities.T1.BWT*
SmartcardExtension->T1.Wtx );
}
status = RBSCReadFromCard( SmartcardExtension, 3 );
if( status == STATUS_IO_TIMEOUT )
{
//
// Since the card did not reply we set the number of
// bytes received to 0. This will trigger a resend
// request
//
SmartcardDebug(
DEBUG_PROTOCOL,
("RNBO3531!T1Transmit: Timeout\n")
);
SmartcardExtension->SmartcardReply.BufferLength = 0;
} else {
if( status != STATUS_SUCCESS )
{
break;
}
//
// Calculate length of the INF part of the response
//
SmartcardExtension->SmartcardReply.BufferLength =
SmartcardExtension->SmartcardReply.Buffer[2] +
(SmartcardExtension->CardCapabilities.T1.EDC & 1) + 1;
SmartcardExtension->SmartcardReply.Buffer += 3;
status = RBSCReadFromCard(
SmartcardExtension,
SmartcardExtension->SmartcardReply.BufferLength );
SmartcardExtension->SmartcardReply.Buffer -= 3;
SmartcardExtension->SmartcardReply.BufferLength += 3;
if( status != STATUS_SUCCESS && status != STATUS_IO_TIMEOUT )
{
break;
}
}
status = SmartcardT1Reply( SmartcardExtension );
} while( status == STATUS_MORE_PROCESSING_REQUIRED );
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!T1Transmit: Exit (%lx)\n",status)
);
return status;
}
NTSTATUS
RBSCTransmit(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This function is called by the smart card library whenever a transmission
is required.
Arguments:
SmartcardExtension - Pointer to smart card data struct.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!Transmit: GT=%ld, etu=%ld\n",
SmartcardExtension->CardCapabilities.GT,
SmartcardExtension->CardCapabilities.etu)
);
switch( SmartcardExtension->CardCapabilities.Protocol.Selected )
{
case SCARD_PROTOCOL_T0:
return RBSCT0Transmit( SmartcardExtension );
case SCARD_PROTOCOL_T1:
return RBSCT1Transmit( SmartcardExtension );
}
return STATUS_INVALID_DEVICE_REQUEST;
}
NTSTATUS
RBSCCardTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
The smart card lib requires to have this function. It is called
to setup event tracking for card insertion and removal events.
Arguments:
SmartcardExtension - pointer to the smart card data struct.
Return Value:
NTSTATUS
--*/
{
KIRQL ioIrql, keIrql;
//
// Set cancel routine for the notification irp
//
KeAcquireSpinLock(
&SmartcardExtension->OsData->SpinLock,
&keIrql
);
IoAcquireCancelSpinLock( &ioIrql );
if( SmartcardExtension->OsData->NotificationIrp )
{
IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp,
RBSCCancel );
}
IoReleaseCancelSpinLock( ioIrql );
KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock,
keIrql );
return STATUS_PENDING;
}
VOID
RBSCCompleteCardTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
{
KIRQL ioIrql, keIrql;
PIRP notificationIrp;
IoAcquireCancelSpinLock(&ioIrql);
KeAcquireSpinLock(
&SmartcardExtension->OsData->SpinLock,
&keIrql
);
notificationIrp = SmartcardExtension->OsData->NotificationIrp;
SmartcardExtension->OsData->NotificationIrp = NULL;
KeReleaseSpinLock(
&SmartcardExtension->OsData->SpinLock,
keIrql
);
if (notificationIrp)
{
IoSetCancelRoutine(
notificationIrp,
NULL
);
}
IoReleaseCancelSpinLock(ioIrql);
if (notificationIrp)
{
SmartcardDebug(
DEBUG_DRIVER,
("%s!RBSCCardTracking: Completing NotificationIrp %lxh\n",
DRIVER_NAME,
notificationIrp)
);
// finish the request
notificationIrp->IoStatus.Information = 0;
notificationIrp->IoStatus.Status = notificationIrp->Cancel ?
STATUS_CANCELLED : STATUS_SUCCESS;
IoCompleteRequest(
notificationIrp,
IO_NO_INCREMENT
);
}
}
#ifdef NT5
VOID
RBSCCloseSerialPort(
PDEVICE_OBJECT DeviceObject,
PVOID Context
)
/*++
Routine Description:
This function closes the connection to the serial driver when the reader
has been removed (unplugged). This function runs as a system thread at
IRQL == PASSIVE_LEVEL. It waits for the remove event that is set by
the IoCompletionRoutine
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION stack;
IO_STATUS_BLOCK ioStatusBlock;
//
// first mark this device as 'gone'.
// This will prevent that someone can re-open the device
//
status = IoSetDeviceInterfaceState( &ReaderExtension->PnPDeviceName,
FALSE );
ASSERT( status == STATUS_SUCCESS );
irp = IoAllocateIrp( (CCHAR)(DeviceObject->StackSize + 1), FALSE );
ASSERT( irp != NULL );
if( irp )
{
SmartcardDebug(
DEBUG_TRACE,
("RNBO3531!CloseSerialPort: Sending IRP_MJ_CLOSE\n")
);
IoSetNextIrpStackLocation( irp );
//
// We send down a close to the serial driver. This close goes
// through serenum first which will trigger it to start looking
// for changes on the com-port. Since our device is gone it will
// call the device removal event of our PnP dispatch.
//
irp->UserIosb = &ioStatusBlock;
stack = IoGetCurrentIrpStackLocation( irp );
stack->MajorFunction = IRP_MJ_CLOSE;
status = RBSCCallSerialDriver(
ReaderExtension->AttachedSerialPort,
irp );
ASSERT( status == STATUS_SUCCESS );
IoFreeIrp( irp );
}
SmartcardDebug(
DEBUG_INFO,
("RNBO3531!CloseSerialPort: Serial Close Done.\n")
);
// inform the remove function that call is complete
KeSetEvent( &ReaderExtension->SerialCloseDone, 0, FALSE );
}
NTSTATUS
RBSCDevicePowerCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine is called after the underlying stack powered
UP the serial port, so it can be used again.
--*/
{
PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
//
// We issue a power request in order to figure out
// what the actual card status is
//
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
RBSCReaderPower( SmartcardExtension );
//
// If a card was present before power down or now there is
// a card in the reader, we complete any pending card monitor
// request, since we do not really know what card is now in the
// reader.
//
if( SmartcardExtension->ReaderExtension->CardPresent ||
SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT )
{
RBSCCompleteCardTracking( SmartcardExtension );
}
// save the current power state of the reader
SmartcardExtension->ReaderExtension->ReaderPowerState =
PowerReaderWorking;
SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'rwoP' );
// inform the power manager of our state.
PoSetPowerState( DeviceObject,
DevicePowerState,
irpStack->Parameters.Power.State );
PoStartNextPowerIrp( Irp );
// signal that we can process ioctls again
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
return STATUS_SUCCESS;
}
VOID
RBSCSystemPowerCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PIRP Irp,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
This function is called when the underlying stacks
completed the power transition.
--*/
{
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
UNREFERENCED_PARAMETER (MinorFunction);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = IoStatus->Status;
SmartcardReleaseRemoveLockWithTag( &ReaderExtension->SmartcardExtension,
'rwoP' );
if (PowerState.SystemState == PowerSystemWorking)
{
PoSetPowerState( DeviceObject,
SystemPowerState,
PowerState );
}
PoStartNextPowerIrp( Irp );
IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
typedef enum _ACTION {
Undefined = 0,
SkipRequest,
WaitForCompletion,
CompleteRequest,
MarkPending
} ACTION;
NTSTATUS
RBSCPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The power dispatch routine.
All we care about is the transition from a low D state to D0.
Arguments:
DeviceObject - Pointer to device object
Irp - pointer to an I/O Request Packet.
Return Value:
NT status code
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION SmartcardExtension =
&ReaderExtension->SmartcardExtension;
PDEVICE_OBJECT AttachedSerialPort;
POWER_STATE powerState;
ACTION action;
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!3Power: Enter\n")
);
status = SmartcardAcquireRemoveLockWithTag( SmartcardExtension, 'rwoP' );
ASSERT( status == STATUS_SUCCESS );
if( !NT_SUCCESS( status ) )
{
PoStartNextPowerIrp( Irp );
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
}
AttachedSerialPort = ReaderExtension->AttachedSerialPort;
if (irpStack->Parameters.Power.Type == DevicePowerState &&
irpStack->MinorFunction == IRP_MN_SET_POWER )
{
switch( irpStack->Parameters.Power.State.DeviceState )
{
case PowerDeviceD0:
// Turn on the reader
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Power: PowerDevice D0\n")
);
//
// First, we send down the request to the bus, in order
// to power on the port. When the request completes,
// we turn on the reader
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine (
Irp,
RBSCDevicePowerCompletion,
SmartcardExtension,
TRUE,
TRUE,
TRUE
);
action = WaitForCompletion;
break;
case PowerDeviceD3:
// Turn off the reader
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Power: PowerDevice D3\n")
);
PoSetPowerState (
DeviceObject,
DevicePowerState,
irpStack->Parameters.Power.State
);
// save the current card state
ReaderExtension->CardPresent =
SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT;
if( ReaderExtension->CardPresent )
{
SmartcardExtension->MinorIoControlCode = SCARD_POWER_DOWN;
status = RBSCReaderPower( SmartcardExtension );
ASSERT(status == STATUS_SUCCESS);
}
//
// If there is a pending card tracking request, setting
// this flag will prevent completion of the request
// when the system will be waked up again.
//
ReaderExtension->PowerRequest = TRUE;
// save the current power state of the reader
ReaderExtension->ReaderPowerState = PowerReaderOff;
action = SkipRequest;
break;
default:
ASSERT(FALSE);
action = SkipRequest;
break;
}
}
if (irpStack->Parameters.Power.Type == SystemPowerState) {
//
// The system wants to change the power state.
// We need to translate the system power state to
// a corresponding device power state.
//
POWER_STATE_TYPE powerType = DevicePowerState;
ASSERT(ReaderExtension->ReaderPowerState != PowerReaderUnspecified);
switch(irpStack->MinorFunction)
{
KIRQL irql;
case IRP_MN_QUERY_POWER:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Power: Query Power\n")
);
//
// By default we succeed and pass down
//
action = SkipRequest;
Irp->IoStatus.Status = STATUS_SUCCESS;
switch (irpStack->Parameters.Power.State.SystemState)
{
case PowerSystemMaximum:
case PowerSystemWorking:
case PowerSystemSleeping1:
case PowerSystemSleeping2:
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
KeAcquireSpinLock(&ReaderExtension->SpinLock, &irql);
if (ReaderExtension->IoCount == 0)
{
// Block any further ioctls
KeClearEvent(&ReaderExtension->ReaderStarted);
} else {
// can't go to sleep mode since the reader is busy.
status = STATUS_DEVICE_BUSY;
action = CompleteRequest;
}
KeReleaseSpinLock(&ReaderExtension->SpinLock, irql);
break;
}
break;
case IRP_MN_SET_POWER:
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Power: PowerSystem S%d\n",
irpStack->Parameters.Power.State.SystemState - 1)
);
switch (irpStack->Parameters.Power.State.SystemState)
{
case PowerSystemMaximum:
case PowerSystemWorking:
case PowerSystemSleeping1:
case PowerSystemSleeping2:
if (SmartcardExtension->ReaderExtension->ReaderPowerState ==
PowerReaderWorking) {
// We're already in the right state
action = CompleteRequest;
break;
}
powerState.DeviceState = PowerDeviceD0;
// wake up the underlying stack...
action = MarkPending;
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
if (SmartcardExtension->ReaderExtension->ReaderPowerState ==
PowerReaderOff) {
// We're already in the right state
action = CompleteRequest;
break;
}
powerState.DeviceState = PowerDeviceD3;
// first, inform the power manager of our new state.
PoSetPowerState (
DeviceObject,
SystemPowerState,
powerState
);
action = MarkPending;
break;
default:
ASSERT(FALSE);
action = CompleteRequest;
break;
}
}
}
switch( action )
{
case CompleteRequest:
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'rwoP' );
PoStartNextPowerIrp( Irp );
IoCompleteRequest( Irp, IO_NO_INCREMENT );
break;
case MarkPending:
Irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending( Irp );
status = PoRequestPowerIrp (
DeviceObject,
IRP_MN_SET_POWER,
powerState,
RBSCSystemPowerCompletion,
Irp,
NULL
);
ASSERT(status == STATUS_PENDING);
break;
case SkipRequest:
SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'rwoP' );
PoStartNextPowerIrp( Irp );
IoSkipCurrentIrpStackLocation( Irp );
status = PoCallDriver( AttachedSerialPort, Irp );
break;
case WaitForCompletion:
status = PoCallDriver( AttachedSerialPort, Irp );
break;
default:
ASSERT(FALSE);
break;
}
SmartcardDebug(
DEBUG_DRIVER,
("RNBO3531!Power: Exit %lx\n",status)
);
return status;
}
#endif