Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2702 lines
68 KiB

/*++
Copyright (c) 1998 Gemplus Development
Name:
GNTSCR0A.C (Gemplus NT Smart Card Reader module for GCR410P )
Description :
This is the main module which holds the main functions for a standard NT driver
Environment:
Kernel mode
Revision History :
dd/mm/yy
13/03/98: V1.00.001 (GPZ)
- Start of development. Pnp support.
14/04/98: V1.00.002 (GPZ)
- Updated for Power Management support.
19/05/98: V1.00.003 (GPZ)
- Updated for the GemCore 1.11-3 version.
28/05/98: V1.00.004 (GPZ)
- The reference to PRTL_REMOVE_LOCK is removed.
03/06/98: V1.00.005 (GPZ)
- After a power down, the card state is updated.
08/06/98: V1.00.006 (GPZ)
18/06/98: V1.00.007 (GPZ)
- The functions which use KeAcquireSpinLock/KeAcquireCancelSpinlock
must be NOT PAGEABLE.
- Modification of GCR410PPnPDeviceControl.
- Modification of GCR410PPowerDeviceControl to support completly the
request of the power management.
16/07/98: V1.00.008 (GPZ)
- To update the card state, we use a System Worker Thread which is called
like a callback function only when an RING Irq occurs. The thread terminates
immediately.
17/07/98: V1.00.009 (GPZ)
- The Interface state is set to true at the end of StartDevice.
06/08/98: V1.00.010 (GPZ)
- If the StartDevice failed and the serial port is openned, we close
the serial port instead of call the WaitForDeviceRemoval function.
11/09/98: V1.00.011 (GPZ)
- GCR410PDeviceControl, GCR410PPnPDeviceControl and GCR410PPowerDeviceControl
updated to block all incoming Ioctls as long as the reader is
in stand by/hibernation mode.
- All functions which use SpinLock cannot be Pageable.
13/09/98: V1.00.012 (GPZ)
- Check if the CardStatus thread runs before to close the serial port.
--*/
#include <stdio.h>
#include "gntscr.h"
#include "gntscr0a.h"
#include "gntser.h"
//
// Pragma section:
//
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(PAGEABLE,GCR410PStartSerialEventTracking)
#pragma alloc_text(PAGEABLE,GCR410PStopSerialEventTracking)
#pragma alloc_text(PAGEABLE,GCR410PAddDevice)
#pragma alloc_text(PAGEABLE,GCR410PCreateDevice)
#pragma alloc_text(PAGEABLE,GCR410PStartDevice)
#pragma alloc_text(PAGEABLE,GCR410PStopDevice)
#pragma alloc_text(PAGEABLE,GCR410PRemoveDevice)
#pragma alloc_text(PAGEABLE,GCR410PDriverUnload)
#if DBG
#pragma optimize ("",off)
#endif
//
// Constant section:
// - GCR410P_DRIVER_VERSION is the current version of the driver
// - MAX_DEVICES is the maximum number of devices (and instances) we want
// to support
//
#define GCR410P_DRIVER_VERSION 0x0112
//ISV
#define MAX_DEVICES 50
//
// Global variable section:
// - ulMaximalBaudRate defines the maximal baud rate for the reader.
// - bDevicePort is an array of boolean to signals if a device is
// already created.
//
ULONG
ulMaximalBaudRate;
BOOLEAN bDevicePort[MAX_DEVICES];
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.
--*/
{
NTSTATUS status;
RTL_QUERY_REGISTRY_TABLE paramTable[4];
UNICODE_STRING driverPath;
WCHAR buffer[MAXIMUM_FILENAME_LENGTH];
SmartcardDebug(
DEBUG_DRIVER,
("%s!DriverEntry: Enter - version: %X - %s %s\n",
SC_DRIVER_NAME,
GCR410P_DRIVER_VERSION,
__DATE__,
__TIME__)
);
//
// we do some stuff in this driver that
// assumes a single digit port number
//
// Initialize the Driver Object with driver's entry points
//
DriverObject->DriverUnload = GCR410PDriverUnload;
DriverObject->DriverExtension->AddDevice = GCR410PAddDevice;
DriverObject->MajorFunction[IRP_MJ_CREATE] = GCR410PCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = GCR410PCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = GCR410PCleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GCR410PDeviceControl;
DriverObject->MajorFunction[IRP_MJ_PNP] = GCR410PPnPDeviceControl;
DriverObject->MajorFunction[IRP_MJ_POWER] = GCR410PPowerDeviceControl;
//
// Read in the the driver registry path "MaximalBaudRate".
//
ulMaximalBaudRate = 0;
RtlZeroMemory(paramTable,sizeof(paramTable));
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"MaximalBaudRate";
paramTable[0].EntryContext = &ulMaximalBaudRate;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &ulMaximalBaudRate;
paramTable[0].DefaultLength = sizeof(ULONG);
driverPath.Buffer = buffer;
driverPath.MaximumLength = sizeof(buffer);
driverPath.Length = 0;
RtlCopyUnicodeString(&driverPath,RegistryPath);
status = RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
driverPath.Buffer,
&paramTable[0],
NULL,
NULL
);
if ((ulMaximalBaudRate != 9600lu) && (ulMaximalBaudRate != 19200lu) &&
(ulMaximalBaudRate != 38400lu)
) {
ulMaximalBaudRate = IFD_STANDARD_BAUD_RATE;
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!DriverEntry: Exit\n",
SC_DRIVER_NAME)
);
return STATUS_SUCCESS;
}
NTSTATUS
GCR410PAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This is the add-device routine.
Arguments:
DriverObject - points to the driver object representing the driver.
PhysicalDeviceObject - points to the PDO for the PnP device being added.
Return Value:
STATUS_SUCCESS - We could initialize at least one device.
STATUS_UNSUCCESSFUL - We cannot connect the device to PDO.
--*/
{
NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
PAGED_CODE();
ASSERT(DriverObject != NULL);
ASSERT(PhysicalDeviceObject != NULL);
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PAddDevice: Enter\n",
SC_DRIVER_NAME)
);
__try {
PDEVICE_EXTENSION deviceExtension;
//
// Try to create a device .
//
status = GCR410PCreateDevice(
DriverObject,
PhysicalDeviceObject,
&DeviceObject
);
if (status != STATUS_SUCCESS) {
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PAddDevice: GCR410PCreateDevice=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
__leave;
}
//
// Attach the PhysicalDeviceObject to the new created device.
//
deviceExtension = DeviceObject->DeviceExtension;
ATTACHED_DEVICE_OBJECT = IoAttachDeviceToDeviceStack(
DeviceObject,
PhysicalDeviceObject
);
if (ATTACHED_DEVICE_OBJECT == NULL) {
SmartcardLogError(
DriverObject,
GEM_CANT_CONNECT_TO_ASSIGNED_PORT,
NULL,
0
);
status = STATUS_UNSUCCESSFUL;
__leave;
}
//
// Register our new device object
//
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 (status != STATUS_SUCCESS) {
GCR410PRemoveDevice(DeviceObject);
}
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PAddDevice: Exit=%X(hex)\n",
SC_DRIVER_NAME,
status
)
);
return status;
}
NTSTATUS
GCR410PCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject,
OUT PDEVICE_OBJECT *DeviceObject
)
/*++
Routine Description:
This routine creates an object for the physical device specified and
sets up the deviceExtension.
Arguments:
DriverObject - points to the driver object representing the driver.
PhysicalDeviceObject - points to the PDO for the PnP device being added.
DeviceObject - the device created.
Return Value:
STATUS_SUCCESS - device created.
--*/
{
PDEVICE_EXTENSION deviceExtension;
NTSTATUS status = STATUS_SUCCESS;
ULONG nDeviceNumber;
PREADER_EXTENSION readerExtension;
PSMARTCARD_EXTENSION smartcardExtension;
PAGED_CODE();
ASSERT(DriverObject != NULL);
ASSERT(PhysicalDeviceObject != NULL);
*DeviceObject = NULL;
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCreateDevice: Enter\n",
SC_DRIVER_NAME)
);
for( nDeviceNumber = 0; nDeviceNumber < MAX_DEVICES; nDeviceNumber++ ) {
if (bDevicePort[nDeviceNumber] == FALSE) {
bDevicePort[nDeviceNumber] = TRUE;
break;
}
}
if (nDeviceNumber == MAX_DEVICES) {
status = STATUS_INSUFFICIENT_RESOURCES;
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PCreateDevice: Insufficient ressources\n",
SC_DRIVER_NAME)
);
SmartcardLogError(
DriverObject,
GEM_CANT_CREATE_MORE_DEVICES,
NULL,
0
);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Try to create a new device smart card object
//
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_SMARTCARD,
0,
TRUE,
DeviceObject
);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCreateDevice: IoCreateDevice status =%X(hex)\n",
SC_DRIVER_NAME,
status)
);
if (!NT_SUCCESS(status)) {
SmartcardLogError(
DriverObject,
GEM_CANT_CREATE_DEVICE,
NULL,
0
);
return status;
}
ASSERT(*DeviceObject != NULL);
//
// Now we have a pointer on the new device.
//
deviceExtension = (*DeviceObject)->DeviceExtension;
ASSERT(deviceExtension != NULL);
// This event signals Start/Stop notification
KeInitializeEvent(
&deviceExtension->ReaderStarted,
NotificationEvent,
FALSE
);
// This event signals that the serial driver has been closed
KeInitializeEvent(
&deviceExtension->SerialCloseDone,
NotificationEvent,
TRUE
);
// This event signals that the card status thread is scheduled
KeInitializeEvent(
&deviceExtension->CardStatusNotInUse,
NotificationEvent,
TRUE
);
// Used to keep track of open close calls
deviceExtension->ReaderOpen = FALSE;
// SpinLock used to manage the card state
KeInitializeSpinLock(&deviceExtension->SpinLock);
// Thread that waits the device removal
deviceExtension->CloseSerial = IoAllocateWorkItem(
*DeviceObject
);
// Thread which will be called when a card state change will occur.
deviceExtension->CardStateChange = IoAllocateWorkItem(
*DeviceObject
);
// We try to allocate memory for the ReaderExtension struct.
smartcardExtension = &deviceExtension->SmartcardExtension;
smartcardExtension->ReaderExtension = ExAllocatePool(
NonPagedPool,
sizeof(READER_EXTENSION)
);
if (deviceExtension->SmartcardExtension.ReaderExtension == NULL ||
deviceExtension->CardStateChange == NULL ||
deviceExtension->CloseSerial == NULL) {
SmartcardLogError(
DriverObject,
GEM_NO_MEMORY,
NULL,
0
);
return STATUS_INSUFFICIENT_RESOURCES;
}
readerExtension = smartcardExtension->ReaderExtension;
RtlZeroMemory(
readerExtension,
sizeof(READER_EXTENSION)
);
// Write the version of the lib we use to the smartcard extension
smartcardExtension->Version = SMCLIB_VERSION;
// Now let the lib allocate the buffer for data transmission
smartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE;
smartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
status = SmartcardInitialize(smartcardExtension);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCreateDevice: SmartcardInitialize=%lX(hex)\n",
SC_DRIVER_NAME,
status)
);
if (status != STATUS_SUCCESS) {
SmartcardLogError(
DriverObject,
(smartcardExtension->OsData ? GEM_WRONG_LIB_VERSION : GEM_NO_MEMORY),
NULL,
0
);
return status;
}
smartcardExtension->VendorAttr.UnitNo = nDeviceNumber;
// Save the deviceObject
deviceExtension->SmartcardExtension.OsData->DeviceObject = *DeviceObject;
//
// Set up the call back functions
// (nota: RDF_CARD_EJECT and RDF_READER_SWALLOW are not supported)
//
smartcardExtension->ReaderFunction[RDF_TRANSMIT] = GDDK_0ATransmit;
smartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = GDDK_0ASetProtocol;
smartcardExtension->ReaderFunction[RDF_CARD_POWER] = GDDK_0AReaderPower;
smartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = GDDK_0ACardTracking;
smartcardExtension->ReaderFunction[RDF_IOCTL_VENDOR] = GDDK_0AVendorIoctl;
// save the current power state of the reader
smartcardExtension->ReaderExtension->ReaderPowerState =
PowerReaderWorking;
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCreateDevice: Exit=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
return status;
}
NTSTATUS
GCR410PStartDevice(
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;
PAGED_CODE();
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PStartDevice: Enter\n",
SC_DRIVER_NAME)
);
irp = IoAllocateIrp(
(CCHAR) (DeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp == NULL) {
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PStartDevice: IoAllocateIrp failed!\n",
SC_DRIVER_NAME)
);
SmartcardLogError(
DeviceObject,
GEM_NO_MEMORY,
NULL,
0
);
return STATUS_NO_MEMORY;
}
__try {
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 = GCR410PCallSerialDriver(
ATTACHED_DEVICE_OBJECT,
irp
);
if (status != STATUS_SUCCESS) {
if (status == STATUS_SHARED_IRQ_BUSY) {
SmartcardLogError(
DeviceObject,
GEM_SHARED_IRQ_BUSY,
NULL,
0
);
}
__leave;
}
// The serial port is openned
KeClearEvent(&deviceExtension->SerialCloseDone);
// Open the communication with the reader
status = GDDK_0AOpenChannel(
&deviceExtension->SmartcardExtension,
deviceExtension->SmartcardExtension.VendorAttr.UnitNo, //Device number
deviceExtension->SmartcardExtension.VendorAttr.UnitNo,
ulMaximalBaudRate
);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PStartDevice: GDDK_0AOpenChannel=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
if (status != STATUS_SUCCESS) {
if (status == STATUS_BAD_DEVICE_TYPE) {
SmartcardLogError(
DeviceObject,
GEM_IFD_BAD_VERSION,
NULL,
0
);
} else {
SmartcardLogError(
DeviceObject,
GEM_IFD_COMMUNICATION_ERROR,
NULL,
0
);
}
__leave;
}
// Start the serial event tracking (DSR and RING events)
status = GCR410PStartSerialEventTracking(&deviceExtension->SmartcardExtension);
if (status != STATUS_SUCCESS) {
SmartcardLogError(
DeviceObject,
GEM_START_SERIAL_EVENT_TRACKING_FAILED,
NULL,
0
);
__leave;
}
// Set the Device Interface state to True
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
if (status != STATUS_SUCCESS) {
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PStartDevice: IoSetDeviceInterfaceState failed!\n",
SC_DRIVER_NAME)
);
SmartcardLogError(
DeviceObject,
GEM_SET_DEVICE_INTERFACE_STATE_FAILED,
NULL,
0
);
__leave;
}
// Set the ReaderStarted event.
KeSetEvent(&deviceExtension->ReaderStarted,0,FALSE);
}
__finally {
if (status != STATUS_SUCCESS) {
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PStartDevice: failed!\n",
SC_DRIVER_NAME)
);
// If we have failed, then we call the StopDevice to undo all we have done here.
GCR410PStopDevice(DeviceObject);
}
IoFreeIrp(irp);
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PStartDevice: Exit=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
return status;
}
VOID
GCR410PStopDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Finishes card tracking requests and closes the connection to the
serial driver.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PSMARTCARD_EXTENSION smartcardExtension;
LARGE_INTEGER timeout;
PAGED_CODE();
ASSERT(DeviceObject != NULL);
deviceExtension = DeviceObject->DeviceExtension;
smartcardExtension = &deviceExtension->SmartcardExtension;
ASSERT(smartcardExtension != NULL);
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PStopDevice: Enter\n",
SC_DRIVER_NAME)
);
// If the serial port is not yet closed.
if (KeReadStateEvent(&deviceExtension->SerialCloseDone) == 0l) {
NTSTATUS status;
// Close the communication with the reader
status = GDDK_0ACloseChannel(
smartcardExtension
);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PStopDevice: GDDK_0ACloseChannel=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
// test if we ever started event tracking
if (smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0) {
// We 'only' need to close the serial port
GCR410PCloseSerialPort(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
// This will also create an event which will close the serial port.
status = GCR410PStopSerialEventTracking(smartcardExtension);
// now wait until the connection to serial is closed
// timeout.QuadPart = -((LONGLONG) HOR3COMM_CHAR_TIME*10*1000);
status = KeWaitForSingleObject(
&deviceExtension->SerialCloseDone,
Executive,
KernelMode,
FALSE,
NULL
);
//ASSERT(status == STATUS_SUCCESS);
if(status != STATUS_SUCCESS)
{
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PStopDevice: Failed waiting to close...\n",
SC_DRIVER_NAME)
);
}
}
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PStopDevice: Exit\n",
SC_DRIVER_NAME)
);
}
NTSTATUS
GCR410PDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This is our IOCTL dispatch function
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
KIRQL irql;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
LARGE_INTEGER timeout;
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PDeviceControl: Enter\n",
SC_DRIVER_NAME)
);
if (smartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0)
{
SmartcardDebug(
DEBUG_ERROR,
( "%s!GCR410PDeviceControl: Exit (Reader was removed or power down!)\n",
SC_DRIVER_NAME)
);
// the wait mask is set to 0 whenever the device was either
// surprise-removed or politely removed
status = STATUS_DEVICE_REMOVED;
return status;
}
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if (deviceExtension->IoCount == 0) {
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
//timeout.QuadPart = -((LONGLONG) HOR3COMM_CHAR_TIME*10*1000);
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;
}
status = SmartcardDeviceControl(
&(deviceExtension->SmartcardExtension),
Irp
);
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'tcoI');
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
deviceExtension->IoCount--;
ASSERT(deviceExtension->IoCount >= 0);
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PDeviceControl: Exit=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
return status;
}
VOID
GCR410PCloseSerialPort(
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
Arguments:
DeviceObject - is a pointer to the device.
Return Value:
Nothing
--*/
{
PDEVICE_EXTENSION deviceExtension;
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION irpStack;
IO_STATUS_BLOCK ioStatusBlock;
LARGE_INTEGER timeout;
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCloseSerialPort: Enter\n",
SC_DRIVER_NAME)
);
ASSERT(DeviceObject!=NULL);
deviceExtension = DeviceObject->DeviceExtension;
ASSERT(KeReadStateEvent(&deviceExtension->SerialCloseDone) == 0l);
//
// 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
);
// wait until the card status thread is running
//timeout.QuadPart = -((LONGLONG) HOR3COMM_CHAR_TIME*10*1000);
status = KeWaitForSingleObject(
&deviceExtension->CardStatusNotInUse,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(status == STATUS_SUCCESS);
if(status != STATUS_SUCCESS)
{
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCloseSerialPort: Failed waiting to stop thread...\n",
SC_DRIVER_NAME)
);
}
irp = IoAllocateIrp(
(CCHAR) (DeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp) {
IoSetNextIrpStackLocation(irp);
// We send down a close to the serial driver. This close goes
// througt 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 = GCR410PCallSerialDriver(
deviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject,
irp
);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCloseSerialPort: Send IRP_MJ_CLOSE to the serial device=%lX\n",
SC_DRIVER_NAME,
status)
);
IoFreeIrp(irp);
}
// Informs that the serial port is closed
KeSetEvent(
&deviceExtension->SerialCloseDone,
0,
FALSE
);
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCloseSerialPort: Exit\n",
SC_DRIVER_NAME)
);
}
VOID
GCR410PWaitForCardStateChange(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
This function send a command to the reader to known the card status. This
function runs as a system thread at IRQL == PASSIVE_LEVEL. It waits for
the CardStateChange event that it set by the IoCompletionRoutine.
Arguments:
DeviceObject - is a pointer to the device.
Return Value:
Nothing
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PWaitForCardStateChange: Enter\n",
SC_DRIVER_NAME)
);
smartcardExtension = &deviceExtension->SmartcardExtension;
ASSERT(smartcardExtension != NULL);
//ISV
// If device is not ready - do not run exchange!
if(smartcardExtension->ReaderExtension->ReaderPowerState != PowerReaderWorking)
{
SmartcardDebug(
DEBUG_ERROR,
( "%s!GCR410PWaitForCardStateChange: Exit (reader is not ready yet!)\n",
SC_DRIVER_NAME)
);
// Informs that the card status update is done
KeSetEvent(
&deviceExtension->CardStatusNotInUse,
0,
FALSE
);
return;
}
// Protect the exchange
GDDK_0ALockExchange(smartcardExtension);
if (GDDK_0AUpdateCardStatus(smartcardExtension) == STATUS_SUCCESS) {
// Complete the card tracking except if we are in a
// power up - power down cycle (Power Management)
if (smartcardExtension->ReaderExtension->PowerRequest == FALSE) {
GCR410PCompleteCardTracking(smartcardExtension);
}
}
GDDK_0AUnlockExchange(smartcardExtension);
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PWaitForCardStateChange: Exit\n",
SC_DRIVER_NAME)
);
// Informs that the card status update is done
KeSetEvent(
&deviceExtension->CardStatusNotInUse,
0,
FALSE
);
}
NTSTATUS
GCR410PIoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
Completion routine for an Irp sent to the serial driver.
It sets only an event that we can use to wait for.
--*/
{
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
GCR410PCallSerialDriver(
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,
GCR410PIoCompletion,
&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) {
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
status = Irp->IoStatus.Status;
}
return status;
}
NTSTATUS
GCR410PPnPDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
--*/
{
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;
BOOLEAN deviceRemoved = FALSE, irpSkipped = FALSE;
KIRQL irql;
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PPnPDeviceControl: Enter\n",
SC_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;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
// Now look what the PnP manager wants...
switch(irpStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_START_DEVICE\n",
SC_DRIVER_NAME)
);
// We have to call the underlying driver first
status = GCR410PCallSerialDriver(AttachedDeviceObject, Irp);
ASSERT(NT_SUCCESS(status));
if (NT_SUCCESS(status)) {
status = GCR410PStartDevice(DeviceObject);
}
break;
case IRP_MN_QUERY_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_QUERY_STOP_DEVICE\n",
SC_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 = GCR410PCallSerialDriver(AttachedDeviceObject, Irp);
}
break;
case IRP_MN_CANCEL_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_CANCEL_STOP_DEVICE\n",
SC_DRIVER_NAME)
);
status = GCR410PCallSerialDriver(
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!GCR410PPnPDeviceControl: IRP_MN_STOP_DEVICE\n",
SC_DRIVER_NAME)
);
GCR410PStopDevice(DeviceObject);
//
// we don't do anything since a stop is only used
// to reconfigure hw-resources like interrupts and io-ports
//
status = GCR410PCallSerialDriver(
AttachedDeviceObject,
Irp
);
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_QUERY_REMOVE_DEVICE\n",
SC_DRIVER_NAME)
);
// disable the reader
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
FALSE
);
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status)) {
break;
}
// 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 = GCR410PCallSerialDriver(AttachedDeviceObject, Irp);
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_CANCEL_REMOVE_DEVICE\n",
SC_DRIVER_NAME)
);
status = GCR410PCallSerialDriver(
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 &&
readerExtension->SerialConfigData.SerialWaitMask != 0)
{
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
ASSERT(status == STATUS_SUCCESS);
}
break;
case IRP_MN_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PPnPDeviceControl: IRP_MN_REMOVE_DEVICE\n",
SC_DRIVER_NAME)
);
GCR410PRemoveDevice(DeviceObject);
status = GCR410PCallSerialDriver(
AttachedDeviceObject,
Irp
);
deviceRemoved = TRUE;
break;
default:
// This is an Irp that is only useful for underlying drivers
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PnPDeviceControl: IRP_MN_...%lx\n",
SC_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_DRIVER,
( "%s!GCR410PPnPDeviceControl: Exit=%X(hex)\n",
SC_DRIVER_NAME,
status)
);
return status;
}
VOID
GCR410PSystemPowerCompletion(
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
UNREFERENCED_PARAMETER (MinorFunction);
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PSystemPowerCompletion: Enter\n",
SC_DRIVER_NAME)
);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = IoStatus->Status;
SmartcardReleaseRemoveLockWithTag(smartcardExtension, 'rwoP');
if (PowerState.SystemState == PowerSystemWorking) {
PoSetPowerState (
DeviceObject,
SystemPowerState,
PowerState
);
}
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PSystemPowerCompletion: Exit\n",
SC_DRIVER_NAME)
);
}
NTSTATUS
GCR410PDevicePowerCompletion (
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);
NTSTATUS status;
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PDevicePowerCompletion: Enter\n",
SC_DRIVER_NAME)
);
//ISV
// We've got power up request, so...
// Report everybody that reader is powered up again!
SmartcardExtension->ReaderExtension->ReaderPowerState =
PowerReaderWorking;
// Restore the communication with the reader
status = GDDK_0ARestoreCommunication(SmartcardExtension);
//
// We issue a power request in order to figure out
// what the actual card status is
//
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
GDDK_0AReaderPower(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) {
GCR410PCompleteCardTracking(SmartcardExtension);
}
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);
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PDevicePowerCompletion: Exit\n",
SC_DRIVER_NAME)
);
return STATUS_SUCCESS;
}
NTSTATUS
GCR410PPowerDeviceControl(
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;
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PPowerDeviceControl: Enter\n",
SC_DRIVER_NAME)
);
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PPowerDeviceControl: Irp = %lx\n",
SC_DRIVER_NAME,
Irp)
);
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_ERROR,
("%s!GCR410PPowerDeviceControl: PowerDevice D0\n",
SC_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,
GCR410PDevicePowerCompletion,
smartcardExtension,
TRUE,
TRUE,
TRUE
);
action = WaitForCompletion;
break;
case PowerDeviceD3:
// Turn off the reader
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PPowerDeviceControl: PowerDevice D3\n",
SC_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 = GDDK_0AReaderPower(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;
}
} else {
ASSERT(FALSE);
action = SkipRequest;
}
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_ERROR,
("%s!GCR410PPowerDeviceControl: Query Power\n",
SC_DRIVER_NAME)
);
switch (irpStack->Parameters.Power.State.SystemState) {
case PowerSystemMaximum:
case PowerSystemWorking:
case PowerSystemSleeping1:
case PowerSystemSleeping2:
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if (deviceExtension->IoCount == 0) {
// Block any further ioctls
KeClearEvent(&deviceExtension->ReaderStarted);
} else {
// can't go to sleep mode since the reader is busy.
status = STATUS_DEVICE_BUSY;
}
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
break;
}
action = CompleteRequest;
break;
case IRP_MN_SET_POWER:
SmartcardDebug(
DEBUG_ERROR,
("%s!GCR410PPowerDeviceControl: PowerSystem S%d\n",
SC_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 = CompleteRequest;
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 = 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;
}
}
}
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,
GCR410PSystemPowerCompletion,
Irp,
NULL
);
ASSERT(status == STATUS_PENDING);
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!GCR410PPowerDeviceControl: Exit %lx\n",
SC_DRIVER_NAME,
status)
);
return status;
}
NTSTATUS
GCR410PCreateClose(
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;
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCreateClose: Enter\n",
SC_DRIVER_NAME)
);
__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!GCR410PCreateClose: 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);
}
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCreateClose: Exit\n",
SC_DRIVER_NAME)
);
return status;
}
NTSTATUS
GCR410PCancel(
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 - is a pointer to the device.
Irp - holds the Irp involved.
Return Value:
STATUS_CANCELLED - the Irp is cancelled.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCancel: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp == smartcardExtension->OsData->NotificationIrp);
IoReleaseCancelSpinLock(
Irp->CancelIrql
);
GCR410PCompleteCardTracking(smartcardExtension);
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCancel: Exit\n",
DRIVER_NAME)
);
return STATUS_CANCELLED;
}
NTSTATUS
GCR410PCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system when the device is closed.
Arguments:
DeviceObject - is a pointer to the device.
Irp - holds the Irp involved.
Return Value:
STATUS_SUCCESS - the request is completed
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
NTSTATUS status = STATUS_SUCCESS;
SmartcardDebug(
DEBUG_TRACE,
("%s!GCR410PCleanup: Enter\n",
DRIVER_NAME)
);
ASSERT(Irp != smartcardExtension->OsData->NotificationIrp);
// We need to complete the notification irp
GCR410PCompleteCardTracking(smartcardExtension);
SmartcardDebug(
DEBUG_DRIVER,
("%s!GCR410PCleanup: 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!GCR410PCleanup: Exit\n",
DRIVER_NAME)
);
return STATUS_SUCCESS;
}
VOID
GCR410PRemoveDevice(
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Remove the device from the system.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PSMARTCARD_EXTENSION smartcardExtension;
PAGED_CODE();
SmartcardDebug(
DEBUG_DRIVER,
( "%s!GCR410PRemoveDevice: Enter\n",
SC_DRIVER_NAME)
);
ASSERT(DeviceObject != NULL);
if (DeviceObject == NULL) {
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PRemoveDevice: Exit immediatly (no device to remove)\n",
SC_DRIVER_NAME)
);
return;
}
deviceExtension = DeviceObject->DeviceExtension;
smartcardExtension = &deviceExtension->SmartcardExtension;
if (smartcardExtension->OsData) {
// complete pending card tracking requests (if any)
GCR410PCompleteCardTracking(smartcardExtension);
ASSERT(smartcardExtension->OsData->NotificationIrp == NULL);
// Wait until we can safely unload the device
SmartcardReleaseRemoveLockAndWait(smartcardExtension);
}
ASSERT(smartcardExtension->VendorAttr.UnitNo < MAX_DEVICES);
ASSERT(bDevicePort[smartcardExtension->VendorAttr.UnitNo] == TRUE);
// Mark this slot as available
//bDevicePort[smartcardExtension->VendorAttr.UnitNo] = FALSE;
GCR410PStopDevice(DeviceObject);
if (deviceExtension->SmartcardExtension.ReaderExtension &&
deviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject) {
IoDetachDevice(
deviceExtension->SmartcardExtension.ReaderExtension->AttachedDeviceObject
);
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PRemoveDevice: IoDetachDevice\n",
SC_DRIVER_NAME)
);
}
if(deviceExtension->PnPDeviceName.Buffer) {
RtlFreeUnicodeString(&deviceExtension->PnPDeviceName);
}
if(smartcardExtension->OsData) {
SmartcardExit(smartcardExtension);
}
if (smartcardExtension->ReaderExtension) {
ExFreePool(smartcardExtension->ReaderExtension);
}
if (deviceExtension->CloseSerial) {
IoFreeWorkItem(deviceExtension->CloseSerial);
}
if (deviceExtension->CardStateChange) {
IoFreeWorkItem(deviceExtension->CardStateChange);
}
IoDeleteDevice(DeviceObject);
DeviceObject=NULL;
SmartcardDebug(
DEBUG_INFO,
( "%s!GCR410PRemoveDevice: Exit\n",
SC_DRIVER_NAME)
);
}
VOID
GCR410PDriverUnload(
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 - is a pointer to the driver object for this device.
Return Value:
Nothing
--*/
{
PAGED_CODE();
SmartcardDebug(
DEBUG_INFO,
("%s!GCR410PDriverUnload\n",
SC_DRIVER_NAME)
);
}
NTSTATUS
GCR410PStartSerialEventTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine initialized serial event tracking. It calls the serial driver to
set a wait mask for RING and DSR tracking. After that it installs a completion
routine to be called when RING or DSR is signaled.
Arguments:
SmartcardExtension - is a pointer on the SmartCardExtension structure of
the current device.
Return Value:
Nothing
--*/
{
NTSTATUS status;
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PStartSerialEventTracking: Enter\n",
SC_DRIVER_NAME)
);
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
readerExtension->SerialConfigData.SerialWaitMask =
SERIAL_EV_DSR | SERIAL_EV_RING;
// Send a wait mask to the serial driver. This call only sets the
// wait mask. We want to be informed when the RING or DSR chnages 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) {
KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL
);
//ASSERT (STATUS_SUCCESS == status);
status = ioStatus.Status;
}
if (status == STATUS_SUCCESS) {
PIO_STACK_LOCATION irpSp;
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;
// We simulate a callback now that triggers the card supervision
SmartcardExtension->ReaderExtension->GetModemStatus = FALSE;
GCR410PSerialEvent(
SmartcardExtension->OsData->DeviceObject,
readerExtension->SerialStatusIrp,
SmartcardExtension
);
status = STATUS_SUCCESS;
}
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PStartSerialEventTracking: Exit\n",
SC_DRIVER_NAME)
);
return status;
}
NTSTATUS
GCR410PStopSerialEventTracking(
PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine disabled the serial event tracking. It calls the serial driver to
set a wait mask to 0.
Arguments:
SmartcardExtension - is a pointer on the SmartCardExtension structure of
the current device.
Return Value:
Nothing
--*/
{
NTSTATUS status;
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PIRP irp;
PAGED_CODE();
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PStopSerialEventTracking: Enter\n",
SC_DRIVER_NAME)
);
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
readerExtension->SerialConfigData.SerialWaitMask = 0;
irp = IoBuildDeviceIoControlRequest(
IOCTL_SERIAL_SET_WAIT_MASK,
readerExtension->AttachedDeviceObject,
&readerExtension->SerialConfigData.SerialWaitMask,
sizeof(readerExtension->SerialConfigData.SerialWaitMask),
NULL,
0,
FALSE,
&event,
&ioStatus
);
if (irp == NULL) {
SmartcardDebug(
DEBUG_ERROR,
( "%s!GCR410PStopSerialEventTracking: Insufficient resources\n",
SC_DRIVER_NAME)
);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(
readerExtension->AttachedDeviceObject,
irp
);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL
);
//ASSERT (STATUS_SUCCESS == status);
status = ioStatus.Status;
}
SmartcardDebug(
DEBUG_INFO,
( "%s!GCR410PStopSerialEventTracking: Exit\n",
SC_DRIVER_NAME)
);
return status;
}
VOID
GCR410PCompleteCardTracking(
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!GCR410PCompleteCardTracking: 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
GCR410PSerialEvent(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine is called in two cases:
a) RING signaled (card inserted or removed)
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
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
is started again.
Arguments:
Device Object - is a pointer on the current device object
Irp - is a pointer on the current irp.
SmartcardExtension - is a pointer on the SmartCardExtension structure of
the current device.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED - we need more process to complet the request.
STATUS_SUCCESS - completed
--*/
{
NTSTATUS status;
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PSerialEvent: Enter\n",
SC_DRIVER_NAME)
);
if (SmartcardExtension->ReaderExtension->GetModemStatus) {
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PSerialEvent: Modem status=%X(hex)\n",
SC_DRIVER_NAME,
SmartcardExtension->ReaderExtension->ModemStatus)
);
if ((SmartcardExtension->ReaderExtension->ModemStatus & SERIAL_DSR_STATE) == 0) {
PDEVICE_EXTENSION deviceExtension;
SmartcardDebug(
DEBUG_INFO,
( "%s!GCR410PSerialEvent: Reader removed\n",
SC_DRIVER_NAME)
);
deviceExtension = SmartcardExtension->OsData->DeviceObject->DeviceExtension;
// 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;
//ISV
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
} else {
PDEVICE_EXTENSION deviceExtension=
SmartcardExtension->OsData->DeviceObject->DeviceExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!GCR410PSerialEvent: cardStateChange\n",
SC_DRIVER_NAME)
);
ASSERT(KeReadStateEvent(&deviceExtension->SerialCloseDone) == 0l);
if (KeReadStateEvent(&deviceExtension->CardStatusNotInUse) != 0L) {
// Informs that the card status update is running
KeClearEvent(&deviceExtension->CardStatusNotInUse);
// Schedule the card status thread
IoQueueWorkItem(
deviceExtension->CardStateChange,
(PIO_WORKITEM_ROUTINE) GCR410PWaitForCardStateChange,
CriticalWorkQueue,
NULL
);
}
}
}
// If the reader is disconnected
if (SmartcardExtension->ReaderExtension->SerialConfigData.SerialWaitMask == 0) {
PDEVICE_EXTENSION deviceExtension=
SmartcardExtension->OsData->DeviceObject->DeviceExtension;
ASSERT(KeReadStateEvent(&deviceExtension->SerialCloseDone) == 0l);
//Schedule our remove thread
IoQueueWorkItem(
deviceExtension->CloseSerial,
(PIO_WORKITEM_ROUTINE) GCR410PCloseSerialPort,
CriticalWorkQueue,
NULL
);
SmartcardDebug(
DEBUG_INFO,
( "%s!GCR410PSerialEvent: Exit (release IRP)\n",
SC_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.
//
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
);
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,
GCR410PSerialEvent,
SmartcardExtension,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(
SmartcardExtension->ReaderExtension->AttachedDeviceObject,
SmartcardExtension->ReaderExtension->SerialStatusIrp
);
return STATUS_MORE_PROCESSING_REQUIRED;
}