/*++ Copyright (c) 1998 Gemplus Development Name: GEMCORE.C (GemCore reader operating system functions) Environment: Kernel mode Revision History : dd/mm/yy 13/03/98: V1.00.001 (GPZ) - Start of development. 25/04/98: V1.00.002 (GPZ) - Remove the G+ error code. 22/01/99: V1.00.003 (YN) - Add GetResponse in GDDK_Oros3SIOConfigure special use for increase com speed. --*/ #include #include "gntscr.h" #include "gntser.h" // // Local constant section: // - IBLOCK_PCB (0x00) and IBLOCK_MASK (0xA0) are used to detect the I-Block PCB // signature (0x0xxxxxb). // - IBLOCK_SEQ_POS indicates the number of left shift to apply to 0000000xb for // x to be the sequence bit for a I-Block. // - RBLOCK_PCB (0x80) and RBLOCK_MASK (0xEC) are used to detect the R-Block PCB // signature (100x00xx). // - RBLOCK_SEQ_POS indicates the number of left shift to apply to 0000000xb for // x to be the sequence bit for a R-Block. // - ERR_OTHER and ERR_EDC are the error bits in a R-Block PCB. // - RESYNCH_REQUEST (0xC0) and RESYNCH_RESPONSE (0xE0) are the PCB used in // S-Blocks. // #define IBLOCK_PCB 0x00 #define IBLOCK_MASK 0xA0 #define IBLOCK_SEQ_POS 0x06 #define RBLOCK_PCB 0x80 #define RBLOCK_MASK 0xEC #define RBLOCK_SEQ_POS 0x04 #define ERR_OTHER 0x02 #define ERR_EDC 0x01 #define RESYNCH_REQUEST 0xC0 #define RESYNCH_RESPONSE 0xE0 // //Macro section: // - HOST2IFD (Handle) returns the NAD byte for message from Host to IFD. // - IFD2HOST (Handle) returns the NAD byte awaited in an IFD message. // - MK_IBLOCK_PCB (x) builds an I-Block PCB where x is the channel handle. // - MK_RBLOCK_PCB (x) builds an R-Block PCB where x is the channel handle. // - ISA_IBLOCK_PCB (x) return TRUE if the given parameter is a I-Block PCB. // - ISA_RBLOCK_PCB (x) return TRUE if the given parameter is a R-Block PCB. // - SEQ_IBLOCK (x) return the state of the sequence bit: 0 or 1. // - INC_SEQUENCE (x) increment a sequence bit: 0 -> 1 and 1 -> 0. // #define HOST2IFD(Handle) ((BYTE ) \ ( \ (gtgbp_channel[(Handle)].IFDAdd << 4) \ + gtgbp_channel[(Handle)].HostAdd \ ) \ ) #define IFD2HOST(Handle) ((BYTE ) \ ( \ (gtgbp_channel[(Handle)].HostAdd << 4) \ + gtgbp_channel[(Handle)].IFDAdd \ ) \ ) #define MK_IBLOCK_PCB(x) ((BYTE ) \ ( \ IBLOCK_PCB \ + (gtgbp_channel[(x)].SSeq << IBLOCK_SEQ_POS) \ ) \ ) #define MK_RBLOCK_PCB(x) ((BYTE ) \ ( \ RBLOCK_PCB \ + (gtgbp_channel[(x)].RSeq << RBLOCK_SEQ_POS) \ + gtgbp_channel[(x)].Error \ ) \ ) #define ISA_IBLOCK_PCB(x) (((x) & IBLOCK_MASK) == IBLOCK_PCB) #define ISA_RBLOCK_PCB(x) (((x) & RBLOCK_MASK) == RBLOCK_PCB) #define SEQ_IBLOCK(x) (((x) & (0x01 << IBLOCK_SEQ_POS)) >> IBLOCK_SEQ_POS) #define INC_SEQUENCE(x) (x) = (BYTE )(((x) + 1) % 2) // // Types section: // - TGTGBP_CHANNEL gathers information about a channel: // * UserNb is the number of user for the channel. // * HostAdd holds the address identifier for the host in 0..15. // * IFDAdd holds the address identifier for the associated IFD in 0..15. // * PortCom holds the serial port number // * SSeq holds the sequence bit for the next I-Block to send: 0 or 1. // * RSeq holds the awaited sequence bit: 0 or 1. // * Error gathers the encountered error conditions. // typedef struct { BYTE UserNb; BYTE HostAdd; BYTE IFDAdd; short PortCom; BYTE SSeq; BYTE RSeq; BYTE Error; } TGTGBP_CHANNEL; // // Global variable section: // - gtgbp_channel[MAX_IFD_CHANNEL] is an array of TGTGBP_CHANNEL which memorises // the communication parameters for each opened channel. // - handle_GBP is a conversion for the logical channel to the GBP channel. // static TGTGBP_CHANNEL gtgbp_channel[MAX_IFD_CHANNEL] = { {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0} }; static short handle_GBP[MAX_IFD_CHANNEL] = { {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0} }; NTSTATUS GDDK_GBPOpen( const SHORT Handle, const USHORT HostAdd, const USHORT IFDAdd, const SHORT PortCom ) /*++ Routine Description: This function initialises internal variables for communicating in Gemplus Block protocol through a serial port. This function must be called before any other in this module. Arguments: Handle - holds the communication handle to associate to the channel. HostAdd - is the host address. A valid value must be in 0.. 15. The memorised value is (HostAdd mod 16). AFDAdd - is the IFD address. A valid value must be in 0.. 15. The memorised value is (IFDAdd mod 16). This value should be different from the HostAdd. PortCom - holds the serial port number Return Value: STATUS_SUCCESS - We could execute the request. --*/ { SHORT cptHandleLog=0,present=-1; // // The given parameters are controlled: // if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((HostAdd <= 0) || (HostAdd >= MAX_IFD_CHANNEL)) || ((IFDAdd <= 0) || (IFDAdd >= MAX_IFD_CHANNEL)) || (HostAdd == IFDAdd) ) { return STATUS_INVALID_PARAMETER; } if ((PortCom < 0) || (PortCom >= HGTSER_MAX_PORT)) { return STATUS_PORT_DISCONNECTED; } // Scan the structure to found an already opened channel for the same PortCom // and HostAdd. while ((cptHandleLog 0) ) { present = cptHandleLog; } cptHandleLog++; } // If IFDAdd, PortCom, and HostAdd fields exists // Increment the user number counter and write in the array handle_GBP // the handle of the GBP. if (present != -1) { gtgbp_channel[present].UserNb += 1; handle_GBP[Handle] = present; return STATUS_SUCCESS; } // Scan the structure to find the first line free cptHandleLog=0; present=-1; while ((cptHandleLog= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } // Test channel state (UseNbr field different from 0) if (gtgbp_channel[handle_GBP[Handle]].UserNb == 0) { return STATUS_PORT_DISCONNECTED; } // Decrement the gtgbp_channel structure UserNb field. gtgbp_channel[handle_GBP[Handle]].UserNb -= 1; return STATUS_SUCCESS; } NTSTATUS GDDK_GBPBuildIBlock( const SHORT Handle, const USHORT CmdLen, const BYTE Cmd[], USHORT *MsgLen, BYTE Msg[] ) /*++ Routine Description: This function takes a command and builds an Information Gemplus Block Protocol. When this command is successful, the send sequence bit is updated for the next exchange. Arguments: Handle - holds the communication handle to associate to the channel. CmdLen - indicates the number of bytes in the Cmd buffer. This value must be lower than HGTGBP_MAX_DATA (Today 254). Cmd - holds the command bytes. MsgLen - indicates the number of available bytes in Msg. Msg - is updated by the message. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE edc; USHORT i,j = 0; ASSERT(Cmd != NULL); ASSERT(MsgLen != NULL); ASSERT(Msg != NULL); // The given parameters are controlled: if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } // Test channel state (UserNb different from 0) if (gtgbp_channel[handle_GBP[Handle]].UserNb == 0) { return STATUS_PORT_DISCONNECTED; } // Test CmdLen (<= HGTGBP_MAX_DATA) and MsgLen (>= 4 + CmdLen) // Msg must be able to receive the following GBP block // [ Data ...] if ((CmdLen > HGTGBP_MAX_DATA) || (*MsgLen < CmdLen + 4)) { return STATUS_INVALID_PARAMETER; } // The message is built: // NAD holds Target address in high part and Source address in low part. // PCB holds I-Block mark: 0 SSeq 0 x x x x x // Len is given by CmdLen // [.. Data ..] are stored in Cmd. // EDC is an exclusive or of all the previous bytes. // It is updated when Msg buffer is updated. edc = Msg[j++] = HOST2IFD(handle_GBP[Handle]); edc ^= Msg[j++] = MK_IBLOCK_PCB(handle_GBP[Handle]); edc ^= Msg[j++] = (BYTE ) CmdLen; for (i = 0; i < CmdLen; i++) { edc ^= Msg[j++] = Cmd[i]; } Msg[j++] = edc; *MsgLen = (USHORT)j; // The sequence number is updated for the next exchange. INC_SEQUENCE(gtgbp_channel[handle_GBP[Handle]].SSeq); return STATUS_SUCCESS; } NTSTATUS GDDK_GBPBuildRBlock( const SHORT Handle, USHORT *MsgLen, BYTE Msg[] ) /*++ Routine Description: This function builds a Repeat Gemplus Block Protocol. Arguments: Handle - holds the communication handle to associate to the channel. MsgLen - indicates the number of available bytes in Msg. This value must be at least 4 to allow to build the message. Msg - holds the message to send. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE edc; USHORT j = 0; ASSERT(MsgLen != NULL); ASSERT(Msg != NULL); // The given parameters are controlled: if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } // Test channel state (UserNb different from 0) if (gtgbp_channel[handle_GBP[Handle]].UserNb == 0) { return STATUS_PORT_DISCONNECTED; } // Test MsgLen (>= 4 ) // Msg must be able to receive the following GBP block <0> if (*MsgLen < 4) { return STATUS_INVALID_PARAMETER; } // The message is built: // NAD holds Target address in high part and Source address in low part. // PCB holds R-Block mark: 1 0 0 RSeq 0 x x x x x // Len is null // EDC is an exclusive or of all the previous bytes. // It is updated when Msg buffer is updated. edc = Msg[j++] = HOST2IFD(handle_GBP[Handle]); edc ^= Msg[j++] = MK_RBLOCK_PCB(handle_GBP[Handle]); edc ^= Msg[j++] = 0; Msg[j++] = edc; *MsgLen = (USHORT)j; return STATUS_SUCCESS; } NTSTATUS GDDK_GBPBuildSBlock( const SHORT Handle, USHORT *MsgLen, BYTE Msg[] ) /*++ Routine Description: This function builds a Synchro request Gemplus Block Protocol. Arguments: Handle - holds the communication handle to associate to the channel. MsgLen - indicates the number of available bytes in Msg. This value must be at least 4 to allow to build the message. Msg - holds the message to send. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE edc; USHORT j = 0; ASSERT(MsgLen != NULL); ASSERT(Msg != NULL); // The given parameters are controlled: if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } // Test channel state (UserNb different from 0) if (gtgbp_channel[handle_GBP[Handle]].UserNb == 0) { return STATUS_PORT_DISCONNECTED; } // Test MsgLen (>= 4 ) // Msg must be able to receive the following GBP block <0> if (*MsgLen < 4) { return STATUS_INVALID_PARAMETER; } // The message is built: // NAD holds Target address in high part and Source address in low part. // PCB holds R-Block mark: 1 1 0 0 0 0 0 0 // Len is null // EDC is an exclusive or of all the previous bytes. // It is updated when Msg buffer is updated. edc = Msg[j++] = HOST2IFD(handle_GBP[Handle]); edc ^= Msg[j++] = RESYNCH_REQUEST; edc ^= Msg[j++] = 0; Msg[j++] = edc; *MsgLen = (USHORT)j; return STATUS_SUCCESS; } NTSTATUS GDDK_GBPDecodeMessage( const SHORT Handle, const USHORT MsgLen, const BYTE Msg[], USHORT *RspLen, BYTE Rsp[] ) /*++ Routine Description: This function takes a Gemplus Block Protocol message and extract the response from it. The awaited sequence bit is updated when a valid I-Block has been received. The sequence bits are reseted when a valid RESYNCH RESPONSE has been received. Arguments: Handle - holds the communication handle to associate to the channel. MsgLen - indicates the number of available bytes in Msg. This value must be at least 4 to allow to build the message. Msg - holds the message to read. RspLen - indicates the number of available bytes in Rsp. Rsp - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE edc; USHORT j; NTSTATUS response; BOOLEAN resynch = FALSE; ASSERT(Msg != NULL); ASSERT(RspLen != NULL); ASSERT(Rsp != NULL); // The given parameters are controlled: if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } // Test channel state (UserNb different from 0) if (gtgbp_channel[handle_GBP[Handle]].UserNb == 0) { return STATUS_PORT_DISCONNECTED; } // Reset the associated error field. gtgbp_channel[handle_GBP[Handle]].Error = 0; // Verifies the message frame and copies the data bytes: // Test NAD (HostAdd | IFDAdd) if (Msg[0] != IFD2HOST(handle_GBP[Handle])) { *RspLen =0; return STATUS_DEVICE_PROTOCOL_ERROR; } edc = Msg[0]; if (Msg[1] == RESYNCH_RESPONSE) { response = STATUS_SYNCHRONIZATION_REQUIRED; resynch = TRUE; } else if (ISA_RBLOCK_PCB(Msg[1])) { response = STATUS_UNSUCCESSFUL; } else if (ISA_IBLOCK_PCB(Msg[1])) { if ( (BYTE ) SEQ_IBLOCK(Msg[1]) != gtgbp_channel[handle_GBP[Handle]].RSeq) { return STATUS_DEVICE_PROTOCOL_ERROR; } response = STATUS_SUCCESS; } else { return STATUS_DEVICE_PROTOCOL_ERROR; } edc ^= Msg[1]; // Test Len (Len + 4 = MsgLen and RspLen >= Len) // This error update the Error.other bit. if (((USHORT)Msg[2] > *RspLen) || ((USHORT)(Msg[2] + 4) != MsgLen)) { *RspLen =0; gtgbp_channel[handle_GBP[Handle]].Error |= ERR_OTHER; return STATUS_DEVICE_PROTOCOL_ERROR; } edc ^= Msg[2]; // Copies the data bytes, updates RspLen and calculated edc. *RspLen = (USHORT)Msg[2]; for (j = 0; j < *RspLen; j++) { Rsp[j] = Msg[j + 3]; edc ^= Rsp[j]; } // Test the read EDC if (edc != Msg[j + 3]) { *RspLen = 0; gtgbp_channel[handle_GBP[Handle]].Error |= ERR_EDC; return STATUS_DEVICE_PROTOCOL_ERROR; } // Updates the awaited sequence bit when a valid I-Block has been received. if (response == STATUS_SUCCESS) { INC_SEQUENCE(gtgbp_channel[handle_GBP[Handle]].RSeq); } else if (resynch) { // Reset the sequence bits when a valid S-Block has been received. gtgbp_channel[handle_GBP[Handle]].SSeq = gtgbp_channel[handle_GBP[Handle]].RSeq = 0; } return response; } NTSTATUS GDDK_GBPChannelToPortComm( const SHORT Handle, SHORT *PortCom ) /*++ Routine Description: This function return a physical port associate with the Logical Channel Arguments: Handle - holds the communication handle to associate to the channel. Return Value: the value of the physical port. --*/ { // The given parameters are controlled: if ( ((Handle < 0) || (Handle >= MAX_IFD_CHANNEL)) || ((handle_GBP[Handle] < 0) || (handle_GBP[Handle] >= MAX_IFD_CHANNEL)) ) { return STATUS_INVALID_PARAMETER; } *PortCom = gtgbp_channel[handle_GBP[Handle]].PortCom; return STATUS_SUCCESS; } NTSTATUS GDDK_Translate( const BYTE IFDStatus, const ULONG IoctlType ) /*++ Routine Description: Translate IFD status in NT status codes. Arguments: IFDStatus - is the value to translate. IoctlType - is the current smart card ioctl. Return Value: the translated code status. --*/ { switch (IFDStatus) { case 0x00 : return STATUS_SUCCESS; case 0x01 : return STATUS_NO_SUCH_DEVICE; case 0x02 : return STATUS_NO_SUCH_DEVICE; case 0x03 : return STATUS_INVALID_PARAMETER; case 0x04 : return STATUS_IO_TIMEOUT; case 0x05 : return STATUS_INVALID_PARAMETER; case 0x09 : return STATUS_INVALID_PARAMETER; case 0x10 : return STATUS_UNRECOGNIZED_MEDIA; case 0x11 : return STATUS_UNRECOGNIZED_MEDIA; case 0x12 : return STATUS_INVALID_PARAMETER; case 0x13 : return STATUS_CONNECTION_ABORTED; case 0x14 : return STATUS_UNRECOGNIZED_MEDIA; case 0x15 : return STATUS_UNRECOGNIZED_MEDIA; case 0x16 : return STATUS_INVALID_PARAMETER; case 0x17 : return STATUS_UNRECOGNIZED_MEDIA; case 0x18 : return STATUS_UNRECOGNIZED_MEDIA; case 0x19 : return STATUS_INVALID_PARAMETER; case 0x1A : return STATUS_INVALID_PARAMETER; case 0x1B : return STATUS_INVALID_PARAMETER; case 0x1C : return STATUS_INVALID_PARAMETER; case 0x1D : return STATUS_UNRECOGNIZED_MEDIA; case 0x1E : return STATUS_INVALID_PARAMETER; case 0x1F : return STATUS_INVALID_PARAMETER; case 0x20 : return STATUS_INVALID_PARAMETER; case 0x30 : return STATUS_IO_TIMEOUT; case 0xA0 : return STATUS_SUCCESS; case 0xA1 : return STATUS_UNRECOGNIZED_MEDIA; case 0xA2 : if (IoctlType == RDF_CARD_POWER) { return STATUS_UNRECOGNIZED_MEDIA;} else { return STATUS_IO_TIMEOUT; } case 0xA3 : return STATUS_PARITY_ERROR; case 0xA4 : return STATUS_REQUEST_ABORTED; case 0xA5 : return STATUS_REQUEST_ABORTED; case 0xA6 : return STATUS_REQUEST_ABORTED; case 0xA7 : return STATUS_UNRECOGNIZED_MEDIA; case 0xCF : return STATUS_INVALID_PARAMETER; case 0xE4 : return STATUS_UNRECOGNIZED_MEDIA; case 0xE5 : return STATUS_SUCCESS; case 0xE7 : return STATUS_SUCCESS; case 0xF7 : return STATUS_NO_MEDIA; case 0xF8 : return STATUS_UNRECOGNIZED_MEDIA; case 0xFB : return STATUS_NO_MEDIA; default : return STATUS_INVALID_PARAMETER; } } NTSTATUS GDDK_Oros3SIOConfigure( const SHORT Handle, const ULONG Timeout, const SHORT Parity, const SHORT ByteSize, const ULONG BaudRate, USHORT *RspLen, BYTE Rsp[], const BOOLEAN GetResponse ) /*++ Routine Description: This command sets the SIO line parity, baud rate and number of bits per character. After a reset, the line defaults is no parity, 8 bits per character and 9600 bauds. This command must be sent 2 times because: - when the IFD receive the command, it switch immediatly and sends its response according to the new parameters. Host cannot receive this response. - the second call change nothing, so the host receive the response. The configure SIO line command format is <0Ah> XXXXXXXXb |||||\-/ codes the selected baud rate according to |||||000 RFU |||||001 76800 |||||010 38400 |||||011 19200 |||||100 9600 |||||101 4800 |||||110 2400 |||||111 1200 ||||---- codes the character size according to ||||0 for 8 bits per character ||||1 for 7 bits per character |||----- codes the parity according to |||0 for no parity |||1 for even parity \-/ are not used. Arguments: Handle - is the handle returned by the communication layer. Timeout - is the time, in ms, for IFD to send its response. Parity - can hold 0 for no parity or 2 for even parity. ByteSize - can hold 7 or 8 bits per character. BaudRate - can hold 1200, 2400, 4800, 9600, 19200, 38400 or 76800. This last value is not allowed on PC. RespLen - holds the available place in RespBuff. The needed value is 1 byte. RespBuff - holds RespLen read bytes. The response has the following format: Return Value: STATUS_SUCCESS - We could execute the request. --*/ { NTSTATUS NTStatus; BYTE cmd[2] = { HOR3GLL_IFD_CMD_SIO_SET }; ASSERT(RspLen != NULL); ASSERT(Rsp != NULL); // The configuration byte is set with the given parameters: // Switch the BaudRate value, the corresponding code initializes cmd[1]. // default case returns the following error: GE_HOST_PARAMETERS switch (BaudRate) { case 38400lu: cmd[1] = 0x02; break; case 19200lu: cmd[1] = 0x03; break; case 9600lu: cmd[1] = 0x04; break; default : return STATUS_INVALID_PARAMETER; } // Switch the ByteSize value, the corresponding code is added to cmd[1]. // default case returns the following error: GE_HOST_PARAMETERS switch (ByteSize) { case 8 : break; case 7 : cmd[1] += 0x08; break; default: return STATUS_INVALID_PARAMETER; } // Switch the Parity value, the corresponding code is added to cmd[1]. // default case returns the following error: GE_HOST_PARAMETERS switch (Parity) { case 0 : break; case 2: cmd[1] += 0x10; break; default: return STATUS_INVALID_PARAMETER; } if(GetResponse){ NTStatus = GDDK_Oros3Exchange(Handle,Timeout,2,cmd,RspLen,Rsp); }else{ BOOLEAN resynch = FALSE; USHORT index; SmartcardDebug( DEBUG_TRACE, ("%s!GDDK_Oros3IOConfigure: CMD=", SC_DRIVER_NAME) ); for(index=0;index<2;index++) { SmartcardDebug( DEBUG_TRACE, ("%02X,", cmd[index]) ); } SmartcardDebug( DEBUG_TRACE, ("\n") ); NTStatus = GDDK_Oros3SendCmd(Handle,2,cmd,resynch); } return NTStatus; } NTSTATUS GDDK_Oros3OpenComm( COM_SERIAL *Param, const SHORT Handle ) /*++ Routine Description: This function opens a communication channel with a GemCore >= 1.x IFD. It runs a sequence to determine the currently in use baud rate and to set the IFD in an idle mode from the communication protocol point of view. Arguments: Param - holds the parameters of the serial port. Handle - holds the logical number to manage. Return Value: a handle on the communication channel (>= 0). --*/ { TGTSER_PORT comm; BYTE r_buff[HOR3GLL_OS_STRING_SIZE]; USHORT r_len; SHORT portcom; KEVENT event; LARGE_INTEGER timeout; NTSTATUS status; ASSERT(Param != NULL); // Open the physical communication channel. // The serial port is opened to 9600 bauds in order to scan all the supported // baud rates. comm.Port = (USHORT) Param->Port; comm.BaudRate = 9600; comm.Mode = HGTSER_WORD_8 + HGTSER_NO_PARITY + HGTSER_STOP_BIT_1; comm.TimeOut = HOR3COMM_CHAR_TIMEOUT; comm.TxSize = HGTGBP_MAX_BUFFER_SIZE; comm.RxSize = HGTGBP_MAX_BUFFER_SIZE; comm.pSmartcardExtension = Param->pSmartcardExtension; status = GDDK_SerPortOpen(&comm,&portcom); if (status != STATUS_SUCCESS) { Param->BaudRate = 9600; return status; } // The Gemplus Block Protocol is initialized. GDDK_GBPOpen(Handle,2,4,portcom); // // Loop while a right response has not been received: // do { // Wait for HOR3COMM_CHAR_TIME ms before any command for IFD to forget any // previous received byte. KeInitializeEvent(&event,NotificationEvent,FALSE); timeout.QuadPart = -((LONGLONG) HOR3COMM_CHAR_TIME*10*1000); KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, &timeout ); // Try an OROS command (ReadMemory at OSString position). r_len = HOR3GLL_IFD_LEN_VERSION+1; status = GDDK_Oros3Exchange( Handle, HOR3GLL_LOW_TIME, 5, (BYTE *)("\x22\x05\x3F\xE0\x0D"), &r_len, r_buff ); // If this exchange fails // Then // If the current baud rate is 9600 // Then // The next try will be 38400 // ElseIf the current baud rate is 38400 // Then // The next try will be 19200 // Else // Then // The communication channel is closed (GBP/Serial). if (status != STATUS_SUCCESS) { //ISV if (comm.BaudRate == 9600lu) { comm.BaudRate = 38400lu; } else if (comm.BaudRate == 38400lu) { comm.BaudRate = 19200lu; } else { GDDK_GBPClose(Handle); GDDK_SerPortClose(portcom); Param->BaudRate = 9600; return STATUS_INVALID_DEVICE_STATE; } // The new baud rate configuration is set. // If the call fails // Then // The communication channel is closed (GBP/Serial). status = GDDK_SerPortSetState( portcom, &comm ); if (status != STATUS_SUCCESS) { GDDK_GBPClose(Handle); GDDK_SerPortClose(portcom); Param->BaudRate = 9600; return status; } } else { break; } } while (r_len != (HOR3GLL_IFD_LEN_VERSION+1)); //ISV // We found connection speed -> report it Param->BaudRate = comm.BaudRate; SmartcardDebug( DEBUG_DRIVER,("%s!GDDK_Oros3OpenComm: connection was established at %d\n", SC_DRIVER_NAME, Param->BaudRate) ); return STATUS_SUCCESS; } NTSTATUS GDDK_Oros3CloseComm( const USHORT Handle ) /*++ Routine Description: This function closes a communication channel with the reader Arguments: Handle - holds the logical number to manage. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { SHORT portcom; NTSTATUS status; status = GDDK_GBPChannelToPortComm(Handle,&portcom); ASSERT(status == STATUS_SUCCESS); if (status != STATUS_SUCCESS) { return status; } GDDK_GBPClose(Handle); return (GDDK_SerPortClose(portcom)); } NTSTATUS GDDK_Oros3SendCmd( const SHORT Handle, const USHORT CmdLen, const BYTE Cmd[], const BOOLEAN Resynch ) /*++ Routine Description: This function send a command to the IFD according to Gemplus Block Protocol. If the CmdLen field is null, this function send a R-Block or a S-Block according to the resynch boolean value. Arguments: Handle - holds the logical number to manage. CmdLen - indicates the number of bytes in the Cmd buffer. If this value is null, a R-Block is sent. Cmd - holds the command bytes. Resynch - is read to find if a R-Block or a S-Block must be sent. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { SHORT portcom; USHORT s_len; BYTE s_buff[HGTGBP_MAX_BUFFER_SIZE]; NTSTATUS response; ASSERT(Cmd != NULL); response = GDDK_GBPChannelToPortComm(Handle,&portcom); if (response != STATUS_SUCCESS) { return response; } // The GBP message to send is build from the given command: // If CmdLen is null Then // If resynch is true // Then a S-Block is built. // Else a R-BLOCK message is built: s_len = HGTGBP_MAX_BUFFER_SIZE; if (CmdLen == 0) { if (Resynch) { response = GDDK_GBPBuildSBlock(Handle,&s_len,s_buff); } else { response = GDDK_GBPBuildRBlock(Handle,&s_len,s_buff); } } else { // Else an I-BLOCK message is built: response = GDDK_GBPBuildIBlock(Handle,CmdLen,Cmd,&s_len,s_buff); } if (response != STATUS_SUCCESS) { return response; } // The communication queues are flushed before the writing. response = GDDK_SerPortFlush(portcom, HGTSER_TX_QUEUE | HGTSER_RX_QUEUE); if (response != STATUS_SUCCESS) { SmartcardDebug( DEBUG_ERROR, ("%s!GDDK_Oros3SendCmd Flush failed=%X(hex)\n", SC_DRIVER_NAME, response) ); return response; } // The message is written. As the queues have been flushed, it remains enough // place in the transmitt queue for the whole message. response = GDDK_SerPortWrite( portcom, s_len, s_buff ); if (response != STATUS_SUCCESS) { SmartcardDebug( DEBUG_ERROR, ("%s!GDDK_Oros3SendCmd SerPortWrite=%X(hex)\n", SC_DRIVER_NAME, response) ); } return (response); } NTSTATUS GDDK_Oros3ReadResp( const SHORT Handle, const ULONG Timeout, USHORT *RspLen, BYTE Rsp[] ) /*++ Routine Description: This function reads the IFD response to a command according to Gemplus Block Protocol. Arguments: Handle - holds the logical number to manage. Timeout - is the command timeout. RspLen - indicates how many bytes can be stored in Rsp buffer. It will be updated with the length of the response. Rsp - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { SHORT portcom; USHORT user_nb,r_len; BYTE r_buff[HGTGBP_MAX_BUFFER_SIZE]; TGTSER_PORT comm; NTSTATUS response; ASSERT(RspLen != NULL); ASSERT(Rsp != NULL); response = GDDK_GBPChannelToPortComm(Handle,&portcom); if (response != STATUS_SUCCESS) { return response; } // Set the communication timeout response = GDDK_SerPortGetState( portcom, &comm, &user_nb ); if (response != STATUS_SUCCESS) { return response; } comm.TimeOut = (USHORT) Timeout; response = GDDK_SerPortSetState( portcom, &comm ); // Read the IFD response: // The first three bytes are read by calling GDDK_SerPortRead. r_len = 3; response = GDDK_SerPortRead( portcom, &r_len, r_buff ); if (response != STATUS_SUCCESS) { *RspLen = 0; return response; } // r_len is udpated with the number of bytes which must be read to receive a // complete Gemplus Block Protocol: the number indicated by the length field // of the GBP message + one byte for the EDC. r_len = (USHORT)(r_buff[2] + 1); // The end of the message is read by calling GDDK_SerPortRead. The character // timeout pass to GDDK_SerPortOpen will be used to determine broken // communication. response = GDDK_SerPortRead( portcom, &r_len, r_buff + 3 ); if (response != STATUS_SUCCESS) { *RspLen = 0; return response; } // The message length is restored by adding 3 to r_len. r_len += 3; // The GBP message is controlled and decoded: return (GDDK_GBPDecodeMessage(Handle,r_len,r_buff,RspLen,Rsp)); } NTSTATUS GDDK_Oros3Exchange( const SHORT Handle, const ULONG Timeout, const USHORT CmdLen, const BYTE Cmd[], USHORT *RspLen, BYTE Rsp[] ) /*++ Routine Description: This function sends a command to the IFD according to Gemplus Block Protocol and read its response. Arguments: Handle - holds the logical number to manage. Timeout - is the command timeout. CmdLen - indicates the number of bytes in the Cmd buffer. Cmd - holds the command bytes. RspLen - indicates how many bytes can be stored in Rsp buffer. It will be updated with the length of the response. Rsp - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { ULONG timeout = Timeout; USHORT index, cmdlen_used = CmdLen, rsplen_save = *RspLen; SHORT try_counter = 0,sync_counter = 0,portcom; NTSTATUS response; BOOLEAN resynch; ASSERT(Cmd != NULL); ASSERT(RspLen != NULL); ASSERT(Rsp != NULL); // initialize the result buffer RtlZeroMemory(Rsp, *RspLen); // Associate Handle( or ChannelNb) with portcom response = GDDK_GBPChannelToPortComm(Handle,&portcom); if (response != STATUS_SUCCESS) { return response; } // Waits the serial communication semaphore if (GDDK_SerPortLockComm(portcom,HOR3COMM_MAX_TRY * Timeout) == FALSE) { return STATUS_CANT_WAIT; } // A first try loop for sending the command is launched: // This loop allows to try to send the command, then, if the try fails, to // resynchronize the reader and then to tru to send the command one more time. resynch = FALSE; while (sync_counter++ < HOR3COMM_MAX_RESYNCH) { // A second try loop for sending the command is launched: // We send the command and, if a communication error occurs, HOR3COMM_MAX_TRY // repetitions are allowed before the communication is considered as broken. // The given command is sent to IFD. try_counter = 0; while (try_counter++ < HOR3COMM_MAX_TRY) { #if DBG || DEBUG SmartcardDebug( DEBUG_TRACE, ("%s!GDDK_Oros3Exchange: CMD=", SC_DRIVER_NAME) ); for(index=0;index same as OROS 2.x, * IFD_WITHOUT_PTS_REQUEST -> no PTS management (baud rate is 9600 bps), * IFD_NEGOTIATE_PTS_OPTIMAL -> PTS management automatically, * IFD_NEGOTIATE_PTS_MANUALLY -> PTS management "manually" by parameters. * PTS0 is updated with the currently PTS parameter PTS0 : * IFD_NEGOTIATE_PTS1, * IFD_NEGOTIATE_PTS2, * IFD_NEGOTIATE_PTS3. * PTS1 holds the PTS parameter PTS1. * PTS2 holds the PTS parameter PTS2. * PTS3 holds the PTS parameter PTS3. RspLen - indicates how many bytes can be stored in Rsp buffer. It will be updated with the length of the response. Rsp - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { NTSTATUS response; USHORT i,len = 0; BYTE CFG = 0,PCK,cmd[7]; USHORT output_size, rlen=HOR3GLL_BUFFER_SIZE; BYTE rbuff[HOR3GLL_BUFFER_SIZE]; ASSERT(RespLen != NULL); ASSERT(RespBuff != NULL); output_size = *RespLen; cmd[len++] = HOR3GLL_IFD_CMD_ICC_POWER_UP; switch(ICCVcc) { case ICC_VCC_3V: CFG = 0x02; break; case ICC_VCC_5V: CFG = 0x01; break; default: CFG = 0x00; break; } switch(PTSMode) { case IFD_WITHOUT_PTS_REQUEST: CFG |= 0x10; cmd[len++] = CFG; response = GDDK_Oros3Exchange(Handle,Timeout,len,cmd,RespLen,RespBuff); break; case IFD_NEGOTIATE_PTS_OPTIMAL: CFG |= 0x20; cmd[len++] = CFG; response = GDDK_Oros3Exchange(Handle,Timeout,len,cmd,RespLen,RespBuff); break; // For a PTS request: // Send a Power Up command without PTS management (to stay at 9600) // Send the PTS request // Return the ATR if the command success // Else return the status. case IFD_NEGOTIATE_PTS_MANUALLY: // first reset Icc without PTS management CFG |= 0x10; cmd[len++] = CFG; response = GDDK_Oros3Exchange(Handle,Timeout,len,cmd,RespLen,RespBuff); if ((response == STATUS_SUCCESS) && (RespLen > 0) && (RespBuff[0] == 0x00)) { // then send PTS parameters len = 1; CFG |= 0xF0; cmd[len++] = CFG; cmd[len++] = PTS0; if ((PTS0 & IFD_NEGOTIATE_PTS1) != 0) cmd[len++] = PTS1; if ((PTS0 & IFD_NEGOTIATE_PTS2) != 0) cmd[len++] = PTS2; if ((PTS0 & IFD_NEGOTIATE_PTS3) != 0) cmd[len++] = PTS3; // computes the exclusive-oring of all characters from CFG to PTS3 PCK = 0xFF; for (i=2; i= rlen); *RespLen = rlen; if (rlen > 0) { memcpy(RespBuff,rbuff,rlen); } } } break; case IFD_DEFAULT_MODE: default: if (CFG != 0x00) { cmd[len++] = CFG; } response = GDDK_Oros3Exchange(Handle,Timeout,len,cmd,RespLen,RespBuff); break; } return response; } NTSTATUS GDDK_Oros3IsoOutput( const SHORT Handle, const ULONG Timeout, const BYTE OrosCmd, const BYTE Command[5], USHORT *RespLen, BYTE RespBuff[] ) /*++ Routine Description: This command sends T=0 ISO out commands, that is, command that retrieve data from an ICC. Arguments: Handle - holds the logical number to manage. Timeout - is the command timeout (ms). OrosCmd - is the OROS command byte. Command - is a buffer with the following format: RespLen - holds the available place in RespBuff. It must be at least Length value incremented by 3 (IFD Status + SW1 + SW2). It will be updated with the length of the response. RespBuff - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE cmd[6]; NTSTATUS response; USHORT resp1_len, resp2_len; BYTE resp_buff[HOR3GLL_BUFFER_SIZE]; ASSERT(Command != NULL); ASSERT(RespLen != NULL); ASSERT(RespBuff != NULL); // Set the OROS command byte. cmd[0] = OrosCmd; // If the length is lower or equal to 252 (255 - ( + + )) // (standard OROS cmds) if ((Command[4] <= (HGTGBP_MAX_DATA - 3)) && (Command[4] != 0)) { // The five command bytes are added in cmd buffer. memcpy(cmd + 1, Command, 5); return (GDDK_Oros3Exchange(Handle,Timeout,6,cmd,RespLen,RespBuff)); } // If the length is greater than 252 and lower or equal to 256 // (extended OROS cmds) else if ((Command[4] > (HGTGBP_MAX_DATA - 3)) || (Command[4] == 0)) { // Prepare and send the first part of the extended ISO Out command: // The five command bytes are added in cmd buffer. memcpy(cmd + 1, Command, 5); resp1_len = HOR3GLL_BUFFER_SIZE; response = GDDK_Oros3Exchange(Handle,Timeout,6,cmd,&resp1_len,RespBuff); if ((response != STATUS_SUCCESS) || (RespBuff[0] != 0x00)) { *RespLen = resp1_len; return response; } // Prepare and send the second part of the extended ISO Out command: // The five command bytes are added in cmd buffer: // 0xFF,0xFF,0xFF,0xFF,LN - length already receive. memcpy(cmd + 1,"\xFF\xFF\xFF\xFF", 4); if (Command[4] == 0x00) { cmd[5] = (BYTE ) (256 - ((BYTE ) (resp1_len - 1))); } else { cmd[5] -= ((BYTE ) (resp1_len - 1)); } resp2_len = HOR3GLL_BUFFER_SIZE; response = GDDK_Oros3Exchange(Handle,Timeout,6,cmd,&resp2_len,resp_buff); if ((response != STATUS_SUCCESS) || (resp_buff[0] != 0x00)) { memcpy(RespBuff,resp_buff,resp2_len); *RespLen = resp2_len; return response; } memcpy(RespBuff + resp1_len,resp_buff + 1,resp2_len - 1); *RespLen = (USHORT) (resp1_len + resp2_len - 1); return response; } else { return STATUS_INVALID_PARAMETER; } } NTSTATUS GDDK_Oros3IsoInput( const SHORT Handle, const ULONG Timeout, const BYTE OrosCmd, const BYTE Command[5], const BYTE Data[], USHORT *RespLen, BYTE RespBuff[] ) /*++ Routine Description: This command sends T=0 ISO In commands, that is, command that retrieve data from an ICC. Arguments: Handle - holds the logical number to manage. Timeout - is the command timeout (ms). OrosCmd - is the OROS command byte. Command - is a buffer with the following format: RespLen - holds the available place in RespBuff. It must be at least Length value incremented by 3 (IFD Status + SW1 + SW2). It will be updated with the length of the response. RespBuff - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { BYTE cmd[HOR3GLL_BUFFER_SIZE]; NTSTATUS response; USHORT resp_len = *RespLen; ASSERT(Command != NULL); ASSERT(Data != NULL); ASSERT(RespLen != NULL); ASSERT(RespBuff != NULL); // Set the OROS command byte. cmd[0] = OrosCmd; // The given length is controlled. // If the length is lower or equal than the standard available space (248) if (Command[4] <= (HGTGBP_MAX_DATA - 7)) { memcpy(cmd + 1, Command, 5); memcpy(cmd + 6, Data, Command[4]); return( GDDK_Oros3Exchange( Handle, Timeout, (USHORT)(6 + Command[4]), cmd, RespLen, RespBuff ) ); } else if (Command[4] <= HT0CASES_LIN_SHORT_MAX) { // If the length is lower or equal than the extended available space (255) // Prepare and send the first part of the extended ISO In command: // The five command bytes are added in cmd buffer: 0xFF,0xFF,0xFF,0xFF,LN-248 memcpy(cmd + 1,"\xFF\xFF\xFF\xFF", 4); cmd[5] = (BYTE ) (Command[4] - 248); // The data field is added. memcpy(cmd + 6, Data + 248, cmd[5]); response = GDDK_Oros3Exchange( Handle, Timeout, (USHORT)(6 + cmd[5]), cmd, &resp_len, RespBuff ); if ((response != STATUS_SUCCESS) || (RespBuff[0] != 0x00) || (resp_len != 1)) { if ((response == STATUS_SUCCESS) && (RespBuff[0] == 0x1B)) { RespBuff[0] = 0x12; } return response; } // Prepare and send the Second part of the extended ISO In command: // The five command bytes are added in cmd buffer. // The data field is added (248 bytes). // The command is sent to IFD. memcpy(cmd + 1, Command, 5); memcpy(cmd + 6, Data,248); return( GDDK_Oros3Exchange( Handle, Timeout, (USHORT)(6 + 248), cmd, RespLen, RespBuff ) ); } else { return STATUS_INVALID_PARAMETER; } } NTSTATUS GDDK_Oros3TransparentExchange( const SHORT Handle, const ULONG Timeout, const USHORT CmdLen, const BYTE *CmdBuff, USHORT *RespLen, BYTE *RespBuff ) /*++ Routine Description: This command sends transparent commands, that is, command that send and receive data to/from an ICC. Arguments: Handle - holds the logical number to manage. Timeout - is the command timeout (ms). OrosCmd - is the OROS command byte. Command - is a buffer with the following format: RespLen - holds the available place in RespBuff. It must be at least Length value incremented by 3 (IFD Status + SW1 + SW2). It will be updated with the length of the response. RespBuff - holds the response. Return Value: STATUS_SUCCESS - We could execute the request. --*/ { NTSTATUS status = STATUS_SUCCESS; BYTE cmd[HOR3GLL_BUFFER_SIZE]; USHORT lnToSend, resp_len1, resp_len2; BYTE resp_buff1[HOR3GLL_BUFFER_SIZE],resp_buff2[HOR3GLL_BUFFER_SIZE]; ASSERT(CmdBuff != NULL); ASSERT(RespLen != NULL); ASSERT(RespBuff != NULL); // The total command length (CLA,INS,P1,P2,LIn,DataIn,LEx) is controlled: // If the length is upper than 261 (LIn = 255 and Lex <> 0) // Then returns GE_HI_CMD_LEN if (CmdLen > 261) { return STATUS_INVALID_PARAMETER; } // If the length is upper than the standard available space (254) // Then // Send the last datas if (CmdLen > 254) { cmd[0] = HOR3GLL_IFD_CMD_TRANS_LONG; resp_len1 = HOR3GLL_BUFFER_SIZE; memcpy( cmd + 1, CmdBuff+254, CmdLen - 254 ); lnToSend = CmdLen - 254 + 1; status = GDDK_Oros3Exchange( Handle, Timeout, lnToSend, cmd, &resp_len1, resp_buff1 ); if (status != STATUS_SUCCESS) { return status; } } // Send the firts datas cmd[0] = HOR3GLL_IFD_CMD_TRANS_SHORT; if (CmdLen > 254) { memcpy(cmd + 1,CmdBuff,CmdLen); lnToSend = 254 + 1; } else { if(CmdLen > 0) { memcpy(cmd + 1,CmdBuff,CmdLen); } lnToSend = CmdLen + 1; } resp_len1 = HOR3GLL_BUFFER_SIZE; status = GDDK_Oros3Exchange( Handle, Timeout, lnToSend, cmd, &resp_len1, resp_buff1 ); if (status != STATUS_SUCCESS) { return status; } // If the IFD signals more data to read if (resp_len1 > 0 && resp_buff1[0] == 0x1B) { // Send a command to read the last data. cmd[0] = HOR3GLL_IFD_CMD_TRANS_RESP; resp_len2 = HOR3GLL_BUFFER_SIZE; status = GDDK_Oros3Exchange( Handle, Timeout, 1, cmd, &resp_len2, resp_buff2 ); if (status != STATUS_SUCCESS) { return status; } if ((resp_len1 + resp_len2 - 2) > *RespLen) { return STATUS_INVALID_PARAMETER; } // Copy the last reader status resp_buff1[0] = resp_buff2[0]; *RespLen = resp_len1 + resp_len2 - 1; memcpy(RespBuff,resp_buff1,resp_len1); memcpy(RespBuff + resp_len1,resp_buff2 + 1,resp_len2 - 1); return STATUS_SUCCESS; } else { if (resp_len1 > *RespLen) { return STATUS_INVALID_PARAMETER; } *RespLen = resp_len1; memcpy(RespBuff,resp_buff1,resp_len1); return STATUS_SUCCESS; } return status; }