Windows2003-3790/drivers/smartcrd/smclib/smcnt.c
2020-09-30 16:53:55 +02:00

1253 lines
34 KiB
C

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
smcnt.c
Abstract:
This module handles all IOCTL requests to the smart card reader.
Environment:
Kernel mode only.
Notes:
Revision History:
- Created December 1996 by Klaus Schutz
--*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <ntddk.h>
#include <strsafe.h>
#define SMARTCARD_POOL_TAG 'bLCS'
#define _ISO_TABLES_
#include "smclib.h"
typedef struct _TAG_LIST_ENTRY {
ULONG Tag;
LIST_ENTRY List;
} TAG_LIST_ENTRY, *PTAG_LIST_ENTRY;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
void
SmartcardDeleteLink(
IN PUNICODE_STRING LinkName
);
#pragma alloc_text(PAGEABLE,DriverEntry)
#pragma alloc_text(PAGEABLE,SmartcardCreateLink)
#pragma alloc_text(PAGEABLE,SmartcardDeleteLink)
#pragma alloc_text(PAGEABLE,SmartcardInitialize)
#pragma alloc_text(PAGEABLE,SmartcardExit)
NTSTATUS
SmartcardDeviceIoControl(
PSMARTCARD_EXTENSION SmartcardExtension
);
PUCHAR
MapIoControlCodeToString(
ULONG IoControlCode
);
#if DEBUG_INTERFACE
#include "smcdbg.c"
#else
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
return STATUS_SUCCESS;
}
#endif
NTSTATUS
SmartcardCreateLink(
IN OUT PUNICODE_STRING LinkName,
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
This routine creates a symbolic link name for the given device name.
NOTE: The buffer for the link name will be allocated here. The caller
is responsible for freeing the buffer.
If the function fails no buffer is allocated.
Arguments:
LinkName - receives the created link name
DeviceName - the device name for which the link should be created
Return Value:
None
--*/
{
NTSTATUS status;
ULONG i;
PWCHAR buffer;
ASSERT(LinkName != NULL);
ASSERT(DeviceName != NULL);
if (LinkName == NULL) {
return STATUS_INVALID_PARAMETER_1;
}
if (DeviceName == NULL) {
return STATUS_INVALID_PARAMETER_2;
}
buffer = ExAllocatePool(
NonPagedPool,
32 * sizeof(WCHAR)
);
ASSERT(buffer != NULL);
if (buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < MAXIMUM_SMARTCARD_READERS; i++) {
StringCchPrintfW(buffer,
32,
L"\\DosDevices\\SCReader%d", i);
RtlInitUnicodeString(
LinkName,
buffer
);
status = IoCreateSymbolicLink(
LinkName,
DeviceName
);
if (NT_SUCCESS(status)) {
SmartcardDebug(
DEBUG_INFO,
("%s!SmartcardCreateLink: %ws linked to %ws\n",
DRIVER_NAME,
DeviceName->Buffer,
LinkName->Buffer)
);
return status;
}
}
ExFreePool(LinkName->Buffer);
return status;
}
void
SmartcardDeleteLink(
IN PUNICODE_STRING LinkName
)
{
//
// Delete the symbolic link of the smart card reader
//
IoDeleteSymbolicLink(
LinkName
);
//
// Free allocated buffer
//
ExFreePool(
LinkName->Buffer
);
}
NTSTATUS
SmartcardInitialize(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This function allocated the send and receive buffers for smart card
data. It also sets the pointer to 2 ISO tables to make them accessible
to the driver
Arguments:
SmartcardExtension
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
SmartcardDebug(
DEBUG_INFO,
("%s!SmartcardInitialize: Enter. Version %lx, %s %s\n",
DRIVER_NAME,
SMCLIB_VERSION,
__DATE__,
__TIME__)
);
ASSERT(SmartcardExtension != NULL);
ASSERT(SmartcardExtension->OsData == NULL);
if (SmartcardExtension == NULL) {
return STATUS_INVALID_PARAMETER_1;
}
if (SmartcardExtension->Version > SMCLIB_VERSION ||
SmartcardExtension->Version < SMCLIB_VERSION_REQUIRED) {
SmartcardDebug(
DEBUG_ERROR,
("%s!SmartcardInitialize: Incompatible version in SMARTCARD_EXTENSION.\n",
DRIVER_NAME)
);
return STATUS_UNSUCCESSFUL;
}
if (SmartcardExtension->SmartcardRequest.BufferSize < MIN_BUFFER_SIZE) {
SmartcardDebug(
DEBUG_ERROR,
("%s!SmartcardInitialize: WARNING: SmartcardRequest.BufferSize (%ld) < MIN_BUFFER_SIZE (%ld)\n",
DRIVER_NAME,
SmartcardExtension->SmartcardRequest.BufferSize,
MIN_BUFFER_SIZE)
);
SmartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE;
}
if (SmartcardExtension->SmartcardReply.BufferSize < MIN_BUFFER_SIZE) {
SmartcardDebug(
DEBUG_ERROR,
("%s!SmartcardInitialize: WARNING: SmartcardReply.BufferSize (%ld) < MIN_BUFFER_SIZE (%ld)\n",
DRIVER_NAME,
SmartcardExtension->SmartcardReply.BufferSize,
MIN_BUFFER_SIZE)
);
SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
}
SmartcardExtension->SmartcardRequest.Buffer = ExAllocatePool(
NonPagedPool,
SmartcardExtension->SmartcardRequest.BufferSize
);
SmartcardExtension->SmartcardReply.Buffer = ExAllocatePool(
NonPagedPool,
SmartcardExtension->SmartcardReply.BufferSize
);
SmartcardExtension->OsData = ExAllocatePool(
NonPagedPool,
sizeof(OS_DEP_DATA)
);
#if defined(DEBUG) && defined(SMCLIB_NT)
SmartcardExtension->PerfInfo = ExAllocatePool(
NonPagedPool,
sizeof(PERF_INFO)
);
#endif
//
// Check if one of the above allocations failed
//
if (SmartcardExtension->SmartcardRequest.Buffer == NULL ||
SmartcardExtension->SmartcardReply.Buffer == NULL ||
SmartcardExtension->OsData == NULL
#if defined(DEBUG) && defined(SMCLIB_NT)
|| SmartcardExtension->PerfInfo == NULL
#endif
) {
status = STATUS_INSUFFICIENT_RESOURCES;
if (SmartcardExtension->SmartcardRequest.Buffer) {
ExFreePool(SmartcardExtension->SmartcardRequest.Buffer);
}
if (SmartcardExtension->SmartcardReply.Buffer) {
ExFreePool(SmartcardExtension->SmartcardReply.Buffer);
}
if (SmartcardExtension->OsData) {
ExFreePool(SmartcardExtension->OsData);
}
#if defined(DEBUG) && defined(SMCLIB_NT)
if (SmartcardExtension->PerfInfo) {
ExFreePool(SmartcardExtension->PerfInfo);
}
#endif
}
if (status != STATUS_SUCCESS) {
return status;
}
RtlZeroMemory(
SmartcardExtension->OsData,
sizeof(OS_DEP_DATA)
);
#if defined(DEBUG) && defined(SMCLIB_NT)
RtlZeroMemory(
SmartcardExtension->PerfInfo,
sizeof(PERF_INFO)
);
#endif
// Initialize the mutex that is used to synch. access to the driver
KeInitializeMutex(
&(SmartcardExtension->OsData->Mutex),
0
);
KeInitializeSpinLock(
&(SmartcardExtension->OsData->SpinLock)
);
// initialize the remove lock
SmartcardExtension->OsData->RemoveLock.Removed = FALSE;
SmartcardExtension->OsData->RemoveLock.RefCount = 1;
KeInitializeEvent(
&SmartcardExtension->OsData->RemoveLock.RemoveEvent,
SynchronizationEvent,
FALSE
);
InitializeListHead(&SmartcardExtension->OsData->RemoveLock.TagList);
// Make the 2 ISO tables accessible to the driver
SmartcardExtension->CardCapabilities.ClockRateConversion =
&ClockRateConversion[0];
SmartcardExtension->CardCapabilities.BitRateAdjustment =
&BitRateAdjustment[0];
#ifdef DEBUG_INTERFACE
SmclibCreateDebugInterface(SmartcardExtension);
#endif
SmartcardDebug(
DEBUG_TRACE,
("%s!SmartcardInitialize: Exit\n",
DRIVER_NAME)
);
return status;
}
VOID
SmartcardExit(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
/*++
Routine Description:
This routine frees the send and receive buffer.
It is usually called when the driver unloads.
Arguments:
SmartcardExtension
--*/
{
SmartcardDebug(
DEBUG_TRACE,
("%s!SmartcardExit: Enter\n",
DRIVER_NAME)
);
#ifdef DEBUG_INTERFACE
SmclibDeleteDebugInterface(SmartcardExtension);
#endif
//
// Free all allocated buffers
//
if (SmartcardExtension->SmartcardRequest.Buffer) {
ExFreePool(SmartcardExtension->SmartcardRequest.Buffer);
SmartcardExtension->SmartcardRequest.Buffer = NULL;
}
if (SmartcardExtension->SmartcardReply.Buffer) {
ExFreePool(SmartcardExtension->SmartcardReply.Buffer);
SmartcardExtension->SmartcardReply.Buffer = NULL;
}
if (SmartcardExtension->OsData) {
ExFreePool(SmartcardExtension->OsData);
SmartcardExtension->OsData = NULL;
}
#if defined(DEBUG) && defined(SMCLIB_NT)
if (SmartcardExtension->PerfInfo) {
ExFreePool(SmartcardExtension->PerfInfo);
SmartcardExtension->OsData = NULL;
}
#endif
if (SmartcardExtension->T1.ReplyData) {
// free the reply data buffer for T=1 transmissions
ExFreePool(SmartcardExtension->T1.ReplyData);
SmartcardExtension->T1.ReplyData = NULL;
}
SmartcardDebug(
DEBUG_INFO,
("%s!SmartcardExit: Exit - Device %.*s\n",
DRIVER_NAME,
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer)
);
}
NTSTATUS
SmartcardAcquireRemoveLockWithTag(
IN PSMARTCARD_EXTENSION SmartcardExtension,
ULONG Tag
)
{
NTSTATUS status = STATUS_SUCCESS;
LONG refCount;
#ifdef DEBUG
PTAG_LIST_ENTRY tagListEntry;
#endif
refCount = InterlockedIncrement(
&SmartcardExtension->OsData->RemoveLock.RefCount
);
ASSERT(refCount > 0);
if (SmartcardExtension->OsData->RemoveLock.Removed == TRUE) {
if (InterlockedDecrement (
&SmartcardExtension->OsData->RemoveLock.RefCount
) == 0) {
KeSetEvent(
&SmartcardExtension->OsData->RemoveLock.RemoveEvent,
0,
FALSE
);
}
status = STATUS_DELETE_PENDING;
}
#ifdef DEBUG
tagListEntry = (PTAG_LIST_ENTRY) ExAllocatePoolWithTag(
NonPagedPool,
sizeof(TAG_LIST_ENTRY),
SMARTCARD_POOL_TAG
);
ASSERT(tagListEntry);
if (tagListEntry == NULL) {
return status;
}
tagListEntry->Tag = Tag;
InsertHeadList(
&SmartcardExtension->OsData->RemoveLock.TagList,
&tagListEntry->List
);
#endif
return status;
}
NTSTATUS
SmartcardAcquireRemoveLock(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
{
return SmartcardAcquireRemoveLockWithTag(
SmartcardExtension,
0
);
}
VOID
SmartcardReleaseRemoveLockWithTag(
IN PSMARTCARD_EXTENSION SmartcardExtension,
IN ULONG Tag
)
{
LONG refCount;
#ifdef DEBUG
PLIST_ENTRY entry;
BOOLEAN tagFound = FALSE;
#endif
refCount = InterlockedDecrement(
&SmartcardExtension->OsData->RemoveLock.RefCount
);
ASSERT(refCount >= 0);
#ifdef DEBUG
for (entry = SmartcardExtension->OsData->RemoveLock.TagList.Flink;
entry->Flink != SmartcardExtension->OsData->RemoveLock.TagList.Flink;
entry = entry->Flink) {
PTAG_LIST_ENTRY tagListEntry = CONTAINING_RECORD(entry, TAG_LIST_ENTRY, List);
if (Tag == tagListEntry->Tag) {
tagFound = TRUE;
RemoveEntryList(entry);
ExFreePool(tagListEntry);
break;
}
}
ASSERTMSG("SmartcardReleaseRemoveLock() called with unknown tag", tagFound == TRUE);
#endif
if (refCount == 0) {
ASSERT (SmartcardExtension->OsData->RemoveLock.Removed);
//
// The device needs to be removed. Signal the remove event
// that it's safe to go ahead.
//
KeSetEvent(
&SmartcardExtension->OsData->RemoveLock.RemoveEvent,
IO_NO_INCREMENT,
FALSE
);
}
}
VOID
SmartcardReleaseRemoveLock(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
{
SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 0);
}
VOID
SmartcardReleaseRemoveLockAndWait(
IN PSMARTCARD_EXTENSION SmartcardExtension
)
{
LONG refCount;
PAGED_CODE ();
ASSERT(SmartcardExtension->OsData->RemoveLock.Removed == FALSE);
SmartcardExtension->OsData->RemoveLock.Removed = TRUE;
refCount = InterlockedDecrement (
&SmartcardExtension->OsData->RemoveLock.RefCount
);
ASSERT (refCount > 0);
if (InterlockedDecrement (
&SmartcardExtension->OsData->RemoveLock.RefCount
) > 0) {
#ifdef DEBUG
// walk the tag list and print all currently held locks
PLIST_ENTRY entry;
for (entry = SmartcardExtension->OsData->RemoveLock.TagList.Flink;
entry->Flink != SmartcardExtension->OsData->RemoveLock.TagList.Flink;
entry = entry->Flink) {
PTAG_LIST_ENTRY tagListEntry = CONTAINING_RECORD(entry, TAG_LIST_ENTRY, List);
SmartcardDebug(
DEBUG_ERROR,
("%s!SmartcardReleaseRemoveLockAndWait: Device %.*s holds lock '%.4s'\n",
DRIVER_NAME,
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer,
&(tagListEntry->Tag))
);
}
#endif
KeWaitForSingleObject (
&SmartcardExtension->OsData->RemoveLock.RemoveEvent,
Executive,
KernelMode,
FALSE,
NULL
);
#ifdef DEBUG
// free all locks.
entry = SmartcardExtension->OsData->RemoveLock.TagList.Flink;
while (entry->Flink !=
SmartcardExtension->OsData->RemoveLock.TagList.Flink) {
PTAG_LIST_ENTRY tagListEntry = CONTAINING_RECORD(entry, TAG_LIST_ENTRY, List);
RemoveEntryList(entry);
ExFreePool(tagListEntry);
entry = SmartcardExtension->OsData->RemoveLock.TagList.Flink;
}
#endif
}
SmartcardDebug(
DEBUG_INFO,
("%s!SmartcardReleaseRemoveLockAndWait: Exit - Device %.*s\n",
DRIVER_NAME,
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer)
);
}
VOID
SmartcardLogError(
IN PVOID Object,
IN NTSTATUS ErrorCode,
IN PUNICODE_STRING Insertion,
IN ULONG DumpData
)
/*++
Routine Description:
This routine allocates an error log entry, copies the supplied data
to it, and requests that it be written to the error log file.
Arguments:
DeviceObject - Supplies a pointer to the device object associated
with the device that had the error, early in
initialization, one may not yet exist.
Insertion - An insertion string that can be used to log
additional data. Note that the message file
needs %2 for this insertion, since %1
is the name of the driver
ErrorCode - Supplies the IO status for this particular error.
DumpData - One word to dump
Return Value:
None.
--*/
{
PIO_ERROR_LOG_PACKET errorLogEntry;
errorLogEntry = IoAllocateErrorLogEntry(
Object,
(UCHAR) (
sizeof(IO_ERROR_LOG_PACKET) +
(Insertion ? Insertion->Length + sizeof(WCHAR) : 0)
)
);
ASSERT(errorLogEntry != NULL);
if (errorLogEntry == NULL) {
return;
}
errorLogEntry->ErrorCode = ErrorCode;
errorLogEntry->SequenceNumber = 0;
errorLogEntry->MajorFunctionCode = 0;
errorLogEntry->RetryCount = 0;
errorLogEntry->UniqueErrorValue = 0;
errorLogEntry->FinalStatus = STATUS_SUCCESS;
errorLogEntry->DumpDataSize = (DumpData ? sizeof(ULONG) : 0);
errorLogEntry->DumpData[0] = DumpData;
if (Insertion) {
errorLogEntry->StringOffset =
sizeof(IO_ERROR_LOG_PACKET);
errorLogEntry->NumberOfStrings = 1;
RtlCopyMemory(
((PCHAR)(errorLogEntry) + errorLogEntry->StringOffset),
Insertion->Buffer,
Insertion->Length
);
}
IoWriteErrorLogEntry(errorLogEntry);
}
NTSTATUS
SmartcardDeviceControl(
PSMARTCARD_EXTENSION SmartcardExtension,
PIRP Irp
)
/*++
Routine Description:
The routine is the general device control dispatch function.
Arguments:
SmartcardExtension - The pointer to the smart card datae
Irp - Supplies the Irp making the request.
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN ClearCurrentIrp = TRUE;
UNICODE_STRING Message;
static BOOLEAN logged = FALSE;
ULONG ioControlCode = ioStackLocation->Parameters.DeviceIoControl.IoControlCode;
// Check the pointer to the smart card extension
ASSERT(SmartcardExtension != NULL);
if (SmartcardExtension == NULL) {
status = STATUS_INVALID_PARAMETER_1;
Irp->IoStatus.Status = status;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return status;
}
// Check the version that the driver requires
ASSERT(SmartcardExtension->Version >= SMCLIB_VERSION_REQUIRED);
if (SmartcardExtension->Version < SMCLIB_VERSION_REQUIRED) {
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = status;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return status;
}
//
// Check the OsData pointer. This can be NULL if SmartcardInit
// has not been called or SmartcardExit has already been called
//
ASSERT(SmartcardExtension->OsData != NULL);
// Check that the driver has set the DeviceObject
ASSERT(SmartcardExtension->OsData->DeviceObject != NULL);
if (SmartcardExtension->OsData == NULL ||
SmartcardExtension->OsData->DeviceObject == NULL) {
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = status;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return status;
}
// We must run at passive level otherwise IoCompleteRequest won't work properly
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
// check that no one wants us to do unbuffered io
if (ioControlCode & (METHOD_IN_DIRECT | METHOD_OUT_DIRECT)) {
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = status;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return status;
}
//
// This resource acts as a mutex. We can't use a 'real' mutex here,
// since a mutex rises the Irql to APC_LEVEL. This leads to some
// side effects we don't want.
// E.g. IoCompleteRequest() will not copy requested data at APC_LEVEL
//
KeWaitForMutexObject(
&(SmartcardExtension->OsData->Mutex),
UserRequest,
KernelMode,
FALSE,
NULL
);
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
#ifdef developerversion
#if DEBUG
if(!logged) {
RtlInitUnicodeString(
&Message,
L"Developer version of smclib.sys installed"
);
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
STATUS_LICENSE_VIOLATION,
&Message,
0
);
logged = TRUE;
}
#endif
#endif
SmartcardDebug(
DEBUG_IOCTL,
("SMCLIB!SmartcardDeviceControl: Enter <%.*s:%1d>, IOCTL = %s, IRP = %lx\n",
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer,
SmartcardExtension->VendorAttr.UnitNo,
MapIoControlCodeToString(ioControlCode),
Irp)
);
// Return if device is busy
if (InterlockedCompareExchangePointer(&SmartcardExtension->OsData->CurrentIrp,
Irp,
NULL)) {
SmartcardDebug(
DEBUG_ERROR,
("%s!SmartcardDeviceControl: Device %.*s is busy\n",
DRIVER_NAME,
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer)
);
// This flag is used to signal that we can't set the current irp to NULL
ClearCurrentIrp = FALSE;
status = STATUS_DEVICE_BUSY;
}
if (status == STATUS_SUCCESS) {
PIRP notificationIrp;
ULONG currentState;
KIRQL irql;
switch (ioControlCode) {
//
// We have to check for _IS_ABSENT and _IS_PRESENT first,
// since these are (the only allowed) asynchronous requests
//
case IOCTL_SMARTCARD_IS_ABSENT:
ClearCurrentIrp = FALSE;
InterlockedExchangePointer(&SmartcardExtension->OsData->CurrentIrp,
NULL);
if (SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] == NULL) {
status = STATUS_NOT_SUPPORTED;
break;
}
AccessUnsafeData(&irql);
currentState = SmartcardExtension->ReaderCapabilities.CurrentState;
// Now check if the driver is already processing a notification irp
if (SmartcardExtension->OsData->NotificationIrp != NULL) {
status = STATUS_DEVICE_BUSY;
EndAccessUnsafeData(irql);
break;
}
//
// if the current state is not known, it doesn't make sense
// to process this call
//
if (currentState == SCARD_UNKNOWN) {
status = STATUS_INVALID_DEVICE_STATE;
EndAccessUnsafeData(irql);
break;
}
//
// If the card is already (or still) absent, we can return immediatly.
// Otherwise we must statrt event tracking.
//
if (currentState > SCARD_ABSENT) {
SmartcardExtension->OsData->NotificationIrp = Irp;
SmartcardExtension->MajorIoControlCode =
IOCTL_SMARTCARD_IS_ABSENT;
EndAccessUnsafeData(irql);
status = SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING](
SmartcardExtension
);
} else {
EndAccessUnsafeData(irql);
}
break;
case IOCTL_SMARTCARD_IS_PRESENT:
ClearCurrentIrp = FALSE;
InterlockedExchangePointer(&SmartcardExtension->OsData->CurrentIrp,
NULL);
if (SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] == NULL) {
status = STATUS_NOT_SUPPORTED;
break;
}
AccessUnsafeData(&irql);
currentState = SmartcardExtension->ReaderCapabilities.CurrentState;
// Now check if the driver is already processing a notification irp
if (SmartcardExtension->OsData->NotificationIrp != NULL) {
status = STATUS_DEVICE_BUSY;
EndAccessUnsafeData(irql);
break;
}
//
// if the current state is not known, it doesn't make sense
// to process this call
//
if (currentState == SCARD_UNKNOWN) {
status = STATUS_INVALID_DEVICE_STATE;
EndAccessUnsafeData(irql);
break;
}
//
// If the card is already (or still) present, we can return immediatly.
// Otherwise we must statrt event tracking.
//
if (currentState <= SCARD_ABSENT) {
#if defined(DEBUG) && defined(SMCLIB_NT)
ULONG timeInMilliSec = (ULONG)
SmartcardExtension->PerfInfo->IoTickCount.QuadPart *
KeQueryTimeIncrement() /
10000;
ULONG bytesTransferred =
SmartcardExtension->PerfInfo->BytesSent +
SmartcardExtension->PerfInfo->BytesReceived;
// to avoid div. errors and to display only useful information
// we check for a valid time.
if (timeInMilliSec > 0) {
SmartcardDebug(
DEBUG_PERF,
("%s!SmartcardDeviceControl: I/O statistics for device %.*s:\n Transferrate: %5ld bps\n Total bytes: %5ld\n I/O time: %5ld ms\n Transmissions: %5ld\n",
DRIVER_NAME,
SmartcardExtension->VendorAttr.VendorName.Length,
SmartcardExtension->VendorAttr.VendorName.Buffer,
bytesTransferred * 1000 / timeInMilliSec,
bytesTransferred,
timeInMilliSec,
SmartcardExtension->PerfInfo->NumTransmissions)
);
}
// reset performance info
RtlZeroMemory(
SmartcardExtension->PerfInfo,
sizeof(PERF_INFO)
);
#endif
SmartcardExtension->OsData->NotificationIrp = Irp;
SmartcardExtension->MajorIoControlCode =
IOCTL_SMARTCARD_IS_PRESENT;
EndAccessUnsafeData(irql);
status = SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING](
SmartcardExtension
);
} else {
EndAccessUnsafeData(irql);
}
break;
default:
// Get major io control code
SmartcardExtension->MajorIoControlCode = ioControlCode;
// Check if buffers are properly allocated
ASSERT(SmartcardExtension->SmartcardRequest.Buffer);
ASSERT(SmartcardExtension->SmartcardReply.Buffer);
if (Irp->AssociatedIrp.SystemBuffer &&
ioStackLocation->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(ULONG)) {
//
// Transfer minor io control code, even if it doesn't make sense for
// this particular major code
//
SmartcardExtension->MinorIoControlCode =
*(PULONG) (Irp->AssociatedIrp.SystemBuffer);
}
// Save pointer to and length of request buffer
SmartcardExtension->IoRequest.RequestBuffer =
Irp->AssociatedIrp.SystemBuffer;
SmartcardExtension->IoRequest.RequestBufferLength =
ioStackLocation->Parameters.DeviceIoControl.InputBufferLength,
// Save pointer to and length of reply buffer
SmartcardExtension->IoRequest.ReplyBuffer =
Irp->AssociatedIrp.SystemBuffer;
SmartcardExtension->IoRequest.ReplyBufferLength =
ioStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
//
// Pointer to variable that receives the actual number
// of bytes returned
//
SmartcardExtension->IoRequest.Information =
(PULONG) &Irp->IoStatus.Information;
// Default number of bytes returned
Irp->IoStatus.Information = 0;
// Process the device io-control-request
status = SmartcardDeviceIoControl(SmartcardExtension);
if (status == STATUS_PENDING) {
IoMarkIrpPending(Irp);
}
#ifndef NO_LOG
if (!NT_SUCCESS(status) && status != STATUS_NOT_SUPPORTED) {
UNICODE_STRING error;
WCHAR buffer[128];
swprintf(
buffer,
L"IOCTL %S failed with status 0x%lx",
MapIoControlCodeToString(ioControlCode),
status
);
RtlInitUnicodeString(
&error,
buffer
);
SmartcardLogError(
SmartcardExtension->OsData->DeviceObject,
0,
&error,
0
);
}
#endif
break;
}
}
if (status == STATUS_PENDING) {
KIRQL irql;
BOOLEAN pending = FALSE;
//
// Send command to smartcard. The ISR receives the result and queues a dpc function
// that handles the completion of the call;
//
SmartcardDebug(
DEBUG_IOCTL,
("%s!SmartcardDeviceControl: IoMarkIrpPending. IRP = %x\n",
DRIVER_NAME,
Irp)
);
//
// When the driver completes an Irp (Notification or Current) it has
// to set either the Irp back to 0 in order to show that it completed
// the Irp.
//
AccessUnsafeData(&irql);
if (Irp == SmartcardExtension->OsData->NotificationIrp ||
Irp == SmartcardExtension->OsData->CurrentIrp) {
pending = TRUE;
}
EndAccessUnsafeData(irql);
if (pending &&
SmartcardExtension->OsData->DeviceObject->DriverObject->DriverStartIo) {
SmartcardDebug(
DEBUG_IOCTL,
("%s!SmartcardDeviceControl: IoStartPacket. IRP = %x\n",
DRIVER_NAME,
Irp)
);
// Start io-processing of a lowest level driver
IoStartPacket(
SmartcardExtension->OsData->DeviceObject,
Irp,
NULL,
NULL
);
}
} else {
SmartcardDebug(
DEBUG_IOCTL,
("%s!SmartcardDeviceControl: IoCompleteRequest. IRP = %x (%lxh)\n",
DRIVER_NAME,
Irp,
status)
);
Irp->IoStatus.Status = status;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
if (ClearCurrentIrp) {
//
// If the devcie is not busy, we can set the current irp back to NULL
//
InterlockedExchangePointer(&SmartcardExtension->OsData->CurrentIrp,
NULL);
RtlZeroMemory(
&(SmartcardExtension->IoRequest),
sizeof(SmartcardExtension->IoRequest)
);
}
}
SmartcardDebug(
(NT_SUCCESS(status) ? DEBUG_IOCTL : DEBUG_ERROR),
("SMCLIB!SmartcardDeviceControl: Exit. IOCTL = %s, IRP = %x (%lxh)\n",
MapIoControlCodeToString(ioControlCode),
Irp,
status)
);
//
// Release our 'mutex'
//
KeReleaseMutex(
&(SmartcardExtension->OsData->Mutex),
FALSE
);
return status;
}