/*++ Copyright (C) 1997, 98 Microsoft Corporation Module Name: tlp3cb.c Abstract: Callback functions for smart card library Author: Klaus U. Schutz Environment: Kernel mode --*/ #include #include "bulltlp3.h" #pragma alloc_text(PAGEABLE, TLP3SetProtocol) #pragma alloc_text(PAGEABLE, TLP3SetParameters) #pragma alloc_text(PAGEABLE, TLP3TransmitT0) #pragma alloc_text(PAGEABLE, TLP3Transmit) #pragma alloc_text(PAGEABLE, TLP3VendorIoctl) NTSTATUS TLP3ReaderPower( 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. --*/ { ULONG step, waitTime, TdIndex, numTry = 0, minWaitTime; NTSTATUS status = STATUS_SUCCESS; PSERIAL_STATUS serialStatus; KIRQL oldIrql; PUCHAR requestBuffer; PSERIAL_READER_CONFIG serialConfigData = &SmartcardExtension->ReaderExtension->SerialConfigData; SmartcardDebug( DEBUG_TRACE, ("%s!TLP3ReaderPower: Enter (%lx)\n", DRIVER_NAME, SmartcardExtension->MinorIoControlCode) ); _try { #if defined (DEBUG) && defined (DETECT_SERIAL_OVERRUNS) // we have to call GetCommStatus to reset the error condition SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_GET_COMMSTATUS; SmartcardExtension->SmartcardRequest.BufferLength = 0; SmartcardExtension->SmartcardReply.BufferLength = sizeof(SERIAL_STATUS); status = TLP3SerialIo(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); #endif // // Set standard parameters for serial port // 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 = TLP3ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); 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 UpdateSerialStatus function, we have // to inform it that we forced the change of the status and not the user // (who might have removed and inserted a card) // // SmartcardExtension->ReaderExtension->PowerRequest = TRUE; // purge the serial buffer (it can contain the pnp id of the reader) SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_PURGE; *(PULONG) SmartcardExtension->SmartcardRequest.Buffer = SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXCLEAR;; SmartcardExtension->SmartcardRequest.BufferLength = sizeof(ULONG); status = TLP3SerialIo(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); if (status != STATUS_SUCCESS) { leave; } SmartcardExtension->CardCapabilities.ATR.Length = 0; for (step = 0; NT_SUCCESS(status); step++) { if (SmartcardExtension->MinorIoControlCode == SCARD_WARM_RESET && step == 0) { step = 4; } switch (step) { case 0: // RTS = 0 means reader is in command mode SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_RTS; // // This is the minimum wait time we have to wait before // we can send commands to the microcontroller. // waitTime = 1000; break; case 1: // write power down command to the reader SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_WRITE; SmartcardExtension->SmartcardRequest.BufferLength = 1; SmartcardExtension->SmartcardRequest.Buffer[0] = READER_CMD_POWER_DOWN; waitTime = 100; break; case 2: // Read back the echo of the reader SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_READ; SmartcardExtension->SmartcardReply.BufferLength = 1; // Wait the recovery time for the microcontroller waitTime = 1000; break; case 3: // set RTS again so the microcontroller can execute the command SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_RTS; waitTime = 10000; break; case 4: if (SmartcardExtension->MinorIoControlCode == SCARD_POWER_DOWN) { if (SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_PRESENT) { SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_PRESENT; } SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED; status = STATUS_SUCCESS; leave; } // clear RTS to switch to command mode SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLR_RTS; // Wait the recovery time for the microcontroller waitTime = 1000; break; case 5: // write the appropriate reset command to the reader SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_WRITE; SmartcardExtension->SmartcardRequest.BufferLength = 1; switch (SmartcardExtension->MinorIoControlCode) { case SCARD_COLD_RESET: SmartcardExtension->SmartcardRequest.Buffer[0] = READER_CMD_COLD_RESET; break; case SCARD_WARM_RESET: SmartcardExtension->SmartcardRequest.Buffer[0] = READER_CMD_WARM_RESET; break; } waitTime = 100; break; case 6: // Read back the echo of the reader SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_READ; SmartcardExtension->SmartcardReply.BufferLength = 1; // // This is the time we need to wait for the microcontroller // to recover before we can set RTS again // waitTime = 1000; break; case 7: // set RTS again so the microcontroller can execute the command SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_SET_RTS; waitTime = 10000; break; case 8: // // 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 9: if (SmartcardExtension->SmartcardReply.BufferLength != 0) { ASSERT( SmartcardExtension->CardCapabilities.ATR.Length + SmartcardExtension->SmartcardReply.BufferLength < MAXIMUM_ATR_LENGTH ); if( SmartcardExtension->CardCapabilities.ATR.Length + SmartcardExtension->SmartcardReply.BufferLength >= MAXIMUM_ATR_LENGTH) { status = STATUS_UNRECOGNIZED_MEDIA; leave; } // 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 < 25) { if (SmartcardExtension->SmartcardReply.BufferLength != 0) { // Because we received some data, we reset our counter numTry = 0; } else { // ATR is incomplete. Try again to get ATR bytes. numTry += 1; } // continue with step 8 step = 7; status = STATUS_TIMEOUT; continue; } if (status != STATUS_SUCCESS) { leave; } // No break case 10: 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; } #ifdef SIMULATION if (SmartcardExtension->ReaderExtension->SimulationLevel & SIM_ATR_TRASH) { ULONG index; LARGE_INTEGER tickCount; KeQueryTickCount( &tickCount ); SmartcardExtension->CardCapabilities.ATR.Length = (UCHAR) tickCount.LowPart % MAXIMUM_ATR_LENGTH; for (index = 0; index < SmartcardExtension->CardCapabilities.ATR.Length; index++) { SmartcardExtension->CardCapabilities.ATR.Buffer[index] *= (UCHAR) tickCount.LowPart; } SmartcardDebug( DEBUG_SIMULATION, ("%s!TLP3ReaderPower: SIM ATR trash\n", DRIVER_NAME) ); } #endif // 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 we 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; } // Change data rate according to the new settings serialConfigData->BaudRate.BaudRate = SmartcardExtension->CardCapabilities.PtsData.DataRate; // depending on the protocol set the timeout values 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; } status = TLP3ConfigureSerialPort(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); // We're done anyway, so leave leave; } status = TLP3SerialIo(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; #ifdef SIMULATION if (SmartcardExtension->ReaderExtension->SimulationLevel & SIM_WRONG_STATE) { // inject a wrong state after warm/cold reset SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_PRESENT; SmartcardDebug( DEBUG_SIMULATION, ("%s!TLP3ReaderPower: SIM wrong state\n", DRIVER_NAME) ); } else if (SmartcardExtension->ReaderExtension->SimulationLevel & SIM_INVALID_STATE) { // inject completely invalid state after reset. LARGE_INTEGER tickCount; KeQueryTickCount( &tickCount ); SmartcardExtension->ReaderCapabilities.CurrentState = (UCHAR) tickCount.LowPart; SmartcardDebug( DEBUG_SIMULATION, ("%s!TLP3ReaderPower: SIM invalid state %ls\n", DRIVER_NAME, SmartcardExtension->ReaderCapabilities.CurrentState) ); } if (SmartcardExtension->ReaderExtension->SimulationLevel & SIM_LONG_RESET_TIMEOUT) { // inject a random timeout of max 60 sec. LARGE_INTEGER tickCount; KeQueryTickCount( &tickCount ); tickCount.LowPart %= 60; SmartcardDebug( DEBUG_SIMULATION, ("%s!TLP3ReaderPower: SIM reset wait %ld\n", DRIVER_NAME, tickCount.LowPart) ); tickCount.QuadPart *= -10000000; KeDelayExecutionThread( KernelMode, FALSE, &tickCount ); } #endif } SmartcardDebug( DEBUG_TRACE, ("%s!TLP3ReaderPower: Exit (%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS TLP3SetProtocol( 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!TLP3SetProtocol: 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 = TLP3ConfigureSerialPort(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 = TLP3SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } // read back the echo of the reader SmartcardExtension->SmartcardReply.BufferLength = 4; SmartcardExtension->ReaderExtension->SerialIoControlCode = SMARTCARD_READ; status = TLP3SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { leave; } // read back the pts data status = TLP3SerialIo(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!TLP3SetProtocol: 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 = TLP3ReaderPower(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 = TLP3ConfigureSerialPort(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!TLP3SetProtocol: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS TLP3TransmitT0( 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; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("%s!TLP3TransmitT0: 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!TLP3TransmitT0: -> 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 = TLP3SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { SmartcardDebug( DEBUG_ERROR, ("%s!TLP3TransmitT0: TLP3SerialIo(SMARTCARD_WRITE) returned %lx\n", DRIVER_NAME, status) ); leave; } // // The TLP3 echos all sent bytes. We read the echo // back into our send buffer // *serialIoControlCode = SMARTCARD_READ; *replyLength = *requestLength; SmartcardExtension->SmartcardReply.Buffer = &requestBuffer[currentByte]; status = TLP3SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { SmartcardDebug( DEBUG_ERROR, ("%s!TLP3TransmitT0: TLP3SerialIo(SMARTCARD_READ) returned %lx\n", DRIVER_NAME, status) ); leave; } currentByte += *requestLength; bytesToSend -= *requestLength; } // Read the 'Procedure byte'. SmartcardExtension->SmartcardReply.Buffer = &procByte; *serialIoControlCode = SMARTCARD_READ; *replyLength = 1; status = TLP3SerialIo(SmartcardExtension); if (status != STATUS_SUCCESS) { SmartcardDebug( DEBUG_ERROR, ("%s!TLP3TransmitT0: TLP3SerialIo(SMARTCARD_READ) returned %lx\n", DRIVER_NAME, status) ); 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!TLP3TransmitT0: <- 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!TLP3TransmitT0: <- 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!TLP3TransmitT0: <- 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 = TLP3SerialIo(SmartcardExtension); SmartcardExtension->SmartcardReply.BufferLength = 2; SmartcardDebug( (status == STATUS_SUCCESS ? DEBUG_PROTOCOL : DEBUG_ERROR), ("%s!TLP3TransmitT0: <- SW1=%02x SW2=%02x (%lx)\n", DRIVER_NAME, replyBuffer[0], replyBuffer[1], status) ); } } while(bytesToSend || restartWorkWaitingTime); if (status != STATUS_SUCCESS) leave; if (bytesToRead != 0) { *serialIoControlCode = SMARTCARD_READ; *replyLength = bytesToRead; SmartcardExtension->SmartcardReply.Buffer = replyBuffer; status = TLP3SerialIo(SmartcardExtension); SmartcardDebug( (status == STATUS_SUCCESS ? DEBUG_PROTOCOL : DEBUG_ERROR), ("%s!TLP3TransmitT0: <- Data %ld bytes, SW1=%02x SW2=%02x (%lx)\n", DRIVER_NAME, bytesToRead, replyBuffer[bytesToRead - 2], replyBuffer[bytesToRead - 1], status) ); } } finally { // Restore pointers to their original location SmartcardExtension->SmartcardRequest.Buffer = requestBuffer; SmartcardExtension->SmartcardReply.Buffer = replyBuffer; if (status == STATUS_TIMEOUT) { // STATUS_TIMEOUT is not mapped to a Win32 error code status = STATUS_IO_TIMEOUT; } if (status == STATUS_SUCCESS) { status = SmartcardT0Reply(SmartcardExtension); } } SmartcardDebug( (status == STATUS_SUCCESS ? DEBUG_TRACE : DEBUG_ERROR), ("%s!TLP3TransmitT0: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS TLP3Transmit( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: This function is called by the smart card library whenever a transmission is required. Arguments: SmartcardExtension - Pointer to smart card data struct. Return Value: NTSTATUS --*/ { NTSTATUS status; PAGED_CODE(); SmartcardDebug( DEBUG_TRACE, ("%s!TLP3Transmit: Enter\n", DRIVER_NAME) ); _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 = TLP3TransmitT0(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 = TLP3SerialIo(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 = TLP3SerialIo(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 = TLP3SerialIo(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 = TLP3SerialIo(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; } } #if defined (DEBUG) && defined (DETECT_SERIAL_OVERRUNS) if (status != STATUS_SUCCESS) { NTSTATUS status; PSERIALPERF_STATS perfData = (PSERIALPERF_STATS) SmartcardExtension->SmartcardReply.Buffer; // we have to call GetCommStatus to reset the error condition SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_GET_COMMSTATUS; SmartcardExtension->SmartcardRequest.BufferLength = 0; SmartcardExtension->SmartcardReply.BufferLength = sizeof(SERIAL_STATUS); status = TLP3SerialIo(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); // now get the reason for the transmission error SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_GET_STATS; SmartcardExtension->SmartcardRequest.BufferLength = 0; SmartcardExtension->SmartcardReply.BufferLength = sizeof(SERIALPERF_STATS); status = TLP3SerialIo(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); SmartcardDebug( DEBUG_ERROR, ("%s!TLP3Transmit: Serial error statistics:\n FrameErrors: %ld\n SerialOverrunErrors: %ld\n BufferOverrunErrors: %ld\n ParityErrors: %ld\n", DRIVER_NAME, perfData->FrameErrorCount, perfData->SerialOverrunErrorCount, perfData->BufferOverrunErrorCount, perfData->ParityErrorCount) ); SmartcardExtension->ReaderExtension->SerialIoControlCode = IOCTL_SERIAL_CLEAR_STATS; SmartcardExtension->SmartcardRequest.BufferLength = 0; SmartcardExtension->SmartcardReply.BufferLength = 0; status = TLP3SerialIo(SmartcardExtension); ASSERT(status == STATUS_SUCCESS); } #if DEBUG && TIMEOUT_TEST else { // inject some timeout errors LARGE_INTEGER Ticks; UCHAR RandomVal; KeQueryTickCount(&Ticks); RandomVal = (UCHAR) Ticks.LowPart % 4; if (RandomVal == 0) { status = STATUS_IO_TIMEOUT; SmartcardDebug( DEBUG_ERROR, ("%s!TLP3Transmit: Simulating timeout\n", DRIVER_NAME) ); } } #endif #endif #ifdef SIMULATION if (SmartcardExtension->ReaderExtension->SimulationLevel & SIM_IO_TIMEOUT) { status = STATUS_IO_TIMEOUT; SmartcardDebug( DEBUG_SIMULATION, ("%s!TLP3Transmit: SIM STATUS_IO_TIMEOUT\n", DRIVER_NAME) ); } #endif SmartcardDebug( DEBUG_TRACE, ("%s!TLP3Transmit: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS TLP3CardTracking( PSMARTCARD_EXTENSION SmartcardExtension ) /*++ Routine Description: The smart card lib requires to have this function. It is called to setup event tracking for card insertion and removal events. Arguments: SmartcardExtension - pointer to the smart card data struct. Return Value: NTSTATUS --*/ { KIRQL ioIrql, keIrql; // // Set cancel routine for the notification irp // KeAcquireSpinLock( &SmartcardExtension->OsData->SpinLock, &keIrql ); IoAcquireCancelSpinLock(&ioIrql); if (SmartcardExtension->OsData->NotificationIrp) { IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, TLP3Cancel ); } IoReleaseCancelSpinLock(ioIrql); KeReleaseSpinLock( &SmartcardExtension->OsData->SpinLock, keIrql ); return STATUS_PENDING; } NTSTATUS TLP3VendorIoctl( PSMARTCARD_EXTENSION SmartcardExtension ) { NTSTATUS status; static char answer[] = "Vendor IOCTL"; PAGED_CODE(); SmartcardDebug( DEBUG_PROTOCOL, ("%s!TLP3VendorIoctl: Enter\n", DRIVER_NAME) ); if (SmartcardExtension->IoRequest.ReplyBuffer != NULL && SmartcardExtension->IoRequest.ReplyBufferLength >= strlen(answer) + 1) { strcpy(SmartcardExtension->IoRequest.ReplyBuffer, answer); *SmartcardExtension->IoRequest.Information = strlen(answer); status = STATUS_SUCCESS; } else { status = STATUS_BUFFER_TOO_SMALL; } SmartcardDebug( DEBUG_PROTOCOL, ("%s!TLP3VendorIoctl: Exit(%lx)\n", DRIVER_NAME, status) ); return status; } NTSTATUS TLP3SerialIo( 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; if (KeReadStateEvent(&READER_EXTENSION(SerialCloseDone))) { // // we have no connection to serial, fail the call // this could be the case if the reader was removed // during stand by / hibernation // return STATUS_UNSUCCESSFUL; } // Check if the buffers are large enough ASSERT(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, TLP3_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 (READER_EXTENSION(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( READER_EXTENSION(SerialIoControlCode), SmartcardExtension->ReaderExtension->AttachedDeviceObject, 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; default: ASSERT(irpNextStack->MajorFunction = IRP_MJ_DEVICE_CONTROL); ASSERT( DEVICE_TYPE_FROM_CTL_CODE(READER_EXTENSION(SerialIoControlCode)) == FILE_DEVICE_SERIAL_PORT ); } status = IoCallDriver( SmartcardExtension->ReaderExtension->AttachedDeviceObject, irp ); if (status == STATUS_PENDING) { KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); status = ioStatus.Status; // save the number of bytes received SmartcardExtension->SmartcardReply.BufferLength = (ULONG) 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; }