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.
1014 lines
33 KiB
1014 lines
33 KiB
/*++
|
|
|
|
Module Name:
|
|
|
|
L220FLTR.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the input filter routine and the notification
|
|
procedure for insertion/removal events.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
- Created December 1996 by Klaus Schutz (kschutz)
|
|
|
|
- Modified December 1997 by Brian Manahan for use with
|
|
our 220 reader.
|
|
|
|
--*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "L220SCR.h"
|
|
|
|
|
|
#pragma alloc_text(PAGEABLE, Lit220StartTimer)
|
|
#pragma alloc_text(PAGEABLE, Lit220StopTimer)
|
|
|
|
|
|
static DWORD bTrue = TRUE;
|
|
static DWORD bFalse = FALSE;
|
|
|
|
|
|
BOOLEAN
|
|
Lit220InputFilter(
|
|
IN BYTE SmartcardByte,
|
|
IN PSMARTCARD_EXTENSION smartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is processes each byte from the serial port.
|
|
Lit220SerialEventCallback call this function when it receives a byte
|
|
of data from the serial port. For card insertion/removal it will call the
|
|
Lit220NotifyCardChange to process the notificiation.
|
|
If an ACK is received it will signal the Lit220SendCommand so it can continue.
|
|
After a data block is received it will signal the Lit220SendCommand
|
|
to notifiy that the data is ready.
|
|
--*/
|
|
{
|
|
PREADER_EXTENSION readerExtension = smartcardExtension->ReaderExtension;
|
|
PDEVICE_EXTENSION deviceExtension = smartcardExtension->OsData->DeviceObject->DeviceExtension;
|
|
|
|
LONG semState;
|
|
BOOLEAN retVal = TRUE;
|
|
KIRQL irql;
|
|
|
|
|
|
//
|
|
// The first byte of each packet identifies the packet-type
|
|
// A packet containing data starts with the packet-type and then
|
|
// 2 bytes of packet length.
|
|
//
|
|
if (++readerExtension->ReceivedByteNo == 1) {
|
|
|
|
readerExtension->GotLengthB0 = FALSE;
|
|
readerExtension->GotLengthB1 = FALSE;
|
|
readerExtension->DataByteNo = 0;
|
|
|
|
switch (SmartcardByte) {
|
|
|
|
case LIT220_READER_TYPE:
|
|
|
|
// Prepare for reader type input
|
|
readerExtension->GotLengthB0 = TRUE;
|
|
readerExtension->GotLengthB1 = TRUE;
|
|
readerExtension->DataLength.l.l0 =
|
|
LIT220_READER_TYPE_LEN;
|
|
break;
|
|
|
|
case LIT220_READER_STATUS:
|
|
|
|
// Prepare for reader status input
|
|
readerExtension->GotLengthB0 = TRUE;
|
|
readerExtension->GotLengthB1 = TRUE;
|
|
readerExtension->DataLength.l.l0 =
|
|
LIT220_READER_STATUS_LEN;
|
|
break;
|
|
|
|
case LIT220_RECEIVE_BLOCK:
|
|
// If a smart card was already inserted in the boot phase
|
|
// the reader sends only the ATR but no CARD_IN - msg.
|
|
// We fix that missing msg here.
|
|
//
|
|
|
|
KeAcquireSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
&irql);
|
|
if (smartcardExtension->ReaderCapabilities.CurrentState == SCARD_UNKNOWN) {
|
|
|
|
smartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED;
|
|
}
|
|
|
|
KeReleaseSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
irql);
|
|
|
|
break;
|
|
|
|
case LIT220_CARD_IN:
|
|
|
|
Lit220NotifyCardChange(
|
|
smartcardExtension,
|
|
TRUE
|
|
);
|
|
|
|
readerExtension->ReceivedByteNo = 0;
|
|
break;
|
|
|
|
case LIT220_CARD_OUT:
|
|
|
|
Lit220NotifyCardChange(
|
|
smartcardExtension,
|
|
FALSE
|
|
);
|
|
|
|
|
|
readerExtension->ReceivedByteNo = 0;
|
|
|
|
break;
|
|
|
|
case LIT220_ACK:
|
|
case KBD_ACK: // Also allow kdb_ack for the case for getting keyboard attention
|
|
readerExtension->GotNack = FALSE;
|
|
readerExtension->ReceivedByteNo = 0;
|
|
|
|
|
|
// Check if anyone needs to be singaled for this event.
|
|
// The Lit220SendCommand waits for the ACK signal so it knows
|
|
// when it can continue.
|
|
if (readerExtension->WaitMask & WAIT_ACK) {
|
|
|
|
// Only signal once
|
|
readerExtension->WaitMask &= ~WAIT_ACK;
|
|
|
|
// Signal the AckEvnt
|
|
KeSetEvent(
|
|
&readerExtension->AckEvnt,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case LIT220_NACK:
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!Lit220InteruptService: LIT220_NACK\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
Lit220ProcessNack(smartcardExtension);
|
|
|
|
break;
|
|
|
|
default:
|
|
readerExtension->ReceivedByteNo = 0;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!Lit220InteruptService: Invalid PacketType %xh\n",
|
|
DRIVER_NAME,
|
|
SmartcardByte)
|
|
);
|
|
|
|
// Return false so the rest of this bad buffer
|
|
// will not be sent to us.
|
|
retVal = FALSE;
|
|
|
|
// We want to force a NACK so the
|
|
// the state of the card being inserted or not
|
|
// is re-checked
|
|
Lit220ProcessNack(smartcardExtension);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//
|
|
// Get length-byte-0 from reader
|
|
//
|
|
if (readerExtension->ReceivedByteNo == 2 &&
|
|
readerExtension->GotLengthB0 == FALSE) {
|
|
|
|
readerExtension->DataLength.b.b1 = SmartcardByte;
|
|
readerExtension->GotLengthB0 = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Get length-byte-1 from reader
|
|
//
|
|
if (readerExtension->ReceivedByteNo == 3 &&
|
|
readerExtension->GotLengthB1 == FALSE) {
|
|
|
|
readerExtension->DataLength.b.b0 = SmartcardByte;
|
|
readerExtension->GotLengthB1 = TRUE;
|
|
|
|
//
|
|
// test if the reader has sent a zero-length block of data
|
|
//
|
|
if (readerExtension->DataLength.l.l0 == 0) {
|
|
|
|
readerExtension->ReceivedByteNo = 0;
|
|
readerExtension->WaitForATR = FALSE;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!SmartcardInterruptService: Zero length block received\n",
|
|
DRIVER_NAME)
|
|
);
|
|
}
|
|
|
|
if (readerExtension->DataLength.l.l0 >
|
|
smartcardExtension->SmartcardReply.BufferSize) {
|
|
|
|
readerExtension->ReceivedByteNo = 0;
|
|
readerExtension->WaitForATR = FALSE;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!SmartcardInterruptService: Reply buffer not large enough\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// store data from reader
|
|
//
|
|
if ((readerExtension->DataByteNo < readerExtension->DataLength.l.l0) &&
|
|
(readerExtension->DataByteNo < smartcardExtension->SmartcardReply.BufferSize))
|
|
{
|
|
|
|
smartcardExtension->SmartcardReply.Buffer[readerExtension->DataByteNo++] =
|
|
SmartcardByte;
|
|
|
|
} else {
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!SmartcardInterruptService: DataByteNo %X too large buffer %X, %X bytest expected\n",
|
|
DRIVER_NAME,
|
|
readerExtension->DataByteNo,
|
|
smartcardExtension->SmartcardReply.BufferSize,
|
|
readerExtension->DataLength.l.l0)
|
|
);
|
|
|
|
}
|
|
|
|
ASSERT(readerExtension->DataByteNo <= readerExtension->DataLength.l.l0);
|
|
//
|
|
// Have we received all the bytes in the packet yet?
|
|
//
|
|
if (readerExtension->DataByteNo == readerExtension->DataLength.l.l0) {
|
|
|
|
// Stop the input timeout timer
|
|
// schedule our remove thread
|
|
Lit220ScheduleTimer(
|
|
smartcardExtension,
|
|
Lit220StopTimer
|
|
);
|
|
|
|
|
|
smartcardExtension->SmartcardReply.BufferLength =
|
|
readerExtension->DataByteNo;
|
|
|
|
readerExtension->ReceivedByteNo = 0;
|
|
|
|
if (readerExtension->WaitForATR) {
|
|
|
|
//
|
|
// Transfer ATR to smartcard-struct
|
|
//
|
|
smartcardExtension->CardCapabilities.ATR.Length =
|
|
(UCHAR) (readerExtension->DataByteNo % (SCARD_ATR_LENGTH + 1));
|
|
|
|
readerExtension->WaitForATR = FALSE;
|
|
|
|
if (smartcardExtension->CardCapabilities.ATR.Length >
|
|
smartcardExtension->SmartcardReply.BufferLength)
|
|
{
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!SmartcardInterruptService: SmarcardReply buffer too small for ATR\n",
|
|
DRIVER_NAME)
|
|
);
|
|
} else {
|
|
|
|
RtlCopyMemory(
|
|
smartcardExtension->CardCapabilities.ATR.Buffer,
|
|
smartcardExtension->SmartcardReply.Buffer,
|
|
smartcardExtension->CardCapabilities.ATR.Length
|
|
);
|
|
|
|
SmartcardUpdateCardCapabilities(
|
|
smartcardExtension
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
// Check if anyone needs to be singaled for this event.
|
|
// The Lit220SendCommand waits for the DataEvnt signal so it knows
|
|
// when the data has been received.
|
|
if (readerExtension->WaitMask & WAIT_DATA) {
|
|
|
|
//
|
|
// Do any necessary post proccessing after we have receive the packet
|
|
//
|
|
if (smartcardExtension->OsData->CurrentIrp != NULL) {
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
switch (smartcardExtension->MajorIoControlCode) {
|
|
|
|
case IOCTL_SMARTCARD_POWER:
|
|
if (smartcardExtension->ReaderExtension->GotNack) {
|
|
|
|
status = STATUS_NO_MEDIA;
|
|
break;
|
|
}
|
|
|
|
switch(smartcardExtension->MinorIoControlCode) {
|
|
|
|
case SCARD_COLD_RESET:
|
|
case SCARD_WARM_RESET:
|
|
if (smartcardExtension->IoRequest.ReplyBufferLength <
|
|
smartcardExtension->CardCapabilities.ATR.Length) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// copy ATR to user buffer
|
|
//
|
|
if (smartcardExtension->CardCapabilities.ATR.Length <=
|
|
sizeof(smartcardExtension->CardCapabilities.ATR.Buffer))
|
|
{
|
|
RtlCopyMemory(
|
|
smartcardExtension->IoRequest.ReplyBuffer,
|
|
&smartcardExtension->CardCapabilities.ATR.Buffer,
|
|
smartcardExtension->CardCapabilities.ATR.Length
|
|
);
|
|
|
|
//
|
|
// length of buffer
|
|
//
|
|
*(smartcardExtension->IoRequest.Information) =
|
|
smartcardExtension->CardCapabilities.ATR.Length;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SCARD_POWER_DOWN:
|
|
KeAcquireSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
&irql);
|
|
if (smartcardExtension->ReaderCapabilities.CurrentState != SCARD_ABSENT) {
|
|
smartcardExtension->ReaderCapabilities.CurrentState =
|
|
SCARD_SWALLOWED;
|
|
|
|
smartcardExtension->CardCapabilities.Protocol.Selected =
|
|
SCARD_PROTOCOL_UNDEFINED;
|
|
}
|
|
KeReleaseSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
irql);
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SMARTCARD_SET_PROTOCOL:
|
|
if (smartcardExtension->ReaderExtension->GotNack) {
|
|
|
|
smartcardExtension->CardCapabilities.Protocol.Selected =
|
|
SCARD_PROTOCOL_UNDEFINED;
|
|
|
|
status = STATUS_NO_MEDIA;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// protocol has been changed successfully
|
|
//
|
|
|
|
KeAcquireSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
&irql);
|
|
if (smartcardExtension->ReaderCapabilities.CurrentState != SCARD_ABSENT) {
|
|
smartcardExtension->ReaderCapabilities.CurrentState =
|
|
SCARD_SPECIFIC;
|
|
}
|
|
KeReleaseSpinLock(&smartcardExtension->OsData->SpinLock,
|
|
irql);
|
|
|
|
//
|
|
// Tell the caller what the current protocol is.
|
|
//
|
|
*(PULONG) smartcardExtension->IoRequest.ReplyBuffer =
|
|
smartcardExtension->CardCapabilities.Protocol.Selected;
|
|
|
|
*(smartcardExtension->IoRequest.Information) =
|
|
sizeof(ULONG);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Only signal once
|
|
readerExtension->WaitMask &= ~WAIT_DATA;
|
|
|
|
// Signal the DataEvnt
|
|
KeSetEvent(
|
|
&readerExtension->DataEvnt,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
Lit220ProcessNack(
|
|
PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles everything that needs to be done when we have an error
|
|
with the reader. The state of the input filter is reset. All signals that
|
|
the Lit220Command function may be waiting on are fired. The flag GotNack is
|
|
set which will trigger Lit220Command to resync with the reader (get the last
|
|
error and refresh the card inserted state).
|
|
|
|
--*/
|
|
{
|
|
PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension;
|
|
|
|
// Set GotNack so we know something went wrong
|
|
readerExtension->GotNack = TRUE;
|
|
|
|
// Reset the input state of the filter
|
|
readerExtension->ReceivedByteNo = 0;
|
|
|
|
|
|
//
|
|
// Signal the ACK and data semaphores and set error code
|
|
// This will keep the Lit220SendCommand from having to
|
|
// wait for a timeout to continue when something goes wrong.
|
|
//
|
|
if (readerExtension->WaitMask & WAIT_ACK) {
|
|
|
|
// Signal the AckEvnt
|
|
KeSetEvent(
|
|
&readerExtension->AckEvnt,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
if (readerExtension->WaitMask & WAIT_DATA) {
|
|
|
|
// Signal the DataEvnt
|
|
KeSetEvent(
|
|
&readerExtension->DataEvnt,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Reset WaitMask since the card has nacked the command
|
|
//
|
|
readerExtension->WaitMask &= (WAIT_INSERTION | WAIT_REMOVAL);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
Lit220NotifyCardChange(
|
|
IN PSMARTCARD_EXTENSION smartcardExtension,
|
|
IN DWORD CardInserted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs at DISPATCH_LEVEL IRQL to finish processing
|
|
a card insertion/removal event. It is queued in the smartcard filter
|
|
and notifies a caller of an insertion/removal event.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREADER_EXTENSION readerExtension = smartcardExtension->ReaderExtension;
|
|
KIRQL oldOsDataIrql;
|
|
|
|
if (readerExtension->CardIn == CardInserted) {
|
|
return;
|
|
}
|
|
readerExtension->CardIn = CardInserted;
|
|
|
|
|
|
KeAcquireSpinLock(
|
|
&smartcardExtension->OsData->SpinLock,
|
|
&oldOsDataIrql
|
|
);
|
|
|
|
|
|
if (CardInserted) {
|
|
// Set the default state for the new card
|
|
smartcardExtension->ReaderCapabilities.CurrentState =
|
|
SCARD_SWALLOWED;
|
|
|
|
smartcardExtension->CardCapabilities.Protocol.Selected =
|
|
SCARD_PROTOCOL_UNDEFINED;
|
|
} else {
|
|
// Reset card state to reflect the card removal
|
|
smartcardExtension->ReaderCapabilities.CurrentState =
|
|
SCARD_ABSENT;
|
|
|
|
smartcardExtension->CardCapabilities.Protocol.Selected =
|
|
SCARD_PROTOCOL_UNDEFINED;
|
|
|
|
smartcardExtension->CardCapabilities.ATR.Length = 0;
|
|
}
|
|
|
|
if (readerExtension->WaitMask & WAIT_INSERTION) {
|
|
|
|
// We only make this notification once
|
|
readerExtension->WaitMask &= ~WAIT_INSERTION;
|
|
}
|
|
|
|
Lit220CompleteCardTracking(smartcardExtension);
|
|
|
|
KeReleaseSpinLock(
|
|
&smartcardExtension->OsData->SpinLock,
|
|
oldOsDataIrql
|
|
);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
Lit220CompleteCardTracking(
|
|
IN PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP notificationIrp;
|
|
|
|
SmartcardDebug(
|
|
DEBUG_TRACE,
|
|
("%s!Lit220CompleteCardTracking: Enter\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
notificationIrp = InterlockedExchangePointer(
|
|
&(SmartcardExtension->OsData->NotificationIrp),
|
|
NULL
|
|
);
|
|
|
|
if (notificationIrp) {
|
|
|
|
IoSetCancelRoutine(
|
|
notificationIrp,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
if (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
|
|
Lit220SerialEventCallback(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMARTCARD_EXTENSION SmartcardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is first called as the deferred procedure when a character is received
|
|
or when DSR changes its status.
|
|
It first calls the serial driver to get the modem status to see if the events was
|
|
due to DSR changing (meaning the reader has been removed).
|
|
If DSR did not change it then checks the input queue size and reads the characters in
|
|
the input queue. It then sends the input characters to the input filter for processing.
|
|
Finally it calls the serial driver again to start new cts tracking (starting all over again).
|
|
This routine gets continually called back from itself until the driver is ready
|
|
to unload (indicated by the WaitMask set to 0).
|
|
When the WaitMask is set to 0 it frees this IRP and signals the Lit220WaitForRemoval thread
|
|
to close the serial port.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
DWORD indx;
|
|
KIRQL irql;
|
|
PDEVICE_EXTENSION deviceExtension = SmartcardExtension->OsData->DeviceObject->DeviceExtension;
|
|
|
|
SmartcardExtension->ReaderExtension->SerialEventState++;
|
|
|
|
//
|
|
// First check to see we are being unloaded
|
|
//
|
|
if (SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask == 0) {
|
|
SmartcardDebug(
|
|
DEBUG_DRIVER,
|
|
("%s!Lit220SerialEventCallback: WAIT MASK 0 UNLOADING !!!!\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
|
|
&irql);
|
|
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
|
|
|
|
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
|
|
irql);
|
|
|
|
//
|
|
// If the WaitMask is 0 then the driver is about to unload and we've
|
|
// been called because the unload function has directed the serial
|
|
// driver to complete the outstanding io completion.
|
|
//
|
|
|
|
// schedule our remove thread
|
|
IoQueueWorkItem(
|
|
deviceExtension->WorkItem,
|
|
(PIO_WORKITEM_ROUTINE) Lit220CloseSerialPort,
|
|
DelayedWorkQueue,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
// Get next stack location for next IRP
|
|
irpStack = IoGetNextIrpStackLocation(
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp
|
|
);
|
|
|
|
if (irpStack == NULL) {
|
|
// Fatal Error
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!Lit220SerialEventCallback: Error IoGetNextIrpStackLocation returned NULL - exiting.\n",
|
|
DRIVER_NAME)
|
|
);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
switch (SmartcardExtension->ReaderExtension->SerialEventState) {
|
|
case 1:
|
|
//
|
|
// First we send a get modem status
|
|
//
|
|
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->CardStatus.Irp->AssociatedIrp.SystemBuffer =
|
|
&SmartcardExtension->ReaderExtension->ModemStatus;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
//
|
|
// Check modem status if DSR = 0 then unload driver if not
|
|
// then get queuestatus
|
|
//
|
|
if ((SmartcardExtension->ReaderExtension->ModemStatus & SERIAL_DSR_STATE) == 0) {
|
|
// DSR is 0 this means the reader has been removed
|
|
|
|
SmartcardDebug(
|
|
DEBUG_DRIVER,
|
|
("%s!Lit220SerialEventCallback: DSR = 0 signaling to close device\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
|
|
&irql);
|
|
SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask = 0;
|
|
SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
|
|
|
|
KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
|
|
irql);
|
|
// schedule our remove thread
|
|
IoQueueWorkItem(
|
|
deviceExtension->WorkItem,
|
|
(PIO_WORKITEM_ROUTINE) Lit220CloseSerialPort,
|
|
DelayedWorkQueue,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// 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;
|
|
|
|
} else {
|
|
|
|
// Device is not removed - there must be a character ready
|
|
// Read the data into our temporary buffer. The temporary buffer
|
|
// is large enough to read whatever the reader can send us a one time.
|
|
// The character interval timeout will stop the read at the end of whatever
|
|
// the reader sends us.
|
|
SmartcardExtension->ReaderExtension->SerialStatus.AmountInInQueue =
|
|
sizeof(SmartcardExtension->ReaderExtension->TempXferBuf);
|
|
|
|
// Read the characters
|
|
irpStack->MajorFunction = IRP_MJ_READ;
|
|
irpStack->Parameters.Read.Length =
|
|
sizeof(SmartcardExtension->ReaderExtension->TempXferBuf);
|
|
irpStack->MinorFunction = 0UL;
|
|
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp->AssociatedIrp.SystemBuffer =
|
|
SmartcardExtension->ReaderExtension->TempXferBuf;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
case 3:
|
|
//
|
|
// Send the characers we read to the input filter then setup for read input
|
|
// queue again (in case some characters came in while we were processing the
|
|
// ones we just read).
|
|
//
|
|
for (indx = 0; indx < Irp->IoStatus.Information; indx++) {
|
|
|
|
if (!Lit220InputFilter(
|
|
SmartcardExtension->ReaderExtension->TempXferBuf[indx],
|
|
SmartcardExtension
|
|
))
|
|
{
|
|
// An invalid character was received so stop sending the rest of
|
|
// the data to the filter because it is probably corrupted.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if we are in the middle of a block of data
|
|
if (SmartcardExtension->ReaderExtension->ReceivedByteNo != 0) {
|
|
|
|
// Start the timeout timer. If we don't get the rest of this
|
|
// data block in a few seconds we will timeout. This prevents
|
|
// communication problems between the reader and the PC causing
|
|
// locking up a T=0 card for too long.
|
|
Lit220ScheduleTimer(
|
|
SmartcardExtension,
|
|
Lit220StartTimer
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Read done - start all over again with the wait_on_mask
|
|
//
|
|
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpStack->MinorFunction = 0UL;
|
|
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
|
|
sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask);
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_SERIAL_WAIT_ON_MASK;
|
|
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp->AssociatedIrp.SystemBuffer =
|
|
&SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask;
|
|
|
|
// Reset SerialEventState value
|
|
SmartcardExtension->ReaderExtension->SerialEventState = 0;
|
|
break;
|
|
|
|
default:
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!Lit220SerialEventCallback: Error SerialEventState is out of bounds - resetting to 0\n",
|
|
DRIVER_NAME)
|
|
);
|
|
//
|
|
// We should never get here, but if we do we should try to recover the
|
|
// best we can by setting up for the wait_on_mask.
|
|
//
|
|
|
|
// Reset value
|
|
SmartcardExtension->ReaderExtension->SerialEventState = 0;
|
|
|
|
// Setup for next callback
|
|
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpStack->MinorFunction = 0UL;
|
|
irpStack->Parameters.DeviceIoControl.OutputBufferLength =
|
|
sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask);
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_SERIAL_WAIT_ON_MASK;
|
|
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp->AssociatedIrp.SystemBuffer =
|
|
&SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask;
|
|
|
|
}
|
|
|
|
// We always call this same function when we complete a call
|
|
IoSetCompletionRoutine(
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp,
|
|
Lit220SerialEventCallback,
|
|
SmartcardExtension,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
// Call the serial driver
|
|
status = IoCallDriver(
|
|
SmartcardExtension->ReaderExtension->ConnectedSerialPort,
|
|
SmartcardExtension->ReaderExtension->CardStatus.Irp
|
|
);
|
|
|
|
// Return STATUS_MORE_PROCESSING_REQUIRED so our IRP stays around
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Lit220ScheduleTimer(
|
|
IN PSMARTCARD_EXTENSION SmartcardExtension,
|
|
IN PIO_WORKITEM_ROUTINE Routine
|
|
)
|
|
{
|
|
PIO_WORKITEM workItem = IoAllocateWorkItem(
|
|
SmartcardExtension->OsData->DeviceObject
|
|
);
|
|
|
|
if (workItem != NULL) {
|
|
|
|
IoQueueWorkItem(
|
|
workItem,
|
|
Routine,
|
|
CriticalWorkQueue,
|
|
workItem
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Lit220StartTimer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_WORKITEM WorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine starts the timeout timer. The function is executed as a worker
|
|
thread so IoStartTimer does not get called at the wrong IRQL.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension->EntryCount = 0;
|
|
|
|
IoStartTimer(DeviceObject);
|
|
|
|
IoFreeWorkItem(WorkItem);
|
|
|
|
}
|
|
|
|
VOID
|
|
Lit220StopTimer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_WORKITEM WorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stops the timeout timer. The function is executed as a worker
|
|
thread so IoStopTimer does not get called at the wrong IRQL.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
IoStopTimer(DeviceObject);
|
|
|
|
IoFreeWorkItem(WorkItem);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Lit220ReceiveBlockTimeout(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is timeout callback. A timeout is setup every time we get an
|
|
incomplete block of data. Once we receive the complete block the timeout
|
|
will be canceled. The type of timer we use for the timeout gets called
|
|
once every second. We want to time out after a few seconds, so we keep track
|
|
of how many time we get called and then timeout after we have been called
|
|
5 times.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension;
|
|
PREADER_EXTENSION readerExtension = smartcardExtension->ReaderExtension;
|
|
|
|
if (readerExtension->DataByteNo == readerExtension->DataLength.l.l0) {
|
|
// Stop the timer we got all the bytes we need
|
|
Lit220ScheduleTimer(
|
|
smartcardExtension,
|
|
Lit220StopTimer
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if (++deviceExtension->EntryCount >= 5) {
|
|
SmartcardDebug(
|
|
DEBUG_ERROR,
|
|
("%s!Lit220ReceiveBlockTimeout: Communication with reader timed-out\n",
|
|
DRIVER_NAME)
|
|
);
|
|
|
|
// Process the timeout
|
|
Lit220ProcessNack(smartcardExtension);
|
|
|
|
// A timeout has occured schedule worker thread to
|
|
// stop the timer
|
|
Lit220ScheduleTimer(
|
|
smartcardExtension,
|
|
Lit220StopTimer
|
|
);
|
|
|
|
deviceExtension->EntryCount = 0;
|
|
}
|
|
}
|
|
|