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

1954 lines
48 KiB
C

/*++
Copyright (C) Microsoft Corporation and Litronic, 1998 - 1999
Module Name:
L220pnp.c
Abstract:
This module contains the functions for the PnP and Power management.
Environment:
Kernel mode only.
Notes:
Revision History:
- Created Febuary 1998 by Brian Manahan for use with
the 220 reader.
--*/
#include <ntddk.h>
#include "L220SCR.h"
#include <stdio.h>
// Next statement so memory for DriverEntry is released when finished
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text(PAGEABLE, Lit220RemoveDevice)
#pragma alloc_text(PAGEABLE, Lit220StopDevice)
#pragma alloc_text(PAGEABLE, Lit220StartDevice)
#include <initguid.h>
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.
--*/
{
SmartcardDebug(
DEBUG_DRIVER,
("%s!DriverEntry: Enter - %s %s\n",
DRIVER_NAME,
__DATE__,
__TIME__)
);
//
// Initialize the Driver Object with driver's entry points
//
DriverObject->DriverUnload = Lit220Unload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = Lit220CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = Lit220CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = Lit220Cleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Lit220DeviceControl;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Lit220SystemControl;
//
// Init PNP entries
//
DriverObject->DriverExtension->AddDevice = Lit220AddDevice;
// Power functionality temporarily removed
DriverObject->MajorFunction[IRP_MJ_PNP] = Lit220PnP;
// Power
// Power functionality temporarily removed
DriverObject->MajorFunction[IRP_MJ_POWER] = Lit220DispatchPower;
// Always return STATUS_SUCCESS
return STATUS_SUCCESS;
}
NTSTATUS
Lit220AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine is called by the Operating System to create a new
instance of a Litronic 220 Smartcard Reader. Still can't touch hardware
at this point, or submit requests to the serial driver. But at
least at this point we get a handle to the serial bus driver, which
we'll use in submitting requests in the future.
Arguments:
DriverObject - Pointer to our driver object
PhysicalDeviceObject - Pointer to Device Object created by parent
Return Value:
Status is returned.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
PSMARTCARD_EXTENSION SmartcardExtension = NULL;
PDEVICE_EXTENSION deviceExtension = NULL;
PREADER_EXTENSION ReaderExtension = NULL;
static BYTE devUnitNo = 0;
BOOLEAN smclibInitialized = FALSE;
BOOLEAN symbolicLinkCreated = FALSE;
BOOLEAN deviceInterfaceStateSet = FALSE;
KIRQL oldIrql;
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220AddDevice: enter\n",
DRIVER_NAME)
);
try {
//
// Create our device object with a our own specific device
// extension.
//
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_SMARTCARD,
0,
TRUE,
&DeviceObject
);
if (!NT_SUCCESS(status)) {
if (status == STATUS_INSUFFICIENT_RESOURCES) {
SmartcardLogError(
DriverObject,
LIT220_INSUFFICIENT_RESOURCES,
NULL,
0
);
} else {
SmartcardLogError(
DriverObject,
LIT220_NAME_CONFLICT,
NULL,
0
);
}
leave;
}
//
// Allocate data struct space for smart card reader
//
SmartcardExtension = DeviceObject->DeviceExtension;
deviceExtension = DeviceObject->DeviceExtension;
SmartcardExtension->ReaderExtension = ExAllocatePool(
NonPagedPool,
sizeof(READER_EXTENSION)
);
if (SmartcardExtension->ReaderExtension == NULL) {
SmartcardLogError(
DriverObject,
LIT220_INSUFFICIENT_RESOURCES,
NULL,
0
);
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
ReaderExtension = SmartcardExtension->ReaderExtension;
// Zero the contents of the ReaderExtension
RtlZeroMemory(
SmartcardExtension->ReaderExtension,
sizeof(READER_EXTENSION)
);
//
// Attach ourself into the driver stack on top of our parent (serial).
//
ReaderExtension->BusDeviceObject = IoAttachDeviceToDeviceStack(
DeviceObject,
PhysicalDeviceObject
);
if (!ReaderExtension->BusDeviceObject) {
status = STATUS_NO_SUCH_DEVICE;
SmartcardLogError(
DriverObject,
LIT220_SERIAL_CONNECTION_FAILURE,
NULL,
0
);
leave;
}
// Set flag so if something fails we know to disable the interface
deviceInterfaceStateSet = TRUE;
//
// Initialize Smartcard Library
//
//
// Write the version of the lib we use to the smartcard extension
//
SmartcardExtension->Version = SMCLIB_VERSION;
SmartcardExtension->SmartcardReply.BufferSize =
MIN_BUFFER_SIZE;
SmartcardExtension->SmartcardRequest.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,
LIT220_SMARTCARD_LIB_ERROR,
NULL,
0
);
leave;
}
// Set flag so if something fails we know to exit out of the
// smartcard library
smclibInitialized = TRUE;
status = IoInitializeTimer(
DeviceObject,
Lit220ReceiveBlockTimeout,
NULL
);
if (status != STATUS_SUCCESS) {
SmartcardLogError(
DriverObject,
LIT220_INSUFFICIENT_RESOURCES,
NULL,
0
);
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
deviceExtension->WorkItem = NULL;
deviceExtension->WorkItem = IoAllocateWorkItem(
DeviceObject
);
if (deviceExtension->WorkItem == NULL) {
SmartcardLogError(
DriverObject,
LIT220_INSUFFICIENT_RESOURCES,
NULL,
0
);
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
// register our new device
status = IoRegisterDeviceInterface(
PhysicalDeviceObject,
&SmartCardReaderGuid,
NULL,
&deviceExtension->PnPDeviceName
);
ASSERT(status == STATUS_SUCCESS);
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220AddDevice: DevName - %ws\n",
DRIVER_NAME, deviceExtension->PnPDeviceName.Buffer)
);
//
// Initialize some events
//
KeInitializeEvent(&ReaderExtension->AckEvnt,
NotificationEvent,
FALSE);
KeInitializeEvent(&ReaderExtension->DataEvnt,
NotificationEvent,
FALSE);
KeInitializeEvent(
&deviceExtension->SerialCloseDone,
NotificationEvent,
TRUE
);
// Used for stop / start notification
KeInitializeEvent(
&deviceExtension->ReaderStarted,
NotificationEvent,
FALSE
);
// Used to keep track of open close calls
deviceExtension->ReaderOpen = FALSE;
} finally {
if (status != STATUS_SUCCESS) {
Lit220RemoveDevice(DeviceObject);
}
}
if (status != STATUS_SUCCESS) {
return (status);
}
//
// Set up call back functions for smartcard library
//
SmartcardExtension->ReaderFunction[RDF_TRANSMIT] =
Lit220IoRequest;
SmartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] =
Lit220SetProtocol;
SmartcardExtension->ReaderFunction[RDF_CARD_POWER] =
Lit220Power;
SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] =
Lit220CardTracking;
//
// Save deviceObject
//
KeAcquireSpinLock(
&SmartcardExtension->OsData->SpinLock,
&oldIrql
);
SmartcardExtension->OsData->DeviceObject =
DeviceObject;
//
// Set the Current and Notification IRPs to NULL
//
SmartcardExtension->OsData->CurrentIrp = NULL;
SmartcardExtension->OsData->NotificationIrp = NULL;
KeReleaseSpinLock(
&SmartcardExtension->OsData->SpinLock,
oldIrql
);
//
// Save the deviceObject for the connected serial port
//
SmartcardExtension->ReaderExtension->ConnectedSerialPort =
PhysicalDeviceObject;
//
// Set the vendor info
//
strcpy(
SmartcardExtension->VendorAttr.VendorName.Buffer,
LIT220_VENDOR_NAME);
SmartcardExtension->VendorAttr.VendorName.Length =
(USHORT) strlen(SmartcardExtension->VendorAttr.VendorName.Buffer);
SmartcardExtension->VendorAttr.UnitNo = devUnitNo++;
strcpy(
SmartcardExtension->VendorAttr.IfdType.Buffer,
LIT220_PRODUCT_NAME);
SmartcardExtension->VendorAttr.IfdType.Length =
(USHORT) strlen(SmartcardExtension->VendorAttr.IfdType.Buffer);
//
// Clk frequency in KHz encoded as little endian integer
//
SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = 3571;
SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = 3571;
SmartcardExtension->ReaderCapabilities.DataRate.Default = 9600;
SmartcardExtension->ReaderCapabilities.DataRate.Max = 115200;
SmartcardExtension->ReaderCapabilities.MaxIFSD = MAX_IFSD;
SmartcardExtension->ReaderCapabilities.SupportedProtocols =
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
// Save a copy to the PhysicalDeviceObject
ReaderExtension->PhysicalDeviceObject = PhysicalDeviceObject;
// Set initial state for SerialEventState
SmartcardExtension->ReaderExtension->SerialEventState = 0;
// Device is connected
SmartcardExtension->ReaderExtension->DeviceRemoved = FALSE;
// Assume reader is attached until we ask the serial driver
SmartcardExtension->ReaderExtension->ModemStatus = SERIAL_DSR_STATE;
// Set initial power state
deviceExtension->PowerState = PowerDeviceD0;
// save the current power state of the reader
SmartcardExtension->ReaderExtension->ReaderPowerState =
PowerReaderWorking;
// Clear the DO_DEVICE_INITIALIZING bit
DeviceObject->Flags |= DO_BUFFERED_IO;
DeviceObject->Flags |= DO_POWER_PAGABLE;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
}
VOID
Lit220CloseSerialPort(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
This routine closes the connection to the serial driver when the
reader has been removed (unplugged). This routine runs as a system
thread at IRQL == PASSIVE_LEVEL.
It waits for a DeviceClose event sent by another part of the driver to
indicate that the serial connection should be close.
If the notification IRP is still pending we complete it.
Once the connection is closed it will signal the SerialCloseDone event so the
PnP Remove IRP knows when it is safe to unload the device.
*/
{
PSMARTCARD_EXTENSION SmartcardExtension = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION irpStack;
IO_STATUS_BLOCK ioStatusBlock;
KIRQL oldIrql;
//
// first mark this device as 'gone'.
// This will prevent that someone can re-open the device
//
// We intentionally ignore error here for the case we are disabling an interface
// that is already disabled.
//
IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
FALSE
);
// Mark the device as removed so no more IRPs will be sent to the
// serial port
ReaderExtension->DeviceRemoved = TRUE;
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220CloseSerialPort: Got Close signal. Checking if we can remove device now.\n",
DRIVER_NAME)
);
//
// Cancel the Notification IRP if it is around
//
Lit220CompleteCardTracking(SmartcardExtension);
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220CloseSerialPort: Sending IRP_MJ_CLOSE\n",
DRIVER_NAME)
);
//
// Create an IRP for closing the serial driver
//
irp = IoAllocateIrp(
(CCHAR)(DeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp) {
//
// Send a close to the serial driver. The serial enumerator
// will receive this and start tracking again. This will
// eventually trigger a device removal.
//
IoSetNextIrpStackLocation(irp);
irp->UserIosb = &ioStatusBlock;
irpStack = IoGetCurrentIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_CLOSE;
status = Lit220CallSerialDriver(
ReaderExtension->BusDeviceObject,
irp
);
ASSERT(status == STATUS_SUCCESS);
IoFreeIrp(irp);
} else {
SmartcardDebug(
DEBUG_ERROR,
("%s!Lit220CloseSerialPort: Could not allocate IRP for close!\n",
DRIVER_NAME)
);
}
// Inform the remove function that the call is complete
KeSetEvent(
&deviceExtension->SerialCloseDone,
0,
FALSE
);
}
NTSTATUS
Lit220SerialCallComplete(
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);
SmartcardDebug(
DEBUG_TRACE,
("%s!Lit220SerialCallComplete: enter\n",
DRIVER_NAME)
);
if (Irp->Cancel) {
Irp->IoStatus.Status = STATUS_CANCELLED;
}
KeSetEvent(Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
Lit220CallSerialDriver(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
Sends an Irp to the serial driver.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
KEVENT Event;
// Copy out stack location to the next
IoCopyCurrentIrpStackLocationToNext(Irp);
// Initializate event for process synchronication.
KeInitializeEvent(
&Event,
NotificationEvent,
FALSE
);
// Set the completion routine
IoSetCompletionRoutine(
Irp,
Lit220SerialCallComplete,
&Event,
TRUE,
TRUE,
TRUE
);
// Call the serial driver
status = IoCallDriver(
DeviceObject,
Irp
);
// Wait for it to complete
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(STATUS_SUCCESS == status);
status = Irp->IoStatus.Status;
}
return status;
}
NTSTATUS
Lit220PnP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine will receive the various Plug N Play messages. It is
here that we start our device, stop it, etc. Safe to submit requests
to the serial bus driver.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
KEVENT Event;
NTSTATUS status;
PIO_STACK_LOCATION IrpStack;
PSMARTCARD_EXTENSION SmartcardExtension = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
PDEVICE_OBJECT busDeviceObject = ReaderExtension->BusDeviceObject;
PIRP pIrp = NULL;
IO_STATUS_BLOCK ioStatusBlock;
LARGE_INTEGER Interval;
PIRP createIrp = NULL;
HANDLE handle;
PIO_STACK_LOCATION NextIrpStack;
PIRP irp;
BOOLEAN deviceRemoved = FALSE;
KIRQL irql;
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;
}
//
// pull the minor code out of our Irp Stack so we know what
// PnP function we're supposed to do
//
IrpStack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(IrpStack);
SmartcardDebug(
DEBUG_TRACE,
("%s!Lit220PnP: Enter - MinorFunction %X\n",
DRIVER_NAME,
IrpStack->MinorFunction)
);
switch (IrpStack->MinorFunction) {
PDEVICE_OBJECT BusDeviceObject = ReaderExtension->BusDeviceObject;
case IRP_MN_START_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: MN_START_DEVICE\n",
DRIVER_NAME)
);
//
// Before we start initializing our device, we must
// call to the layer below us first.
//
IoCopyCurrentIrpStackLocationToNext (Irp);
KeInitializeEvent(
&Event,
SynchronizationEvent,
FALSE
);
IoSetCompletionRoutine(
Irp,
Lit220SynchCompletionRoutine,
&Event,
TRUE,
TRUE,
TRUE
);
//
// Call down to the serial bus driver.
//
status = IoCallDriver(
ReaderExtension->BusDeviceObject,
Irp
);
if (status == STATUS_PENDING) {
//
// Still pending, wait for the IRP to complete
//
status = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
}
if (NT_SUCCESS(status)) {
status = Lit220StartDevice(SmartcardExtension);
} else {
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_SERIAL_CONNECTION_FAILURE,
NULL,
0
);
}
//
// Complete the IRP first otherwise if we failed we may remove
// the driver before we have a chance to complete this IRP.
// Causing a system crash.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
case IRP_MN_QUERY_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: IRP_MN_QUERY_STOP_DEVICE\n",
DRIVER_NAME)
);
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
if ((deviceExtension->IoCount > 0) /* ***&& (!ReaderExtension->DeviceRemoved)*/) {
// 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);
IoCopyCurrentIrpStackLocationToNext (Irp);
status = IoCallDriver(ReaderExtension->BusDeviceObject, Irp);
}
break;
case IRP_MN_CANCEL_STOP_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: IRP_MN_CANCEL_STOP_DEVICE\n",
DRIVER_NAME)
);
IoCopyCurrentIrpStackLocationToNext (Irp);
status = IoCallDriver(ReaderExtension->BusDeviceObject, Irp);
if (status == STATUS_SUCCESS) {
// we can continue to process requests
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
}
break;
case IRP_MN_STOP_DEVICE:
//
// Do whatever you need to do to shutdown the device.
//
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: MN_STOP_DEVICE\n",
DRIVER_NAME)
);
Lit220StopDevice(SmartcardExtension);
//
// Send the stop IRP down
//
IoCopyCurrentIrpStackLocationToNext (Irp);
status = (IoCallDriver(
ReaderExtension->BusDeviceObject,
Irp)
);
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
// 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;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest (
Irp,
IO_NO_INCREMENT
);
break;
}
// disable the reader
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
FALSE
);
// Send the call down the DevNode
IoCopyCurrentIrpStackLocationToNext (Irp);
status = IoCallDriver(
ReaderExtension->BusDeviceObject,
Irp
);
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
//
// Send call down to serial driver - we need
// to process this call on the way up the devNode
//
IoCopyCurrentIrpStackLocationToNext (Irp);
//
// Initialize the event
//
KeInitializeEvent(
&Event,
SynchronizationEvent,
FALSE
);
IoSetCompletionRoutine (
Irp,
Lit220SynchCompletionRoutine,
&Event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver (
ReaderExtension->BusDeviceObject,
Irp
);
if (STATUS_PENDING == status) {
KeWaitForSingleObject(
&Event,
Executive, // Waiting for reason of a driver
KernelMode, // Waiting in kernel mode
FALSE, // No allert
NULL // No timeout
);
status = Irp->IoStatus.Status;
}
// Re-enable the device interface
if ((status == STATUS_SUCCESS) &&
(ReaderExtension->SerialConfigData.WaitMask != 0))
{
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
ASSERT(NT_SUCCESS(status));
}
//
// We must now complete the IRP, since we stopped it in the
// completetion routine with MORE_PROCESSING_REQUIRED.
//
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest (
Irp,
IO_NO_INCREMENT
);
break;
case IRP_MN_REMOVE_DEVICE:
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: MN_REMOVE_DEVICE\n",
DRIVER_NAME)
);
// Wait until we can safely unload the device
SmartcardReleaseRemoveLockAndWait(SmartcardExtension);
Lit220RemoveDevice(DeviceObject);
// Mark the device as removed
deviceRemoved = TRUE;
//
// Send on the remove IRP.
// We need to send the remove down the stack before we detach,
// but we don't need to wait for the completion of this operation
// (and to register a completion routine).
//
IoCopyCurrentIrpStackLocationToNext (Irp);
status = IoCallDriver(
busDeviceObject,
Irp
);
break;
default:
IoCopyCurrentIrpStackLocationToNext (Irp);
status = IoCallDriver(
ReaderExtension->BusDeviceObject,
Irp
);
break;
}
if (deviceRemoved == FALSE) {
SmartcardReleaseRemoveLockWithTag(
SmartcardExtension,
'PnP'
);
}
SmartcardDebug(
DEBUG_TRACE,
("%s!Lit220PnP: Exit %X\n",
DRIVER_NAME,
status)
);
return status;
}
NTSTATUS
Lit220StartDevice(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
{
PDEVICE_OBJECT deviceObject = SmartcardExtension->OsData->DeviceObject;
PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
KEVENT Event;
PIRP irp;
IO_STATUS_BLOCK ioStatusBlock;
PIO_STACK_LOCATION IrpStack;
PAGED_CODE();
try {
//
// Send a create IRP to the serial driver
//
KeInitializeEvent(
&Event,
NotificationEvent,
FALSE
);
//
// Create an IRP for opening the serial driver
//
irp = IoAllocateIrp(
(CCHAR)(deviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_INSUFFICIENT_RESOURCES,
NULL,
0
);
leave;
}
//
// 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.
//
IoSetNextIrpStackLocation(irp);
irp->UserIosb = &ioStatusBlock;
IrpStack = IoGetCurrentIrpStackLocation(irp);
IrpStack->MajorFunction = IRP_MJ_CREATE;
IrpStack->MinorFunction = 0UL;
IrpStack->Parameters.Create.Options = 0;
IrpStack->Parameters.Create.ShareAccess = 0;
IrpStack->Parameters.Create.FileAttributes = 0;
IrpStack->Parameters.Create.EaLength = 0;
status = Lit220CallSerialDriver(
readerExtension->BusDeviceObject,
irp
);
ASSERT(status == STATUS_SUCCESS);
IoFreeIrp(irp);
if (status != STATUS_SUCCESS) {
SmartcardDebug(
DEBUG_ERROR,
("%s!Lit220PNP: CreateIRP failed %X\n",
DRIVER_NAME,
status)
);
if (status == STATUS_SHARED_IRQ_BUSY) {
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_SERIAL_SHARE_IRQ_CONFLICT,
NULL,
0
);
} else {
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_SERIAL_CONNECTION_FAILURE,
NULL,
0
);
}
leave;
}
KeClearEvent(&deviceExtension->SerialCloseDone);
//
// Configure the reader
//
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220PnP: Now doing Lit220Initialize - SmartcardExt %X\n",
DRIVER_NAME,
SmartcardExtension)
);
ASSERT(SmartcardExtension != NULL);
status = Lit220Initialize(SmartcardExtension);
if (status != STATUS_SUCCESS) {
// The function fails in Lit220Initialize will log
// the appropriate error. So we don't need to do that
// here
SmartcardDebug(
DEBUG_ERROR,
("%s!Lit220PNP: Lit220Initialize failed %X\n",
DRIVER_NAME,
status)
);
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_INITIALIZATION_FAILURE,
NULL,
0
);
leave;
}
// Enable the interface for the device
status = IoSetDeviceInterfaceState(
&deviceExtension->PnPDeviceName,
TRUE
);
if (!NT_SUCCESS(status)) {
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
LIT220_SERIAL_CONNECTION_FAILURE,
NULL,
0
);
leave;
}
KeSetEvent(&deviceExtension->ReaderStarted, 0, FALSE);
}
finally {
if (status != STATUS_SUCCESS) {
Lit220StopDevice(SmartcardExtension);
}
}
return status;
}
VOID
Lit220StopDevice(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine handles stopping the device. It closes
connection to serial port and stops the input filter
if it has been activated.
Arguments:
SmartcardExtension - pointer to the smart card data struct.
Return Value:
NTSTATUS
--*/
{
PDEVICE_OBJECT deviceObject = SmartcardExtension->OsData->DeviceObject;
PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
NTSTATUS status;
PAGED_CODE();
if (KeReadStateEvent(&deviceExtension->SerialCloseDone) == 0l) {
// test if we ever started event tracking
if (SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask == 0) {
// no, we did not
// We 'only' need to close the serial port
Lit220CloseSerialPort(deviceObject, NULL);
} else {
PUCHAR requestBuffer;
//
// Stop the wait for character input and DSR changes.
// When this happens it will signal the our waitforclose
// thread to close the connection to the serial driver (if
// it is not already closed).
//
readerExtension->SerialConfigData.WaitMask = 0;
// save the pointer
requestBuffer = SmartcardExtension->SmartcardRequest.Buffer;
// Stop the event requests
*(PULONG) SmartcardExtension->SmartcardRequest.Buffer =
readerExtension->SerialConfigData.WaitMask;
SmartcardExtension->SmartcardRequest.BufferLength =
sizeof(ULONG);
readerExtension->SerialIoControlCode =
IOCTL_SERIAL_SET_WAIT_MASK;
// No bytes expected back
SmartcardExtension->SmartcardReply.BufferLength = 0;
status = Lit220SerialIo(SmartcardExtension);
ASSERT(status == STATUS_SUCCESS);
// Restore the pointer
SmartcardExtension->SmartcardRequest.Buffer = requestBuffer;
// Wait for the close thread to complete
KeWaitForSingleObject(
&deviceExtension->SerialCloseDone,
Executive,
KernelMode,
FALSE,
NULL
);
}
}
}
VOID
Lit220RemoveDevice(
IN PDEVICE_OBJECT DeviceObject
)
{
PDEVICE_EXTENSION deviceExtension;
PSMARTCARD_EXTENSION smartcardExtension;
NTSTATUS status;
PAGED_CODE();
if (DeviceObject == NULL) {
return;
}
deviceExtension = DeviceObject->DeviceExtension;
smartcardExtension = &deviceExtension->SmartcardExtension;
SmartcardDebug(
DEBUG_TRACE,
( "%s!Lit220RemoveDevice: Enter\n",
DRIVER_NAME)
);
// We need to stop the device before we can remove it.
Lit220StopDevice(smartcardExtension);
/* Superfluous -- Remove later
// now wait until our device has been closed
status = KeWaitForSingleObject(
&deviceExtension->ReaderClosed,
Executive,
KernelMode,
FALSE,
NULL
);
ASSERT(status == STATUS_SUCCESS);
*/
//
// Clean ourself out of the driver stack layer
//
if (deviceExtension->SmartcardExtension.ReaderExtension &&
deviceExtension->SmartcardExtension.ReaderExtension->BusDeviceObject) {
IoDetachDevice(
deviceExtension->SmartcardExtension.ReaderExtension->BusDeviceObject
);
}
// Free PnPDeviceName
if(deviceExtension->PnPDeviceName.Buffer != NULL) {
RtlFreeUnicodeString(&deviceExtension->PnPDeviceName);
}
//
// Let the lib free the send/receive buffers
//
if(smartcardExtension->OsData != NULL) {
SmartcardExit(smartcardExtension);
}
// Free reader extension
if (smartcardExtension->ReaderExtension != NULL) {
ExFreePool(smartcardExtension->ReaderExtension);
}
// Free the work item
if (deviceExtension->WorkItem != NULL) {
IoFreeWorkItem(deviceExtension->WorkItem);
deviceExtension->WorkItem = NULL;
}
// Delete the device object
IoDeleteDevice(DeviceObject);
}
NTSTATUS
Lit220SynchCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This routine is for use with synchronous IRP processing.
All it does is signal an event, so the driver knows it
can continue.
Arguments:
DriverObject - Pointer to driver object created by system.
Irp - Irp that just completed
Event - Event we'll signal to say Irp is done
Return Value:
None.
--*/
{
SmartcardDebug(
DEBUG_TRACE,
("%s!Lit220SynchCompletionRoutine: Enter\n",
DRIVER_NAME)
);
KeSetEvent(
(PKEVENT) Event,
0,
FALSE
);
return (STATUS_MORE_PROCESSING_REQUIRED);
}
VOID
Lit220SystemPowerCompletion(
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);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = IoStatus->Status;
SmartcardReleaseRemoveLockWithTag(
smartcardExtension,
'rwoP'
);
if (PowerState.SystemState == PowerSystemWorking) {
PoSetPowerState (
DeviceObject,
SystemPowerState,
PowerState
);
}
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
PoCallDriver(smartcardExtension->ReaderExtension->BusDeviceObject, Irp);
// IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
NTSTATUS
Lit220DevicePowerCompletion (
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;
ASSERT(irpStack != NULL);
if(Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// Check if the card is inserted
//
SmartcardExtension->ReaderCapabilities.CurrentState =
(Lit220IsCardPresent(SmartcardExtension) ? SCARD_PRESENT : SCARD_ABSENT);
//
// Issue a power request in order to reset the card's status
//
if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_PRESENT) {
SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
status = Lit220Power(SmartcardExtension);
ASSERT(status == STATUS_SUCCESS);
}
//
// 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) {
Lit220NotifyCardChange(
SmartcardExtension,
SmartcardExtension->ReaderCapabilities.CurrentState & SCARD_PRESENT
);
}
// 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
Lit220DispatchPower (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The power dispatch routine.
All we care about is the transition from a low D state to D0.
Arguments:
DeviceObject - pointer to 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;
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220DispatchPower: 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 = smartcardExtension->ReaderExtension->BusDeviceObject;
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!Lit220DispatchPower: 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,
Lit220DevicePowerCompletion,
smartcardExtension,
TRUE,
TRUE,
TRUE
);
action = WaitForCompletion;
break;
case PowerDeviceD3:
// Turn off the reader
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220DispatchPower: 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 = Lit220Power(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;
}
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!Lit220DispatchPower: Query Power\n",
DRIVER_NAME)
);
//
// By default we succeed and pass down
//
action = SkipRequest;
Irp->IoStatus.Status = STATUS_SUCCESS;
switch (irpStack->Parameters.Power.State.SystemState) {
case PowerSystemMaximum:
case PowerSystemWorking:
case PowerSystemSleeping1:
case PowerSystemSleeping2:
break;
case PowerSystemSleeping3:
case PowerSystemHibernate:
case PowerSystemShutdown:
KeAcquireSpinLock(&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;
action = CompleteRequest;
}
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
break;
}
break;
case IRP_MN_SET_POWER:
SmartcardDebug(
DEBUG_DRIVER,
("%s!Lit220DispatchPower: 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 = 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,
Lit220SystemPowerCompletion,
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!Lit220DispatchPower: Exit %lx\n",
DRIVER_NAME,
status)
);
return status;
}