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

2525 lines
65 KiB
C

/*++
Copyright (C) 1997, 98 Microsoft Corporation
Module Name:
bulltlp3.c
Abstract:
Smart card driver for Bull TLP3 reader
Author:
Klaus U. Schutz
Environment:
Kernel mode
Revision History :
Nov. 1997 - 1.0 Release
Jan. 1998 - Fix for vendor defined IOCTLs
TLP3SerialIo now writes the whole data packet if GT is 0
Support for higher data rates added
Feb. 1998 - PnP version
--*/
#include <stdio.h>
#include "bulltlp3.h"
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGEABLE, TLP3CreateAndStartDevice)
#pragma alloc_text(PAGEABLE, TLP3StartSerialEventTracking)
#pragma alloc_text(PAGEABLE, TLP3AddDevice)
#pragma alloc_text(PAGEABLE, TLP3CreateDevice)
#pragma alloc_text(PAGEABLE, TLP3RemoveDevice)
#pragma alloc_text(PAGEABLE, TLP3DriverUnload)
#if DBG
#pragma optimize ("", off)
#endif
#ifdef SIMULATION
PWSTR DriverKey;
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN 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.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
ULONG device;
SmartcardDebug(
DEBUG_INFO,
("%s!DriverEntry: Enter - %s %s\n",
DRIVER_NAME,
__DATE__,
__TIME__)
)
//
// we do some stuff in this driver that
// assumes a single digit port number
//
ASSERT(MAXIMUM_SERIAL_READERS < 10);
// Initialize the Driver Object with driver's entry points
DriverObject->DriverUnload = TLP3DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = TLP3CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = TLP3CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = TLP3Cleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TLP3DeviceControl;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = TLP3SystemControl;
DriverObject->MajorFunction[IRP_MJ_PNP] = TLP3PnP;
DriverObject->MajorFunction[IRP_MJ_POWER] = TLP3Power;
DriverObject->DriverExtension->AddDevice = TLP3AddDevice;
#ifdef SIMULATION
DriverKey = ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(L""));
if (DriverKey) {
RtlZeroMemory(
DriverKey,
RegistryPath->Length + sizeof(L"")
);
RtlCopyMemory(
DriverKey,
RegistryPath->Buffer,
RegistryPath->Length
);
}
#endif
return status;
}
NTSTATUS
TLP3AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine creates an object for the physical device specified and
sets up the deviceExtension.
--*/
{
PDEVICE_EXTENSION deviceExtension;
NTSTATUS status = STATUS_SUCCESS;
PREADER_EXTENSION readerExtension;
PSMARTCARD_EXTENSION smartcardExtension;
ULONG deviceInstance;
HANDLE regKey = NULL;
PDEVICE_OBJECT DeviceObject = NULL;
// this is a list of our supported data rates
static ULONG dataRatesSupported[] = { 9600, 19200, 38400, 57600, 115200 };
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3AddDevice: Enter\n",
DRIVER_NAME)
);
PAGED_CODE();
__try {
// Create the device object
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_SMARTCARD,
0,
TRUE,
&DeviceObject
);
if (status != STATUS_SUCCESS) {
SmartcardLogError(
DriverObject,
TLP3_CANT_CREATE_DEVICE,
NULL,
0
);
__leave;
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3CreateDevice: Device created\n",
DRIVER_NAME)
);
// set up the device extension.
deviceExtension = DeviceObject->DeviceExtension;
smartcardExtension = &deviceExtension->SmartcardExtension;
deviceExtension->CloseSerial = IoAllocateWorkItem(
DeviceObject
);
// Used for stop / start notification
KeInitializeEvent(
&deviceExtension->ReaderStarted,
NotificationEvent,
FALSE
);
// Used to keep track of open close calls
deviceExtension->ReaderOpen = FALSE;
KeInitializeSpinLock(&deviceExtension->SpinLock);
// Allocate data struct space for smart card reader
smartcardExtension->ReaderExtension = ExAllocatePool(
NonPagedPool,
sizeof(READER_EXTENSION)
);
if (smartcardExtension->ReaderExtension == NULL) {
SmartcardLogError(
DriverObject,
TLP3_NO_MEMORY,
NULL,
0
);
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
readerExtension = smartcardExtension->ReaderExtension;
RtlZeroMemory(readerExtension, sizeof(READER_EXTENSION));
// Write the version of the lib we use to the smartcard extension
smartcardExtension->Version = SMCLIB_VERSION;
smartcardExtension->SmartcardRequest.BufferSize =
smartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
//
// Now let the lib allocate the buffer for data transmission
// We can either tell the lib how big the buffer should be
// by assigning a value to BufferSize or let the lib
// allocate the default size
//
status = SmartcardInitialize(smartcardExtension);
if (status != STATUS_SUCCESS) {
SmartcardLogError(
DriverObject,
(smartcardExtension->OsData ? TLP3_WRONG_LIB_VERSION : TLP3_NO_MEMORY),
NULL,
0
);
__leave;
}
// Save deviceObject
smartcardExtension->OsData->DeviceObject = DeviceObject;
// Set up call back functions
smartcardExtension->ReaderFunction[RDF_TRANSMIT] = TLP3Transmit;
smartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = TLP3SetProtocol;
smartcardExtension->ReaderFunction[RDF_CARD_POWER] = TLP3ReaderPower;
smartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = TLP3CardTracking;
smartcardExtension->ReaderFunction[RDF_IOCTL_VENDOR] = TLP3VendorIoctl;
// This event signals that the serial driver has been closed
KeInitializeEvent(
&READER_EXTENSION_L(SerialCloseDone),
NotificationEvent,
TRUE
);
//
// Set the vendor information
//
strcpy(smartcardExtension->VendorAttr.VendorName.Buffer, "Bull");
smartcardExtension->VendorAttr.VendorName.Length =
(USHORT) strlen(deviceExtension->SmartcardExtension.VendorAttr.VendorName.Buffer);
strcpy(smartcardExtension->VendorAttr.IfdType.Buffer, "SmarTLP");
smartcardExtension->VendorAttr.IfdType.Length =
(USHORT) strlen(smartcardExtension->VendorAttr.IfdType.Buffer);
smartcardExtension->VendorAttr.UnitNo = MAXULONG;
for (deviceInstance = 0; deviceInstance < MAXULONG; deviceInstance++) {
PDEVICE_OBJECT devObj;
for (devObj = DeviceObject;
devObj != NULL;
devObj = devObj->NextDevice) {
PDEVICE_EXTENSION devExt = devObj->DeviceExtension;
PSMARTCARD_EXTENSION smcExt = &devExt->SmartcardExtension;
if (deviceInstance == smcExt->VendorAttr.UnitNo) {
break;
}
}
if (devObj == NULL) {
smartcardExtension->VendorAttr.UnitNo = deviceInstance;
break;
}
}
//
// Set the reader capabilities
//
// Clk frequency in KHz encoded as little endian integer
smartcardExtension->ReaderCapabilities.CLKFrequency.Default = 3571;
smartcardExtension->ReaderCapabilities.CLKFrequency.Max = 3571;
smartcardExtension->ReaderCapabilities.DataRate.Default =
smartcardExtension->ReaderCapabilities.DataRate.Max =
dataRatesSupported[0];
// reader could support higher data rates
smartcardExtension->ReaderCapabilities.DataRatesSupported.List =
dataRatesSupported;
smartcardExtension->ReaderCapabilities.DataRatesSupported.Entries =
sizeof(dataRatesSupported) / sizeof(dataRatesSupported[0]);
smartcardExtension->ReaderCapabilities.MaxIFSD = 254;
// Now setup information in our deviceExtension
smartcardExtension->ReaderCapabilities.CurrentState =
(ULONG) SCARD_UNKNOWN;
// This reader supports T=0 and T=1
smartcardExtension->ReaderCapabilities.SupportedProtocols =
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
smartcardExtension->ReaderCapabilities.MechProperties = 0;
//
// Set serial configuration parameters
//
readerExtension->SerialConfigData.BaudRate.BaudRate = 9600;
readerExtension->SerialConfigData.LineControl.StopBits =
STOP_BITS_2;
readerExtension->SerialConfigData.LineControl.Parity =
EVEN_PARITY;
readerExtension->SerialConfigData.LineControl.WordLength =
SERIAL_DATABITS_8;
// set timeouts
readerExtension->SerialConfigData.Timeouts.ReadIntervalTimeout =
READ_INTERVAL_TIMEOUT_DEFAULT;
readerExtension->SerialConfigData.Timeouts.ReadTotalTimeoutConstant =
READ_TOTAL_TIMEOUT_CONSTANT_DEFAULT;
readerExtension->SerialConfigData.Timeouts.ReadTotalTimeoutMultiplier = 0;
// set special characters
readerExtension->SerialConfigData.SerialChars.ErrorChar = 0;
readerExtension->SerialConfigData.SerialChars.EofChar = 0;
readerExtension->SerialConfigData.SerialChars.EventChar = 0;
readerExtension->SerialConfigData.SerialChars.XonChar = 0;
readerExtension->SerialConfigData.SerialChars.XoffChar = 0;
readerExtension->SerialConfigData.SerialChars.BreakChar = 0xFF;
// Set handflow
readerExtension->SerialConfigData.HandFlow.XonLimit = 0;
readerExtension->SerialConfigData.HandFlow.XoffLimit = 0;
readerExtension->SerialConfigData.HandFlow.ControlHandShake = 0;
readerExtension->SerialConfigData.HandFlow.FlowReplace =
SERIAL_XOFF_CONTINUE;
#if defined (DEBUG) && defined (DETECT_SERIAL_OVERRUNS)
readerExtension->SerialConfigData.HandFlow.ControlHandShake =
SERIAL_ERROR_ABORT;
#endif
// save the current power state of the reader
readerExtension->ReaderPowerState = PowerReaderWorking;
// and attach to the PDO
ATTACHED_DEVICE_OBJECT =
IoAttachDeviceToDeviceStack(
DeviceObject,
PhysicalDeviceObject
);
ASSERT(ATTACHED_DEVICE_OBJECT != NULL);
if (ATTACHED_DEVICE_OBJECT == NULL) {
SmartcardLogError(
DriverObject,
TLP3_CANT_CONNECT_TO_ASSIGNED_PORT,
NULL,
status
);
status = STATUS_UNSUCCESSFUL;
__leave;
}
// register our new device
status = IoRegisterDeviceInterface(
PhysicalDeviceObject,
&SmartCardReaderGuid,
NULL,
&deviceExtension->PnPDeviceName
);
ASSERT(status == STATUS_SUCCESS);
DeviceObject->Flags |= DO_BUFFERED_IO;
DeviceObject->Flags |= DO_POWER_PAGABLE;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}
__finally {
if (regKey) {
ZwClose(regKey);
}
if (status != STATUS_SUCCESS) {
TLP3RemoveDevice(DeviceObject);
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3AddDevice: Exit %x\n",
DRIVER_NAME,
status)
);
}
return status;
}
NTSTATUS
TLP3StartDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Open the serial device, start card tracking and register our
device interface. If any of the calls here fails we don't care
to rollback since a stop will be called later which we then
use to clean up.
--*/
{
NTSTATUS status;
PIRP irp;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3StartDevice: Enter\n",
DRIVER_NAME)
);
irp = IoAllocateIrp(
(CCHAR) (DeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp == NULL) {
return STATUS_NO_MEMORY;
}
_try {
PIO_STACK_LOCATION irpStack;
HANDLE handle = 0;
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 = TLP3CallSerialDriver(
ATTACHED_DEVICE_OBJECT,
irp
);
if (status != STATUS_SUCCESS) {
leave;
}
KeClearEvent(&READER_EXTENSION_L(SerialCloseDone));
status = TLP3ConfigureSerialPort(&deviceExtension->SmartcardExtension);
if (status != STATUS_SUCCESS) {
leave;
}
status = TLP3StartSerialEventTracking(
&deviceExtension->SmartcardExtension
);
if (status != STATUS_SUCCESS) {
leave;
}
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
if (status != STATUS_SUCCESS) {
leave;
}
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
}
_finally {
if (status == STATUS_SHARED_IRQ_BUSY) {
SmartcardLogError(
DeviceObject,
TLP3_IRQ_BUSY,
NULL,
status
);
}
if (status != STATUS_SUCCESS) {
TLP3StopDevice(DeviceObject);
}
IoFreeIrp(irp);
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3StartDevice: Exit %lx\n",
DRIVER_NAME,
status)
);
return status;
}
VOID
TLP3StopDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Finishes card tracking requests and closes the connection to the
serial driver.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3StopDevice: Enter\n",
DRIVER_NAME)
);
if (KeReadStateEvent(&READER_EXTENSION_L(SerialCloseDone)) == 0l) {
NTSTATUS status;
PUCHAR requestBuffer;
PSMARTCARD_EXTENSION smartcardExtension =
&deviceExtension->SmartcardExtension;
// test if we ever started event tracking
if (smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0) {
// no, we did not
// We 'only' need to close the serial port
TLP3CloseSerialPort(DeviceObject, NULL);
} else {
//
// We now inform the serial driver that we're not longer
// interested in serial events. This will also free the irp
// we use for those io-completions
//
smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask = 0;
// save the pointer
requestBuffer = smartcardExtension->SmartcardRequest.Buffer;
*(PULONG) smartcardExtension->SmartcardRequest.Buffer =
smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask;
smartcardExtension->SmartcardRequest.BufferLength = sizeof(ULONG);
smartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
// We don't expect to get bytes back
smartcardExtension->SmartcardReply.BufferLength = 0;
TLP3SerialIo(smartcardExtension);
// restore the pointer
smartcardExtension->SmartcardRequest.Buffer = requestBuffer;
// now wait until the connetion to serial is closed
status = KeWaitForSingleObject(
&READER_EXTENSION_L(SerialCloseDone),
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(status == STATUS_SUCCESS);
}
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3StopDevice: Exit\n",
DRIVER_NAME)
);
}
NTSTATUS
TLP3SystemControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
--*/
{
PDEVICE_EXTENSION DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
DeviceExtension = DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(DeviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject, Irp);
return status;
}
NTSTATUS
TLP3DeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This is our IOCTL dispatch function
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
NTSTATUS status = STATUS_SUCCESS;
KIRQL irql;
#ifdef SIMULATION
RTL_QUERY_REGISTRY_TABLE parameters[2];
#endif
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3DeviceControl: Enter\n",
DRIVER_NAME)
);
if (smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0) {
//
// the wait mask is set to 0 whenever the device was either
// surprise-removed or politely removed
//
status = STATUS_DEVICE_REMOVED;
}
if (status == STATUS_SUCCESS) {
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if (deviceExtension->IoCount == 0) {
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
status = KeWaitForSingleObject(
&deviceExtension->ReaderStarted,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(status == STATUS_SUCCESS);
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
}
ASSERT(deviceExtension->IoCount >= 0);
deviceExtension->IoCount++;
KeReleaseSpinLock(&deviceExtension->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;
}
#ifdef SIMULATION
if (DriverKey) {
ULONG oldLevel =
smartcardExtension->ReaderExtension->SimulationLevel;
RtlZeroMemory(parameters, sizeof(parameters));
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[0].Name = L"SimulationLevel";
parameters[0].EntryContext =
&smartcardExtension->ReaderExtension->SimulationLevel;
parameters[0].DefaultType = REG_DWORD;
parameters[0].DefaultData =
&smartcardExtension->ReaderExtension->SimulationLevel;
parameters[0].DefaultLength = sizeof(ULONG);
if (RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
DriverKey,
parameters,
NULL,
NULL
) == STATUS_SUCCESS) {
SmartcardDebug(
smartcardExtension->ReaderExtension->SimulationLevel == oldLevel ? 0 : DEBUG_SIMULATION,
( "%s!TLP3AddDevice: SimulationLevel set to %lx\n",
DRIVER_NAME,
smartcardExtension->ReaderExtension->SimulationLevel)
);
}
}
#endif
ASSERT(smartcardExtension->ReaderExtension->ReaderPowerState ==
PowerReaderWorking);
status = SmartcardDeviceControl(
smartcardExtension,
Irp
);
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'tcoI');
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
deviceExtension->IoCount--;
ASSERT(deviceExtension->IoCount >= 0);
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3DeviceControl: Exit %lx\n",
DRIVER_NAME,
status)
);
return status;
}
VOID
TLP3CloseSerialPort(
IN PDEVICE_OBJECT DeviceObject,
IN 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
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION irpStack;
IO_STATUS_BLOCK ioStatusBlock;
UNREFERENCED_PARAMETER(Context);
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3CloseSerialPort: Enter\n",
DRIVER_NAME)
);
//
// first mark this device as 'gone'.
// This will prevent that someone can re-open the device
// We intentionally ignore possible errors
//
IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
FALSE
);
irp = IoAllocateIrp(
(CCHAR) (DeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp) {
SmartcardDebug(
DEBUG_DRIVER,
( "%s!TLP3CloseSerialPort: Sending IRP_MJ_CLOSE\n",
DRIVER_NAME)
);
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;
irpStack = IoGetCurrentIrpStackLocation( irp );
irpStack->MajorFunction = IRP_MJ_CLOSE;
status = TLP3CallSerialDriver(
ATTACHED_DEVICE_OBJECT,
irp
);
ASSERT(status == STATUS_SUCCESS);
IoFreeIrp(irp);
}
// now 'signal' that we closed the serial driver
KeSetEvent(
&READER_EXTENSION_L(SerialCloseDone),
0,
FALSE
);
SmartcardDebug(
DEBUG_INFO,
( "%s!TLP3CloseSerialPort: Exit\n",
DRIVER_NAME)
);
}
NTSTATUS
TLP3IoCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
{
UNREFERENCED_PARAMETER (DeviceObject);
if (Irp->Cancel) {
Irp->IoStatus.Status = STATUS_CANCELLED;
} else {
Irp->IoStatus.Status = STATUS_MORE_PROCESSING_REQUIRED;
}
KeSetEvent (Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
TLP3CallSerialDriver(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Send an Irp to the serial driver.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
KEVENT Event;
// Copy our stack location to the next.
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// initialize an event for process synchronization. The event is passed
// to our completion routine and will be set when the serial driver is done
//
KeInitializeEvent(
&Event,
NotificationEvent,
FALSE
);
// Our IoCompletionRoutine sets only our event
IoSetCompletionRoutine (
Irp,
TLP3IoCompletion,
&Event,
TRUE,
TRUE,
TRUE
);
if (IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_POWER) {
status = PoCallDriver(DeviceObject, Irp);
} else {
// Call the serial driver
status = IoCallDriver(DeviceObject, Irp);
}
// Wait until the serial driver has processed the Irp
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT (STATUS_SUCCESS == status);
status = Irp->IoStatus.Status;
}
return status;
}
NTSTATUS
TLP3PnP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
--*/
{
PUCHAR requestBuffer;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
PREADER_EXTENSION readerExtension = smartcardExtension->ReaderExtension;
PDEVICE_OBJECT AttachedDeviceObject;
PIO_STACK_LOCATION irpStack;
IO_STATUS_BLOCK ioStatusBlock;
BOOLEAN deviceRemoved = FALSE, irpSkipped = FALSE;
KIRQL irql;
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3PnPDeviceControl: Enter\n",
DRIVER_NAME)
);
status = SmartcardAcquireRemoveLockWithTag(smartcardExtension, ' PnP');
ASSERT(status == STATUS_SUCCESS);
if (status != STATUS_SUCCESS) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
AttachedDeviceObject = ATTACHED_DEVICE_OBJECT;
irpStack = IoGetCurrentIrpStackLocation(Irp);
// Now look what the PnP manager wants...
switch(irpStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_START_DEVICE\n",
DRIVER_NAME)
);
// We have to call the underlying driver first
status = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
ASSERT(NT_SUCCESS(status));
if (NT_SUCCESS(status)) {
status = TLP3StartDevice(DeviceObject);
}
break;
case IRP_MN_QUERY_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_QUERY_STOP_DEVICE\n",
DRIVER_NAME)
);
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if (deviceExtension->IoCount > 0) {
// we refuse to stop if we have pending io
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
status = STATUS_DEVICE_BUSY;
} else {
// stop processing requests
KeClearEvent(&deviceExtension->ReaderStarted);
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
status = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
}
break;
case IRP_MN_CANCEL_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_CANCEL_STOP_DEVICE\n",
DRIVER_NAME)
);
status = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
if (status == STATUS_SUCCESS) {
// we can continue to process requests
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
}
break;
case IRP_MN_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_STOP_DEVICE\n",
DRIVER_NAME)
);
TLP3StopDevice(DeviceObject);
//
// we don't do anything since a stop is only used
// to reconfigure hw-resources like interrupts and io-ports
//
status = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_QUERY_REMOVE_DEVICE\n",
DRIVER_NAME)
);
// disable the interface (and ignore possible errors)
IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
FALSE
);
// now look if someone is currently connected to us
if (deviceExtension->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 = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_CANCEL_REMOVE_DEVICE\n",
DRIVER_NAME)
);
status = TLP3CallSerialDriver(AttachedDeviceObject, 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 &&
READER_EXTENSION_L(SerialConfigData.SerialWaitMask) != 0) {
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
ASSERT(status == STATUS_SUCCESS);
}
break;
case IRP_MN_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_REMOVE_DEVICE\n",
DRIVER_NAME)
);
TLP3RemoveDevice(DeviceObject);
status = TLP3CallSerialDriver(AttachedDeviceObject, Irp);
deviceRemoved = TRUE;
break;
default:
// This is an Irp that is only useful for underlying drivers
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3PnPDeviceControl: IRP_MN_...%lx\n",
DRIVER_NAME,
irpStack->MinorFunction)
);
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(AttachedDeviceObject, Irp);
irpSkipped = TRUE;
break;
}
if (irpSkipped == FALSE) {
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
if (deviceRemoved == FALSE) {
SmartcardReleaseRemoveLockWithTag(smartcardExtension, ' PnP');
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3PnPDeviceControl: Exit %lx\n",
DRIVER_NAME,
status)
);
return status;
}
VOID
TLP3SystemPowerCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PKEVENT Event,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
This function is called when the underlying stacks
completed the power transition.
--*/
{
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (MinorFunction);
UNREFERENCED_PARAMETER (PowerState);
UNREFERENCED_PARAMETER (IoStatus);
KeSetEvent(Event, 0, FALSE);
}
NTSTATUS
TLP3DevicePowerCompletion (
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
LARGE_INTEGER delayPeriod;
//
// Allow the reader enough time to power itself up
//
delayPeriod.HighPart = -1;
delayPeriod.LowPart = 100000 * (-10);
KeDelayExecutionThread(
KernelMode,
FALSE,
&delayPeriod
);
//
// We issue a power request in order to figure out
// what the actual card status is
//
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
TLP3ReaderPower(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) {
TLP3CompleteCardTracking(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;
}
typedef enum _ACTION {
Undefined = 0,
SkipRequest,
WaitForCompletion,
CompleteRequest,
MarkPending
} ACTION;
NTSTATUS
TLP3Power (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The power dispatch routine.
This driver is the power policy owner of the device stack,
because this driver knows about the connected reader.
Therefor this driver will translate system power states
to device power states.
Arguments:
DeviceObject - pointer to a device object.
Irp - pointer to an I/O Request Packet.
Return Value:
NT status code
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
PDEVICE_OBJECT AttachedDeviceObject;
POWER_STATE powerState;
ACTION action = SkipRequest;
KEVENT event;
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: Enter\n",
DRIVER_NAME)
);
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;
}
AttachedDeviceObject = ATTACHED_DEVICE_OBJECT;
switch (irpStack->Parameters.Power.Type) {
case DevicePowerState:
if (irpStack->MinorFunction == IRP_MN_SET_POWER) {
switch (irpStack->Parameters.Power.State.DeviceState) {
case PowerDeviceD0:
// Turn on the reader
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: PowerDevice D0\n",
DRIVER_NAME)
);
//
// 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,
TLP3DevicePowerCompletion,
smartcardExtension,
TRUE,
TRUE,
TRUE
);
action = WaitForCompletion;
break;
case PowerDeviceD3:
// Turn off the reader
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: PowerDevice D3\n",
DRIVER_NAME)
);
PoSetPowerState (
DeviceObject,
DevicePowerState,
irpStack->Parameters.Power.State
);
// save the current card state
smartcardExtension->ReaderExtension->CardPresent =
smartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT;
if (smartcardExtension->ReaderExtension->CardPresent) {
smartcardExtension->MinorIoControlCode = SCARD_POWER_DOWN;
status = TLP3ReaderPower(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.
//
smartcardExtension->ReaderExtension->PowerRequest = TRUE;
// save the current power state of the reader
smartcardExtension->ReaderExtension->ReaderPowerState =
PowerReaderOff;
action = SkipRequest;
break;
default:
ASSERT(FALSE);
action = SkipRequest;
break;
}
}
break;
case 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(smartcardExtension->ReaderExtension->ReaderPowerState !=
PowerReaderUnspecified);
switch (irpStack->MinorFunction) {
KIRQL irql;
case IRP_MN_QUERY_POWER:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: Query Power\n",
DRIVER_NAME)
);
switch (irpStack->Parameters.Power.State.SystemState) {
case PowerSystemMaximum:
case PowerSystemWorking:
case PowerSystemSleeping1:
case PowerSystemSleeping2:
action = SkipRequest;
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if (deviceExtension->IoCount == 0) {
// Block any further ioctls
KeClearEvent(&deviceExtension->ReaderStarted);
action = SkipRequest;
} else {
// can't go to sleep mode since the reader is busy.
status = STATUS_DEVICE_BUSY;
action = CompleteRequest;
}
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
break;
}
break;
case IRP_MN_SET_POWER:
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: PowerSystem S%d\n",
DRIVER_NAME,
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
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
action = SkipRequest;
break;
}
// wake up the underlying stack...
powerState.DeviceState = PowerDeviceD0;
action = MarkPending;
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
if (smartcardExtension->ReaderExtension->ReaderPowerState ==
PowerReaderOff) {
// We're already in the right state
action = SkipRequest;
break;
}
powerState.DeviceState = PowerDeviceD3;
// first, inform the power manager of our new state.
PoSetPowerState (
DeviceObject,
SystemPowerState,
powerState
);
action = MarkPending;
break;
default:
ASSERT(FALSE);
action = SkipRequest;
break;
}
break;
}
}
break;
default:
ASSERT(FALSE);
action = SkipRequest;
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:
// initialize the event we need in the completion function
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
// request the device power irp
status = PoRequestPowerIrp (
DeviceObject,
IRP_MN_SET_POWER,
powerState,
TLP3SystemPowerCompletion,
&event,
NULL
);
ASSERT(status == STATUS_PENDING);
if (status == STATUS_PENDING) {
// wait until the device power irp completed
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'rwoP');
if (powerState.SystemState == PowerSystemWorking) {
PoSetPowerState (
DeviceObject,
SystemPowerState,
powerState
);
}
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(AttachedDeviceObject, Irp);
} else {
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'rwoP');
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
break;
case SkipRequest:
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'rwoP');
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
status = PoCallDriver(AttachedDeviceObject, Irp);
break;
case WaitForCompletion:
status = PoCallDriver(AttachedDeviceObject, Irp);
break;
default:
ASSERT(FALSE);
break;
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Power: Exit %lx\n",
DRIVER_NAME,
status)
);
return status;
}
NTSTATUS
TLP3CreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN 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 for this miniport
Irp - IRP involved.
Return Value:
STATUS_SUCCESS.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
__try {
if (irpStack->MajorFunction == IRP_MJ_CREATE) {
status = SmartcardAcquireRemoveLockWithTag(
&deviceExtension->SmartcardExtension,
'lCrC'
);
if (status != STATUS_SUCCESS) {
status = STATUS_DEVICE_REMOVED;
__leave;
}
// test if the device has been opened already
if (InterlockedCompareExchange(
&deviceExtension->ReaderOpen,
TRUE,
FALSE) == FALSE) {
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3CreateClose: Open\n",
DRIVER_NAME)
);
} else {
// the device is already in use
status = STATUS_UNSUCCESSFUL;
// release the lock
SmartcardReleaseRemoveLockWithTag(
&deviceExtension->SmartcardExtension,
'lCrC'
);
}
} else {
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3CreateClose: Close\n",
DRIVER_NAME)
);
SmartcardReleaseRemoveLockWithTag(
&deviceExtension->SmartcardExtension,
'lCrC'
);
deviceExtension->ReaderOpen = FALSE;
}
}
__finally {
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS
TLP3Cancel(
IN PDEVICE_OBJECT DeviceObject,
IN 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 for this miniport
Irp - IRP involved.
Return Value:
STATUS_CANCELLED
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
("%s!TLP3Cancel: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp == smartcardExtension->OsData->NotificationIrp);
IoReleaseCancelSpinLock(
Irp->CancelIrql
);
TLP3CompleteCardTracking(smartcardExtension);
SmartcardDebug(
DEBUG_TRACE,
("%s!TLP3Cancel: Exit\n",
DRIVER_NAME)
);
return STATUS_CANCELLED;
}
NTSTATUS
TLP3Cleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called when the calling application terminates.
We can actually only have the notification irp that we have to cancel.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
NTSTATUS status = STATUS_SUCCESS;
SmartcardDebug(
DEBUG_TRACE,
("%s!TLP3Cleanup: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp != smartcardExtension->OsData->NotificationIrp);
// We need to complete the notification irp
TLP3CompleteCardTracking(smartcardExtension);
SmartcardDebug(
DEBUG_DRIVER,
("%s!TLP3Cleanup: 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!TLP3Cleanup: Exit\n",
DRIVER_NAME)
);
return STATUS_SUCCESS;
}
VOID
TLP3RemoveDevice(
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Remove the device from the system.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PSMARTCARD_EXTENSION smartcardExtension;
NTSTATUS status;
PAGED_CODE();
if (DeviceObject == NULL) {
return;
}
deviceExtension = DeviceObject->DeviceExtension;
smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!TLP3RemoveDevice: Enter\n",
DRIVER_NAME)
);
if (smartcardExtension->OsData) {
// complete pending card tracking requests (if any)
TLP3CompleteCardTracking(smartcardExtension);
ASSERT(smartcardExtension->OsData->NotificationIrp == NULL);
// Wait until we can safely unload the device
SmartcardReleaseRemoveLockAndWait(smartcardExtension);
}
TLP3StopDevice(DeviceObject);
if (deviceExtension->SmartcardExtension.ReaderExtension &&
deviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject) {
IoDetachDevice(
deviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject
);
}
if(deviceExtension->PnPDeviceName.Buffer != NULL) {
RtlFreeUnicodeString(&deviceExtension->PnPDeviceName);
}
if(smartcardExtension->OsData != NULL) {
SmartcardExit(smartcardExtension);
}
if (smartcardExtension->ReaderExtension != NULL) {
ExFreePool(smartcardExtension->ReaderExtension);
}
if (deviceExtension->CloseSerial != NULL) {
IoFreeWorkItem(deviceExtension->CloseSerial);
}
IoDeleteDevice(DeviceObject);
SmartcardDebug(
DEBUG_INFO,
( "%s!TLP3RemoveDevice: Exit\n",
DRIVER_NAME)
);
}
VOID
TLP3DriverUnload(
IN 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.
--*/
{
PAGED_CODE();
SmartcardDebug(
DEBUG_INFO,
("%s!TLP3DriverUnload\n",
DRIVER_NAME)
);
}
NTSTATUS
TLP3ConfigureSerialPort(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine will appropriately configure the serial port.
It makes synchronous calls to the serial port.
Arguments:
SmartcardExtension - Pointer to smart card struct
Return Value:
NTSTATUS
--*/
{
PSERIAL_READER_CONFIG configData = &SmartcardExtension->ReaderExtension->SerialConfigData;
NTSTATUS status = STATUS_SUCCESS;
USHORT i;
PUCHAR request = SmartcardExtension->SmartcardRequest.Buffer;
SmartcardExtension->SmartcardRequest.BufferLength = 0;
SmartcardExtension->SmartcardReply.BufferLength =
SmartcardExtension->SmartcardReply.BufferSize;
for (i = 0; status == STATUS_SUCCESS; i++) {
switch (i) {
case 0:
//
// Set up baudrate for the TLP3 reader
//
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_BAUD_RATE;
SmartcardExtension->SmartcardRequest.Buffer =
(PUCHAR) &configData->BaudRate;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(SERIAL_BAUD_RATE);
break;
case 1:
//
// Set up line control parameters
//
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_LINE_CONTROL;
SmartcardExtension->SmartcardRequest.Buffer =
(PUCHAR) &configData->LineControl;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(SERIAL_LINE_CONTROL);
break;
case 2:
//
// Set serial special characters
//
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_CHARS;
SmartcardExtension->SmartcardRequest.Buffer =
(PUCHAR) &configData->SerialChars;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(SERIAL_CHARS);
break;
case 3:
//
// Set up timeouts
//
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_TIMEOUTS;
SmartcardExtension->SmartcardRequest.Buffer =
(PUCHAR) &configData->Timeouts;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(SERIAL_TIMEOUTS);
break;
case 4:
// Set flowcontrol and handshaking
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_HANDFLOW;
SmartcardExtension->SmartcardRequest.Buffer =
(PUCHAR) &configData->HandFlow;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(SERIAL_HANDFLOW);
break;
case 5:
// Set break off
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_BREAK_OFF;
break;
case 6:
// set DTR for the reader
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_DTR;
break;
case 7:
SmartcardExtension->ReaderExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_RTS;
break;
case 8:
return STATUS_SUCCESS;
}
status = TLP3SerialIo(SmartcardExtension);
// restore pointer to original request buffer
SmartcardExtension->SmartcardRequest.Buffer = request;
}
return status;
}
NTSTATUS
TLP3StartSerialEventTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine initializes serial event tracking.
It calls the serial driver to set a wait mask for CTS and DSR tracking.
--*/
{
NTSTATUS status;
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PAGED_CODE();
readerExtension->SerialConfigData.SerialWaitMask =
SERIAL_EV_CTS | SERIAL_EV_DSR;
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
//
// 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
//
readerExtension->SerialStatusIrp = IoBuildDeviceIoControlRequest(
IOCTL_SERIAL_SET_WAIT_MASK,
readerExtension->AttachedDeviceObject,
&readerExtension->SerialConfigData.SerialWaitMask,
sizeof(readerExtension->SerialConfigData.SerialWaitMask),
NULL,
0,
FALSE,
&event,
&ioStatus
);
if (readerExtension->SerialStatusIrp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(
readerExtension->AttachedDeviceObject,
readerExtension->SerialStatusIrp,
);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(status == STATUS_SUCCESS);
status = ioStatus.Status;
}
if (status == STATUS_SUCCESS) {
KIRQL oldIrql;
LARGE_INTEGER delayPeriod;
PIO_STACK_LOCATION irpSp;
//
// Now tell the serial driver that we want to be informed
// when CTS or DSR changes its state.
//
readerExtension->SerialStatusIrp = IoAllocateIrp(
(CCHAR) (SmartcardExtension->OsData->DeviceObject->StackSize + 1),
FALSE
);
if (readerExtension->SerialStatusIrp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpSp = IoGetNextIrpStackLocation( readerExtension->SerialStatusIrp );
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
irpSp->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(readerExtension->SerialConfigData.SerialWaitMask);
irpSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
readerExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&readerExtension->SerialConfigData.SerialWaitMask;
//
// this artificial delay is necessary to make this driver work
// with digi board cards
//
delayPeriod.HighPart = -1;
delayPeriod.LowPart = 100l * 1000 * (-10);
KeDelayExecutionThread(
KernelMode,
FALSE,
&delayPeriod
);
// We simulate a callback now that triggers the card supervision
TLP3SerialEvent(
SmartcardExtension->OsData->DeviceObject,
readerExtension->SerialStatusIrp,
SmartcardExtension
);
status = STATUS_SUCCESS;
}
return status;
}
VOID
TLP3CompleteCardTracking(
IN 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_INFO,
("%s!TLP3CompleteCardTracking: Completing NotificationIrp %lxh\n",
DRIVER_NAME,
notificationIrp)
);
// finish the request
if (notificationIrp->Cancel) {
notificationIrp->IoStatus.Status = STATUS_CANCELLED;
} else {
notificationIrp->IoStatus.Status = STATUS_SUCCESS;
}
notificationIrp->IoStatus.Information = 0;
IoCompleteRequest(
notificationIrp,
IO_NO_INCREMENT
);
}
}
NTSTATUS
TLP3SerialEvent(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine is called in two cases:
a) CTS changed (card inserted or removed) or
b) DSR changed (reader has been removed)
For a) we update the card status and complete outstanding
card tracking requests.
For b) we start to unload the driver
NOTE: This function calls itself using IoCompletion. In the 'first'
callback the serial driver only tells us that something has changed.
We set up a call for 'what has changed' (GetModemStatus) which then
call this function again.
When we updated everything and we don't unload the driver card
tracking is started again.
--*/
{
NTSTATUS status;
KIRQL irql;
KeAcquireSpinLock(
&SmartcardExtension->OsData->SpinLock,
&irql
);
if (SmartcardExtension->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
//
if ((SmartcardExtension->ReaderExtension->ModemStatus & SERIAL_DSR_STATE) == 0) {
SmartcardDebug(
DEBUG_INFO,
("%s!TLP3SerialEvent: Reader removed\n",
DRIVER_NAME)
);
//
// We set the mask to zero to signal that we can
// release the irp that we use for the serial events
//
SmartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask = 0;
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
} else {
if (SmartcardExtension->ReaderExtension->ModemStatus & SERIAL_CTS_STATE) {
// Card is inserted
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_SWALLOWED;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
SmartcardDebug(
DEBUG_INFO,
("%s!TLP3SerialEvent: Smart card inserted\n",
DRIVER_NAME)
);
} else {
// Card is removed
SmartcardExtension->CardCapabilities.ATR.Length = 0;
SmartcardExtension->ReaderCapabilities.CurrentState =
SCARD_ABSENT;
SmartcardExtension->CardCapabilities.Protocol.Selected =
SCARD_PROTOCOL_UNDEFINED;
SmartcardDebug(
DEBUG_INFO,
("%s!TLP3SerialEvent: Smart card removed\n",
DRIVER_NAME)
);
}
}
}
KeReleaseSpinLock(
&SmartcardExtension->OsData->SpinLock,
irql
);
//
// Only inform the user of a card insertion/removal event
// if this function isn't called due to a power down - power up cycle
//
if (SmartcardExtension->ReaderExtension->PowerRequest == FALSE) {
TLP3CompleteCardTracking(SmartcardExtension);
}
// The wait mask is set to 0 when the driver unloads
if (SmartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0) {
// The reader has been unplugged.
PDEVICE_EXTENSION deviceExtension =
SmartcardExtension->OsData->DeviceObject->DeviceExtension;
// schedule our remove thread
IoQueueWorkItem(
deviceExtension->CloseSerial,
(PIO_WORKITEM_ROUTINE) TLP3CloseSerialPort,
DelayedWorkQueue,
NULL
);
SmartcardDebug(
DEBUG_TRACE,
("%s!TLP3SerialEvent: Exit (Release IRP)\n",
DRIVER_NAME)
);
//
// 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;
}
if (SmartcardExtension->ReaderExtension->GetModemStatus == FALSE) {
//
// 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.
//
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(
SmartcardExtension->ReaderExtension->SerialStatusIrp
);
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpStack->MinorFunction = 0UL;
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(SmartcardExtension->ReaderExtension->ModemStatus);
irpStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_GET_MODEMSTATUS;
SmartcardExtension->ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&SmartcardExtension->ReaderExtension->ModemStatus;
SmartcardExtension->ReaderExtension->GetModemStatus = TRUE;
} else {
PIO_STACK_LOCATION irpStack;
// Setup call for device control to wait for a serial event
irpStack = IoGetNextIrpStackLocation(
SmartcardExtension->ReaderExtension->SerialStatusIrp
);
#if defined (DEBUG) && defined (DETECT_SERIAL_OVERRUNS)
if (Irp->IoStatus.Status != STATUS_SUCCESS) {
//
// we need to call the serial driver to reset the internal
// error counters, otherwise the serial driver refuses to work
//
static SERIAL_STATUS serialStatus;
SmartcardDebug(
DEBUG_ERROR,
( "%s!TLP3SerialEvent: Reset of serial error condition...\n",
DRIVER_NAME)
);
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpStack->MinorFunction = 0UL;
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(serialStatus);
irpStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_GET_COMMSTATUS;
SmartcardExtension->ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&serialStatus;
} else
#endif
{
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpStack->MinorFunction = 0UL;
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask);
irpStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
SmartcardExtension->ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer =
&SmartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask;
}
SmartcardExtension->ReaderExtension->GetModemStatus = FALSE;
}
IoSetCompletionRoutine(
SmartcardExtension->ReaderExtension->SerialStatusIrp,
TLP3SerialEvent,
SmartcardExtension,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(
SmartcardExtension->ReaderExtension->AttachedDeviceObject,
SmartcardExtension->ReaderExtension->SerialStatusIrp
);
return STATUS_MORE_PROCESSING_REQUIRED;
}