/*++ Copyright (c) 1996 Microsoft Corporation Module Name: scrcp8t.c Abstract: Author: Environment: Kernel mode Revision History : Nov. 1997 - 1.0 Release Jan. 1998 - Fix for vendor defined IOCTLs CP8SerialIo now write the whole data packet if GT is 0 Support for higher data rates added --*/ #include #include "scrcp8t.h" // // We do not need these functions after init anymore // #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(INIT, CP8AddDevice) #pragma alloc_text(INIT, CP8CreateDevice) #pragma alloc_text(INIT, CP8Initialize) #if DBG #pragma optimize ("", off) #endif NTSTATUS CP8VendorIoctl( PSMARTCARD_EXTENSION SmartcardExtension ); NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN 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. --*/ { RTL_QUERY_REGISTRY_TABLE paramTable[2]; BOOLEAN detectReader = FALSE; ULONG i, noOfDevices = 0; UNICODE_STRING serialPort, parameters, parameterPath; NTSTATUS status; WCHAR buffer[128]; SmartcardDebug( DEBUG_DRIVER, ("%s!DriverEntry: Enter - %s %s\n", DRIVER_NAME, __DATE__, __TIME__) ) // // Initialize the Driver Object with driver's entry points // DriverObject->DriverUnload = CP8Unload; DriverObject->MajorFunction[IRP_MJ_CREATE] = CP8CreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = CP8CreateClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = CP8Cleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CP8DeviceControl; RtlZeroMemory( paramTable, sizeof(paramTable) ); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"Device0"; paramTable[0].EntryContext = &serialPort; paramTable[0].DefaultType = REG_SZ; paramTable[0].DefaultData = &serialPort; paramTable[0].DefaultLength = 0; parameterPath.MaximumLength = sizeof(buffer); parameterPath.Length = 0; parameterPath.Buffer = buffer; RtlCopyUnicodeString( ¶meterPath, RegistryPath ); RtlInitUnicodeString( ¶meters, L"\\Parameters" ); RtlAppendUnicodeStringToString( ¶meterPath, ¶meters ); // // We now search the registry for DeviceN value // for (i = 0; i < MAXIMUM_SERIAL_READERS; i++) { UNICODE_STRING serialDeviceName; RtlInitUnicodeString( &serialDeviceName, L"\\Device\\Serial " ); RtlInitUnicodeString( &serialPort, L"COM?" ); if (detectReader == FALSE) { // // create string like DeviceN // paramTable[0].Name[6] = L'0' + (WCHAR) i; // // now try to find the entry in the registry // status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, parameterPath.Buffer, ¶mTable[0], NULL, NULL ); if (status != STATUS_SUCCESS) { // // The entry does no exist in the registry // if (i > 0) { break; } // // we found not even an entry for device0. // Try to detect readers. We assume now that // the user has inserted a card into the reader // Actually we try to detect the card, not the reader // detectReader = TRUE; } } if (detectReader) { serialPort.Buffer[3] = L'1' + (WCHAR) i; } // // create string like \Device\SerialN // serialDeviceName.Buffer[14] = serialPort.Buffer[3] - (WCHAR) 1; // // Now try to create a device and connect to the serial port // status = CP8AddDevice( DriverObject, &serialDeviceName ); if (status == STATUS_SUCCESS) { // // We have successfully created a device // PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; // // Save the serial port number this device is connected to // smartcardExtension->ReaderCapabilities.Channel = serialPort.Buffer[3] - L'0'; if (detectReader) { // // We now assume that the user has inserted a smart card // and we try to get an ATR in order to figure out if there // is really a reader connected to this port // smartcardExtension->IoRequest.ReplyBuffer = NULL; smartcardExtension->MinorIoControlCode = SCARD_COLD_RESET; status = CP8ReaderPower(smartcardExtension); if (status == STATUS_SUCCESS) { // // create an entry in the registry // paramTable[0].Name[6] = L'0' + (WCHAR) noOfDevices; RtlWriteRegistryValue( RTL_REGISTRY_ABSOLUTE, parameterPath.Buffer, paramTable[0].Name, REG_SZ, serialPort.Buffer, serialPort.Length ); SmartcardLogError( DriverObject, CP8_NEW_DEVICE_ADDED, &serialPort, status ); } else { // // We were unable to get an ATR from the card. // So remove the device again. // CP8RemoveDevice(deviceObject); } } if (status == STATUS_SUCCESS) { noOfDevices++; } } } if (noOfDevices == 0) { SmartcardLogError( DriverObject, CP8_NO_SUCH_DEVICE, NULL, status ); return STATUS_NO_SUCH_DEVICE; } return STATUS_SUCCESS; } NTSTATUS CP8AddDevice( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING SerialDeviceName ) /*++ Routine Description: This is the add-device routine for the miniport. Arguments: DriverObject - a pointer to the driver object for this device SerialDeviceName - The device name of the serial port to attach to Return Value: NT_STATUS --*/ { UNICODE_STRING smartcardDeviceName, deviceNo, device; WCHAR buffer[32]; ULONG i; NTSTATUS status; for (i = 0; i < MAXIMUM_SERIAL_READERS; i++) { // // Build a device name for the smart card reader // \Device\BullCP8TN (N is the 0 based device number) // smartcardDeviceName.Buffer = buffer; smartcardDeviceName.MaximumLength = sizeof(buffer); smartcardDeviceName.Length = 0; RtlInitUnicodeString( &device, L"\\Device\\BullCP8T" ); RtlCopyUnicodeString( &smartcardDeviceName, &device ); deviceNo.Buffer = smartcardDeviceName.Buffer + smartcardDeviceName.Length / sizeof(WCHAR); deviceNo.MaximumLength = 2 * sizeof(WCHAR); deviceNo.Length = 0; RtlIntegerToUnicodeString( i, 10, &deviceNo ); smartcardDeviceName.Length += deviceNo.Length; // // 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 = CP8CreateDevice( DriverObject, &smartcardDeviceName, SerialDeviceName ); if (status == STATUS_SUCCESS) { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; // this is a list of our supported data rates static ULONG dataRatesSupported[] = { 9600, 19200 }; // // Initialize the vendor information // strcpy( deviceExtension->SmartcardExtension.VendorAttr.VendorName.Buffer, "Bull" ); deviceExtension->SmartcardExtension.VendorAttr.VendorName.Length = strlen(deviceExtension->SmartcardExtension.VendorAttr.VendorName.Buffer); strcpy( deviceExtension->SmartcardExtension.VendorAttr.IfdType.Buffer, "CP8 Transac" ); deviceExtension->SmartcardExtension.VendorAttr.IfdType.Length = strlen(deviceExtension->SmartcardExtension.VendorAttr.IfdType.Buffer); smartcardExtension->VendorAttr.UnitNo = i; // // Set reader info // // // Clk frequency in KHz encoded as little endian integer // smartcardExtension->ReaderCapabilities.CLKFrequency.Default = 3571; smartcardExtension->ReaderCapabilities.CLKFrequency.Max = 3571; smartcardExtension->ReaderCapabilities.DataRate.Default = 9600; smartcardExtension->ReaderCapabilities.DataRate.Max = 19200; smartcardExtension->ReaderCapabilities.DataRatesSupported.List = dataRatesSupported; smartcardExtension->ReaderCapabilities.DataRatesSupported.Entries = sizeof(dataRatesSupported) / sizeof(dataRatesSupported[0]); smartcardExtension->ReaderCapabilities.MaxIFSD = 254; break; } if (status != STATUS_OBJECT_NAME_COLLISION) { // // The corresponding serial port is already in use // So don't try to create a smart card device with a different name // break; } } return status; } NTSTATUS CP8SerialCallComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT Event ) /*++ Routine Description: Completion routine for an Irp sent to the serial driver. It sets only an event that we can use to wait for. --*/ { UNREFERENCED_PARAMETER (DeviceObject); Irp->IoStatus.Information = 0; 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 CP8CreateDevice( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING SmartcardDeviceName, IN PUNICODE_STRING SerialDeviceName ) /*++ Routine Description: This routine creates an object for the physical device specified and sets up the deviceExtension. Arguments: Return Value: NTSTATUS --*/ { PFILE_OBJECT serialFileObject; PDEVICE_OBJECT deviceObject, serialDeviceObject; PDEVICE_EXTENSION deviceExtension; NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION irpStack; IO_STATUS_BLOCK ioStatusBlock; KEVENT Event; PIRP irp; ULONG i; 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 = 2; 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, CP8_CANT_CONNECT_TO_SERIAL_PORT, &comPortNumber, status ); return status; } status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), SmartcardDeviceName, FILE_DEVICE_SMARTCARD, 0, #if DBG FALSE, #else TRUE, #endif &deviceObject ); if (!NT_SUCCESS(status)) { ObDereferenceObject( serialFileObject ); SmartcardLogError( DriverObject, CP8_CANT_CREATE_DEVICE, SmartcardDeviceName, status ); return status; } deviceExtension = deviceObject->DeviceExtension; deviceObject->Flags = deviceObject->Flags | DO_BUFFERED_IO; RtlZeroMemory( deviceExtension, sizeof(PDEVICE_EXTENSION) ); // // Allocate data struct space for smart card reader // deviceExtension->SmartcardExtension.ReaderExtension = ExAllocatePool( NonPagedPool, sizeof(READER_EXTENSION) ); if (deviceExtension->SmartcardExtension.ReaderExtension == NULL) { SmartcardLogError( DriverObject, CP8_NO_MEMORY, NULL, 0 ); status = STATUS_INSUFFICIENT_RESOURCES; } if (status == STATUS_SUCCESS) { RtlZeroMemory( deviceExtension->SmartcardExtension.ReaderExtension, sizeof(READER_EXTENSION) ); // // Write the version of the lib we use to the smartcard extension // deviceExtension->SmartcardExtension.Version = SMCLIB_VERSION; // // Now let the lib allocate the buffer for data transmission // We can either tell the lib how big the buffer should be // by assigning a value to BufferSize or let the lib // allocate the default size // status = SmartcardInitialize( &deviceExtension->SmartcardExtension ); } if (status == STATUS_SUCCESS) { // // Set up call back functions // deviceExtension->SmartcardExtension.ReaderFunction[RDF_TRANSMIT] = CP8Transmit; deviceExtension->SmartcardExtension.ReaderFunction[RDF_SET_PROTOCOL] = CP8SetProtocol; deviceExtension->SmartcardExtension.ReaderFunction[RDF_CARD_POWER] = CP8ReaderPower; deviceExtension->SmartcardExtension.ReaderFunction[RDF_CARD_TRACKING] = CP8CardTracking; deviceExtension->SmartcardExtension.ReaderFunction[RDF_IOCTL_VENDOR] = CP8VendorIoctl; // // Save deviceObject // deviceExtension->SmartcardExtension.OsData->DeviceObject = deviceObject; // // Save the deviceObject for the connected serial port // deviceExtension->SmartcardExtension.ReaderExtension->ConnectedSerialPort = serialDeviceObject; // // The fileObject is used in the unload function for dereferencing // deviceExtension->SmartcardExtension.ReaderExtension->SerialFileObject = serialFileObject; // // Configure serial reader // status = CP8Initialize( &deviceExtension->SmartcardExtension ); if (NT_SUCCESS(status)) { // // Create a symbolic link // status = SmartcardCreateLink( &deviceExtension->SmartcardExtension.ReaderExtension->DosDeviceName, SmartcardDeviceName ); } } if (status != STATUS_SUCCESS) { ExFreePool( deviceExtension->SmartcardExtension.ReaderExtension ); SmartcardExit( &deviceExtension->SmartcardExtension ); IoDeleteDevice( deviceObject ); ObDereferenceObject( serialFileObject ); } else { SmartcardDebug( DEBUG_INFO, ("%s!CP8CreateDevice: Device %ws created\n", DRIVER_NAME, deviceExtension->SmartcardExtension.ReaderExtension->DosDeviceName.Buffer) ); } return status; } NTSTATUS CP8Initialize( IN PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This routine reads the default configuraton values for this device. Arguments: DriverObject - Pointer to driver object created by system. Return Value: none --*/ { PREADER_EXTENSION readerExtension = SmartcardExtension->ReaderExtension; NTSTATUS status; readerExtension->SerialConfigData.WaitMask = SERIAL_EV_CTS; readerExtension->SerialConfigData.BaudRate.BaudRate = 9600; readerExtension->SerialConfigData.LineControl.StopBits = STOP_BITS_2; readerExtension->SerialConfigData.LineControl.Parity = EVEN_PARITY; readerExtension->SerialConfigData.LineControl.WordLength = SERIAL_DATABITS_8; // // set timeouts // readerExtension->SerialConfigData.Timeouts.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT_DEFAULT; readerExtension->SerialConfigData.Timeouts.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT_CONSTANT_DEFAULT; readerExtension->SerialConfigData.Timeouts.ReadTotalTimeoutMultiplier = 0; // // set special characters // readerExtension->SerialConfigData.SerialChars.ErrorChar = 0; readerExtension->SerialConfigData.SerialChars.EofChar = 0; readerExtension->SerialConfigData.SerialChars.EventChar = 0; readerExtension->SerialConfigData.SerialChars.XonChar = 0; readerExtension->SerialConfigData.SerialChars.XoffChar = 0; readerExtension->SerialConfigData.SerialChars.BreakChar = 0xFF; // // Set handflow // readerExtension->SerialConfigData.HandFlow.XonLimit = 0; readerExtension->SerialConfigData.HandFlow.XoffLimit = 0; readerExtension->SerialConfigData.HandFlow.FlowReplace = SERIAL_XOFF_CONTINUE ; readerExtension->SerialConfigData.HandFlow.ControlHandShake = 0; // // Now setup information in our deviceExtension // SmartcardExtension->ReaderCapabilities.CurrentState = (ULONG) SCARD_UNKNOWN; // // This reader supports T=0 and T=1 // SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; SmartcardExtension->ReaderCapabilities.MechProperties = 0; status = CP8ConfigureSerialPort( SmartcardExtension ); if (status == STATUS_SUCCESS) { status = CP8InitializeCardTracking( SmartcardExtension ); } return status; } NTSTATUS CP8ConfigureSerialPort( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This routine will appropriately configure the serial port. It makes synchronous calls to the serial port. Arguments: SmartcardExtension - Pointer to smart card struct Return Value: NTSTATUS --*/ { PSERIAL_READER_CONFIG configData = &SmartcardExtension->ReaderExtension->SerialConfigData; NTSTATUS status = STATUS_SUCCESS; USHORT i; PUCHAR request = SmartcardExtension->SmartcardRequest.Buffer; SmartcardExtension->SmartcardRequest.BufferLength = 0; SmartcardExtension->SmartcardReply.BufferLength = SmartcardExtension->SmartcardReply.BufferSize; for (i = 0; NT_SUCCESS(status); i++) { switch (i) { case 0: // // Set up baudrate for the CP8 reader // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_BAUD_RATE; SmartcardExtension->SmartcardRequest.Buffer = (PUCHAR) &configData->BaudRate; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(SERIAL_BAUD_RATE); break; case 1: // // Set up line control parameters // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_LINE_CONTROL; SmartcardExtension->SmartcardRequest.Buffer = (PUCHAR) &configData->LineControl; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(SERIAL_LINE_CONTROL); break; case 2: // // Set serial special characters // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_CHARS; SmartcardExtension->SmartcardRequest.Buffer = (PUCHAR) &configData->SerialChars; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(SERIAL_CHARS); break; case 3: // // Set up timeouts // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_TIMEOUTS; SmartcardExtension->SmartcardRequest.Buffer = (PUCHAR) &configData->Timeouts; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(SERIAL_TIMEOUTS); break; case 4: // // Set flowcontrol and handshaking // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_HANDFLOW; SmartcardExtension->SmartcardRequest.Buffer = (PUCHAR) &configData->HandFlow; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(SERIAL_HANDFLOW); break; case 5: // // Set break off // SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_BREAK_OFF; break; case 6: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_DTR; break; case 7: return STATUS_SUCCESS; } status = CP8SerialIo( SmartcardExtension ); // // restore pointer to original request buffer // SmartcardExtension->SmartcardRequest.Buffer = request; } return status; } NTSTATUS CP8CreateClose( 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. --*/ { #if DBG PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); if (irpStack->MajorFunction == IRP_MJ_CREATE) { SmartcardDebug( DEBUG_DRIVER, ("%s!CP8CreateClose: Device %ws opened\n", DRIVER_NAME, smartcardExtension->ReaderExtension->DosDeviceName.Buffer) ); } else { SmartcardDebug( DEBUG_DRIVER, ("%s!CP8CreateClose: Device %ws closed\n", DRIVER_NAME, smartcardExtension->ReaderExtension->DosDeviceName.Buffer) ); } #endif Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; return STATUS_SUCCESS; } NTSTATUS CP8Cancel( IN PDEVICE_OBJECT DeviceObject, IN 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 for this miniport Irp - IRP involved. Return Value: STATUS_CANCELLED --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; SmartcardDebug( DEBUG_TRACE, ("%s!CP8Cancel: Enter\n", DRIVER_NAME) ); ASSERT(Irp == smartcardExtension->OsData->NotificationIrp); smartcardExtension->OsData->NotificationIrp->CancelRoutine = NULL; smartcardExtension->OsData->NotificationIrp->IoStatus.Information = 0; smartcardExtension->OsData->NotificationIrp->IoStatus.Status = STATUS_CANCELLED; IoReleaseCancelSpinLock( Irp->CancelIrql ); SmartcardDebug( DEBUG_DRIVER, ("%s!CP8Cancel: Completing wait for Irp = %lx\n", DRIVER_NAME, smartcardExtension->OsData->NotificationIrp) ); IoCompleteRequest( smartcardExtension->OsData->NotificationIrp, IO_NO_INCREMENT ); smartcardExtension->OsData->NotificationIrp = NULL; SmartcardDebug( DEBUG_TRACE, ("%s!CP8Cancel: Exit\n", DRIVER_NAME) ); return STATUS_CANCELLED; } NTSTATUS CP8Cleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system when the calling thread terminates Arguments: DeviceObject - Pointer to device object for this miniport Irp - IRP involved. Return Value: STATUS_CANCELLED --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; NTSTATUS status = STATUS_SUCCESS; SmartcardDebug( DEBUG_TRACE, ("%s!CP8Cleanup: Enter\n", DRIVER_NAME) ); if (smartcardExtension->OsData->NotificationIrp) { // // We need to complete the notification irp // IoAcquireCancelSpinLock( &(Irp->CancelIrql) ); CP8Cancel( DeviceObject, smartcardExtension->OsData->NotificationIrp ); } SmartcardDebug( DEBUG_DRIVER, ("%s!CP8Cleanup: 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!CP8Cleanup: Exit\n", DRIVER_NAME) ); return STATUS_SUCCESS; } VOID CP8RemoveDevice( IN PDEVICE_OBJECT DeviceObject ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PSMARTCARD_EXTENSION smartcardExtension = &deviceExtension->SmartcardExtension; SmartcardDebug( DEBUG_INFO, ("%s!CP8RemoveDevice: Unloading device %ws\n", DRIVER_NAME, smartcardExtension->ReaderExtension->DosDeviceName.Buffer) ); // // Tell the serial driver that we don't need the // call back (IoCompletion) anymore // smartcardExtension->ReaderExtension->SerialConfigData.WaitMask = 0; *(PULONG) smartcardExtension->SmartcardRequest.Buffer = smartcardExtension->ReaderExtension->SerialConfigData.WaitMask; smartcardExtension->SmartcardRequest.BufferLength = sizeof(ULONG); smartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_WAIT_MASK; // // We don't expect to get bytes back // smartcardExtension->SmartcardReply.BufferLength = 0; CP8SerialIo( smartcardExtension ); // // We do not longet need the reference to the serial reader // ObDereferenceObject( smartcardExtension->ReaderExtension->SerialFileObject ); // // Delete the symbolic link of the smart card reader // IoDeleteSymbolicLink( &smartcardExtension->ReaderExtension->DosDeviceName ); // // Let the lib free the send/receive buffers // SmartcardExit( smartcardExtension ); // // Free all allocated buffer // ExFreePool( smartcardExtension->ReaderExtension->DosDeviceName.Buffer ); ExFreePool( smartcardExtension->ReaderExtension ); // // Delete the device from the system // IoDeleteDevice( DeviceObject ); } VOID CP8Unload( IN 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. --*/ { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; NTSTATUS status; SmartcardDebug( DEBUG_TRACE, ("%s!CP8Unload: Enter\n", DRIVER_NAME) ); do { CP8RemoveDevice(deviceObject); } while(deviceObject = DriverObject->DeviceObject); SmartcardDebug( DEBUG_TRACE, ("%s!CP8Unload: Exit\n", DRIVER_NAME) ); } NTSTATUS CP8SerialIo( IN PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This routine sends IOCTL's to the serial driver. It waits on for their completion, and then returns. Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG currentByte = 0; // // Check if the buffers are large enough // ASSERT(SmartcardExtension->SmartcardReply.BufferLength <= SmartcardExtension->SmartcardReply.BufferSize); ASSERT(SmartcardExtension->SmartcardRequest.BufferLength <= SmartcardExtension->SmartcardRequest.BufferSize); if (SmartcardExtension->SmartcardReply.BufferLength > SmartcardExtension->SmartcardReply.BufferSize || SmartcardExtension->SmartcardRequest.BufferLength > SmartcardExtension->SmartcardRequest.BufferSize) { SmartcardLogError( SmartcardExtension->OsData->DeviceObject, CP8_BUFFER_TOO_SMALL, NULL, 0 ); return STATUS_BUFFER_TOO_SMALL; } do { IO_STATUS_BLOCK ioStatus; KEVENT event; PIRP irp; PIO_STACK_LOCATION irpNextStack; PUCHAR requestBuffer = NULL; PUCHAR replyBuffer = SmartcardExtension->SmartcardReply.Buffer; ULONG requestBufferLength = SmartcardExtension->SmartcardRequest.BufferLength; ULONG replyBufferLength = SmartcardExtension->SmartcardReply.BufferLength; KeInitializeEvent( &event, NotificationEvent, FALSE ); if (SmartcardExtension->ReaderExtension->SerialIoControlCode == SMARTCARD_WRITE) { if (SmartcardExtension->CardCapabilities.GT != 0) { // // If the guardtime isn't 0 and we write data to the smart card // we only write byte by byte, because we have to insert a delay // between every sent byte // requestBufferLength = 1; } requestBuffer = &SmartcardExtension->SmartcardRequest.Buffer[currentByte++]; replyBuffer = NULL; replyBufferLength = 0; } else { requestBuffer = (requestBufferLength ? SmartcardExtension->SmartcardRequest.Buffer : NULL); } // Build irp to be sent to serial driver irp = IoBuildDeviceIoControlRequest( SmartcardExtension->ReaderExtension->SerialIoControlCode, SmartcardExtension->ReaderExtension->ConnectedSerialPort, requestBuffer, requestBufferLength, replyBuffer, replyBufferLength, FALSE, &event, &ioStatus ); ASSERT(irp != NULL); if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } irpNextStack = IoGetNextIrpStackLocation(irp); switch (SmartcardExtension->ReaderExtension->SerialIoControlCode) { // // The serial driver trasfers data from/to irp->AssociatedIrp.SystemBuffer // case SMARTCARD_WRITE: // // Since we 'manually' change parameters, the io-manager // does not really know if this is an input or an ouput operation // unless the reply buffer is 0. We do the assertion here, because // if the reply buffer is not NULL, the io-manager will copy // data back to the reply buffer. // ASSERT(replyBuffer == NULL); irpNextStack->MajorFunction = IRP_MJ_WRITE; irpNextStack->Parameters.Write.Length = requestBufferLength; irpNextStack->Parameters.Write.ByteOffset.QuadPart = 0; break; case SMARTCARD_READ: irpNextStack->MajorFunction = IRP_MJ_READ; irpNextStack->Parameters.Read.Length = replyBufferLength; irpNextStack->Parameters.Read.ByteOffset.QuadPart = 0; break; } status = IoCallDriver( SmartcardExtension->ReaderExtension->ConnectedSerialPort, irp ); if (status == STATUS_PENDING) { KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL ); status = ioStatus.Status; // save the number of bytes received SmartcardExtension->SmartcardReply.BufferLength = ioStatus.Information; } // Check if we have to write more bytes to the reader if (SmartcardExtension->ReaderExtension->SerialIoControlCode == SMARTCARD_WRITE && SmartcardExtension->CardCapabilities.GT != 0 && currentByte < SmartcardExtension->SmartcardRequest.BufferLength) { // Now wait the required guard time KeStallExecutionProcessor(SmartcardExtension->CardCapabilities.GT); status = STATUS_MORE_PROCESSING_REQUIRED; } } while (status == STATUS_MORE_PROCESSING_REQUIRED); return status; } NTSTATUS CP8InitializeCardTracking( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This routine initialized card tracking. It calls the serial driver to set a wait mask for CTS tracking. After that it installs a completion routine to be called when CTS changes state. Arguments: SmartcardExtension - Pointer to our smartcard structure Return Value: NTSTATUS --*/ { NTSTATUS status; KeInitializeEvent( &SmartcardExtension->ReaderExtension->CardStatus.Event, NotificationEvent, FALSE ); // // Send a wait mask to the serial driver. // This call only sets the wait mask. // We want to be informed when CTS changes its state // SmartcardExtension->ReaderExtension->CardStatus.Irp = IoBuildDeviceIoControlRequest( IOCTL_SERIAL_SET_WAIT_MASK, SmartcardExtension->ReaderExtension->ConnectedSerialPort, &SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask, sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask), NULL, 0, FALSE, &(SmartcardExtension->ReaderExtension->CardStatus.Event), &(SmartcardExtension->ReaderExtension->CardStatus.IoStatus) ); if (SmartcardExtension->ReaderExtension->CardStatus.Irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } status = IoCallDriver( SmartcardExtension->ReaderExtension->ConnectedSerialPort, SmartcardExtension->ReaderExtension->CardStatus.Irp, ); if (status == STATUS_SUCCESS) { KIRQL oldIrql; ULONG ioctlCode = IOCTL_SERIAL_WAIT_ON_MASK | 0x03; // // Now tell the serial driver that we want to be informed // when CTS 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. // SmartcardExtension->ReaderExtension->CardStatus.Irp = IoBuildDeviceIoControlRequest( ioctlCode, SmartcardExtension->ReaderExtension->ConnectedSerialPort, &SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask, sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask), &SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask, sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask), FALSE, &SmartcardExtension->ReaderExtension->CardStatus.Event, &SmartcardExtension->ReaderExtension->CardStatus.IoStatus ); if (SmartcardExtension->ReaderExtension->CardStatus.Irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // We simulate a callback now that triggers the card supervision // CP8SerialCtsChanged( SmartcardExtension->OsData->DeviceObject, SmartcardExtension->ReaderExtension->CardStatus.Irp, SmartcardExtension ); status = STATUS_SUCCESS; } return status; } NTSTATUS CP8SerialCtsChanged( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This routine is called when CTS changes its status, which means a card was inserted or removed. Arguments: Return Value: NTSTATUS --*/ { UNREFERENCED_PARAMETER(DeviceObject); SmartcardDebug( DEBUG_TRACE, ("%s!CP8SerialCtsChanged: Enter\n", DRIVER_NAME) ); if (SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask) { NTSTATUS status; // // Only inform the user of a card insertion/removal event // if this function isn't called due to a power down - power up cycle // if (SmartcardExtension->ReaderExtension->PowerRequest == FALSE && SmartcardExtension->OsData->NotificationIrp) { KIRQL oldIrql; // // If the user had setup an irp for event tracking complete that irp now // IoAcquireCancelSpinLock( &oldIrql ); IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, NULL ); IoReleaseCancelSpinLock( oldIrql ); SmartcardExtension->OsData->NotificationIrp->IoStatus.Information = 0; SmartcardExtension->OsData->NotificationIrp->IoStatus.Status = STATUS_SUCCESS; SmartcardDebug( DEBUG_DRIVER, ("%s!CP8SerialCtsChanged: Completing IRP %lx\n", DRIVER_NAME, SmartcardExtension->OsData->NotificationIrp); ); IoCompleteRequest( SmartcardExtension->OsData->NotificationIrp, IO_NO_INCREMENT ); SmartcardExtension->OsData->NotificationIrp = NULL; } if (SmartcardExtension->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 // PIO_STACK_LOCATION irpStack; KIRQL oldIrql; KeAcquireSpinLock( &SmartcardExtension->ReaderExtension->SpinLock, &oldIrql ); if (SmartcardExtension->ReaderExtension->ModemStatus & SERIAL_CTS_STATE) { // // Card is inserted // SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; SmartcardDebug( DEBUG_DRIVER, ("%s!CP8SerialCtsChanged: Smart card inserted\n", DRIVER_NAME) ); } else { // // Card is removed // SmartcardExtension->CardCapabilities.ATR.Length = 0; SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; SmartcardDebug( DEBUG_DRIVER, ("%s!CP8SerialCtsChanged: Smart card removed\n", DRIVER_NAME) ); } KeReleaseSpinLock( &SmartcardExtension->ReaderExtension->SpinLock, oldIrql ); irpStack = IoGetNextIrpStackLocation( SmartcardExtension->ReaderExtension->CardStatus.Irp ); irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpStack->MinorFunction = 0UL; irpStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask); irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_WAIT_ON_MASK; SmartcardExtension->ReaderExtension->CardStatus.Irp->AssociatedIrp.SystemBuffer = &SmartcardExtension->ReaderExtension->SerialConfigData.WaitMask; SmartcardExtension->ReaderExtension->GetModemStatus = FALSE; SmartcardDebug( DEBUG_DRIVER, ("%s!CP8SerialCtsChanged: IOCTL_SERIAL_WAIT_ON_MASK\n", DRIVER_NAME) ); } 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. // PIO_STACK_LOCATION irpStack; irpStack = IoGetNextIrpStackLocation( SmartcardExtension->ReaderExtension->CardStatus.Irp ); irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpStack->MinorFunction = 0UL; irpStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(SmartcardExtension->ReaderExtension->ModemStatus); irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SERIAL_GET_MODEMSTATUS; SmartcardExtension->ReaderExtension->CardStatus.Irp->AssociatedIrp.SystemBuffer = &SmartcardExtension->ReaderExtension->ModemStatus; SmartcardExtension->ReaderExtension->GetModemStatus = TRUE; SmartcardDebug( DEBUG_DRIVER, ("%s!CP8SerialCtsChanged: IOCTL_SERIAL_GET_MODEMSTATUS\n", DRIVER_NAME) ); } IoSetCompletionRoutine( SmartcardExtension->ReaderExtension->CardStatus.Irp, CP8SerialCtsChanged, SmartcardExtension, TRUE, TRUE, TRUE ); status = IoCallDriver( SmartcardExtension->ReaderExtension->ConnectedSerialPort, SmartcardExtension->ReaderExtension->CardStatus.Irp ); SmartcardDebug( DEBUG_TRACE, ("%s!CP8SerialCtsChanged: Exit\n", DRIVER_NAME) ); return STATUS_MORE_PROCESSING_REQUIRED; } else { SmartcardDebug( DEBUG_TRACE, ("%s!CP8SerialCtsChanged: Exit (Release IRP)\n", DRIVER_NAME) ); return STATUS_SUCCESS; } } NTSTATUS CP8DeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; return SmartcardDeviceControl( &(deviceExtension->SmartcardExtension), Irp ); } static ULONG NumBitsSet( ULONG value ) { ULONG i, numBits = 0; for (i = 0; i < sizeof(value) * 8; i++) { if (value & (1 << i)) { numBits ++; } } return numBits; } NTSTATUS CP8ReaderPower( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called for certain power requests to the card. We do nothing here, because this action is performed in the StartIo function. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { ULONG step, waitTime, TdIndex, numTry = 0, minWaitTime; NTSTATUS status = STATUS_SUCCESS; PSERIAL_STATUS serialStatus; KIRQL oldIrql; PSERIAL_READER_CONFIG serialConfigData = &SmartcardExtension->ReaderExtension->SerialConfigData; SmartcardDebug( DEBUG_TRACE, ("%s!CP8ReaderPower: Enter (%lx)\n", DRIVER_NAME, SmartcardExtension->MinorIoControlCode) ); _try { serialConfigData->LineControl.Parity = EVEN_PARITY; serialConfigData->LineControl.StopBits = STOP_BITS_2; serialConfigData->BaudRate.BaudRate = SmartcardExtension->ReaderCapabilities.DataRate.Default; // we set very short timeouts to get the ATR as fast as possible serialConfigData->Timeouts.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT_ATR; serialConfigData->Timeouts.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT_CONSTANT_ATR; status = CP8ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); if (status != STATUS_SUCCESS) { leave; } status = CP8ConfigureSerialPort( SmartcardExtension ); if (status != STATUS_SUCCESS) { leave; } // // We don't send data to the reader, so set Number of bytes to send = 0 // SmartcardExtension->SmartcardRequest.BufferLength = 0; // // Default number of bytes we expect to get back // SmartcardExtension->SmartcardReply.BufferLength = 0; // // Since power down triggers the UpdateCardStatus 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) // SmartcardExtension->ReaderExtension->PowerRequest = TRUE; for (step = 0; NT_SUCCESS(status) ; step++) { switch(SmartcardExtension->MinorIoControlCode) { case SCARD_WARM_RESET: // // Skip the power down (clear DTR) - power up (set DTR) sequence // switch (step) { case 0: step = 1; break; case 2: step = 3; break; } // // No break here !!! // case SCARD_COLD_RESET: // // Send a power-down followed by a power-up // switch (step) { case 0: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_DTR; waitTime = 15000; break; case 1: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_RTS; waitTime = 15000; break; case 2: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_DTR; waitTime = 15000; break; case 3: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_PURGE; *(PULONG) SmartcardExtension->SmartcardRequest.Buffer = SERIAL_PURGE_RXCLEAR; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(ULONG); waitTime = 0; break; case 4: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_RTS; waitTime = 0; break; case 5: // // We now try to get the ATR as fast as possible. // Therefor we prev. set a very short read timeout and // 'hope' that the card delivered its ATR within this // short time. To verify the correctness of the ATR we call // SmartcardUpdateCardCapabilities(). If this call returns // with STATUS_SUCCESS we know that the ATR is complete. // Otherwise we read again and append the new data to the // ATR buffer in the CardCapabilities and try again. // SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_READ; SmartcardExtension->SmartcardReply.BufferLength = MAXIMUM_ATR_LENGTH - SmartcardExtension->CardCapabilities.ATR.Length; waitTime = 0; break; case 6: if (SmartcardExtension->SmartcardReply.BufferLength != 0) { ASSERT( SmartcardExtension->CardCapabilities.ATR.Length + SmartcardExtension->SmartcardReply.BufferLength < MAXIMUM_ATR_LENGTH ); // we got some ATR bytes. RtlCopyMemory( SmartcardExtension->CardCapabilities.ATR.Buffer + SmartcardExtension->CardCapabilities.ATR.Length, SmartcardExtension->SmartcardReply.Buffer, SmartcardExtension->SmartcardReply.BufferLength ); SmartcardExtension->CardCapabilities.ATR.Length += (UCHAR) SmartcardExtension->SmartcardReply.BufferLength; status = SmartcardUpdateCardCapabilities( SmartcardExtension ); } if (status != STATUS_SUCCESS && numTry < 100) { // ATR is incomplete. Try again to get ATR bytes. numTry += 1; // continue with step 5 step = 4; status = STATUS_TIMEOUT; continue; } if (status != STATUS_SUCCESS) { leave; } // No break case 7: KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &oldIrql ); if (SmartcardExtension->ReaderCapabilities.CurrentState <= SCARD_ABSENT) { status = STATUS_MEDIA_CHANGED; } KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, oldIrql ); if (status != STATUS_SUCCESS) { leave; } // 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; } // // If the card uses invers convention we need to switch // the serial driver to odd paritiy // if (SmartcardExtension->CardCapabilities.InversConvention) { serialConfigData->LineControl.Parity = ODD_PARITY; } // // If the extra guard time is 255 it means that our // frame with have to expect from the card has only // 1 instead of 2 stop bits // 1start bit + 8data bits + 1parity + 1stop == 11 etu // see iso 7816-3 6.1.4.4 Extra Guard Time N // if (SmartcardExtension->CardCapabilities.PtsData.StopBits == 1) { serialConfigData->LineControl.StopBits = STOP_BIT_1; } // depending on the protocol set the timeout values if (SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T1) { // set timeouts serialConfigData->Timeouts.ReadTotalTimeoutConstant = serialConfigData->Timeouts.ReadIntervalTimeout = SmartcardExtension->CardCapabilities.T1.CWT / 1000; } else if (SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T0) { // set timeouts serialConfigData->Timeouts.ReadTotalTimeoutConstant = serialConfigData->Timeouts.ReadIntervalTimeout = SmartcardExtension->CardCapabilities.T0.WT / 1000; } // Now make some adjustments depending on the system speed minWaitTime = (KeQueryTimeIncrement() / 10000) * 5; if (serialConfigData->Timeouts.ReadTotalTimeoutConstant < minWaitTime) { serialConfigData->Timeouts.ReadTotalTimeoutConstant = minWaitTime; } if (serialConfigData->Timeouts.ReadIntervalTimeout < minWaitTime) { serialConfigData->Timeouts.ReadIntervalTimeout = minWaitTime; } status = CP8ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); leave; } break; case SCARD_POWER_DOWN: switch (step) { case 0: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_DTR; waitTime = 15000; break; case 1: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_RTS; waitTime = 15000; break; case 2: SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_DTR; waitTime = 15000; break; case 3: SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; leave; } break; } status = CP8SerialIo(SmartcardExtension); if (!NT_SUCCESS(status)) { leave; } if (waitTime) { LARGE_INTEGER delayPeriod; delayPeriod.HighPart = -1; delayPeriod.LowPart = waitTime * (-10); KeDelayExecutionThread( KernelMode, FALSE, &delayPeriod ); } } } _finally { if (status == STATUS_TIMEOUT) { status = STATUS_UNRECOGNIZED_MEDIA; } SmartcardExtension->ReaderExtension->PowerRequest = FALSE; } SmartcardDebug( DEBUG_TRACE, ("%s!CP8ReaderPower: Exit (%lxh)\n", DRIVER_NAME, status) ); return status; } NTSTATUS CP8SetProtocol( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called to set a the transmission protocol and parameters. If this function is called with a protocol mask (which means the caller doesn't card about a particular protocol to be set) we first look if we can set T=1 and the T=0 Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { NTSTATUS status; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("%s!CP8SetProtocol: Enter\n", DRIVER_NAME) ); try { PUCHAR ptsRequest = SmartcardExtension->SmartcardRequest.Buffer; PUCHAR ptsReply = SmartcardExtension->SmartcardReply.Buffer; PSERIAL_READER_CONFIG serialConfigData = &SmartcardExtension->ReaderExtension->SerialConfigData; ULONG minWaitTime, newProtocol; // // 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; leave; } // set normal timeout serialConfigData->Timeouts.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT_DEFAULT; serialConfigData->Timeouts.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT_CONSTANT_DEFAULT; status = CP8ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); if (status != STATUS_SUCCESS) { leave; } // // Assemble and send a pts selection // newProtocol = SmartcardExtension->MinorIoControlCode; while(TRUE) { // set initial character of PTS ptsRequest[0] = 0xff; // set the format character if (SmartcardExtension->CardCapabilities.Protocol.Supported & newProtocol & SCARD_PROTOCOL_T1) { // select T=1 and indicate that pts1 follows ptsRequest[1] = 0x11; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T1; } else if (SmartcardExtension->CardCapabilities.Protocol.Supported & newProtocol & SCARD_PROTOCOL_T0) { // select T=0 and indicate that pts1 follows ptsRequest[1] = 0x10; SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0; } else { status = STATUS_INVALID_DEVICE_REQUEST; leave; } // set pts1 which codes Fl and Dl ptsRequest[2] = SmartcardExtension->CardCapabilities.PtsData.Fl << 4 | SmartcardExtension->CardCapabilities.PtsData.Dl; // set pck (check character) ptsRequest[3] = ptsRequest[0] ^ ptsRequest[1] ^ ptsRequest[2]; SmartcardExtension->SmartcardRequest.BufferLength = 4; SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_WRITE; status = CP8SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } // read back the echo of the reader SmartcardExtension->SmartcardReply.BufferLength = 4; SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_READ; status = CP8SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } // read back the pts data status = CP8SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS && status != STATUS_TIMEOUT) { leave; } if (status != STATUS_TIMEOUT && memcmp(ptsRequest, ptsReply, 4) == 0) { // the card replied correctly to our pts-request break; } if (SmartcardExtension->CardCapabilities.PtsData.Type != PTS_TYPE_DEFAULT) { SmartcardDebug( DEBUG_TRACE, ("%s!CP8SetProtocol: PTS failed. Trying default parameters...\n", DRIVER_NAME, status) ); // // 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 = CP8ReaderPower(SmartcardExtension); continue; } // the card failed the pts-request status = STATUS_DEVICE_PROTOCOL_ERROR; leave; } // // The card replied correctly to the pts request // Set the appropriate parameters for the port // if (SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T1) { // set timeouts serialConfigData->Timeouts.ReadTotalTimeoutConstant = SmartcardExtension->CardCapabilities.T1.BWT / 1000; serialConfigData->Timeouts.ReadIntervalTimeout = SmartcardExtension->CardCapabilities.T1.CWT / 1000; } else if (SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T0) { // set timeouts serialConfigData->Timeouts.ReadTotalTimeoutConstant = serialConfigData->Timeouts.ReadIntervalTimeout = SmartcardExtension->CardCapabilities.T0.WT / 1000; } // Now make some adjustments depending on the system speed minWaitTime = (KeQueryTimeIncrement() / 10000) * 5; if (serialConfigData->Timeouts.ReadTotalTimeoutConstant < minWaitTime) { serialConfigData->Timeouts.ReadTotalTimeoutConstant = minWaitTime; } if (serialConfigData->Timeouts.ReadIntervalTimeout < minWaitTime) { serialConfigData->Timeouts.ReadIntervalTimeout = minWaitTime; } // Change data rate according to the new settings serialConfigData->BaudRate.BaudRate = SmartcardExtension->CardCapabilities.PtsData.DataRate; status = CP8ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); // now indicate that we're in specific mode SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC; // return the selected protocol to the caller *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer = SmartcardExtension->CardCapabilities.Protocol.Selected; *SmartcardExtension->IoRequest.Information = sizeof(SmartcardExtension->CardCapabilities.Protocol.Selected); } finally { if (status == STATUS_TIMEOUT) { // STATUS_TIMEOUT is not mapped to a Win32 error code status = STATUS_IO_TIMEOUT; *SmartcardExtension->IoRequest.Information = 0; } else if (status != STATUS_SUCCESS) { SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; *SmartcardExtension->IoRequest.Information = 0; } } SmartcardDebug( DEBUG_TRACE, ("%s!CP8SetProtocol: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS CP8TransmitT0( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This function performs a T=0 transmission. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { PUCHAR requestBuffer = SmartcardExtension->SmartcardRequest.Buffer; PUCHAR replyBuffer = SmartcardExtension->SmartcardReply.Buffer; PULONG requestLength = &SmartcardExtension->SmartcardRequest.BufferLength; PULONG replyLength = &SmartcardExtension->SmartcardReply.BufferLength; PULONG serialIoControlCode = &SmartcardExtension->ReaderExtension->SerialIoControlCode; ULONG bytesToSend, bytesToRead, currentByte = 0; BOOLEAN restartWorkWaitingTime = FALSE; NTSTATUS status; SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: Enter\n", DRIVER_NAME) ); try { // Let the lib build a T=0 packet status = SmartcardT0Request( SmartcardExtension ); if (status != STATUS_SUCCESS) leave; // // The number of bytes we expect from the card // is Le + 2 status bytes // bytesToSend = *requestLength; bytesToRead = SmartcardExtension->T0.Le + 2; // // Send the first 5 bytes to the card // *requestLength = 5; do { UCHAR procByte; // // According to ISO 7816 a procedure byte of // 60 should be treated as a request for a one time wait. // In this case we do not write anything to the card // if (restartWorkWaitingTime == FALSE) { SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: -> Sending %s (%ld bytes)\n", DRIVER_NAME, (currentByte == 0 ? "header" : "data"), *requestLength) ); // // Write to the card // *serialIoControlCode = SMARTCARD_WRITE; SmartcardExtension->SmartcardRequest.Buffer = &requestBuffer[currentByte]; status = CP8SerialIo( SmartcardExtension ); if (status != STATUS_SUCCESS) leave; // // The CP8 echos all sent bytes. We read the echo // back into our send buffer // *serialIoControlCode = SMARTCARD_READ; *replyLength = *requestLength; SmartcardExtension->SmartcardReply.Buffer = &requestBuffer[currentByte]; status = CP8SerialIo( SmartcardExtension ); if (status != STATUS_SUCCESS) leave; currentByte += *requestLength; bytesToSend -= *requestLength; } // Read the 'Procedure byte'. SmartcardExtension->SmartcardReply.Buffer = &procByte; *serialIoControlCode = SMARTCARD_READ; *replyLength = 1; status = CP8SerialIo( SmartcardExtension ); if (status != STATUS_SUCCESS) leave; restartWorkWaitingTime = FALSE; // // Check the procedure byte. // Please take a look at ISO 7816 Part 3 Section 8.2.2 // if (procByte == requestBuffer[1] || procByte == requestBuffer[1] + 1) { SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: <- ACK (send all)\n", DRIVER_NAME) ); // All remaining data bytes can be sent at once *requestLength = bytesToSend; } else if (procByte == (UCHAR) ~requestBuffer[1] || procByte == (UCHAR) ~(requestBuffer[1] + 1)) { SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: <- ACK (send single)\n", DRIVER_NAME) ); // We can send only one byte *requestLength = 1; } else if (procByte == 0x60 || SmartcardExtension->CardCapabilities.InversConvention && procByte == 0xf9) { // // We have to reset the wait time and try again to read // ULONG TimeRes; LARGE_INTEGER delayTime; SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: <- NULL (%ldms)\n", DRIVER_NAME, SmartcardExtension->CardCapabilities.T0.WT / 1000) ); TimeRes = KeQueryTimeIncrement(); delayTime.HighPart = -1; delayTime.LowPart = (-1) * TimeRes * ((SmartcardExtension->CardCapabilities.T0.WT * 10l / TimeRes) + 1); KeDelayExecutionThread( KernelMode, FALSE, &delayTime ); // // Set flag that we only should read the proc byte // without writing data to the card // restartWorkWaitingTime = TRUE; } else { // // The card returned a status byte. // Status bytes are always two bytes long. // Store this byte first and then read the next // replyBuffer[0] = procByte; *serialIoControlCode = SMARTCARD_READ; *replyLength = 1; bytesToSend = 0; bytesToRead = 0; // // Read in the second status byte // SmartcardExtension->SmartcardReply.Buffer = &replyBuffer[1]; status = CP8SerialIo( SmartcardExtension ); SmartcardExtension->SmartcardReply.BufferLength = 2; SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: <- SW1=%02x SW2=%02x\n", DRIVER_NAME, replyBuffer[0], replyBuffer[1]) ); } } while(bytesToSend || restartWorkWaitingTime); if (status != STATUS_SUCCESS) leave; if (bytesToRead != 0) { *serialIoControlCode = SMARTCARD_READ; *replyLength = bytesToRead; SmartcardExtension->SmartcardReply.Buffer = replyBuffer; status = CP8SerialIo( SmartcardExtension ); #if DEBUG if (status == STATUS_SUCCESS) { SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: <- Data %ld bytes, SW1=%02x SW2=%02x\n", DRIVER_NAME, bytesToRead, replyBuffer[bytesToRead - 2], replyBuffer[bytesToRead - 1]) ); } #endif } } finally { // Restore pointers to their original location SmartcardExtension->SmartcardRequest.Buffer = requestBuffer; SmartcardExtension->SmartcardReply.Buffer = replyBuffer; if (status == STATUS_SUCCESS) { status = SmartcardT0Reply( SmartcardExtension ); } } SmartcardDebug( DEBUG_PROTOCOL, ("%s!CP8TransmitT0: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS CP8Transmit( 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; _try { do { PUCHAR requestBuffer = SmartcardExtension->SmartcardRequest.Buffer; PUCHAR replyBuffer = SmartcardExtension->SmartcardReply.Buffer; PULONG requestLength = &SmartcardExtension->SmartcardRequest.BufferLength; PULONG replyLength = &SmartcardExtension->SmartcardReply.BufferLength; PULONG serialIoControlCode = &SmartcardExtension->ReaderExtension->SerialIoControlCode; // // Tell the lib function how many bytes I need for the prologue // *requestLength = 0; switch (SmartcardExtension->CardCapabilities.Protocol.Selected) { case SCARD_PROTOCOL_RAW: status = SmartcardRawRequest(SmartcardExtension); break; case SCARD_PROTOCOL_T0: // // T=0 requires a bit more work. // So we do this in a seperate function. // status = CP8TransmitT0(SmartcardExtension); leave; case SCARD_PROTOCOL_T1: status = SmartcardT1Request(SmartcardExtension); break; default: status = STATUS_INVALID_DEVICE_REQUEST; leave; } if (status != STATUS_SUCCESS) { leave; } // // Write the command to the card // *replyLength = 0; *serialIoControlCode = SMARTCARD_WRITE; status = CP8SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } // // The Bull reader always echos the bytes sent, so read that echo back // *serialIoControlCode = SMARTCARD_READ; *replyLength = *requestLength; status = CP8SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } switch (SmartcardExtension->CardCapabilities.Protocol.Selected) { case SCARD_PROTOCOL_RAW: status = SmartcardRawReply(SmartcardExtension); break; case SCARD_PROTOCOL_T1: // // Check if the card requested a waiting time extension // if (SmartcardExtension->T1.Wtx) { LARGE_INTEGER waitTime; waitTime.HighPart = -1; waitTime.LowPart = SmartcardExtension->T1.Wtx * SmartcardExtension->CardCapabilities.T1.BWT * (-10); KeDelayExecutionThread( KernelMode, FALSE, &waitTime ); } // // Read NAD, PCB and LEN fields // *replyLength = 3; status = CP8SerialIo(SmartcardExtension); // // Check for timeout first. If the card did not reply // we need to send a resend request // if (status != STATUS_TIMEOUT) { if (status != STATUS_SUCCESS) { leave; } // // The third byte contains the length of the data in the packet // and we additinally want to have the EDC bytes which // is one for LRC and 2 for CRC // *replyLength = replyBuffer[2] + (SmartcardExtension->CardCapabilities.T1.EDC & 0x01 ? 2 : 1); // // We want to have the remaining bytes just after the first 3 // SmartcardExtension->SmartcardReply.Buffer += 3; status = CP8SerialIo(SmartcardExtension); SmartcardExtension->SmartcardReply.Buffer -= 3; SmartcardExtension->SmartcardReply.BufferLength += 3; if (status != STATUS_SUCCESS && status != STATUS_TIMEOUT) { leave; } } if (status == STATUS_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, ("%s!TLP3TransmitT1: Timeout\n", DRIVER_NAME) ); SmartcardExtension->SmartcardReply.BufferLength = 0; } status = SmartcardT1Reply(SmartcardExtension); break; default: status = STATUS_INVALID_DEVICE_REQUEST; leave; } } while (status == STATUS_MORE_PROCESSING_REQUIRED); } _finally { if (status == STATUS_TIMEOUT) { // STATUS_TIMEOUT is not mapped to a Win32 error code status = STATUS_IO_TIMEOUT; } } return status; } NTSTATUS CP8CardTracking( 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 oldIrql; // // Set cancel routine for the notification irp // IoAcquireCancelSpinLock( &oldIrql ); IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, CP8Cancel ); IoReleaseCancelSpinLock( oldIrql ); return STATUS_PENDING; } NTSTATUS CP8VendorIoctl( PSMARTCARD_EXTENSION SmartcardExtension ) { if (SmartcardExtension->IoRequest.ReplyBuffer != NULL && SmartcardExtension->IoRequest.ReplyBufferLength >= sizeof(ULONG)) { *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer = 0xCDAB3412; *SmartcardExtension->IoRequest.Information = sizeof(ULONG); return STATUS_SUCCESS; } return STATUS_BUFFER_TOO_SMALL; }