//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) SCM Microsystems, 1998 - 1999 // // File: drvnt5.c // //-------------------------------------------------------------------------- #include "DriverNT.h" #include "DrvNT5.h" #include "CBHndlr.h" #include "STCCmd.h" #include "SRVers.h" // declare pageable/initialization code #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( PAGEABLE, DrvAddDevice ) #pragma alloc_text( PAGEABLE, DrvCreateDevice ) #pragma alloc_text( PAGEABLE, DrvRemoveDevice ) #pragma alloc_text( PAGEABLE, DrvDriverUnload ) //________________________________ D R I V E R E N T R Y ________________________________________ NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus = STATUS_SUCCESS; SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DriverEntry: Enter\n")); // initialization of the drivers entry points DriverObject->DriverUnload = DrvDriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = DrvCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DrvCreateClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DrvCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDeviceIoControl; DriverObject->MajorFunction[IRP_MJ_PNP] = DrvPnPHandler; DriverObject->MajorFunction[IRP_MJ_POWER] = DrvPowerHandler; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DrvSystemControl; DriverObject->DriverExtension->AddDevice = DrvAddDevice; SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DriverEntry: Exit\n")); return( NTStatus ); } //________________________________ I N I T I A L I Z A T I O N ____________________________________ NTSTATUS DrvAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: Arguments: Return Value: --*/ { PDEVICE_OBJECT DeviceObject = NULL; NTSTATUS NTStatus = STATUS_SUCCESS; ULONG deviceInstance; UNICODE_STRING vendorNameU, ifdTypeU; ANSI_STRING vendorNameA, ifdTypeA; HANDLE regKey = NULL; // this is a list of our supported data rates static ULONG dataRatesSupported[] = { 9600, 19200, 28800, 38400, 48000, 57600, 67200, 76800, 86400, 96000, 115200 }; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvAddDevice: Enter\n" )); __try { PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; PREADER_EXTENSION ReaderExtension; RTL_QUERY_REGISTRY_TABLE parameters[3]; RtlZeroMemory(parameters, sizeof(parameters)); RtlZeroMemory(&vendorNameU, sizeof(vendorNameU)); RtlZeroMemory(&ifdTypeU, sizeof(ifdTypeU)); RtlZeroMemory(&vendorNameA, sizeof(vendorNameA)); RtlZeroMemory(&ifdTypeA, sizeof(ifdTypeA)); // create the device object NTStatus = IoCreateDevice( DriverObject, sizeof( DEVICE_EXTENSION ), NULL, FILE_DEVICE_SMARTCARD, 0, TRUE, &DeviceObject ); if( NTStatus != STATUS_SUCCESS ) { SmartcardLogError( DriverObject, STC_CANT_CREATE_DEVICE, NULL, 0 ); __leave; } // initialize device extension DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; KeInitializeEvent( &DeviceExtension->ReaderStarted, NotificationEvent, FALSE ); // Used to keep track of open close calls DeviceExtension->ReaderOpen = FALSE; KeInitializeSpinLock(&DeviceExtension->SpinLock); // initialize smartcard extension - version & callbacks SmartcardExtension->Version = SMCLIB_VERSION; SmartcardExtension->ReaderFunction[RDF_TRANSMIT] = CBTransmit; SmartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = CBSetProtocol; SmartcardExtension->ReaderFunction[RDF_CARD_POWER] = CBCardPower; SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = CBCardTracking; // initialize smartcard extension - vendor attribute RtlCopyMemory( SmartcardExtension->VendorAttr.VendorName.Buffer, SR_VENDOR_NAME, sizeof( SR_VENDOR_NAME ) ); SmartcardExtension->VendorAttr.VendorName.Length = sizeof( SR_VENDOR_NAME ); RtlCopyMemory( SmartcardExtension->VendorAttr.IfdType.Buffer, SR_PRODUCT_NAME, sizeof( SR_PRODUCT_NAME ) ); SmartcardExtension->VendorAttr.IfdType.Length = sizeof( SR_PRODUCT_NAME ); SmartcardExtension->VendorAttr.UnitNo = MAXULONG; for (deviceInstance = 0; deviceInstance < MAXULONG; deviceInstance++) { PDEVICE_OBJECT devObj; for (devObj = DeviceObject; devObj != NULL; devObj = devObj->NextDevice) { PDEVICE_EXTENSION devExt = devObj->DeviceExtension; PSMARTCARD_EXTENSION smcExt = &devExt->SmartcardExtension; if (deviceInstance == smcExt->VendorAttr.UnitNo) { break; } } if (devObj == NULL) { SmartcardExtension->VendorAttr.UnitNo = deviceInstance; break; } } SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = 0; // initialize smartcard extension - reader capabilities SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; SmartcardExtension->ReaderCapabilities.ReaderType = SCARD_READER_TYPE_SERIAL; SmartcardExtension->ReaderCapabilities.MechProperties = 0; SmartcardExtension->ReaderCapabilities.Channel = 0; SmartcardExtension->ReaderCapabilities.MaxIFSD = STC_BUFFER_SIZE - PACKET_OVERHEAD; SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = 3571; SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = 3571; SmartcardExtension->ReaderCapabilities.DataRate.Default = SmartcardExtension->ReaderCapabilities.DataRate.Max = dataRatesSupported[0]; // reader could support higher data rates SmartcardExtension->ReaderCapabilities.DataRatesSupported.List = dataRatesSupported; SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries = sizeof(dataRatesSupported) / sizeof(dataRatesSupported[0]); SmartcardExtension->ReaderCapabilities.CurrentState = (ULONG) SCARD_UNKNOWN; SmartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE; SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE; // allocate & initialize reader extension SmartcardExtension->ReaderExtension = ExAllocatePool( NonPagedPool, sizeof( READER_EXTENSION ) ); if( SmartcardExtension->ReaderExtension == NULL ) { SmartcardLogError( DriverObject, STC_NO_MEMORY, NULL, 0 ); NTStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } ReaderExtension = SmartcardExtension->ReaderExtension; ASSERT( ReaderExtension != NULL ); RtlZeroMemory(ReaderExtension, sizeof( READER_EXTENSION )); ReaderExtension->SmartcardExtension = SmartcardExtension; ReaderExtension->ReadTimeout = 5000; KeInitializeEvent( &ReaderExtension->SerialCloseDone, NotificationEvent, TRUE ); ReaderExtension->CloseSerial = IoAllocateWorkItem( DeviceObject ); ReaderExtension->ReadWorkItem = IoAllocateWorkItem( DeviceObject ); if (ReaderExtension->CloseSerial == NULL || ReaderExtension->ReadWorkItem == NULL) { NTStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } KeInitializeEvent( &ReaderExtension->DataAvailable, NotificationEvent, FALSE ); KeInitializeEvent( &ReaderExtension->IoEvent, NotificationEvent, FALSE ); NTStatus = SmartcardInitialize( SmartcardExtension ); if( NTStatus != STATUS_SUCCESS ) { SmartcardLogError( DriverObject, (SmartcardExtension->OsData ? STC_WRONG_LIB_VERSION : STC_NO_MEMORY ), NULL, 0 ); __leave; } // Save deviceObject SmartcardExtension->OsData->DeviceObject = DeviceObject; // save the current Power state of the reader SmartcardExtension->ReaderExtension->ReaderPowerState = PowerReaderWorking; DeviceExtension = DeviceObject->DeviceExtension; ReaderExtension = DeviceExtension->SmartcardExtension.ReaderExtension; // attach the device object to the physical device object ReaderExtension->SerialDeviceObject = IoAttachDeviceToDeviceStack( DeviceObject, PhysicalDeviceObject ); ASSERT( ReaderExtension->SerialDeviceObject != NULL ); if( ReaderExtension->SerialDeviceObject == NULL ) { SmartcardLogError( DriverObject, STC_CANT_CONNECT_TO_ASSIGNED_PORT, NULL, NTStatus ); NTStatus = STATUS_UNSUCCESSFUL; __leave; } // register our new device NTStatus = IoRegisterDeviceInterface( PhysicalDeviceObject, &SmartCardReaderGuid, NULL, &DeviceExtension->PnPDeviceName ); ASSERT( NTStatus == STATUS_SUCCESS ); DeviceObject->Flags |= DO_BUFFERED_IO; DeviceObject->Flags |= DO_POWER_PAGABLE; DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; // // try to read the reader name from the registry // if that does not work, we will use the default // (hardcoded) name // if (IoOpenDeviceRegistryKey( PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_READ, ®Key ) != STATUS_SUCCESS) { __leave; } parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; parameters[0].Name = L"VendorName"; parameters[0].EntryContext = &vendorNameU; parameters[0].DefaultType = REG_SZ; parameters[0].DefaultData = &vendorNameU; parameters[0].DefaultLength = 0; parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT; parameters[1].Name = L"IfdType"; parameters[1].EntryContext = &ifdTypeU; parameters[1].DefaultType = REG_SZ; parameters[1].DefaultData = &ifdTypeU; parameters[1].DefaultLength = 0; if (RtlQueryRegistryValues( RTL_REGISTRY_HANDLE, (PWSTR) regKey, parameters, NULL, NULL ) != STATUS_SUCCESS) { __leave; } if (RtlUnicodeStringToAnsiString( &vendorNameA, &vendorNameU, TRUE ) != STATUS_SUCCESS) { __leave; } if (RtlUnicodeStringToAnsiString( &ifdTypeA, &ifdTypeU, TRUE ) != STATUS_SUCCESS) { __leave; } if (vendorNameA.Length == 0 || vendorNameA.Length > MAXIMUM_ATTR_STRING_LENGTH || ifdTypeA.Length == 0 || ifdTypeA.Length > MAXIMUM_ATTR_STRING_LENGTH) { __leave; } RtlCopyMemory( SmartcardExtension->VendorAttr.VendorName.Buffer, vendorNameA.Buffer, vendorNameA.Length ); SmartcardExtension->VendorAttr.VendorName.Length = vendorNameA.Length; RtlCopyMemory( SmartcardExtension->VendorAttr.IfdType.Buffer, ifdTypeA.Buffer, ifdTypeA.Length ); SmartcardExtension->VendorAttr.IfdType.Length = ifdTypeA.Length; } __finally { if (vendorNameU.Buffer) { RtlFreeUnicodeString(&vendorNameU); } if (ifdTypeU.Buffer) { RtlFreeUnicodeString(&ifdTypeU); } if (vendorNameA.Buffer) { RtlFreeAnsiString(&vendorNameA); } if (ifdTypeA.Buffer) { RtlFreeAnsiString(&ifdTypeA); } if (regKey != NULL) { ZwClose(regKey); } if (NTStatus != STATUS_SUCCESS) { DrvRemoveDevice( DeviceObject ); } SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvAddDevice: Exit (%lx)\n", NTStatus ) ); } return NTStatus; } NTSTATUS DrvStartDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus; PIRP Irp; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvStartDevice: Enter\n" )); Irp = IoAllocateIrp((CCHAR)( DeviceObject->StackSize + 1 ), FALSE ); ASSERT( Irp != NULL ); if( Irp != NULL ) { PDEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; IO_STATUS_BLOCK IoStatusBlock; PSMARTCARD_EXTENSION SmartcardExtension; PREADER_EXTENSION ReaderExtension; DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; ReaderExtension = SmartcardExtension->ReaderExtension; ASSERT( DeviceExtension != NULL ); ASSERT( SmartcardExtension != NULL ); ASSERT( ReaderExtension != NULL ); KeClearEvent( &ReaderExtension->SerialCloseDone ); // // send MJ_CREATE to the serial driver. a side effect of this call is that the serial // enumerator will be informed about the device and not longer poll the interface // 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; NTStatus = DrvCallSerialDriver( ReaderExtension->SerialDeviceObject, Irp ); if( NTStatus == STATUS_SUCCESS ) { SERIAL_PORT_CONFIG COMConfig; // configure the serial port COMConfig.BaudRate.BaudRate = SR_BAUD_RATE; COMConfig.LineControl.StopBits = SR_STOP_BITS; COMConfig.LineControl.Parity = SR_PARITY; COMConfig.LineControl.WordLength = SR_DATA_LENGTH; // timeouts COMConfig.Timeouts.ReadIntervalTimeout = SR_READ_INTERVAL_TIMEOUT; COMConfig.Timeouts.ReadTotalTimeoutConstant = SR_READ_TOTAL_TIMEOUT_CONSTANT; COMConfig.Timeouts.ReadTotalTimeoutMultiplier = 0; COMConfig.Timeouts.WriteTotalTimeoutConstant = SR_WRITE_TOTAL_TIMEOUT_CONSTANT; COMConfig.Timeouts.WriteTotalTimeoutMultiplier = 0; // special characters COMConfig.SerialChars.ErrorChar = 0; COMConfig.SerialChars.EofChar = 0; COMConfig.SerialChars.EventChar = 0; COMConfig.SerialChars.XonChar = 0; COMConfig.SerialChars.XoffChar = 0; COMConfig.SerialChars.BreakChar = 0; // handflow COMConfig.HandFlow.XonLimit = 0; COMConfig.HandFlow.XoffLimit = 0; COMConfig.HandFlow.ControlHandShake = 0; COMConfig.HandFlow.FlowReplace = SERIAL_XOFF_CONTINUE; // miscellenaeous COMConfig.WaitMask = SR_NOTIFICATION_EVENT; COMConfig.Purge = SR_PURGE; NTStatus = IFInitializeInterface( ReaderExtension, &COMConfig ); if( NTStatus == STATUS_SUCCESS ) { // configure the reader & initialize the card state NTStatus = STCConfigureSTC( ReaderExtension, ( PSTC_REGISTER ) STCInitialize ); CBUpdateCardState( SmartcardExtension, SCARD_UNKNOWN ); // // store firmware revision in ifd version // STCGetFirmwareRevision( ReaderExtension ); SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = ReaderExtension->FirmwareMajor; SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = ReaderExtension->FirmwareMinor; SmartcardExtension->VendorAttr.IfdSerialNo.Length = 0; if( NTStatus == STATUS_SUCCESS ) { NTStatus = IoSetDeviceInterfaceState( &DeviceExtension->PnPDeviceName, TRUE ); if( NTStatus == STATUS_SUCCESS ) { KeSetEvent( &DeviceExtension->ReaderStarted, 0, FALSE ); } } else { SmartcardLogError( DeviceObject, STC_NO_READER_FOUND, NULL, 0 ); } } else { SmartcardLogError( DeviceObject, STC_ERROR_INIT_INTERFACE, NULL, 0 ); } } else { SmartcardLogError( DeviceObject, STC_CONNECT_FAILS, NULL, 0 ); } IoFreeIrp( Irp ); } else { SmartcardLogError( DeviceObject, STC_NO_MEMORY, NULL, 0 ); NTStatus = STATUS_NO_MEMORY; } if (NTStatus != STATUS_SUCCESS) { DrvStopDevice(DeviceObject->DeviceExtension); } SmartcardDebug( (NTStatus == STATUS_SUCCESS ? DEBUG_TRACE : DEBUG_ERROR), ( "SCMSTCS!DrvStartDevice: Exit %lx\n", NTStatus ) ); return( NTStatus ); } //________________________________________ U N L O A D ____________________________________________ VOID DrvStopDevice( IN PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { PSMARTCARD_EXTENSION SmartcardExtension; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvStopDevice: Enter\n" )); SmartcardExtension = &DeviceExtension->SmartcardExtension; if( KeReadStateEvent( &SmartcardExtension->ReaderExtension->SerialCloseDone ) == 0l ) { NTSTATUS NTStatus; ULONG WaitMask; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvStopDevice: Power Down\n" )); // power down the reader STCConfigureSTC( SmartcardExtension->ReaderExtension, ( PSTC_REGISTER ) STCClose ); // the following delay is neccessary to make sure the last read operation is completed // and a IOCTL_SERIAL_WAIT_ON_MASK is started SysDelay( 2 * SR_READ_TOTAL_TIMEOUT_CONSTANT ); // // no more event notification neccessary. a side effect is the // finishing of all pending notification irp's by the serial driver, // so the callback will complete the irp & initiate the close of the // connection to the serial driver // WaitMask = 0; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!Set Wait Mask\n" )); NTStatus = IFSerialIoctl( SmartcardExtension->ReaderExtension, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof( ULONG ), NULL, 0 ); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!Wait For Done\n" )); // wait until the connetion to the serial driver is closed NTStatus = KeWaitForSingleObject( &SmartcardExtension->ReaderExtension->SerialCloseDone, Executive, KernelMode, FALSE, NULL ); } SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvStopDevice: Exit\n" )); } VOID DrvRemoveDevice( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus; PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvRemoveDevice: Enter\n" )); PAGED_CODE(); if( DeviceObject != NULL ) { DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; DrvStopDevice( DeviceExtension ); if( SmartcardExtension->OsData ) { ASSERT( SmartcardExtension->OsData->NotificationIrp == NULL ); // Wait until we can safely unload the device SmartcardReleaseRemoveLockAndWait( SmartcardExtension ); } if( SmartcardExtension->ReaderExtension->SerialDeviceObject ) { IoDetachDevice( SmartcardExtension->ReaderExtension->SerialDeviceObject ); } if( DeviceExtension->PnPDeviceName.Buffer != NULL ) { RtlFreeUnicodeString( &DeviceExtension->PnPDeviceName ); } if( SmartcardExtension->OsData != NULL ) { SmartcardExit( SmartcardExtension ); } if( SmartcardExtension->ReaderExtension != NULL ) { if (SmartcardExtension->ReaderExtension->CloseSerial != NULL) { IoFreeWorkItem(SmartcardExtension->ReaderExtension->CloseSerial); } if (SmartcardExtension->ReaderExtension->ReadWorkItem != NULL) { IoFreeWorkItem(SmartcardExtension->ReaderExtension->ReadWorkItem); } ExFreePool( SmartcardExtension->ReaderExtension ); } IoDeleteDevice( DeviceObject ); } SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvRemoveDevice: Exit\n" )); } VOID DrvDriverUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Arguments: Return Value: --*/ { PDEVICE_OBJECT DeviceObject; NTSTATUS NTStatus; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvDriverUnload: Enter\n" )); // just make sure that all device instances have been unloaded while( DeviceObject = DriverObject->DeviceObject ) { DrvRemoveDevice( DeviceObject ); } while( DeviceObject = DriverObject->DeviceObject ); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvDriverUnload: Exit\n" )); } NTSTATUS DrvSystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: DeviceObject - Pointer to device object for this miniport Irp - IRP involved. Return Value: STATUS_SUCCESS. --*/ { PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; PREADER_EXTENSION ReaderExtension; NTSTATUS status = STATUS_SUCCESS; DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; ReaderExtension = SmartcardExtension->ReaderExtension; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(ReaderExtension->SerialDeviceObject, Irp); return status; } //______________________________ D E V I C E I O C O N T R O L ________________________________ NTSTATUS DrvCreateClose( IN PDEVICE_OBJECT DeviceObject, IN 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 for this miniport Irp - IRP involved. Return Value: STATUS_SUCCESS. --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; __try { if (irpStack->MajorFunction == IRP_MJ_CREATE) { status = SmartcardAcquireRemoveLockWithTag( &deviceExtension->SmartcardExtension, 'lCrC' ); if (status != STATUS_SUCCESS) { status = STATUS_DEVICE_REMOVED; __leave; } // test if the device has been opened already if (InterlockedCompareExchange( &deviceExtension->ReaderOpen, TRUE, FALSE) == FALSE) { SmartcardDebug( DEBUG_DRIVER, ("%s!DrvCreateClose: Open\n", DRIVER_NAME) ); } else { // the device is already in use status = STATUS_UNSUCCESSFUL; // release the lock SmartcardReleaseRemoveLockWithTag( &deviceExtension->SmartcardExtension, 'lCrC' ); } } else { SmartcardDebug( DEBUG_DRIVER, ("%s!DrvCreateClose: Close\n", DRIVER_NAME) ); SmartcardReleaseRemoveLockWithTag( &deviceExtension->SmartcardExtension, 'lCrC' ); deviceExtension->ReaderOpen = FALSE; } } __finally { Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } NTSTATUS DrvDeviceIoControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus=STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; KIRQL CurrentIrql; DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; if (KeReadStateEvent(&(SmartcardExtension->ReaderExtension->SerialCloseDone))) { // // we have no connection to serial, the device was either // surprise-removed or politely removed // NTStatus = STATUS_DEVICE_REMOVED; } if (NTStatus == STATUS_SUCCESS) { KeAcquireSpinLock( &DeviceExtension->SpinLock, &CurrentIrql ); // make sure that the reader is already started if( DeviceExtension->IoCount == 0 ) { KeReleaseSpinLock( &DeviceExtension->SpinLock, CurrentIrql ); // wait until the pnp manager has started the device NTStatus = KeWaitForSingleObject( &DeviceExtension->ReaderStarted, Executive, KernelMode, FALSE, NULL ); KeAcquireSpinLock( &DeviceExtension->SpinLock, &CurrentIrql ); } DeviceExtension->IoCount++; KeReleaseSpinLock( &DeviceExtension->SpinLock, CurrentIrql ); NTStatus = SmartcardAcquireRemoveLockWithTag(SmartcardExtension, 'tcoI'); } if( NTStatus != STATUS_SUCCESS ) { // if no remove lock can be acquired, the device has been removed Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; IoCompleteRequest(Irp, IO_NO_INCREMENT); NTStatus = STATUS_DEVICE_REMOVED; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvDeviceIoControl: the device has been removed\n" )); } else { // let the lib process the call NTStatus = SmartcardDeviceControl( SmartcardExtension, Irp ); SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 'tcoI'); KeAcquireSpinLock( &DeviceExtension->SpinLock, &CurrentIrql ); DeviceExtension->IoCount--; KeReleaseSpinLock(&DeviceExtension->SpinLock, CurrentIrql); } return( NTStatus ); } NTSTATUS DrvGenericIOCTL( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ DrvGenericIOCTL: Performs generic callbacks to the reader Arguments: SmartcardExtension context of the call Return Value: STATUS_SUCCESS --*/ { NTSTATUS NTStatus; PIRP Irp; PIO_STACK_LOCATION IrpStack; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvGenericIOCTL: Enter\n" )); // get pointer to current IRP stack location Irp = SmartcardExtension->OsData->CurrentIrp; IrpStack = IoGetCurrentIrpStackLocation( Irp ); // assume error NTStatus = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; // dispatch IOCTL switch( IrpStack->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_GET_VERSIONS: if( IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( VERSION_CONTROL )) { NTStatus = STATUS_BUFFER_TOO_SMALL; } else { PVERSION_CONTROL VersionControl; VersionControl = (PVERSION_CONTROL)Irp->AssociatedIrp.SystemBuffer; VersionControl->SmclibVersion = SmartcardExtension->Version; VersionControl->DriverMajor = SCMSTCS_MAJOR_VERSION; VersionControl->DriverMinor = SCMSTCS_MINOR_VERSION; // update firmware version STCGetFirmwareRevision( SmartcardExtension->ReaderExtension ); VersionControl->FirmwareMajor = SmartcardExtension->ReaderExtension->FirmwareMajor; VersionControl->FirmwareMinor = SmartcardExtension->ReaderExtension->FirmwareMinor; Irp->IoStatus.Information = sizeof( VERSION_CONTROL ); NTStatus = STATUS_SUCCESS; } break; default: break; } // set status of the packet Irp->IoStatus.Status = NTStatus; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvGenericIOCTL: Exit\n" )); return( NTStatus ); } NTSTATUS DrvCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function is called whenever the caller wants to cancel a pending irp. Arguments: DeviceObject - Our device object Irp - the pending irp that we should cancel --*/ { PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvCancel: Enter\n" )); DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; ASSERT( Irp == SmartcardExtension->OsData->NotificationIrp ); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; SmartcardExtension->OsData->NotificationIrp = NULL; IoReleaseCancelSpinLock( Irp->CancelIrql ); SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!Cancel Irp %lx\n", Irp )); IoCompleteRequest( Irp, IO_NO_INCREMENT ); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvCancel: Exit\n" )); return( STATUS_CANCELLED ); } NTSTATUS DrvCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function is called, when the 'calling app' terminates (unexpectedly). We have to clean up all pending irps. In our case it can only be the notification irp. --*/ { NTSTATUS NTStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; KIRQL CancelIrql; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvCleanup: Enter\n" )); DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; IoAcquireCancelSpinLock(&CancelIrql); ASSERT( Irp != SmartcardExtension->OsData->NotificationIrp ); // cancel pending notification irps if( SmartcardExtension->OsData->NotificationIrp ) { // reset the cancel function so that it won't be called anymore IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, NULL ); SmartcardExtension->OsData->NotificationIrp->CancelIrql = CancelIrql; // DrvCancel will release the cancel spin lock DrvCancel( DeviceObject, SmartcardExtension->OsData->NotificationIrp ); } else { IoReleaseCancelSpinLock(CancelIrql); } SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!Completing Irp %lx\n", Irp )); // complete the irp that was passed to this function Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest( Irp, IO_NO_INCREMENT ); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvCleanup: Exit\n" )); return( STATUS_SUCCESS ); } VOID DrvWaitForDeviceRemoval( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus; PDEVICE_EXTENSION DeviceExtension; PREADER_EXTENSION ReaderExtension; PIRP Irp; PIO_STACK_LOCATION IrpStack; IO_STATUS_BLOCK IoStatusBlock; SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvWaitForDeviceRemoval: Enter\n" )); DeviceExtension = DeviceObject->DeviceExtension; ReaderExtension = DeviceExtension->SmartcardExtension.ReaderExtension; ASSERT( DeviceExtension != NULL ); ASSERT( ReaderExtension != NULL ); // mark the device as invalid, so no application can re-open it IoSetDeviceInterfaceState( &DeviceExtension->PnPDeviceName, FALSE ); // close the connection to the serial driver Irp = IoAllocateIrp( (CCHAR)( DeviceObject->StackSize + 1 ), FALSE ); ASSERT( Irp != NULL ); if( Irp != NULL ) { SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvWaitForDeviceRemoval: Sending IRP_MJ_CLOSE\n" )); IoSetNextIrpStackLocation( Irp ); // // send MJ_CLOSE to the serial driver. a side effect of this call is that the serial // enumerator will be informed about changes at the COM port, so it will trigger the // appropriate pnp calls // Irp->UserIosb = &IoStatusBlock; IrpStack = IoGetCurrentIrpStackLocation( Irp ); IrpStack->MajorFunction = IRP_MJ_CLOSE; NTStatus = DrvCallSerialDriver( ReaderExtension->SerialDeviceObject, Irp ); IoFreeIrp( Irp ); } // inform waiting threads that the close to the serial driver has finished KeSetEvent( &ReaderExtension->SerialCloseDone, 0, FALSE ); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvWaitForDeviceRemoval: Exit\n" )); return; } NTSTATUS DrvIoCompletion ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT Event ) /*++ Routine Description: Arguments: Return Value: --*/ { UNREFERENCED_PARAMETER( DeviceObject ); if( Irp->Cancel ) { Irp->IoStatus.Status = STATUS_CANCELLED; } else { Irp->IoStatus.Status = STATUS_MORE_PROCESSING_REQUIRED; } KeSetEvent( Event, 0, FALSE ); return( STATUS_MORE_PROCESSING_REQUIRED ); } NTSTATUS DrvCallSerialDriver( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus = STATUS_SUCCESS; KEVENT Event; // copy the stack location of the actual call to the next position IoCopyCurrentIrpStackLocationToNext( Irp ); // this event will be passed to the completion routine & signaled if the call // is finished KeInitializeEvent( &Event, NotificationEvent, FALSE ); // the DrvIoCompletion signals the event & keeps the irp alive by setting the // status to STATUS_MORE_PROCESSING_REQUIRED IoSetCompletionRoutine ( Irp, DrvIoCompletion, &Event, TRUE, TRUE, TRUE ); // call the appropriate driver if( IoGetCurrentIrpStackLocation( Irp )->MajorFunction == IRP_MJ_POWER ) { NTStatus = PoCallDriver( DeviceObject, Irp ); } else { NTStatus = IoCallDriver( DeviceObject, Irp ); } // wait until the irp was processed if( NTStatus == STATUS_PENDING ) { NTStatus = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); NTStatus = Irp->IoStatus.Status; } return( NTStatus ); } //__________________________________ P L U G ' N ' P L A Y ________________________________________ NTSTATUS DrvPnPHandler( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; PREADER_EXTENSION ReaderExtension; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvPnPDeviceControl: Enter\n" )); DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; ReaderExtension = SmartcardExtension->ReaderExtension; NTStatus = SmartcardAcquireRemoveLockWithTag(SmartcardExtension, ' PnP'); if( NTStatus != STATUS_SUCCESS ) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = NTStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); } else { PDEVICE_OBJECT AttachedDeviceObject; BOOLEAN DeviceRemoved, IrpSkipped; AttachedDeviceObject = ReaderExtension->SerialDeviceObject; DeviceRemoved = FALSE, IrpSkipped = FALSE; // dispatch on pnp minor function switch( IoGetCurrentIrpStackLocation( Irp )->MinorFunction ) { case IRP_MN_START_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_START_DEVICE\n" )); // call the serial driver first to make sure the interface is ready NTStatus = DrvCallSerialDriver(AttachedDeviceObject, Irp ); if( NT_SUCCESS(NTStatus)) { NTStatus = DrvStartDevice(DeviceObject); } break; case IRP_MN_QUERY_STOP_DEVICE: { KIRQL CurrentIrql; SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_QUERY_STOP_DEVICE\n" )); KeAcquireSpinLock(&DeviceExtension->SpinLock, &CurrentIrql ); if( DeviceExtension->IoCount > 0 ) { // don't stop if any io requests are pending KeReleaseSpinLock(&DeviceExtension->SpinLock, CurrentIrql ); NTStatus = STATUS_DEVICE_BUSY; } else { // don't allow further io requests KeClearEvent( &DeviceExtension->ReaderStarted ); KeReleaseSpinLock( &DeviceExtension->SpinLock, CurrentIrql ); NTStatus = DrvCallSerialDriver( AttachedDeviceObject, Irp ); } break; } case IRP_MN_CANCEL_STOP_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_CANCEL_STOP_DEVICE\n" )); NTStatus = DrvCallSerialDriver( AttachedDeviceObject, Irp ); if( NTStatus == STATUS_SUCCESS ) { // driver is ready to process io requests KeSetEvent( &DeviceExtension->ReaderStarted, 0, FALSE ); } break; case IRP_MN_STOP_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_STOP_DEVICE\n" )); DrvStopDevice( DeviceExtension ); NTStatus = DrvCallSerialDriver(AttachedDeviceObject, Irp ); break; case IRP_MN_QUERY_REMOVE_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_QUERY_REMOVE_DEVICE\n" )); // disable the reader (and ignore possibles errors) IoSetDeviceInterfaceState( &DeviceExtension->PnPDeviceName, FALSE ); // check if the reader is in use if(DeviceExtension->ReaderOpen) { // // someone is connected, fail the call // we will enable the device interface in // IRP_MN_CANCEL_REMOVE_DEVICE again // NTStatus = STATUS_UNSUCCESSFUL; } else { // ready to remove the device NTStatus = DrvCallSerialDriver(AttachedDeviceObject, Irp ); } break; case IRP_MN_CANCEL_REMOVE_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_CANCEL_REMOVE_DEVICE\n" )); NTStatus = DrvCallSerialDriver( AttachedDeviceObject, 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(( NTStatus == STATUS_SUCCESS )&& (KeReadStateEvent(&(ReaderExtension->SerialCloseDone))!= TRUE)) { // enable the reader SmartcardDebug( DEBUG_DRIVER, ( "IoSetDeviceInterfaceState( &DeviceExtension->PnPDeviceName, TRUE )\n" )); NTStatus = IoSetDeviceInterfaceState( &DeviceExtension->PnPDeviceName, TRUE ); } break; case IRP_MN_REMOVE_DEVICE: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_REMOVE_DEVICE\n" )); DrvRemoveDevice( DeviceObject ); NTStatus = DrvCallSerialDriver( AttachedDeviceObject, Irp ); DeviceRemoved = TRUE; break; default: // the irp is not handled by the driver, so pass it to theserial driver SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!IRP_MN_%lx\n", IoGetCurrentIrpStackLocation( Irp )->MinorFunction ) ); IoSkipCurrentIrpStackLocation( Irp ); NTStatus = IoCallDriver( AttachedDeviceObject, Irp ); IrpSkipped = TRUE; break; } if( IrpSkipped == FALSE) { Irp->IoStatus.Status = NTStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); } if( DeviceRemoved == FALSE) { SmartcardReleaseRemoveLockWithTag(SmartcardExtension, ' PnP'); } } SmartcardDebug( DEBUG_TRACE, ( "SCMSTCS!DrvPnPDeviceControl: Exit %X\n", NTStatus )); return( NTStatus ); } //__________________________________________ P O W E R ____________________________________________ VOID DrvSystemPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PKEVENT Event, IN PIO_STATUS_BLOCK IoStatus ) /*++ Routine Description: This function is called when the underlying stacks completed the power transition. --*/ { UNREFERENCED_PARAMETER (DeviceObject); UNREFERENCED_PARAMETER (MinorFunction); UNREFERENCED_PARAMETER (PowerState); UNREFERENCED_PARAMETER (IoStatus); KeSetEvent(Event, 0, FALSE); } NTSTATUS DrvDevicePowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus; PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; BOOLEAN CardPresent; KIRQL irql; if(Irp->PendingReturned) { IoMarkIrpPending(Irp); } // re-initialize the the reader & get the current card state NTStatus = STCConfigureSTC( SmartcardExtension->ReaderExtension, ( PSTC_REGISTER ) STCInitialize ); // Save the state of the card BEFORE stand by / hibernation KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock, &irql); CardPresent = SmartcardExtension->ReaderCapabilities.CurrentState >= SCARD_ABSENT; KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql); // get the current state of the card CBUpdateCardState(SmartcardExtension, SCARD_UNKNOWN); KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock, &irql); if (CardPresent || SmartcardExtension->ReaderCapabilities.CurrentState >= SCARD_ABSENT) { // // 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. // SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT; KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql); CBUpdateCardState(SmartcardExtension, SCARD_UNKNOWN); } else { KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql); } // 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, IoGetCurrentIrpStackLocation( Irp )->Parameters.Power.State ); PoStartNextPowerIrp( Irp ); // signal that we can process ioctls again KeSetEvent(&DeviceExtension->ReaderStarted, 0, FALSE); return( STATUS_SUCCESS ); } typedef enum _ACTION { Undefined = 0, SkipRequest, WaitForCompletion, CompleteRequest, MarkPending } ACTION; NTSTATUS DrvPowerHandler( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS NTStatus = STATUS_SUCCESS; PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION DeviceExtension; PSMARTCARD_EXTENSION SmartcardExtension; PDEVICE_OBJECT AttachedDeviceObject; POWER_STATE PowerState; ACTION Action; KEVENT event; KIRQL irql; PAGED_CODE(); SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: Enter\n" )); IrpStack = IoGetCurrentIrpStackLocation( Irp ); DeviceExtension = DeviceObject->DeviceExtension; SmartcardExtension = &DeviceExtension->SmartcardExtension; AttachedDeviceObject = SmartcardExtension->ReaderExtension->SerialDeviceObject; NTStatus = SmartcardAcquireRemoveLockWithTag(SmartcardExtension, 'rwoP'); if( !NT_SUCCESS( NTStatus )) { PoStartNextPowerIrp( Irp ); Irp->IoStatus.Status = NTStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); } else { switch (IrpStack->Parameters.Power.Type) { case DevicePowerState: if (IrpStack->MinorFunction == IRP_MN_SET_POWER ) { switch ( IrpStack->Parameters.Power.State.DeviceState ) { case PowerDeviceD0: // turn the reader on SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: PowerDevice D0\n" )); // // send the request to the serial driver to power up the port. // the reader will be powered from our completion routine // IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine ( Irp, DrvDevicePowerCompletion, SmartcardExtension, TRUE, TRUE, TRUE ); Action = WaitForCompletion; break; case PowerDeviceD3: // turn the reader off SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: PowerDevice D3\n" )); PoSetPowerState ( DeviceObject, DevicePowerState, IrpStack->Parameters.Power.State ); // // check if we're still connected to the reader // someone might have pulled the plug without re-scanning for hw/changes // if (KeReadStateEvent( &SmartcardExtension->ReaderExtension->SerialCloseDone ) == 0l) { // power down the card KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock, &irql); if ( SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT ) { KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql); SmartcardExtension->MinorIoControlCode = SCARD_POWER_DOWN; NTStatus = CBCardPower( SmartcardExtension ); // // This will trigger the card monitor, since we do not really // know if the user will remove / re-insert a card while the // system is asleep // } else { KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql); } // power down the reader STCConfigureSTC( SmartcardExtension->ReaderExtension, ( PSTC_REGISTER ) STCClose ); } // wait until the last read is finished to make sure we go to power // down with a pending tracking irp SysDelay( 2 * SR_READ_TOTAL_TIMEOUT_CONSTANT ); // save the current Power state of the reader SmartcardExtension->ReaderExtension->ReaderPowerState = PowerReaderOff; Action = SkipRequest; break; default: Action = SkipRequest; break; } } else { Action = SkipRequest; break; } break; case 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(SmartcardExtension->ReaderExtension->ReaderPowerState != PowerReaderUnspecified); switch ( IrpStack->MinorFunction ) { case IRP_MN_QUERY_POWER: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: Query Power\n" )); switch (IrpStack->Parameters.Power.State.SystemState) { case PowerSystemMaximum: case PowerSystemWorking: case PowerSystemSleeping1: case PowerSystemSleeping2: Action = SkipRequest; break; case PowerSystemSleeping3: case PowerSystemHibernate: case PowerSystemShutdown: KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql); if (DeviceExtension->IoCount == 0) { // Block any further ioctls KeClearEvent(&DeviceExtension->ReaderStarted); Action = SkipRequest; } else { // can't go to sleep mode since the reader is busy. NTStatus = STATUS_DEVICE_BUSY; Action = CompleteRequest; } KeReleaseSpinLock(&DeviceExtension->SpinLock, irql); break; } break; case IRP_MN_SET_POWER: SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: 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 KeSetEvent(&DeviceExtension->ReaderStarted, 0, FALSE); Action = SkipRequest; 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 = SkipRequest; break; } PowerState.DeviceState = PowerDeviceD3; // first, inform the Power manager of our new state. PoSetPowerState ( DeviceObject, SystemPowerState, PowerState ); Action = MarkPending; break; default: Action = CompleteRequest; break; } break; default: Action = SkipRequest; break; } } break; default: Action = CompleteRequest; break; } switch( Action ) { case CompleteRequest: Irp->IoStatus.Status = NTStatus; Irp->IoStatus.Information = 0; SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 'rwoP'); PoStartNextPowerIrp( Irp ); IoCompleteRequest( Irp, IO_NO_INCREMENT ); break; case MarkPending: // initialize the event we need in the completion function KeInitializeEvent( &event, NotificationEvent, FALSE ); // request the device power irp NTStatus = PoRequestPowerIrp ( DeviceObject, IRP_MN_SET_POWER, PowerState, DrvSystemPowerCompletion, &event, NULL ); if (NTStatus == STATUS_PENDING) { // wait until the device power irp completed NTStatus = KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 'rwoP'); if (PowerState.SystemState == PowerSystemWorking) { PoSetPowerState ( DeviceObject, SystemPowerState, PowerState ); } PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); NTStatus = PoCallDriver(AttachedDeviceObject, Irp); } else { SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 'rwoP'); Irp->IoStatus.Status = NTStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); } break; case SkipRequest: SmartcardReleaseRemoveLockWithTag(SmartcardExtension, 'rwoP'); PoStartNextPowerIrp( Irp ); IoSkipCurrentIrpStackLocation( Irp ); NTStatus = PoCallDriver( AttachedDeviceObject, Irp ); break; case WaitForCompletion: NTStatus = PoCallDriver( AttachedDeviceObject, Irp ); break; default: break; } } SmartcardDebug( DEBUG_DRIVER, ( "SCMSTCS!DrvPowerHandler: Exit %X\n", NTStatus )); return( NTStatus ); } void SysDelay( ULONG Timeout ) /*++ SysDelay: performs a required delay Arguments: Timeout delay in milliseconds --*/ { if( KeGetCurrentIrql() >= DISPATCH_LEVEL ) { ULONG Cnt = 20 * Timeout; while( Cnt-- ) { // KeStallExecutionProcessor: counted in us KeStallExecutionProcessor( 50 ); } } else { LARGE_INTEGER SysTimeout; SysTimeout.QuadPart = (LONGLONG) Timeout * -10 * 1000; // KeDelayExecutionThread: counted in 100 ns KeDelayExecutionThread( KernelMode, FALSE, &SysTimeout ); } return; } //_________________________________________ END OF FILE _________________________________________