|
|
//+-------------------------------------------------------------------------
//
// 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:
--*/ {
// 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" ) );
ReaderExtension->SmartcardExtension->ReaderCapabilities.CurrentState = (ULONG) SCARD_UNKNOWN; // 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 ----------------------------------------
|