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

757 lines
18 KiB
C

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) SCM Microsystems, 1998 - 1999
//
// File: serialnt.c
//
//--------------------------------------------------------------------------
#include "drivernt.h"
#include "stc.h"
#include "cbhndlr.h"
#include "drvnt5.h"
const ULONG ConfigTable[] =
{
IOCTL_SERIAL_SET_BAUD_RATE,
IOCTL_SERIAL_SET_LINE_CONTROL,
IOCTL_SERIAL_SET_CHARS,
IOCTL_SERIAL_SET_TIMEOUTS,
IOCTL_SERIAL_SET_HANDFLOW,
#if !defined( __NT4__ )
IOCTL_SERIAL_PURGE,
#endif
IOCTL_SERIAL_SET_BREAK_OFF,
IOCTL_SERIAL_SET_WAIT_MASK,
0
};
NTSTATUS
IFInitializeInterface(
PREADER_EXTENSION ReaderExtension,
PVOID ConfigData
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS NTStatus = STATUS_SUCCESS;
ULONG OutDataLen;
PVOID OutData;
PULONG ActIoctl;
PSERIAL_PORT_CONFIG SerialPortConfig = (PSERIAL_PORT_CONFIG) ConfigData;
//
// set all parameters defined in config table
//
ActIoctl = (PULONG) ConfigTable;
do
{
switch( *ActIoctl )
{
case IOCTL_SERIAL_SET_BAUD_RATE:
OutData = &SerialPortConfig->BaudRate;
OutDataLen = sizeof( SERIAL_BAUD_RATE );
break;
case IOCTL_SERIAL_SET_LINE_CONTROL:
OutData = &SerialPortConfig->LineControl;
OutDataLen = sizeof( SERIAL_LINE_CONTROL );
break;
case IOCTL_SERIAL_SET_CHARS:
OutData = &SerialPortConfig->SerialChars;
OutDataLen = sizeof( SERIAL_CHARS );
break;
case IOCTL_SERIAL_SET_TIMEOUTS:
OutData = &SerialPortConfig->Timeouts;
OutDataLen = sizeof( SERIAL_TIMEOUTS );
break;
case IOCTL_SERIAL_SET_HANDFLOW:
OutData = &SerialPortConfig->HandFlow;
OutDataLen = sizeof( SERIAL_HANDFLOW );
break;
case IOCTL_SERIAL_SET_WAIT_MASK:
OutData = &SerialPortConfig->WaitMask;
OutDataLen = sizeof( ULONG );
break;
case IOCTL_SERIAL_PURGE:
OutData = &SerialPortConfig->Purge;
OutDataLen = sizeof( ULONG );
break;
case IOCTL_SERIAL_SET_BREAK_OFF:
OutData = NULL;
OutDataLen = 0;
break;
}
NTStatus = IFSerialIoctl(
ReaderExtension,
*ActIoctl,
OutData,
OutDataLen,
NULL,
0
);
SysDelay(25);
} while( *(++ActIoctl) && ( NTStatus == STATUS_SUCCESS ));
if( NTStatus == STATUS_SUCCESS )
{
// initialize the read thread
NTStatus = IFSerialWaitOnMask( ReaderExtension );
}
return( NTStatus );
}
NTSTATUS
IFWrite(
PREADER_EXTENSION ReaderExtension,
PUCHAR OutData,
ULONG OutDataLen
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS NTStatus = STATUS_INSUFFICIENT_RESOURCES;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
PIRP Irp;
PIO_STACK_LOCATION IrpStack;
if (KeReadStateEvent(&(ReaderExtension->SerialCloseDone))) {
//
// we have no connection to serial, fail the call
// this could be the case if the reader was removed
// during stand by / hibernation
//
return STATUS_UNSUCCESSFUL;
}
ReaderExtension->Available = 0;
KeInitializeEvent( &Event, NotificationEvent, FALSE );
//
// build irp to be send to serial driver
//
Irp = IoBuildDeviceIoControlRequest(
SERIAL_WRITE,
ReaderExtension->SerialDeviceObject,
OutData,
OutDataLen,
NULL,
0,
FALSE,
&Event,
&IoStatus
);
if( Irp != NULL )
{
IrpStack = IoGetNextIrpStackLocation( Irp );
IrpStack->MajorFunction = IRP_MJ_WRITE;
IrpStack->Parameters.Write.Length = OutDataLen;
IrpStack->Parameters.Write.ByteOffset.QuadPart = 0;
NTStatus = IoCallDriver( ReaderExtension->SerialDeviceObject, Irp );
if( NTStatus == STATUS_PENDING )
{
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
NTStatus = IoStatus.Status;
}
}
return( NTStatus );
}
NTSTATUS
IFRead(
PREADER_EXTENSION ReaderExtension,
PUCHAR InData,
ULONG InDataLen
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS NTStatus = STATUS_UNSUCCESSFUL;
KIRQL CurrentIrql;
// acquire spinlock to protect buffer/flag manipulation
KeAcquireSpinLock( &ReaderExtension->ReadSpinLock, &CurrentIrql );
// check if data already available
if( ReaderExtension->Available >= InDataLen )
{
NTStatus = STATUS_SUCCESS;
}
else
{
LARGE_INTEGER Timeout;
// setup read thread
ReaderExtension->Expected = InDataLen;
KeClearEvent( &ReaderExtension->DataAvailable );
KeReleaseSpinLock( &ReaderExtension->ReadSpinLock, CurrentIrql );
// setup wait time (in 100 ns)
Timeout.QuadPart =
(LONGLONG) ReaderExtension->ReadTimeout * -10L * 1000;
NTStatus = KeWaitForSingleObject(
&ReaderExtension->DataAvailable,
Executive,
KernelMode,
FALSE,
&Timeout
);
KeAcquireSpinLock(&ReaderExtension->ReadSpinLock, &CurrentIrql);
// reset the read queue
KeClearEvent(&ReaderExtension->DataAvailable);
}
if( NTStatus == STATUS_SUCCESS )
{
if (ReaderExtension->Available >= InDataLen) {
SysCopyMemory(
InData,
&ReaderExtension->TPDUStack[0],
InDataLen
);
ReaderExtension->Available -= InDataLen;
SysCopyMemory(
&ReaderExtension->TPDUStack[ 0 ],
&ReaderExtension->TPDUStack[ InDataLen ],
ReaderExtension->Available
);
} else {
//
// oops, that should not happen.
// InDataLen should not be bigger than
// the number of bytes available
//
ASSERT(FALSE);
NTStatus = STATUS_IO_TIMEOUT;
}
}
else
{
// ReaderExtension->Available = 0;;
NTStatus = STATUS_IO_TIMEOUT;
}
if( NTStatus != STATUS_SUCCESS )
{
NTStatus = STATUS_IO_TIMEOUT;
}
ReaderExtension->Expected = 0;
KeReleaseSpinLock( &ReaderExtension->ReadSpinLock, CurrentIrql );
return( NTStatus );
}
NTSTATUS
IFSerialIoctl(
PREADER_EXTENSION ReaderExtension,
ULONG IoctlCode,
PVOID OutData,
ULONG OutDataLen,
PVOID InData,
ULONG InDataLen
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS NTStatus = STATUS_INSUFFICIENT_RESOURCES;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
PIRP Irp;
if (KeReadStateEvent(&(ReaderExtension->SerialCloseDone))) {
//
// we have no connection to serial, fail the call
// this could be the case if the reader was removed
// during stand by / hibernation
//
return STATUS_UNSUCCESSFUL;
}
KeInitializeEvent( &Event, NotificationEvent, FALSE );
//
// build irp to be send to serial driver
//
Irp = IoBuildDeviceIoControlRequest(
IoctlCode,
ReaderExtension->SerialDeviceObject,
OutData,
OutDataLen,
InData,
InDataLen,
FALSE,
&Event,
&IoStatus
);
if( Irp != NULL )
{
NTStatus = IoCallDriver( ReaderExtension->SerialDeviceObject, Irp );
if( NTStatus == STATUS_PENDING )
{
LARGE_INTEGER Timeout;
Timeout.QuadPart =
(LONGLONG) ReaderExtension->ReadTimeout * -10 * 1000;
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
&Timeout
);
NTStatus = IoStatus.Status;
}
}
return( NTStatus );
}
NTSTATUS
IFSerialRead(
PREADER_EXTENSION ReaderExtension,
PUCHAR InData,
ULONG InDataLen
)
{
NTSTATUS NTStatus = STATUS_INSUFFICIENT_RESOURCES;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
PIRP Irp;
PIO_STACK_LOCATION IrpStack;
if (KeReadStateEvent(&(ReaderExtension->SerialCloseDone))) {
//
// we have no connection to serial, fail the call
// this could be the case if the reader was removed
// during stand by / hibernation
//
return STATUS_UNSUCCESSFUL;
}
// build irp to be send to serial driver
KeInitializeEvent( &Event, NotificationEvent, FALSE );
Irp = IoBuildDeviceIoControlRequest(
SERIAL_READ,
ReaderExtension->SerialDeviceObject,
NULL,
0,
InData,
InDataLen,
FALSE,
&Event,
&IoStatus
);
if( Irp != NULL )
{
IrpStack = IoGetNextIrpStackLocation( Irp );
IrpStack->MajorFunction = IRP_MJ_READ;
IrpStack->Parameters.Read.Length = InDataLen;
NTStatus = IoCallDriver( ReaderExtension->SerialDeviceObject, Irp );
if( NTStatus == STATUS_PENDING )
{
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
NULL
);
NTStatus = IoStatus.Status;
}
}
return( NTStatus );
}
NTSTATUS
IFSerialWaitOnMask( PREADER_EXTENSION ReaderExtension )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
NTSTATUS NTStatus;
if (KeReadStateEvent(&(ReaderExtension->SerialCloseDone))) {
//
// we have no connection to serial, fail the call
// this could be the case if the reader was removed
// during stand by / hibernation
//
return STATUS_UNSUCCESSFUL;
}
irp = IoAllocateIrp(
(CCHAR) (ReaderExtension->SerialDeviceObject->StackSize + 1),
FALSE
);
ASSERT(irp != NULL);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
irpSp->Parameters.DeviceIoControl.OutputBufferLength =
sizeof(ReaderExtension->EventMask);
irpSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SERIAL_WAIT_ON_MASK;
irp->AssociatedIrp.SystemBuffer = &ReaderExtension->EventMask;
// set completion routine & start io
IoSetCompletionRoutine(
irp,
IFReadThreadCallback,
ReaderExtension,
TRUE,
TRUE,
TRUE
);
NTStatus = IoCallDriver( ReaderExtension->SerialDeviceObject, irp );
return (NTStatus == STATUS_PENDING ? STATUS_SUCCESS : NTStatus);
}
NTSTATUS
IFReadThreadCallback(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PREADER_EXTENSION ReaderExtension
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL irql;
// event_rx?
if( ReaderExtension->EventMask & SERIAL_EV_RXCHAR )
{
IoQueueWorkItem(
ReaderExtension->ReadWorkItem,
(PIO_WORKITEM_ROUTINE) IFReadWorkRoutine,
CriticalWorkQueue,
ReaderExtension
);
}
else
{
SmartcardDebug(
DEBUG_TRACE,
("SCMSTCS!IFReadThreadCallback: Device removed\n" )
);
KeAcquireSpinLock(&ReaderExtension->SmartcardExtension->OsData->SpinLock,
&irql);
ReaderExtension->SmartcardExtension->ReaderCapabilities.CurrentState =
(ULONG) SCARD_UNKNOWN;
KeReleaseSpinLock(&ReaderExtension->SmartcardExtension->OsData->SpinLock,
irql);
// last call: disconnect from the serial driver
IoQueueWorkItem(
ReaderExtension->CloseSerial,
(PIO_WORKITEM_ROUTINE) DrvWaitForDeviceRemoval,
DelayedWorkQueue,
NULL
);
}
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
IFReadWorkRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PREADER_EXTENSION ReaderExtension
)
{
NTSTATUS NTStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpStack;
PIRP Irp;
SERIAL_STATUS CommStatus;
PUCHAR IOData;
BOOLEAN purgePort = FALSE;
BOOLEAN bUpdatCardState = FALSE;
USHORT shrtBuf;
ASSERT( ReaderExtension != NULL );
if (ReaderExtension == NULL) {
return;
}
__try {
IOData = &ReaderExtension->IOData[0];
while (NTStatus == STATUS_SUCCESS) {
// read head
NTStatus = IFSerialRead(
ReaderExtension,
&IOData[0],
3
);
if (NTStatus != STATUS_SUCCESS) {
__leave;
}
if ((IOData[NAD_IDX] & 0x20) != 0x20) {
SmartcardDebug(
DEBUG_ERROR,
("SCMSTCS!IFReadWorkRoutine: Invalid packet received\n" )
);
purgePort = TRUE;
__leave;
}
if (IOData[LEN_IDX] > STC_BUFFER_SIZE - 4) {
purgePort = TRUE;
__leave;
}
// read tail
NTStatus = IFSerialRead(
ReaderExtension,
&IOData[DATA_IDX],
IOData[LEN_IDX] + 1
);
ASSERT(NTStatus == STATUS_SUCCESS);
if (NTStatus != STATUS_SUCCESS) {
purgePort = TRUE;
__leave;
}
if (IOData[LEN_IDX] == 0) {
purgePort = TRUE;
__leave;
}
// check for card insertion / removal
RtlRetrieveUshort(&shrtBuf, &IOData[DATA_IDX]);
if( ( IOData[NAD_IDX] == STC1_TO_HOST ) &&
( IOData[LEN_IDX] == 2 ) &&
( (shrtBuf == SW_INSERTED) ||
(shrtBuf == SW_REMOVED))) {
CBUpdateCardState(
ReaderExtension->SmartcardExtension,
(shrtBuf == SW_INSERTED ? SCARD_PRESENT : SCARD_ABSENT)
);
} else {
KIRQL CurrentIrql;
// acquire spinlock to protect buffer/flag manipulation
KeAcquireSpinLock(
&ReaderExtension->ReadSpinLock,
&CurrentIrql
);
// check size & copy data to TPDU stack
ASSERT(
ReaderExtension->Available+IOData[LEN_IDX] + 4 <
TPDU_STACK_SIZE
);
if (ReaderExtension->Available + IOData[LEN_IDX] + 4 <
TPDU_STACK_SIZE ) {
SysCopyMemory(
&ReaderExtension->TPDUStack[ReaderExtension->Available],
&IOData[ 0 ],
IOData[ LEN_IDX ] + 4
);
ReaderExtension->Available +=
IOData[LEN_IDX] + 4;
if(ReaderExtension->Available >= ReaderExtension->Expected ) {
KeSetEvent(
&ReaderExtension->DataAvailable,
IO_SERIAL_INCREMENT,
FALSE
);
}
}
KeReleaseSpinLock( &ReaderExtension->ReadSpinLock, CurrentIrql );
}
}
}
__finally {
if (purgePort) {
ULONG request;
KIRQL CurrentIrql;
// acquire spinlock to protect buffer/flag manipulation
KeAcquireSpinLock(
&ReaderExtension->ReadSpinLock,
&CurrentIrql
);
ReaderExtension->Available = 0;
KeReleaseSpinLock(
&ReaderExtension->ReadSpinLock,
CurrentIrql
);
// we got an error and need to clean up the port
request = SR_PURGE;
NTStatus = IFSerialIoctl(
ReaderExtension,
IOCTL_SERIAL_PURGE,
&request,
sizeof(request),
NULL,
0
);
ASSERT(NTStatus == STATUS_SUCCESS);
}
IFSerialWaitOnMask( ReaderExtension );
}
}
UCHAR IFCalcLRC( PUCHAR IOData, ULONG IODataLen )
{
ULONG Idx = 0;
UCHAR CS = 0;
do CS ^= IOData[ Idx ];
while( ++Idx < IODataLen );
return( CS );
}
//---------------------------------------- END OF FILE ----------------------------------------