/*++ Copyright (c) 1997 Rainbow Technologies Inc. Module Name: RNBO3531.c Abstract: Author: Mehdi Sotoodeh mehdi@rainbow.com Environment: Kernel mode Revision History : Jan 98 - Started from the DDK sources from Klaus Schutz Mar 98 - PnP support added Apr 98 - Changes on card timings and PnP - Added power management May 98 - RemoveLock changes - RBSCT1Transmit timeout bug fixed - Sent to MicroSoft May 98 - Added variable baudrate support - Reduced ATR response time - Sent to MicroSoft - Data transmission size of 256 bug fixed. Jun 98 - Power management update - RBSCReaderPower and RBSCCardTracking moved to non-paged code (use of KeAcquireSpinLock/IoAcquireCancelSpinLock) - fixed: completeion function when IoSkipCurrentIrpStackLocation is called. - turned off PowerRequest flag on exit from RBSCReaderPower. - Sent to MicroSoft Sep 98 - Power management update - Sent to MicroSoft Oct 98 - warning fixes for win64 compatibility. Nov 98 - Set to default when PTS fails. - Mondex fix - Sent to MicroSoft Dec 98 - Reties removed from the reset operations - Sent to MicroSoft Apr 99 - Misc. changes --*/ #include #include "RNBO3531.h" // // We do not need these functions after init anymore // #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGEABLE, RBSCAddDevice) #pragma alloc_text(PAGEABLE, RBSCUnloadDriver) #pragma alloc_text(PAGEABLE, RBSCRemoveDevice) #pragma alloc_text(PAGEABLE, RBSCTransmit) #pragma alloc_text(PAGEABLE, RBSCSetProtocol) #pragma alloc_text(PAGEABLE, RBSCT0Transmit) #pragma alloc_text(PAGEABLE, RBSCT1Transmit) #if DBG #pragma optimize ("", off) #endif UCHAR GetReaderType[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_GET_TYPE}; UCHAR CardPowerDown[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_PWR_OFF}; UCHAR CardPowerUp[] = {IFDCMD_HEADER1,IFDCMD_HEADER2,IFDCMD_PWR_ON}; #ifdef NT4 #define SmartcardAcquireRemoveLockWithTag(x,y) SmartcardAcquireRemoveLock(x) #define SmartcardReleaseRemoveLockWithTag(x,y) SmartcardReleaseRemoveLock(x) #endif #ifndef NT5 DWORD UsedPortsMask = 0; // Bit mask for used ports #endif // // Table to convert inverse convention data // static UCHAR InverseCharTable[256] = { 0xFF,0x7F,0xBF,0x3F,0xDF,0x5F,0x9F,0x1F, 0xEF,0x6F,0xAF,0x2F,0xCF,0x4F,0x8F,0x0F, 0xF7,0x77,0xB7,0x37,0xD7,0x57,0x97,0x17, 0xE7,0x67,0xA7,0x27,0xC7,0x47,0x87,0x07, 0xFB,0x7B,0xBB,0x3B,0xDB,0x5B,0x9B,0x1B, 0xEB,0x6B,0xAB,0x2B,0xCB,0x4B,0x8B,0x0B, 0xF3,0x73,0xB3,0x33,0xD3,0x53,0x93,0x13, 0xE3,0x63,0xA3,0x23,0xC3,0x43,0x83,0x03, 0xFD,0x7D,0xBD,0x3D,0xDD,0x5D,0x9D,0x1D, 0xED,0x6D,0xAD,0x2D,0xCD,0x4D,0x8D,0x0D, 0xF5,0x75,0xB5,0x35,0xD5,0x55,0x95,0x15, 0xE5,0x65,0xA5,0x25,0xC5,0x45,0x85,0x05, 0xF9,0x79,0xB9,0x39,0xD9,0x59,0x99,0x19, 0xE9,0x69,0xA9,0x29,0xC9,0x49,0x89,0x09, 0xF1,0x71,0xB1,0x31,0xD1,0x51,0x91,0x11, 0xE1,0x61,0xA1,0x21,0xC1,0x41,0x81,0x01, 0xFE,0x7E,0xBE,0x3E,0xDE,0x5E,0x9E,0x1E, 0xEE,0x6E,0xAE,0x2E,0xCE,0x4E,0x8E,0x0E, 0xF6,0x76,0xB6,0x36,0xD6,0x56,0x96,0x16, 0xE6,0x66,0xA6,0x26,0xC6,0x46,0x86,0x06, 0xFA,0x7A,0xBA,0x3A,0xDA,0x5A,0x9A,0x1A, 0xEA,0x6A,0xAA,0x2A,0xCA,0x4A,0x8A,0x0A, 0xF2,0x72,0xB2,0x32,0xD2,0x52,0x92,0x12, 0xE2,0x62,0xA2,0x22,0xC2,0x42,0x82,0x02, 0xFC,0x7C,0xBC,0x3C,0xDC,0x5C,0x9C,0x1C, 0xEC,0x6C,0xAC,0x2C,0xCC,0x4C,0x8C,0x0C, 0xF4,0x74,0xB4,0x34,0xD4,0x54,0x94,0x14, 0xE4,0x64,0xA4,0x24,0xC4,0x44,0x84,0x04, 0xF8,0x78,0xB8,0x38,0xD8,0x58,0x98,0x18, 0xE8,0x68,0xA8,0x28,0xC8,0x48,0x88,0x08, 0xF0,0x70,0xB0,0x30,0xD0,0x50,0x90,0x10, 0xE0,0x60,0xA0,0x20,0xC0,0x40,0x80,0x00 }; #if DBG void _stdcall DumpData( PUCHAR Msg, PUCHAR Buffer, ULONG Length ) /*++ Routine Description: This function formats the debug output and shows it on the debug screen Arguments: Buffer - pointer to the buffer which contains the debug data Length - length of the datablock Return Value: Note: Output Format: 0000 : FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 0010 : FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 0020 : ..... --*/ { ULONG i, n; char buff[80]; n = sprintf( buff, "RNBO3531!%s = %ld", Msg, Length ); for( i = 0; i < Length; i++ ) { if( (i & 15) == 0 ) { SmartcardDebug( DEBUG_PROTOCOL, ("%s\n", buff) ); n = sprintf( buff, "%04X :", i ); } n += sprintf( buff+n, " %02X", Buffer[i] ); if( ((i + 1) & 7) == 0 ) { n += sprintf( buff+n, " " ); } } SmartcardDebug( DEBUG_PROTOCOL, ("%s\n", buff) ); } #else #define DumpData(Msg,Buffer,Length) #endif VOID RBSCDelay( ULONG waitTime ) /*++ Routine Description: Delay function. Arguments: waitTime - Waiting time in micro-seconds Return Value: None --*/ { if( waitTime ) { LARGE_INTEGER delayPeriod; delayPeriod.HighPart = -1; delayPeriod.LowPart = waitTime * (-10); // units of 100nsec KeDelayExecutionThread( KernelMode, FALSE, &delayPeriod ); } } NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, 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. --*/ { #ifndef NT5 RTL_QUERY_REGISTRY_TABLE paramTable[2]; ULONG i, noOfDevices = 0; DWORD UsedPorts = 1; // COM1 as default NTSTATUS status; #endif SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!DriverEntry: Enter - %s %s\n", __DATE__, __TIME__) ); // // Initialize the Driver Object with driver's entry points // DriverObject->DriverUnload = RBSCUnloadDriver; DriverObject->MajorFunction[IRP_MJ_CREATE] = RBSCCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = RBSCCreateClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = RBSCCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RBSCDeviceControl; #ifdef NT5 DriverObject->DriverExtension->AddDevice = RBSCAddDevice; DriverObject->MajorFunction[IRP_MJ_POWER] = RBSCPower; DriverObject->MajorFunction[IRP_MJ_PNP] = RBSCPnPDeviceControl; #else RtlZeroMemory( paramTable, sizeof(paramTable) ); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"ComPorts"; paramTable[0].EntryContext = &UsedPorts; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &UsedPorts; paramTable[0].DefaultLength = 0; // // now try to find the entry in the registry // RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, RegistryPath->Buffer, ¶mTable[0], NULL, NULL ); // // We now search the registry for DeviceN value // for( i = 0; noOfDevices < MAXIMUM_SERIAL_READERS && UsedPorts != 0; i++, UsedPorts >>= 1 ) { PFILE_OBJECT SerialFileObject; PDEVICE_OBJECT SerialDeviceObject; UNICODE_STRING SerialDeviceName; while( (UsedPorts & 1) == 0 ) { UsedPorts >>= 1; i++; } RtlInitUnicodeString( &SerialDeviceName, L"\\Device\\Serial0" ); // // create string like \Device\SerialN // SerialDeviceName.Buffer[14] = L'0' + (WCHAR) i; // // Try to create a device with the just created device name // It is possible that a smart card device with this name // already exists from a previous call. // It is possible to have up to 4 devices in a system // status = IoGetDeviceObjectPointer( &SerialDeviceName, FILE_ALL_ACCESS, &SerialFileObject, &SerialDeviceObject ); if( !NT_SUCCESS(status) ) { UNICODE_STRING comPortNumber; WCHAR buffer[2]; // // Extract the com-port-number // comPortNumber.Buffer = buffer; comPortNumber.Length = sizeof(WCHAR); comPortNumber.MaximumLength = sizeof(buffer); comPortNumber.Buffer[0] = SerialDeviceName.Buffer[14] + (WCHAR)1; // // Write an event-log msg that this com port is not available // SmartcardLogError( DriverObject, RBSC_CANT_CONNECT_TO_SERIAL_PORT, &comPortNumber, status ); continue; } // // Now try to create a device and connect to the serial port // status = RBSCAddDevice( DriverObject, SerialDeviceObject ); if( status == STATUS_SUCCESS ) { // // We have successfully created a device // PREADER_EXTENSION ReaderExtension = DriverObject->DeviceObject->DeviceExtension; // // Save the serial port number this device is connected to // ReaderExtension->SmartcardExtension.ReaderCapabilities.Channel = i + 1; // // The fileObject is used in the unload function for // dereferencing // ReaderExtension->SerialFileObject = SerialFileObject; noOfDevices++; } else { // // We were unable to get the reader type. // So remove the device. // ObDereferenceObject( SerialFileObject ); SmartcardLogError( DriverObject, RBSC_CANNOT_OPEN_DEVICE, &SerialDeviceName, status ); } } if( noOfDevices == 0 ) { SmartcardLogError( DriverObject, RBSC_NO_SUCH_DEVICE, NULL, status ); return STATUS_NO_SUCH_DEVICE; } #endif return STATUS_SUCCESS; } VOID RBSCUnloadDriver( PDRIVER_OBJECT DriverObject ) /*++ Routine Description: The driver unload routine. This is called by the I/O system when the device is unloaded from memory. Arguments: DriverObject - Pointer to driver object created by system. Return Value: STATUS_SUCCESS. --*/ { #ifdef NT5 // // We do not need to do anything since all the cleanups are done by // RBSCRemoveDevice(). // #else while( DriverObject->DeviceObject ) { RBSCRemoveDevice( DriverObject->DeviceObject ); } #endif SmartcardDebug( DEBUG_INFO, ("RNBO3531!UnloadDriver\n") ); } #ifdef NT5 NTSTATUS RBSCIoCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PKEVENT Event ) /*++ Routine Description: Completion routine for an Irp sent to the serial driver. The event will be set to notify that the serial driver is done. The routine will not 'complete' the Irp, so the caller of RBSCCallSerialDriver can continue. Arguments: DeviceObject - Pointer to device object Irp - Irp to complete Event - Used for process synchronization Return Value: STATUS_CANCELLED Irp was cancelled by the IO manager STATUS_MORE_PROCESSING_REQUIRED Irp will be finished by caller of RBSCCallSerialDriver --*/ { UNREFERENCED_PARAMETER( DeviceObject ); Irp->IoStatus.Status = Irp->Cancel ? STATUS_CANCELLED : STATUS_MORE_PROCESSING_REQUIRED; KeSetEvent( Event, 0, FALSE ); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS RBSCCallSerialDriver( PDEVICE_OBJECT AttachedSerialPort, PIRP Irp ) /*++ Routine Description: Send an Irp to the serial driver and wait until the serial driver has finished the request. To make sure that the serial driver will not complete the Irp we first initialize an event and set our own completion routine for the Irp. When the serial driver has processed the Irp the completion routine will set the event and tell the IO manager that more processing is required. By waiting for the event we make sure that we continue only if the serial driver has processed the Irp completely. Arguments: AttachedSerialPort - The PDO Irp - Irp to send to the serial driver Return Value: status returned by the serial driver Note: Since this function can be called after our device is removed, do not use any of the reader extension data here. --*/ { NTSTATUS status = STATUS_SUCCESS; KEVENT Event; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!CallSerialDriver: Enter\n") ); // // Prepare everything to call the underlying driver // // // Copy our stack to the next stack location. // IoCopyCurrentIrpStackLocationToNext( Irp); // // initialize an event for process synchronization. the event is passed // to our completion routine and will be set if the serial driver is done // KeInitializeEvent( &Event, NotificationEvent, FALSE ); // // Our IoCompletionRoutine sets only our event // IoSetCompletionRoutine( Irp, RBSCIoCompletion, &Event, TRUE, TRUE, TRUE ); // // Call the serial driver // status = (IoGetCurrentIrpStackLocation( Irp )->MajorFunction == IRP_MJ_POWER) ? PoCallDriver( AttachedSerialPort, Irp ) : IoCallDriver( AttachedSerialPort, Irp ); // // Wait until the serial driver has processed the Irp // if( status == STATUS_PENDING ) { status = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, // No alert NULL ); // No timeout ASSERT( STATUS_SUCCESS == status ); status = Irp->IoStatus.Status; } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!CallSerialDriver: Exit %x\n",status) ); return status; } #endif NTSTATUS RBSCStartDevice( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Open the underlying serial driver and initialize it. Then initialize the reader hardware. Arguments: DeviceObject - Pointer to device object Return Value: STATUS_SUCCESS STATUS_NO_MEMORY status returned by LowLevel routines --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; PDEVICE_OBJECT AttachedSerialPort = ReaderExtension->AttachedSerialPort; NTSTATUS status; #ifdef NT5 PIO_STACK_LOCATION irpSp; PIRP irp = IoAllocateIrp( (CCHAR)(DeviceObject->StackSize + 1), FALSE ); ASSERT( irp != NULL ); if( irp == NULL ) { return STATUS_NO_MEMORY; } #endif _try { #ifdef NT5 PIO_STACK_LOCATION irpStack; IO_STATUS_BLOCK ioStatusBlock; // // 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. // irp->UserIosb = &ioStatusBlock; IoSetNextIrpStackLocation(irp); irpStack = IoGetCurrentIrpStackLocation( irp ); irpStack->MajorFunction = IRP_MJ_CREATE; irpStack->Parameters.Create.Options = 0; irpStack->Parameters.Create.ShareAccess = 0; irpStack->Parameters.Create.FileAttributes = 0; irpStack->Parameters.Create.EaLength = 0; status = RBSCCallSerialDriver( AttachedSerialPort, irp ); if( status != STATUS_SUCCESS ) { if( status == STATUS_SHARED_IRQ_BUSY ) { // Port is in use by another device SmartcardLogError( DeviceObject, RBSC_CANT_SHARE_IRQ, NULL, 0 ); } leave; } #endif KeClearEvent( &ReaderExtension->SerialCloseDone ); ReaderExtension->SmartcardExtension.ReaderCapabilities.CurrentState = (ULONG) SCARD_UNKNOWN; status = RBSCConfigureSerialPort( ReaderExtension ); if( status != STATUS_SUCCESS ) { leave; } // // Send a wait mask to the serial driver. // This call only sets the wait mask. // We want to be informed when CTS or DSR changes its state // #ifdef NT5 ReaderExtension->WaitMask = SERIAL_EV_CTS | SERIAL_EV_DSR; #else ReaderExtension->WaitMask = SERIAL_EV_CTS; #endif status = RBSCSerialIo( &ReaderExtension->SmartcardExtension, IOCTL_SERIAL_SET_WAIT_MASK, (PUCHAR) &ReaderExtension->WaitMask, sizeof(ReaderExtension->WaitMask), 0 ); if( status != STATUS_SUCCESS) { leave; } // // Now tell the serial driver that we want to be informed // when CTS or DSR changes its state. // Changing the lowest two bits tells IoBuildDeviceIoControlRequest // NOT to allocate a system buffer for the I/O operation. // We don't need a system buffer since we use our own buffers. // ReaderExtension->SerialStatusIrp = IoAllocateIrp( (CCHAR) (DeviceObject->StackSize + 1), FALSE ); if (ReaderExtension->SerialStatusIrp == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; leave; } irpSp = IoGetNextIrpStackLocation( ReaderExtension->SerialStatusIrp ); irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpSp->Parameters.DeviceIoControl.InputBufferLength = 0; irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ReaderExtension->WaitMask); irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_WAIT_ON_MASK; ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer = &ReaderExtension->WaitMask; // // this artificial delay is necessary to make this driver work // with digi board cards // RBSCDelay( 100000 ); // // We simulate a callback now that triggers the card supervision // RBSCSerialEvents( ReaderExtension->SmartcardExtension.OsData->DeviceObject, ReaderExtension->SerialStatusIrp, ReaderExtension ); #ifdef NT5 status = IoSetDeviceInterfaceState( &ReaderExtension->PnPDeviceName, TRUE ); if( status != STATUS_SUCCESS ) { leave; } #endif KeSetEvent( &ReaderExtension->ReaderStarted, 0, FALSE ); } _finally { if (status == STATUS_SHARED_IRQ_BUSY) { SmartcardLogError( DeviceObject, RBSC_IRQ_BUSY, NULL, status ); } #ifdef NT5 IoFreeIrp( irp ); #endif } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!StartDevice: Exit (%x)\n",status) ); return status; } VOID RBSCStopDevice( PREADER_EXTENSION ReaderExtension ) /*++ Routine Description: Waits for the device to be closed Arguments: ReaderExtension - Pointer to our device data Return Value: void --*/ { SmartcardDebug( DEBUG_TRACE, ("RNBO3531!StopDevice: Enter\n") ); if( !KeReadStateEvent( &ReaderExtension->SerialCloseDone ) ) { NTSTATUS status; // test if we ever started event tracking if( ReaderExtension->WaitMask == 0 ) { // no, we did not // We 'only' need to close the serial port RBSCCloseSerialPort( ReaderExtension->SmartcardExtension.OsData->DeviceObject, NULL ); } else { // // We now inform the serial driver that we're no longer // interested in serial events. This will also free the irp // we use for those io-completions // ReaderExtension->WaitMask = 0; status = RBSCSerialIo( &ReaderExtension->SmartcardExtension, IOCTL_SERIAL_SET_WAIT_MASK, (PUCHAR)&ReaderExtension->WaitMask, sizeof(ULONG), 0 ); ASSERT( status == STATUS_SUCCESS ); // now wait until the connetion to serial is closed status = KeWaitForSingleObject( &ReaderExtension->SerialCloseDone, Executive, KernelMode, FALSE, NULL ); ASSERT( status == STATUS_SUCCESS ); } } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!StopDevice\n") ); } VOID RBSCRemoveDevice( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Stops the device then releases all the resources used. Arguments: DeviceObject - Pointer to device object Return Value: void --*/ { NTSTATUS status; PREADER_EXTENSION ReaderExtension; PSMARTCARD_EXTENSION SmartcardExtension; PAGED_CODE(); SmartcardDebug( DEBUG_INFO, ("RNBO3531!RemoveDevice: Enter\n") ); ReaderExtension = DeviceObject->DeviceExtension; SmartcardExtension = &ReaderExtension->SmartcardExtension; if( SmartcardExtension->OsData ) { // complete pending card tracking requests (if any) RBSCCompleteCardTracking( SmartcardExtension ); ASSERT( SmartcardExtension->OsData->NotificationIrp == NULL ); #ifdef NT5 SmartcardReleaseRemoveLockAndWait( SmartcardExtension ); #endif } #ifndef NT5 // // Free the device slot (if in use) // UsedPortsMask &= ~(1<VendorAttr.UnitNo); #endif RBSCStopDevice( ReaderExtension ); #ifdef NT5 if( ReaderExtension->AttachedSerialPort ) { IoDetachDevice( ReaderExtension->AttachedSerialPort ); } if( ReaderExtension->PnPDeviceName.Buffer != NULL ) { RtlFreeUnicodeString( &ReaderExtension->PnPDeviceName ); } if( ReaderExtension->CloseSerial != NULL ) { IoFreeWorkItem(ReaderExtension->CloseSerial); } #else if( ReaderExtension->SerialFileObject ) { ObDereferenceObject( ReaderExtension->SerialFileObject ); } if( ReaderExtension->DosDeviceName.Buffer != NULL ) { // // Delete the symbolic link of the smart card reader // IoDeleteSymbolicLink( &ReaderExtension->DosDeviceName ); RtlFreeUnicodeString( &ReaderExtension->DosDeviceName ); } #endif if( SmartcardExtension->OsData != NULL ) { SmartcardExit( SmartcardExtension ); } IoDeleteDevice( DeviceObject ); SmartcardDebug( DEBUG_INFO, ("RNBO3531!RemoveDevice: Exit\n") ); } #ifdef NT5 NTSTATUS RBSCPnPDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: driver callback for pnp manager Request: Action: IRP_MN_START_DEVICE Notify the driver about the new device and start the device IRP_MN_STOP_DEVICE Free all resources used by the device and tell the driver that the device was stopped IRP_MN_QUERY_REMOVE_DEVICE If the device is opened (i.e. in use) an error will be returned to prevent the PnP manager to stop the driver IRP_MN_CANCEL_REMOVE_DEVICE just notify that we can continue without any restrictions IRP_MN_REMOVE_DEVICE notify the driver that the device was removed, stop & unload the device All other requests will be passed to the driver to ensure correct processing. Arguments: DeviceObject - Pointer to device object Irp - Irp from the PnP manager Return Value: STATUS_SUCCESS STATUS_UNSUCCESSFUL status returned by serial driver --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; PDEVICE_OBJECT AttachedSerialPort = ReaderExtension->AttachedSerialPort; NTSTATUS status; PIO_STACK_LOCATION stack; BOOLEAN deviceRemoved = FALSE, irpSkipped = FALSE; KEVENT Event; KIRQL irql; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!PnPDeviceControl: Enter (%08x)\n", IoGetCurrentIrpStackLocation( Irp )->MinorFunction) ); status = SmartcardAcquireRemoveLockWithTag( &ReaderExtension->SmartcardExtension, ' PnP' ); ASSERT( status == STATUS_SUCCESS ); Irp->IoStatus.Information = 0; if( status != STATUS_SUCCESS ) { Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } stack = IoGetCurrentIrpStackLocation( Irp ); // // Now look what the PnP manager wants... // switch( stack->MinorFunction ) { case IRP_MN_START_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_START_DEVICE\n") ); // We have to call the underlying driver first status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); ASSERT( status == STATUS_SUCCESS ); if( status == STATUS_SUCCESS) { status = RBSCStartDevice( DeviceObject ); } break; case IRP_MN_QUERY_STOP_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_QUERY_STOP_DEVICE\n") ); KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql ); if( ReaderExtension->IoCount > 0 ) { // we refuse to stop if we have pending io KeReleaseSpinLock( &ReaderExtension->SpinLock, irql ); status = STATUS_DEVICE_BUSY; break; } // stop processing requests KeClearEvent( &ReaderExtension->ReaderStarted ); KeReleaseSpinLock( &ReaderExtension->SpinLock, irql ); status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); break; case IRP_MN_CANCEL_STOP_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_CANCEL_STOP_DEVICE\n") ); status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); if( status == STATUS_SUCCESS ) { // we can continue to process requests KeSetEvent( &ReaderExtension->ReaderStarted, 0, FALSE ); } break; case IRP_MN_STOP_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_STOP_DEVICE\n") ); RBSCStopDevice( ReaderExtension ); status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); break; case IRP_MN_QUERY_REMOVE_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_QUERY_REMOVE_DEVICE\n") ); // disable the interface (and ignore possible errors) IoSetDeviceInterfaceState( &ReaderExtension->PnPDeviceName, FALSE ); // now look if someone is currently connected to us if (ReaderExtension->ReaderOpen) { // // someone is connected, fail the call // we will enable the device interface in // IRP_MN_CANCEL_REMOVE_DEVICE again // status = STATUS_UNSUCCESSFUL; break; } // pass the call to the next driver in the stack status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); break; case IRP_MN_CANCEL_REMOVE_DEVICE: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_CANCEL_REMOVE_DEVICE\n") ); status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); // // reenable the interface only in case that the reader is // still connected. This covers the following case: // hibernate machine, disconnect reader, wake up, stop device // (from task bar) and stop fails since an app. holds the device open // if( status == STATUS_SUCCESS && ReaderExtension->WaitMask != 0 ) { status = IoSetDeviceInterfaceState( &ReaderExtension->PnPDeviceName, TRUE ); ASSERT(status == STATUS_SUCCESS); } break; case IRP_MN_REMOVE_DEVICE: // Remove our device SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!PnPDeviceControl: IRP_MN_REMOVE_DEVICE\n") ); RBSCRemoveDevice( DeviceObject ); // DeviceObject is freed, do not reference it again. status = RBSCCallSerialDriver( AttachedSerialPort, Irp ); deviceRemoved = TRUE; break; default: // This is an Irp that is only useful for underlying drivers IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver( AttachedSerialPort, Irp ); irpSkipped = TRUE; break; } if( !irpSkipped ) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } if( !deviceRemoved ) { SmartcardReleaseRemoveLockWithTag( &ReaderExtension->SmartcardExtension, ' PnP' ); } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!PnPDeviceControl: Exit %x\n",status) ); return status; } #endif NTSTATUS RBSCAddDevice( PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This is the add-device routine for the miniport. Find a device slot then, create device object and attach it to the given PhysicalDeviceObject. Initialize the device extension and its smart card data. Arguments: DriverObject - a pointer to the driver object for this device PhysicalDeviceObject- The device object of the serial port to attach to Return Value: NTSTATUS --*/ { NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL; PSMARTCARD_EXTENSION SmartcardExtension = NULL; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!AddDevice: Enter\n") ); try { ULONG DeviceNo; PREADER_EXTENSION ReaderExtension; #ifndef NT5 UNICODE_STRING SmartcardDeviceName; for( DeviceNo = 0; DeviceNo < MAXIMUM_SERIAL_READERS; DeviceNo++ ) { if( (UsedPortsMask & (1<DeviceExtension; SmartcardExtension = &ReaderExtension->SmartcardExtension; SmartcardExtension->ReaderExtension = ReaderExtension; #ifdef NT5 ReaderExtension->CloseSerial = IoAllocateWorkItem( DeviceObject ); if (ReaderExtension->CloseSerial == NULL) { SmartcardLogError( DriverObject, RBSC_NO_MEMORY, NULL, 0 ); leave; } #endif // Used for stop / start notification KeInitializeEvent( &ReaderExtension->ReaderStarted, NotificationEvent, FALSE ); KeInitializeEvent( &ReaderExtension->SerialCloseDone, NotificationEvent, TRUE ); // Used to keep track of open close calls ReaderExtension->ReaderOpen = FALSE; KeInitializeSpinLock( &ReaderExtension->SpinLock ); // // enter correct version of the lib // SmartcardExtension->Version = SMCLIB_VERSION; SmartcardExtension->SmartcardRequest.BufferSize = SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE; status = SmartcardInitialize( SmartcardExtension ); if( status != STATUS_SUCCESS ) { SmartcardLogError( DriverObject, (SmartcardExtension->OsData ? RBSC_WRONG_LIB_VESION : RBSC_NO_MEMORY), NULL, 0 ); leave; } // // Set up call back functions // SmartcardExtension->ReaderFunction[ RDF_TRANSMIT ] = RBSCTransmit; SmartcardExtension->ReaderFunction[ RDF_SET_PROTOCOL ] = RBSCSetProtocol; SmartcardExtension->ReaderFunction[ RDF_CARD_POWER ] = RBSCReaderPower; SmartcardExtension->ReaderFunction[ RDF_CARD_TRACKING ] = RBSCCardTracking; // // setup smartcard extension // // // Initialize the vendor information // strcpy( SmartcardExtension->VendorAttr.VendorName.Buffer, VENDOR_NAME ); SmartcardExtension->VendorAttr.VendorName.Length = sizeof(VENDOR_NAME); strcpy( SmartcardExtension->VendorAttr.IfdType.Buffer, READER_NAME ); SmartcardExtension->VendorAttr.IfdType.Length = sizeof(READER_NAME); SmartcardExtension->VendorAttr.UnitNo = MAXULONG; for (DeviceNo = 0; DeviceNo < MAXULONG; DeviceNo++) { PDEVICE_OBJECT devObj; for (devObj = DeviceObject; devObj != NULL; devObj = devObj->NextDevice) { PREADER_EXTENSION devExt = devObj->DeviceExtension; if (DeviceNo == devExt->SmartcardExtension.VendorAttr.UnitNo) { break; } } if (devObj == NULL) { SmartcardExtension->VendorAttr.UnitNo = DeviceNo; break; } } // // tell the lib our device object // SmartcardExtension->OsData->DeviceObject = DeviceObject; #ifdef NT5 ReaderExtension->AttachedSerialPort = IoAttachDeviceToDeviceStack( DeviceObject, PhysicalDeviceObject ); if( ReaderExtension->AttachedSerialPort == NULL ) { SmartcardLogError( DriverObject, RBSC_CANT_ATTACH_TO_SERIAL_PORT, &DriverObject->DriverName, 0 ); status = STATUS_UNSUCCESSFUL; leave; } // register our new device status = IoRegisterDeviceInterface( PhysicalDeviceObject, &SmartCardReaderGuid, NULL, &ReaderExtension->PnPDeviceName ); ASSERT( status == STATUS_SUCCESS ); ReaderExtension->ReaderPowerState = PowerReaderWorking; #else // // Save the deviceObject for the connected serial port // ReaderExtension->AttachedSerialPort = PhysicalDeviceObject; status = RBSCStartDevice( DeviceObject ); if( status != STATUS_SUCCESS ) { leave; } // // Create a symbolic link // status = SmartcardCreateLink( &ReaderExtension->DosDeviceName, &SmartcardDeviceName ); #endif if( status != STATUS_SUCCESS ) { leave; } // // tell the OS that we supposed to do buffered io // #ifdef NT5 DeviceObject->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE); #else DeviceObject->Flags |= DO_BUFFERED_IO; UsedPortsMask |= (1<Flags &= ~DO_DEVICE_INITIALIZING; SmartcardDebug( DEBUG_INFO, ("RNBO3531!AddDevice: Device #%d created Ext=%08x.\n", DeviceNo,ReaderExtension) ); } finally { if( status != STATUS_SUCCESS && DeviceObject != NULL ) { RBSCRemoveDevice( DeviceObject ); } } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!AddDevice: Exit %x\n",status) ); return status; } NTSTATUS RBSCConfigureSerialPort( PREADER_EXTENSION ReaderExtension ) /*++ Routine Description: This routine reads the default configuraton values for this device. Arguments: ReaderExtension - Pointer to our device data Return Value: none --*/ { PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension; PUCHAR RxBuffer = SmartcardExtension->SmartcardReply.Buffer; NTSTATUS status; { // // Set up baudrate // SERIAL_BAUD_RATE BaudRate; BaudRate.BaudRate = DATARATE_DEFAULT; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_BAUD_RATE, (PUCHAR) &BaudRate, sizeof(SERIAL_BAUD_RATE), 0 ); } if( status == STATUS_SUCCESS ) { // // Set up line control parameters // ReaderExtension->LineControl.Parity = ODD_PARITY; ReaderExtension->LineControl.StopBits = STOP_BIT_1; ReaderExtension->LineControl.WordLength = SERIAL_DATABITS_8; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_LINE_CONTROL, (PUCHAR)&ReaderExtension->LineControl, sizeof(SERIAL_LINE_CONTROL), 0 ); } if( status == STATUS_SUCCESS ) { // // Set serial special characters // SERIAL_CHARS SerialChars; SerialChars.ErrorChar = 0; SerialChars.EofChar = 0; SerialChars.EventChar = 0; SerialChars.XonChar = 0; SerialChars.XoffChar = 0; SerialChars.BreakChar = 0xFF; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_CHARS, (PUCHAR) &SerialChars, sizeof(SERIAL_CHARS), 0 ); } if( status == STATUS_SUCCESS ) { // // Set up timeouts (all in msec) // ReaderExtension->SerialTimeouts.ReadTotalTimeoutMultiplier = 1; ReaderExtension->SerialTimeouts.ReadIntervalTimeout = 2* ATR_CHAR_TIMEOUT/1000; ReaderExtension->SerialTimeouts.ReadTotalTimeoutConstant = 2* ATR_BLOCK_TIMEOUT/1000; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_TIMEOUTS, (PUCHAR)&ReaderExtension->SerialTimeouts, sizeof(SERIAL_TIMEOUTS), 0 ); } if( status == STATUS_SUCCESS ) { // // Set flowcontrol and handshaking // SERIAL_HANDFLOW HandFlow; HandFlow.XonLimit = 0; HandFlow.XoffLimit = 0; HandFlow.FlowReplace = SERIAL_XOFF_CONTINUE ; HandFlow.ControlHandShake = 0; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_HANDFLOW, (PUCHAR) &HandFlow, sizeof(SERIAL_HANDFLOW), 0 ); } if( status == STATUS_SUCCESS ) { // // Set break off // status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_BREAK_OFF, NULL, 0, 0 ); } if( status == STATUS_SUCCESS ) { status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_RTS, NULL, 0, 0 ); } if( status == STATUS_SUCCESS ) { status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_DTR, NULL, 0, 0 ); } if( status == STATUS_SUCCESS ) { // // Toggle DTR line to wake-up the reader if it is // in SLEEP mode // RBSCDelay( 10000 ); status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_CLR_DTR, NULL, 0, 0 ); } if( status == STATUS_SUCCESS ) { status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_DTR, NULL, 0, 0 ); } if( status == STATUS_SUCCESS ) { RBSCDelay( 10000 ); status = RBSCPacketExchange( SmartcardExtension, GetReaderType, sizeof(GetReaderType), WAIT_TIME_READER_TYPE, TRUE ); } if( status != STATUS_SUCCESS ) { return status; } if( SmartcardExtension->SmartcardReply.BufferLength < SIZE_READER_TYPE || RxBuffer[0] != 'R' || RxBuffer[1] != 'N' || RxBuffer[2] != 'B' || RxBuffer[3] != 'O' || RxBuffer[15] != READER_TYPE_1 ) { return STATUS_DEVICE_DATA_ERROR; } // // Set reader info // SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = (UCHAR)RxBuffer[6]; SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = (UCHAR)RxBuffer[7]; SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = ((USHORT)RxBuffer[8]<<8) + ((USHORT)RxBuffer[9]); // // Clk frequency in KHz encoded as little endian integer // SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = ((USHORT)RxBuffer[10]<<8) + ((USHORT)RxBuffer[11]); SmartcardExtension->ReaderCapabilities.DataRate.Default = SmartcardExtension->ReaderCapabilities.DataRate.Max = DATARATE_DEFAULT; if( RxBuffer[13] != 0 ) { // reader is supposed to support higher data rates ULONG NumRates = 1; if( RxBuffer[13] & DATARATE_14400 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 14400; } if( RxBuffer[13] & DATARATE_19200 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 19200; } if( RxBuffer[13] & DATARATE_28800 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 28800; } if( RxBuffer[13] & DATARATE_38400 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 38400; } if( RxBuffer[13] & DATARATE_57600 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 57600; } if( RxBuffer[13] & DATARATE_115200 ) { SmartcardExtension->ReaderCapabilities.DataRate.Max = ReaderExtension->DataRatesSupported[ NumRates++ ] = 115200; } if( NumRates > 1 ) { ReaderExtension->DataRatesSupported[0] = 9600; // always supported SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries = (UCHAR) NumRates; SmartcardExtension->ReaderCapabilities.DataRatesSupported.List = ReaderExtension->DataRatesSupported; SmartcardDebug( DEBUG_INFO, ("RNBO3531!ConfigureSerialPort: %ld DataRates, Max=%ld\n", NumRates, SmartcardExtension->ReaderCapabilities.DataRate.Max) ); } } SmartcardExtension->ReaderCapabilities.MaxIFSD = MAX_IFSD; // // setup smartcard extension - reader capabilities // SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; SmartcardExtension->ReaderCapabilities.ReaderType = SCARD_READER_TYPE_SERIAL; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_GET_MODEMSTATUS, NULL, 0, sizeof(ReaderExtension->ModemStatus) ); if( status == STATUS_SUCCESS ) { ReaderExtension->ModemStatus = *(PULONG)SmartcardExtension->SmartcardReply.Buffer; SmartcardExtension->ReaderCapabilities.CurrentState = (ReaderExtension->ModemStatus & SERIAL_CTS_STATE) ? SCARD_SWALLOWED : SCARD_ABSENT; } return STATUS_SUCCESS; } NTSTATUS RBSCCreateClose( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system when the device is opened or closed. Arguments: DeviceObject - Pointer to device object Irp - IRP involved. Return Value: STATUS_SUCCESS. --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; __try { if( irpStack->MajorFunction == IRP_MJ_CREATE ) { SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Create\n") ); #ifdef NT5 if( SmartcardAcquireRemoveLockWithTag( SmartcardExtension, 'lCrC' ) != STATUS_SUCCESS ) { status = STATUS_DEVICE_REMOVED; __leave; } // test if the device has been opened already if (InterlockedCompareExchange( &ReaderExtension->ReaderOpen, TRUE, FALSE) == FALSE) { SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!CreateClose: Open\n") ); } else { // the device is already in use status = STATUS_UNSUCCESSFUL; // release the lock SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'lCrC' ); } #endif } else { SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Close\n") ); #ifdef NT5 SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'lCrC' ); #endif ReaderExtension->ReaderOpen = FALSE; } } __finally { Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } NTSTATUS RBSCCancel( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system when the irp should be cancelled Arguments: DeviceObject - Pointer to device object Irp - IRP involved. Return Value: STATUS_CANCELLED --*/ { PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension = &deviceExtension->SmartcardExtension; SmartcardDebug( DEBUG_TRACE, ("%s!RBSCCancel: Enter\n", DRIVER_NAME) ); ASSERT(Irp == SmartcardExtension->OsData->NotificationIrp); IoReleaseCancelSpinLock( Irp->CancelIrql ); RBSCCompleteCardTracking(SmartcardExtension); SmartcardDebug( DEBUG_TRACE, ("%s!RBSCCancel: Exit\n", DRIVER_NAME) ); return STATUS_CANCELLED; } NTSTATUS RBSCCleanup( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system when the calling thread terminates Arguments: DeviceObject - Pointer to device object Irp - IRP involved. Return Value: STATUS_CANCELLED --*/ { PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension = &deviceExtension->SmartcardExtension; NTSTATUS status = STATUS_SUCCESS; SmartcardDebug( DEBUG_TRACE, ("%s!RBSCCleanup: Enter\n", DRIVER_NAME) ); ASSERT(Irp != SmartcardExtension->OsData->NotificationIrp); // We need to complete the notification irp RBSCCompleteCardTracking(SmartcardExtension); SmartcardDebug( DEBUG_DRIVER, ("%s!RBSCCleanup: Completing IRP %lx\n", DRIVER_NAME, Irp) ); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest( Irp, IO_NO_INCREMENT ); SmartcardDebug( DEBUG_TRACE, ("%s!RBSCCleanup: Exit\n", DRIVER_NAME) ); return STATUS_SUCCESS; } NTSTATUS RBSCSerialIo( PSMARTCARD_EXTENSION SmartcardExtension, ULONG SerialIoControlCode, PUCHAR TxBuffer, ULONG TxSize, ULONG RxSize ) /*++ Routine Description: This routine sends IOCTL's to the serial driver. It waits on for their completion, and then returns. Arguments: SmartcardExtension - Pointer to the smart card data SerialIoControlCode - IOCTL code to be sent to the serial driver TxBuffer - Ponter to the data to send TxSize - Size of data to send RxSize - Number of bytes expected to receive Return Value: NTSTATUS --*/ { PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension; NTSTATUS status; 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; } // // Check if the buffers are large enough // ASSERT( RxSize <= SmartcardExtension->SmartcardReply.BufferSize ); if( RxSize > SmartcardExtension->SmartcardReply.BufferSize ) { SmartcardLogError( SmartcardExtension->OsData->DeviceObject, RBSC_BUFFER_TOO_SMALL, NULL, 0 ); return STATUS_BUFFER_TOO_SMALL; } KeInitializeEvent( &event, NotificationEvent, FALSE ); // // Build irp to be sent to serial driver // irp = IoBuildDeviceIoControlRequest( SerialIoControlCode, ReaderExtension->AttachedSerialPort, (TxSize ? TxBuffer : NULL), TxSize, (RxSize ? SmartcardExtension->SmartcardReply.Buffer : NULL), RxSize, FALSE, &event, &ioStatus ); ASSERT( irp != NULL ); if( irp == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } switch( SerialIoControlCode ) { PIO_STACK_LOCATION nextsp; // // The serial driver trasfers data from/to // irp->AssociatedIrp.SystemBuffer // case SMARTCARD_WRITE: nextsp = IoGetNextIrpStackLocation( irp ); nextsp->MajorFunction = IRP_MJ_WRITE; nextsp->Parameters.Write.Length = TxSize; nextsp->Parameters.Write.ByteOffset.QuadPart = 0; break; case SMARTCARD_READ: nextsp = IoGetNextIrpStackLocation( irp ); nextsp->MajorFunction = IRP_MJ_READ; nextsp->Parameters.Read.Length = RxSize; nextsp->Parameters.Read.ByteOffset.QuadPart = 0; break; } status = IoCallDriver( ReaderExtension->AttachedSerialPort, irp ); if( status == STATUS_PENDING ) { KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL ); status = ioStatus.Status; if( status == STATUS_TIMEOUT && ioStatus.Information != 0 && ioStatus.Information < RxSize ) { // Ignore time-out if some data returned status = STATUS_SUCCESS; } // save the number of bytes received SmartcardExtension->SmartcardReply.BufferLength = (RxSize != 0 && status == STATUS_SUCCESS) ? (ULONG) ioStatus.Information : 0; } else { SmartcardExtension->SmartcardReply.BufferLength = (status == STATUS_SUCCESS) ? RxSize : 0; } #if 0 if( SerialIoControlCode == SMARTCARD_WRITE ) { // TDB: Wait for Tx buffer empty } #endif // // STATUS_TIMEOUT isn't correctly mapped // to a WIN32 error, that's why we change it here // to STATUS_IO_TIMEOUT // return( (status == STATUS_TIMEOUT) ? STATUS_IO_TIMEOUT : status ); } NTSTATUS RBSCWriteToCard( PSMARTCARD_EXTENSION SmartcardExtension, PUCHAR Buffer, ULONG Length ) /*++ Routine Description: This routine sends data to the smart card. It handles the inverse convention and gaurd time. Arguments: SmartcardExtension - Pointer to our smartcard data Buffer - Points to the data to send Length - Length of data to send (bytes) Return Value: NTSTATUS --*/ { NTSTATUS status; UCHAR TempBuffer[MIN_BUFFER_SIZE]; ULONG StopBits = SmartcardExtension->CardCapabilities.PtsData.StopBits; DumpData( "Data to the card", Buffer, Length ); if( SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F ) { ULONG l; if( Length > sizeof(TempBuffer) ) { return STATUS_DEVICE_DATA_ERROR; } for( l = 0; l < Length; l++ ) { TempBuffer[l] = InverseCharTable[ Buffer[l] ]; } Buffer = TempBuffer; } if( StopBits <= 2 ) { return RBSCSerialIo( SmartcardExtension, SMARTCARD_WRITE, Buffer, Length, 0 ); } while( Length != 0 ) { status = RBSCSerialIo( SmartcardExtension, SMARTCARD_WRITE, Buffer, 1, 0 ); if( status != STATUS_SUCCESS ) { return status; } Buffer++; Length--; if( Length != 0 ) KeStallExecutionProcessor( (StopBits-1)*SmartcardExtension->CardCapabilities.etu ); } return STATUS_SUCCESS; } NTSTATUS RBSCSetCommParams( PSMARTCARD_EXTENSION SmartcardExtension, ULONG DataRate ) /*++ Routine Description: This routine sets the stop bits and read timeouts of the serial driver. Arguments: SmartcardExtension - Pointer to our smartcard data Return Value: NTSTATUS --*/ { NTSTATUS status; PSERIAL_LINE_CONTROL plc = &SmartcardExtension->ReaderExtension->LineControl; PSERIAL_TIMEOUTS timeout = &SmartcardExtension->ReaderExtension->SerialTimeouts; SmartcardDebug( DEBUG_PROTOCOL, ("RNBO3531!SetCommParams: DataRate=%ld, StopBits=%d\n", DataRate,SmartcardExtension->CardCapabilities.PtsData.StopBits) ); plc->StopBits = (SmartcardExtension->CardCapabilities.PtsData.StopBits == 2) ? STOP_BITS_2 : STOP_BIT_1; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_LINE_CONTROL, (PUCHAR) plc, sizeof(SERIAL_LINE_CONTROL), 0 ); if( status != STATUS_SUCCESS ) { return status; } if( SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries > 1 ) { // Setup baudrate SERIAL_BAUD_RATE BaudRate; BaudRate.BaudRate = DataRate; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_BAUD_RATE, (PUCHAR) &BaudRate, sizeof(SERIAL_BAUD_RATE), 0 ); if( status != STATUS_SUCCESS ) { return status; } } if( SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T1 ) { timeout->ReadTotalTimeoutConstant = 2* // for safty (SmartcardExtension->CardCapabilities.T1.BWT + WAIT_TIME_MINIMUM)/1000; timeout->ReadIntervalTimeout = 2* // for safty (SmartcardExtension->CardCapabilities.T1.CWT + WAIT_TIME_MINIMUM)/1000; } else { timeout->ReadTotalTimeoutConstant = timeout->ReadIntervalTimeout = 2* // for safty (SmartcardExtension->CardCapabilities.T0.WT + WAIT_TIME_MINIMUM)/1000; } // // Set up timeout (in msec) // return RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_TIMEOUTS, (PUCHAR) timeout, sizeof(SERIAL_TIMEOUTS), 0 ); } NTSTATUS RBSCReadFromCard( PSMARTCARD_EXTENSION SmartcardExtension, ULONG Length ) /*++ Routine Description: Reads a block of data from the card. On successful read, it converts the data if inverse conversion used. Arguments: SmartcardExtension - Pointer to smart card data. Length - Number of bytes to read. Return Value: NTSTATUS --*/ { NTSTATUS status; SmartcardDebug( DEBUG_PROTOCOL, ("RNBO3531!ReadFromCard(%ld)\n",Length) ); status = RBSCSerialIo( SmartcardExtension, SMARTCARD_READ, NULL, 0, Length ); if( status == STATUS_SUCCESS && SmartcardExtension->SmartcardReply.BufferLength != Length ) { status = STATUS_IO_TIMEOUT; } if( SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F ) { ULONG i; for( i = 0; i < SmartcardExtension->SmartcardReply.BufferLength; i++ ) { SmartcardExtension->SmartcardReply.Buffer[i] = InverseCharTable[ SmartcardExtension->SmartcardReply.Buffer[i] ]; } } #if 0 // The data dump here can time-out some cards DumpData( "Data from card", SmartcardExtension->SmartcardReply.Buffer, SmartcardExtension->SmartcardReply.BufferLength ); SmartcardDebug( DEBUG_PROTOCOL, ("RNBO3531!ReadFromCard Exit %08X\n", status) ); #endif return status; } NTSTATUS RBSCSerialEvents( PDEVICE_OBJECT DeviceObject, PIRP Irp, PREADER_EXTENSION ReaderExtension ) /*++ Routine Description: This routine is called in three cases: a) CTS changed (card inserted or removed) or b) DSR changed (reader has been removed) or c) The serial WaitMask is set to zero For a) we update the card status and complete outstanding card tracking requests. For b) and c) we start to unload the driver Arguments: DeviceObject - Pointer to device object Irp - The notofication Irp ReaderExtension - Pointer to our device data Return Value: NTSTATUS --*/ { PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension; PIO_STACK_LOCATION nextsp; NTSTATUS status; KIRQL irql; UNREFERENCED_PARAMETER(DeviceObject); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!SerialEvents: Enter\n") ); KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &irql ); if( ReaderExtension->GetModemStatus ) { // // This function requested the modem status previously. // As part of the io-completion, this function is then // called again. When we're here we can read the actual // modem-status to figure out if the card is in the reader // #ifdef NT5 if( (ReaderExtension->ModemStatus & SERIAL_DSR_STATE) == 0 ) { SmartcardDebug( DEBUG_INFO, ("RNBO3531!SerialEvent: Reader removed\n") ); // // We set the mask to zero to signal that we can // release the irp that we use for the serial events // ReaderExtension->WaitMask = 0; SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN; } else #endif { if( ReaderExtension->ModemStatus & SERIAL_CTS_STATE ) { // // Card is inserted // SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!SerialEvents: Smart card inserted\n") ); } else { // // Card is removed // SmartcardExtension->CardCapabilities.ATR.Length = 0; SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!SerialEvents: Smart card removed\n") ); } } } KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, irql ); if( SmartcardExtension->ReaderExtension->PowerRequest == FALSE ) { // Inform the user of a card insertion/removal event RBSCCompleteCardTracking( SmartcardExtension ); } // The wait mask is set to 0 when the driver unloads if( ReaderExtension->WaitMask == 0 ) { #ifdef NT5 // schedule our remove thread IoQueueWorkItem( ReaderExtension->CloseSerial, (PIO_WORKITEM_ROUTINE) RBSCCloseSerialPort, DelayedWorkQueue, NULL ); #endif SmartcardDebug( DEBUG_INFO, ("RNBO3531!SerialEvent: Exit (Release IRP)\n") ); ReaderExtension->PowerRequest = FALSE; // // 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; } nextsp = IoGetNextIrpStackLocation( ReaderExtension->SerialStatusIrp ); nextsp->MajorFunction = IRP_MJ_DEVICE_CONTROL; nextsp->MinorFunction = 0UL; if( ReaderExtension->GetModemStatus ) { nextsp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ReaderExtension->WaitMask); nextsp->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_WAIT_ON_MASK; ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer = &ReaderExtension->WaitMask; ReaderExtension->GetModemStatus = FALSE; SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!SerialEvents: IOCTL_SERIAL_WAIT_ON_MASK\n") ); } else { // // Setup call for device control to get modem status. // The CTS signal tells us if the card is inserted or removed. // CTS is high if the card is inserted. // nextsp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ReaderExtension->ModemStatus); nextsp->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_GET_MODEMSTATUS; ReaderExtension->SerialStatusIrp->AssociatedIrp.SystemBuffer = &ReaderExtension->ModemStatus; ReaderExtension->GetModemStatus = TRUE; SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!SerialEvents: IOCTL_SERIAL_GET_MODEMSTATUS\n") ); } IoSetCompletionRoutine( ReaderExtension->SerialStatusIrp, RBSCSerialEvents, ReaderExtension, TRUE, TRUE, TRUE ); status = IoCallDriver( ReaderExtension->AttachedSerialPort, ReaderExtension->SerialStatusIrp ); ReaderExtension->PowerRequest = FALSE; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!SerialEvents: Exit\n") ); return STATUS_MORE_PROCESSING_REQUIRED; } #ifdef NT5 NTSTATUS RBSCDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This is our IOCTL dispatch function Arguments: DeviceObject - Pointer to device object Return Value: NTSTATUS --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension; NTSTATUS status; KIRQL irql; if( ReaderExtension->WaitMask == 0 ) { // // the wait mask is set to 0 whenever the device was either // surprise-removed or politely removed // status = STATUS_DEVICE_REMOVED; } else { KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql ); if( ReaderExtension->IoCount == 0 ) { KeReleaseSpinLock( &ReaderExtension->SpinLock, irql ); status = KeWaitForSingleObject( &ReaderExtension->ReaderStarted, Executive, KernelMode, FALSE, NULL ); ASSERT( status == STATUS_SUCCESS ); KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql ); } ASSERT( ReaderExtension->IoCount >= 0 ); ReaderExtension->IoCount++; KeReleaseSpinLock( &ReaderExtension->SpinLock, irql ); status = SmartcardAcquireRemoveLockWithTag( SmartcardExtension, 'tcoI' ); } if (status != STATUS_SUCCESS) { // the device has been removed. Fail the call Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_DEVICE_REMOVED; } ASSERT(ReaderExtension->ReaderPowerState == PowerReaderWorking); status = SmartcardDeviceControl( SmartcardExtension, Irp ); SmartcardReleaseRemoveLockWithTag( SmartcardExtension,'tcoI' ); KeAcquireSpinLock( &ReaderExtension->SpinLock, &irql ); ReaderExtension->IoCount--; ASSERT( ReaderExtension->IoCount >= 0 ); KeReleaseSpinLock( &ReaderExtension->SpinLock, irql ); return status; } #else NTSTATUS RBSCDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) { SmartcardDebug( DEBUG_TRACE, ("RNBO3531!DeviceControl: Enter (%lx)\n", IoGetCurrentIrpStackLocation( Irp )->MinorFunction) ); return SmartcardDeviceControl( &((PREADER_EXTENSION)DeviceObject->DeviceExtension)->SmartcardExtension, Irp ); } #endif NTSTATUS RBSCReaderPower( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called for certain power requests to the card. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension; NTSTATUS status; KIRQL oldIrql; ULONG step; //PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ReaderPower: Enter (%lx)\n", SmartcardExtension->MinorIoControlCode) ); #define STEP_POWER_DOWN 0 #define STEP_COLD_RESET (STEP_POWER_DOWN+2) #define STEP_WARM_RESET (STEP_COLD_RESET+10) switch( SmartcardExtension->MinorIoControlCode ) { case SCARD_WARM_RESET: step = STEP_WARM_RESET; break; case SCARD_COLD_RESET: step = STEP_COLD_RESET; break; case SCARD_POWER_DOWN: step = STEP_POWER_DOWN; break; default : return STATUS_INVALID_DEVICE_REQUEST; } // // Since power down triggers the UpdateSerialStatus function, we have // to inform it that we forced the change of the status and not the user // (who might have removed and inserted a card) // ReaderExtension->PowerRequest = TRUE; do { switch( step++ ) { case STEP_COLD_RESET+0: case STEP_POWER_DOWN+0: status = RBSCPacketExchange( SmartcardExtension, CardPowerDown, sizeof(CardPowerDown), WAIT_TIME_PWR_OFF, TRUE ); break; case STEP_POWER_DOWN+1: if( SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_PRESENT ) { SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_PRESENT; } SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; ReaderExtension->PowerRequest = FALSE; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ReaderPower: Exit PowerDown OK\n") ); return STATUS_SUCCESS; case STEP_COLD_RESET+1: case STEP_WARM_RESET+0: status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_DTR, NULL, 0, 0 ); //RBSCDelay( 15000 ); break; case STEP_COLD_RESET+2: case STEP_WARM_RESET+1: status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_RTS, NULL, 0, 0 ); // RBSCDelay( 15000 ); break; case STEP_WARM_RESET+2: case STEP_COLD_RESET+3: // // Set up ATR timeouts (all in msec) // ReaderExtension->SerialTimeouts.ReadTotalTimeoutMultiplier = 1; ReaderExtension->SerialTimeouts.ReadIntervalTimeout = 2* ATR_CHAR_TIMEOUT/1000; ReaderExtension->SerialTimeouts.ReadTotalTimeoutConstant = 2* ATR_BLOCK_TIMEOUT/1000; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_TIMEOUTS, (PUCHAR) &ReaderExtension->SerialTimeouts, sizeof(SERIAL_TIMEOUTS), 0 ); break; case STEP_WARM_RESET+3: case STEP_COLD_RESET+4: if( SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries > 1 ) { // Setup default baudrate SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ReaderPower: Set DataRate=%ld\n",DATARATE_DEFAULT) ); { SERIAL_BAUD_RATE BaudRate; BaudRate.BaudRate = DATARATE_DEFAULT; status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_SET_BAUD_RATE, (PUCHAR) &BaudRate, sizeof(SERIAL_BAUD_RATE), 0 ); } } break; case STEP_WARM_RESET+4: case STEP_COLD_RESET+5: RBSCDelay(15000); status = RBSCPacketExchange( SmartcardExtension, CardPowerUp, sizeof(CardPowerUp), 0, //WAIT_TIME_PWR_ON, FALSE ); break; case STEP_WARM_RESET+5: case STEP_COLD_RESET+6: // // Now read in part or all of the ATR // status = RBSCSerialIo( SmartcardExtension, SMARTCARD_READ, NULL, 0, MAXIMUM_ATR_LENGTH ); break; case STEP_WARM_RESET+6: case STEP_COLD_RESET+7: { ULONG i; if( SmartcardExtension->SmartcardReply.Buffer[0] == 0x03 ) { // // Inverse convention (Inv(3F) = 03) // SmartcardExtension->CardCapabilities.ATR.Buffer[0] = 0x3F; SmartcardExtension->CardCapabilities.ATR.Length = 1; } else if( SmartcardExtension->SmartcardReply.Buffer[0] == 0x3B ) { // // Direct convention // SmartcardExtension->CardCapabilities.ATR.Buffer[0] = 0x3B; SmartcardExtension->CardCapabilities.ATR.Length = 1; } else { status = STATUS_UNRECOGNIZED_MEDIA; } for( i = 1; status == STATUS_SUCCESS; i = 0 ) { while( i < SmartcardExtension->SmartcardReply.BufferLength && SmartcardExtension->CardCapabilities.ATR.Length < MAXIMUM_ATR_LENGTH ) { SmartcardExtension->CardCapabilities.ATR.Buffer[ SmartcardExtension->CardCapabilities.ATR.Length++ ] = (SmartcardExtension->CardCapabilities.ATR.Buffer[0] == 0x3F) ? InverseCharTable[ SmartcardExtension->SmartcardReply.Buffer[i] ] : SmartcardExtension->SmartcardReply.Buffer[i]; i++; } // // Check if ATR is complete // status = SmartcardUpdateCardCapabilities( SmartcardExtension ); if( status == STATUS_SUCCESS ) { break; } // // Read remaining bytes of ATR // status = RBSCSerialIo( SmartcardExtension, SMARTCARD_READ, NULL, 0, MAXIMUM_ATR_LENGTH - SmartcardExtension->CardCapabilities.ATR.Length ); } break; } case STEP_WARM_RESET+7: case STEP_COLD_RESET+8: status = RBSCSetCommParams( SmartcardExtension, SmartcardExtension->CardCapabilities.PtsData.DataRate ); break; case STEP_WARM_RESET+8: case STEP_COLD_RESET+9: //RBSCDelay( 15000 ); // // Make sure card is still in the reader. // if( (ReaderExtension->ModemStatus & SERIAL_CTS_STATE) == 0 ) { status = STATUS_NO_MEDIA; break; } KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &oldIrql ); // // Copy ATR to user space // if( SmartcardExtension->IoRequest.ReplyBuffer ) { RtlCopyMemory( SmartcardExtension->IoRequest.ReplyBuffer, SmartcardExtension->CardCapabilities.ATR.Buffer, SmartcardExtension->CardCapabilities.ATR.Length ); // // Tell user length of ATR // *SmartcardExtension->IoRequest.Information = SmartcardExtension->CardCapabilities.ATR.Length; } KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, oldIrql ); ReaderExtension->PowerRequest = FALSE; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ReaderPower: Exit Reset OK\n") ); return STATUS_SUCCESS; } } while( status == STATUS_SUCCESS ); // // Reset failed, make sure card is powered OFF // RBSCPacketExchange( SmartcardExtension, CardPowerDown, sizeof(CardPowerDown), WAIT_TIME_PWR_OFF, TRUE ); SmartcardExtension->CardCapabilities.ATR.Length = 0; status = (ReaderExtension->ModemStatus & SERIAL_CTS_STATE) ? STATUS_UNRECOGNIZED_MEDIA : STATUS_NO_MEDIA; ReaderExtension->PowerRequest = FALSE; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ReaderPower: Failed %08x\n",status) ); return status; } NTSTATUS RBSCSetProtocol( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called to set a particular protocol. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { UCHAR PtsRequest[8]; NTSTATUS status; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!SetProtocol: Enter\n") ); // // Check if the card is already in specific state // and if the caller wants to have the already selected protocol. // We return success if this is the case. // if( SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC && ( SmartcardExtension->CardCapabilities.Protocol.Selected & SmartcardExtension->MinorIoControlCode) ) { status = STATUS_SUCCESS; } else while(TRUE) { PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer; if( SmartcardExtension->CardCapabilities.Protocol.Supported & SmartcardExtension->MinorIoControlCode & SCARD_PROTOCOL_T1 ) { PtsRequest[5] = 0x11; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T1; } else if( SmartcardExtension->CardCapabilities.Protocol.Supported & SmartcardExtension->MinorIoControlCode & SCARD_PROTOCOL_T0 ) { PtsRequest[5] = 0x10; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0; } else { return STATUS_INVALID_DEVICE_REQUEST; } // // pts // PtsRequest[0] = IFDCMD_HEADER1; PtsRequest[1] = IFDCMD_HEADER2; PtsRequest[2] = IFDCMD_SEND_0xx; PtsRequest[3] = 4; // Length PtsRequest[4] = 0xff; // PTS PtsRequest[6] = // set pts1 which codes Fl and Dl (SmartcardExtension->CardCapabilities.PtsData.Fl << 4) | SmartcardExtension->CardCapabilities.PtsData.Dl; // // pck (check character) // PtsRequest[7] = PtsRequest[4] ^ PtsRequest[5] ^ PtsRequest[6]; status = RBSCPacketExchange( SmartcardExtension, PtsRequest, 4, 0, FALSE ); if( status == STATUS_SUCCESS ) { status = RBSCWriteToCard( SmartcardExtension, PtsRequest+4, 4 ); if( status == STATUS_SUCCESS ) { status = RBSCReadFromCard( SmartcardExtension, 4 ); } } if( status == STATUS_SUCCESS && reply[0] == PtsRequest[4] && ((reply[1] ^ PtsRequest[5]) & 0x0f) == 0 ) { // // Update the current protocol // SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC; status = RBSCSetCommParams( SmartcardExtension, SmartcardExtension->CardCapabilities.PtsData.DataRate ); } else { if (status == STATUS_IO_TIMEOUT && SmartcardExtension->CardCapabilities.PtsData.Type != PTS_TYPE_DEFAULT) { SmartcardDebug( DEBUG_TRACE, ("RNBO3531!SetProtocol: PTS failed. Trying default parameters...\n") ); // // The card did either NOT reply or it replied incorrectly // so try default values // SmartcardExtension->CardCapabilities.PtsData.Type = PTS_TYPE_DEFAULT; SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET; status = RBSCReaderPower( SmartcardExtension ); if( status == STATUS_SUCCESS ) continue; } status = STATUS_DEVICE_PROTOCOL_ERROR; } break; } if( status != STATUS_SUCCESS ) { SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; } else { // // return the selected protocol to the caller // *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer = SmartcardExtension->CardCapabilities.Protocol.Selected; *SmartcardExtension->IoRequest.Information = sizeof(SmartcardExtension->CardCapabilities.Protocol.Selected); } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!SetProtocol: Exit\n") ); return status; } NTSTATUS RBSCPacketExchange( PSMARTCARD_EXTENSION SmartcardExtension, PUCHAR Command, ULONG Length, ULONG WaitTime, BOOLEAN IfdResponse ) /*++ Routine Description: This function is used to transmit a command to the reader (not card) and return the response (if asked). Arguments: SmartcardExtension - Pointer to smart card data struct. Command - Points to the IFD command to send Length - Length of the command in bytes WaitTime - Waiting time expected (in microseconds) IfdResponse - When TRUE, IFD response is expected (vs card) Return Value: NTSTATUS --*/ { PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer; ULONG SerialRequest = SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXCLEAR; ULONG ResponseSize; NTSTATUS status; int TrySend; DumpData( "PacketExchange", Command, Length ); for( TrySend = 0; TrySend < 4; TrySend++ ) { // // start with clean buffers // status = RBSCSerialIo( SmartcardExtension, IOCTL_SERIAL_PURGE, (PUCHAR) &SerialRequest, sizeof(ULONG), 0 ); if( status == STATUS_SUCCESS ) { status = RBSCSerialIo( SmartcardExtension, SMARTCARD_WRITE, Command, Length, 0 ); if( status == STATUS_SUCCESS && WaitTime != 0 ) RBSCDelay( WaitTime ); } if( status == STATUS_SUCCESS && IfdResponse ) { status = RBSCSerialIo( SmartcardExtension, SMARTCARD_READ, NULL, 0, IFDRSP_HEADER_SIZE ); if( status != STATUS_SUCCESS || SmartcardExtension->SmartcardReply.BufferLength != IFDRSP_HEADER_SIZE ) { continue; } DumpData( "PacketExchange: Received Header", reply, IFDRSP_HEADER_SIZE ); // // We should have the marker plus the status/length byte // if( reply[0] != IFDRSP_MARKER ) { status = STATUS_DEVICE_DATA_ERROR; continue; } ResponseSize = (ULONG)reply[1]; if( ResponseSize > 0 && ResponseSize < IFDRSP_ACK ) { status = RBSCSerialIo( SmartcardExtension, SMARTCARD_READ, NULL, 0, ResponseSize ); if( status == STATUS_SUCCESS && SmartcardExtension->SmartcardReply.BufferLength != ResponseSize ) { status = STATUS_DEVICE_DATA_ERROR; } DumpData( "PacketExchange: Received Data", SmartcardExtension->SmartcardReply.Buffer, SmartcardExtension->SmartcardReply.BufferLength ); } else { // // Map IFD errors // switch( ResponseSize ) { case IFDRSP_ACK : return STATUS_SUCCESS; case IFDRSP_NOCARD : return STATUS_NO_MEDIA; case IFDRSP_BADCMD : return STATUS_INVALID_DEVICE_REQUEST; case IFDRSP_PARITY : default : status = STATUS_DEVICE_DATA_ERROR; break; } } } if( status == STATUS_SUCCESS) return STATUS_SUCCESS; } SmartcardExtension->SmartcardReply.BufferLength = 0; return status; } NTSTATUS RBSCT0Transmit( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This function performs the T=0 transmission. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension; NTSTATUS status; PUCHAR RxBuffer = SmartcardExtension->SmartcardReply.Buffer; PUCHAR TxBuffer = SmartcardExtension->SmartcardRequest.Buffer; ULONG RxLength, TxLength, RequestSize; UCHAR SendHeader[4], Ins, ProcByte; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!T0Transmit: Enter\n") ); // // Tell the lib function how many bytes I need for the prologue // SmartcardExtension->SmartcardRequest.BufferLength = 0; // // Let the lib build a T=0 packet // status = SmartcardT0Request( SmartcardExtension ); if( status != STATUS_SUCCESS ) return status; TxLength = SmartcardExtension->SmartcardRequest.BufferLength; if( TxLength < 5 || TxLength > 0x1ff ) { return STATUS_DEVICE_DATA_ERROR; } // // The number of bytes we expect from the card // is Le + 2 status bytes // RxLength = SmartcardExtension->T0.Le + 2; // // Write command to the reader // Ins = TxBuffer[1]; // INS RequestSize = 5; for( ;; ) { if( TxLength > 0 ) { SendHeader[0] = IFDCMD_HEADER1; SendHeader[1] = IFDCMD_HEADER2; SendHeader[2] = (RequestSize > 256) ? IFDCMD_SEND_1xx : IFDCMD_SEND_0xx; SendHeader[3] = (UCHAR)RequestSize; // 0=256 status = RBSCPacketExchange( SmartcardExtension, SendHeader, 4, 0, FALSE ); if( status != STATUS_SUCCESS) { break; } status = RBSCWriteToCard( SmartcardExtension, TxBuffer, RequestSize ); if( status != STATUS_SUCCESS) { break; } TxLength -= RequestSize; TxBuffer += RequestSize; } do { status = RBSCReadFromCard( SmartcardExtension, 1 ); // Proc. byte if( status != STATUS_SUCCESS ) { break; } ProcByte = SmartcardExtension->SmartcardReply.Buffer[0]; SmartcardDebug( DEBUG_TRACE, ("RNBO3531!ProcByte=%02x Ins=%02x\n",ProcByte,Ins) ); } while( ProcByte == 0x60); if( status != STATUS_SUCCESS ) { break; } if( ProcByte == Ins || ProcByte == (UCHAR)(Ins+1) ) { // // Send all remaining bytes // if( TxLength > 0 ) { RequestSize = TxLength; continue; } RequestSize = RxLength; } else if( ProcByte == (UCHAR)~Ins || ProcByte == (UCHAR)~(Ins+1) ) { // // Send 1 byte only // RequestSize = 1; if( TxLength > 0 ) { continue; } } else { // // Card returned status byte // SmartcardExtension->SmartcardReply.Buffer++; TxLength = 0; RxLength = 1; // Second byte of status (SW2) RequestSize = 1; } status = RBSCReadFromCard( SmartcardExtension, RequestSize ); if( status != STATUS_SUCCESS ) { break; } SmartcardExtension->SmartcardReply.Buffer += RequestSize; RxLength -= RequestSize; if( RxLength == 0 ) { break; } } // // Restore reply buffer pointer // SmartcardExtension->SmartcardReply.BufferLength = (ULONG) (SmartcardExtension->SmartcardReply.Buffer - RxBuffer); SmartcardExtension->SmartcardReply.Buffer = RxBuffer; if( status == STATUS_SUCCESS ) { status = SmartcardT0Reply( SmartcardExtension ); } SmartcardDebug( DEBUG_TRACE, ("RNBO3531!T0Transmit: Exit (%lx)\n",status) ); return status; } NTSTATUS RBSCT1Transmit( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This function performs the T=1 transmission. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { NTSTATUS status; UCHAR SendHeader[4]; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!T1Transmit: Enter\n") ); do { PUCHAR TxBuffer = SmartcardExtension->SmartcardRequest.Buffer; PUCHAR reply = SmartcardExtension->SmartcardReply.Buffer; ULONG TxLength; // // Tell the lib function how many bytes I need for the prologue // SmartcardExtension->SmartcardRequest.BufferLength = 0; status = SmartcardT1Request( SmartcardExtension ); if( status != STATUS_SUCCESS ) { break; } TxLength = SmartcardExtension->SmartcardRequest.BufferLength; //RBSCDelay( SmartcardExtension->CardCapabilities.T1.BGT ); // // Write the command to the reader // SendHeader[0] = IFDCMD_HEADER1; SendHeader[1] = IFDCMD_HEADER2; SendHeader[2] = (UCHAR)(TxLength > 256) ? IFDCMD_SEND_1xx : IFDCMD_SEND_0xx; SendHeader[3] = (UCHAR)TxLength; // 0=256 status = RBSCPacketExchange( SmartcardExtension, SendHeader, 4, 0, FALSE ); if( status != STATUS_SUCCESS ) { break; } status = RBSCWriteToCard( SmartcardExtension, TxBuffer, TxLength ); if( status != STATUS_SUCCESS ) { break; } if( SmartcardExtension->T1.Wtx > 1 ) { SmartcardDebug( DEBUG_PROTOCOL, ("BWT=%ld, WTX=%d\n", SmartcardExtension->CardCapabilities.T1.BWT, SmartcardExtension->T1.Wtx) ); RBSCDelay( SmartcardExtension->CardCapabilities.T1.BWT* SmartcardExtension->T1.Wtx ); } status = RBSCReadFromCard( SmartcardExtension, 3 ); if( status == STATUS_IO_TIMEOUT ) { // // Since the card did not reply we set the number of // bytes received to 0. This will trigger a resend // request // SmartcardDebug( DEBUG_PROTOCOL, ("RNBO3531!T1Transmit: Timeout\n") ); SmartcardExtension->SmartcardReply.BufferLength = 0; } else { if( status != STATUS_SUCCESS ) { break; } // // Calculate length of the INF part of the response // SmartcardExtension->SmartcardReply.BufferLength = SmartcardExtension->SmartcardReply.Buffer[2] + (SmartcardExtension->CardCapabilities.T1.EDC & 1) + 1; SmartcardExtension->SmartcardReply.Buffer += 3; status = RBSCReadFromCard( SmartcardExtension, SmartcardExtension->SmartcardReply.BufferLength ); SmartcardExtension->SmartcardReply.Buffer -= 3; SmartcardExtension->SmartcardReply.BufferLength += 3; if( status != STATUS_SUCCESS && status != STATUS_IO_TIMEOUT ) { break; } } status = SmartcardT1Reply( SmartcardExtension ); } while( status == STATUS_MORE_PROCESSING_REQUIRED ); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!T1Transmit: Exit (%lx)\n",status) ); return status; } NTSTATUS RBSCTransmit( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This function is called by the smart card library whenever a transmission is required. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { NTSTATUS status; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("RNBO3531!Transmit: GT=%ld, etu=%ld\n", SmartcardExtension->CardCapabilities.GT, SmartcardExtension->CardCapabilities.etu) ); switch( SmartcardExtension->CardCapabilities.Protocol.Selected ) { case SCARD_PROTOCOL_T0: return RBSCT0Transmit( SmartcardExtension ); case SCARD_PROTOCOL_T1: return RBSCT1Transmit( SmartcardExtension ); } return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS RBSCCardTracking( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called to setup event tracking for card insertion and removal events. Arguments: SmartcardExtension - pointer to the smart card data struct. Return Value: NTSTATUS --*/ { KIRQL ioIrql, keIrql; // // Set cancel routine for the notification irp // KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &keIrql ); IoAcquireCancelSpinLock( &ioIrql ); if( SmartcardExtension->OsData->NotificationIrp ) { IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, RBSCCancel ); } IoReleaseCancelSpinLock( ioIrql ); KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, keIrql ); return STATUS_PENDING; } VOID RBSCCompleteCardTracking( PSMARTCARD_EXTENSION SmartcardExtension ) { KIRQL ioIrql, keIrql; PIRP notificationIrp; IoAcquireCancelSpinLock(&ioIrql); KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &keIrql ); notificationIrp = SmartcardExtension->OsData->NotificationIrp; SmartcardExtension->OsData->NotificationIrp = NULL; KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, keIrql ); if (notificationIrp) { IoSetCancelRoutine( notificationIrp, NULL ); } IoReleaseCancelSpinLock(ioIrql); if (notificationIrp) { SmartcardDebug( DEBUG_DRIVER, ("%s!RBSCCardTracking: Completing NotificationIrp %lxh\n", DRIVER_NAME, notificationIrp) ); // finish the request notificationIrp->IoStatus.Information = 0; notificationIrp->IoStatus.Status = notificationIrp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS; IoCompleteRequest( notificationIrp, IO_NO_INCREMENT ); } } #ifdef NT5 VOID RBSCCloseSerialPort( PDEVICE_OBJECT DeviceObject, PVOID Context ) /*++ Routine Description: This function closes the connection to the serial driver when the reader has been removed (unplugged). This function runs as a system thread at IRQL == PASSIVE_LEVEL. It waits for the remove event that is set by the IoCompletionRoutine --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; NTSTATUS status; PIRP irp; PIO_STACK_LOCATION stack; IO_STATUS_BLOCK ioStatusBlock; // // first mark this device as 'gone'. // This will prevent that someone can re-open the device // status = IoSetDeviceInterfaceState( &ReaderExtension->PnPDeviceName, FALSE ); ASSERT( status == STATUS_SUCCESS ); irp = IoAllocateIrp( (CCHAR)(DeviceObject->StackSize + 1), FALSE ); ASSERT( irp != NULL ); if( irp ) { SmartcardDebug( DEBUG_TRACE, ("RNBO3531!CloseSerialPort: Sending IRP_MJ_CLOSE\n") ); IoSetNextIrpStackLocation( irp ); // // We send down a close to the serial driver. This close goes // through serenum first which will trigger it to start looking // for changes on the com-port. Since our device is gone it will // call the device removal event of our PnP dispatch. // irp->UserIosb = &ioStatusBlock; stack = IoGetCurrentIrpStackLocation( irp ); stack->MajorFunction = IRP_MJ_CLOSE; status = RBSCCallSerialDriver( ReaderExtension->AttachedSerialPort, irp ); ASSERT( status == STATUS_SUCCESS ); IoFreeIrp( irp ); } SmartcardDebug( DEBUG_INFO, ("RNBO3531!CloseSerialPort: Serial Close Done.\n") ); // inform the remove function that call is complete KeSetEvent( &ReaderExtension->SerialCloseDone, 0, FALSE ); } NTSTATUS RBSCDevicePowerCompletion ( 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. --*/ { PREADER_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); // // We issue a power request in order to figure out // what the actual card status is // SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET; RBSCReaderPower( SmartcardExtension ); // // 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 ) { RBSCCompleteCardTracking( SmartcardExtension ); } // 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; } VOID RBSCSystemPowerCompletion( 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. --*/ { PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; UNREFERENCED_PARAMETER (MinorFunction); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = IoStatus->Status; SmartcardReleaseRemoveLockWithTag( &ReaderExtension->SmartcardExtension, 'rwoP' ); if (PowerState.SystemState == PowerSystemWorking) { PoSetPowerState( DeviceObject, SystemPowerState, PowerState ); } PoStartNextPowerIrp( Irp ); IoCompleteRequest( Irp, IO_NO_INCREMENT ); } typedef enum _ACTION { Undefined = 0, SkipRequest, WaitForCompletion, CompleteRequest, MarkPending } ACTION; NTSTATUS RBSCPower( 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 device object Irp - pointer to an I/O Request Packet. Return Value: NT status code --*/ { NTSTATUS status; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PREADER_EXTENSION ReaderExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension = &ReaderExtension->SmartcardExtension; PDEVICE_OBJECT AttachedSerialPort; POWER_STATE powerState; ACTION action; SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!3Power: Enter\n") ); 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; } AttachedSerialPort = ReaderExtension->AttachedSerialPort; if (irpStack->Parameters.Power.Type == DevicePowerState && irpStack->MinorFunction == IRP_MN_SET_POWER ) { switch( irpStack->Parameters.Power.State.DeviceState ) { case PowerDeviceD0: // Turn on the reader SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Power: PowerDevice D0\n") ); // // 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, RBSCDevicePowerCompletion, SmartcardExtension, TRUE, TRUE, TRUE ); action = WaitForCompletion; break; case PowerDeviceD3: // Turn off the reader SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Power: PowerDevice D3\n") ); PoSetPowerState ( DeviceObject, DevicePowerState, irpStack->Parameters.Power.State ); // save the current card state ReaderExtension->CardPresent = SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT; if( ReaderExtension->CardPresent ) { SmartcardExtension->MinorIoControlCode = SCARD_POWER_DOWN; status = RBSCReaderPower( 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. // ReaderExtension->PowerRequest = TRUE; // save the current power state of the reader ReaderExtension->ReaderPowerState = PowerReaderOff; action = SkipRequest; break; default: ASSERT(FALSE); action = SkipRequest; break; } } if (irpStack->Parameters.Power.Type == 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(ReaderExtension->ReaderPowerState != PowerReaderUnspecified); switch(irpStack->MinorFunction) { KIRQL irql; case IRP_MN_QUERY_POWER: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Power: Query Power\n") ); // // 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(&ReaderExtension->SpinLock, &irql); if (ReaderExtension->IoCount == 0) { // Block any further ioctls KeClearEvent(&ReaderExtension->ReaderStarted); } else { // can't go to sleep mode since the reader is busy. status = STATUS_DEVICE_BUSY; action = CompleteRequest; } KeReleaseSpinLock(&ReaderExtension->SpinLock, irql); break; } break; case IRP_MN_SET_POWER: SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Power: PowerSystem S%d\n", 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 action = CompleteRequest; break; } powerState.DeviceState = PowerDeviceD0; // wake up the underlying stack... action = MarkPending; break; case PowerSystemSleeping3: case PowerSystemHibernate: case PowerSystemShutdown: if (SmartcardExtension->ReaderExtension->ReaderPowerState == PowerReaderOff) { // We're already in the right state action = CompleteRequest; 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; } } } 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, RBSCSystemPowerCompletion, Irp, NULL ); ASSERT(status == STATUS_PENDING); break; case SkipRequest: SmartcardReleaseRemoveLockWithTag( SmartcardExtension, 'rwoP' ); PoStartNextPowerIrp( Irp ); IoSkipCurrentIrpStackLocation( Irp ); status = PoCallDriver( AttachedSerialPort, Irp ); break; case WaitForCompletion: status = PoCallDriver( AttachedSerialPort, Irp ); break; default: ASSERT(FALSE); break; } SmartcardDebug( DEBUG_DRIVER, ("RNBO3531!Power: Exit %lx\n",status) ); return status; } #endif