[Pcsclite-cvs-commit] Drivers/ccid/src commands.c,1.1.1.1,1.2 commands.h,1.1.1.1,1.2

rousseau@quantz.debian.org rousseau@quantz.debian.org
Wed, 10 Sep 2003 11:06:43 +0200


Update of /cvsroot/pcsclite/Drivers/ccid/src
In directory quantz:/tmp/cvs-serv843

Modified Files:
	commands.c commands.h 
Log Message:
- support auto voltage at power on if reader support it
- support Gempluc proprietary command to switch reader from TPDU to APDU mode
- use APDU mode if reader support it. make it easy to support extended
  APDU but I have no reader to test this.


Index: commands.c
===================================================================
RCS file: /cvsroot/pcsclite/Drivers/ccid/src/commands.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- commands.c	12 Aug 2003 12:40:24 -0000	1.1.1.1
+++ commands.c	10 Sep 2003 09:06:41 -0000	1.2
@@ -26,19 +26,24 @@
 #include "pcscdefines.h"
 #include "ifdhandler.h"
 #include "commands.h"
-#include "ccid_usb.h"
+#include "ccid.h"
 #include "defs.h"
 #include "config.h"
 #include "debug.h"
 
-#define SET_ISO_MODE 1
 
+/*****************************************************************************
+ *
+ *					CmdPowerOn
+ *
+ ****************************************************************************/
 RESPONSECODE CmdPowerOn(int lun, int * nlength, unsigned char buffer[])
 {
 	unsigned char cmd[10];
 	status_t res;
 	int atr_len, length, count = 1;
 	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
 
 	/* store length of buffer[] */
 	length = *nlength;
@@ -46,11 +51,14 @@
 	cmd[0] = 0x62; /* IccPowerOn */
 	cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;	/* dwLength */
 	cmd[5] = 0;	/* slot number */
-	cmd[6] = ccid_get_seq(lun);
-	cmd[7] = 0x01;	/* 5.0V */
+	cmd[6] = ccid_descriptor->bSeq++;
+	if (ccid_descriptor->dwFeatures & CCID_CLASS_AUTO_VOLTAGE)
+		cmd[7] = 0x00;	/* Automatic voltage selection */
+	else
+		cmd[7] = 0x01;	/* 5.0V */
 	cmd[8] = cmd[9] = 0; /* RFU */
 
-	res = WriteUSB(lun, sizeof(cmd), cmd);
+	res = WritePort(lun, sizeof(cmd), cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
@@ -58,19 +66,19 @@
 	/* needed if we go back after a switch to ISO mode */
 	*nlength = length;
 
-	res = ReadUSB(lun, nlength, buffer);
+	res = ReadPort(lun, nlength, buffer);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
-	if (buffer[STATUS_OFFSET] & 0x40)
+	if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
 	{
 		ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__);    /* bError */
 
 		/* Protocol error in EMV mode */
-		if (buffer[ERROR_OFFSET] == 0xBB)
+		if (buffer[ERROR_OFFSET] == 0xBB &&
+			ccid_descriptor->readerID == GEMPC433)
 		{
-			DEBUG_INFO("Switch reader in ISO mode");
-			if ((return_value = Escape(lun, SET_ISO_MODE)) != IFD_SUCCESS)
+			if ((return_value = CmdEscape(lun, ESC_GEMPC_SET_ISO_MODE)) != IFD_SUCCESS)
 				return return_value;
 
 			/* avoid looping if we can't switch mode */
@@ -95,44 +103,61 @@
 	return return_value;
 } /* CmdPowerOn */
 
-RESPONSECODE Escape(int lun, int command)
+
+/*****************************************************************************
+ *
+ *					Escape
+ *
+ ****************************************************************************/
+RESPONSECODE CmdEscape(int lun, int command)
 {
 	unsigned char cmd[12];
 	status_t res;
 	int length;
 	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
 
 	cmd[0] = 0x6B; /* PC_to_RDR_Escape */
 	cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;	/* dwLength */
 	cmd[5] = 0;	/* slot number */
-	cmd[6] = ccid_get_seq(lun);
+	cmd[6] = ccid_descriptor->bSeq++;
 	cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
 
 	switch (command)
 	{
 		/* Gemplus proprietary */
-		case SET_ISO_MODE:
+		case ESC_GEMPC_SET_ISO_MODE:
+			DEBUG_INFO("Switch reader in ISO mode");
 			cmd[10] = 0x1F;	/* switch mode */
 			cmd[11] = 0x01;	/* set ISO mode */
 			cmd[1] = 2;	/* length of data */
 			length = 12;
 			break;
 
+		/* Gemplus proprietary */
+		case ESC_GEMPC_SET_APDU_MODE:
+			DEBUG_INFO("Switch reader in APDU mode");
+			cmd[10] = 0xA0;	/* switch mode */
+			cmd[11] = 0x02;	/* set APDU mode */
+			cmd[1] = 2;	/* length of data */
+			length = 12;
+			break;
+
 		default:
 			DEBUG_CRITICAL2("Unkown Escape command: %d", command);
 			return return_value;
 	}
 
-	res = WriteUSB(lun, length, cmd);
+	res = WritePort(lun, length, cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
 	length = sizeof(cmd);
-	res = ReadUSB(lun, &length, cmd);
+	res = ReadPort(lun, &length, cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
-	if (cmd[STATUS_OFFSET] & 0x40)
+	if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
 	{
 		ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__);    /* bError */
 		return_value = IFD_COMMUNICATION_ERROR;
@@ -141,29 +166,36 @@
 	return return_value;
 } /* Escape */
 
+
+/*****************************************************************************
+ *
+ *					CmdPowerOff
+ *
+ ****************************************************************************/
 RESPONSECODE CmdPowerOff(int lun)
 {
 	unsigned char cmd[10];
 	status_t res;
 	int length;
 	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
 
 	cmd[0] = 0x63; /* IccPowerOff */
 	cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;	/* dwLength */
 	cmd[5] = 0;	/* slot number */
-	cmd[6] = ccid_get_seq(lun);
+	cmd[6] = ccid_descriptor->bSeq++;
 	cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
 
-	res = WriteUSB(lun, sizeof(cmd), cmd);
+	res = WritePort(lun, sizeof(cmd), cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
 	length = sizeof(cmd);
-	res = ReadUSB(lun, &length, cmd);
+	res = ReadPort(lun, &length, cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
-	if (cmd[STATUS_OFFSET] & 0x40)
+	if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
 	{
 		ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__);    /* bError */
 		return_value = IFD_COMMUNICATION_ERROR;
@@ -172,29 +204,36 @@
 	return return_value;
 } /* CmdPowerOff */
 
+
+/*****************************************************************************
+ *
+ *					CmdGetSlotStatus
+ *
+ ****************************************************************************/
 RESPONSECODE CmdGetSlotStatus(int lun, unsigned char buffer[])
 {
 	unsigned char cmd[10];
 	status_t res;
 	int length;
 	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
 
 	cmd[0] = 0x65; /* GetSlotStatus */
 	cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;	/* dwLength */
 	cmd[5] = 0;	/* slot number */
-	cmd[6] = ccid_get_seq(lun);
+	cmd[6] = ccid_descriptor->bSeq++;
 	cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
 
-	res = WriteUSB(lun, sizeof(cmd), cmd);
+	res = WritePort(lun, sizeof(cmd), cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
 	length = SIZE_GET_SLOT_STATUS;
-	res = ReadUSB(lun, &length, buffer);
+	res = ReadPort(lun, &length, buffer);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
-	if (buffer[STATUS_OFFSET] & 0x40)
+	if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
 	{
 		ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__);    /* bError */
 		return_value = IFD_COMMUNICATION_ERROR;
@@ -203,37 +242,107 @@
 	return return_value;
 } /* CmdGetSlotStatus */
 
+
+/*****************************************************************************
+ *
+ *					CmdXfrBlock
+ *
+ ****************************************************************************/
 RESPONSECODE CmdXfrBlock(int lun, int tx_length, unsigned char tx_buffer[],
 	PDWORD rx_length, unsigned char rx_buffer[])
 {
+	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
+
+	/* command length too big for CCID reader? */
+	if (tx_length > ccid_descriptor->dwMaxCCIDMessageLength)
+	{
+		DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
+			tx_length, ccid_descriptor->dwMaxCCIDMessageLength);
+		return_value = IFD_COMMUNICATION_ERROR;
+		goto clean_up_and_return;
+	}
+
+	/* command length too big for CCID driver? */
+	if (tx_length > CMD_BUF_SIZE)
+	{
+		DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
+			tx_length, CMD_BUF_SIZE);
+		return_value = IFD_COMMUNICATION_ERROR;
+		goto clean_up_and_return;
+	}
+
+	/* APDU or TPDU? */
+	switch (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)
+	{
+		case CCID_CLASS_TPDU:
+			return_value = CmdXfrBlockTPDU(lun, tx_length, tx_buffer, rx_length,
+				rx_buffer);
+			break;
+
+		case CCID_CLASS_SHORT_APDU:
+		case CCID_CLASS_EXTENDED_APDU:
+			/* We only support extended APDU if the reader can support the
+			 * command length. See test above */
+			return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer,
+				rx_length, rx_buffer);
+			break;
+
+		default:
+			return_value = IFD_COMMUNICATION_ERROR;
+	}
+
+clean_up_and_return:
+	return return_value;
+} /* CmdXfrBlock */
+
+
+/*****************************************************************************
+ *
+ *					CmdXfrBlockShortAPDU
+ *
+ ****************************************************************************/
+RESPONSECODE CmdXfrBlockShortAPDU(int lun, int tx_length,
+	unsigned char tx_buffer[], PDWORD rx_length, unsigned char rx_buffer[])
+{
 	unsigned char cmd[10+CMD_BUF_SIZE];	/* CCID + APDU buffer */
 	status_t res;
 	int length;
 	RESPONSECODE return_value = IFD_SUCCESS;
+	_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
+
+	DEBUG_COMM2("ShortAPDU: %d bytes", tx_length);
 
 	cmd[0] = 0x6F; /* IccPowerOff */
 	i2dw(tx_length, cmd+1);	/* APDU length */
 	cmd[5] = 0;	/* slot number */
-	cmd[6] = ccid_get_seq(lun);
+	cmd[6] = ccid_descriptor->bSeq++;
 	cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
 	memcpy(cmd+10, tx_buffer, tx_length);
 
-	res = WriteUSB(lun, 10+tx_length, cmd);
+	res = WritePort(lun, 10+tx_length, cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
+time_request:
 	length = sizeof(cmd);
-	res = ReadUSB(lun, &length, cmd);
+	res = ReadPort(lun, &length, cmd);
 	if (res != STATUS_SUCCESS)
 		return IFD_COMMUNICATION_ERROR;
 
-	if (cmd[STATUS_OFFSET] & 0x40)
+	if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
 	{
 		ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__);    /* bError */
 		*rx_length = 0; /* nothing received */
 		return_value = IFD_COMMUNICATION_ERROR;
 	}
 
+	if (cmd[STATUS_OFFSET] & CCID_TIME_EXTENSION)
+	{
+		DEBUG_CRITICAL2("Time extension requested: 0x%02X", cmd[ERROR_OFFSET]);
+		goto time_request;
+	}
+
 	length = dw2i(cmd, 1);
 	if (length < *rx_length)
 		*rx_length = length;
@@ -242,8 +351,134 @@
 	memcpy(rx_buffer, cmd+10, length);
 
 	return return_value;
-} /* CmdXfrBlock */
+} /* CmdXfrBlockShortAPDU */
+
+
+/*****************************************************************************
+ *
+ *					CmdXfrBlockTPDU
+ *
+ ****************************************************************************/
+RESPONSECODE CmdXfrBlockTPDU(int lun, int tx_length, unsigned char tx_buffer[],
+	PDWORD rx_length, unsigned char rx_buffer[])
+{
+	RESPONSECODE return_value = IFD_SUCCESS;
+	int Le;
+
+	DEBUG_COMM2("TPDU: %d bytes", tx_length);
+
+	/* Size should be command + one byte of length for
+	 * an outgoing TPDU (CLA, INS, P1, P2, P3) */
+	if (tx_length == (ISO_CMD_SIZE + ISO_LENGTH_SIZE))
+	{
+		return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer, rx_length,
+			rx_buffer);
+	}
+	else
+	{
+		/* just (CLA, INS, P1, P2) for an APDU */
+		if (tx_length == ISO_CMD_SIZE)
+		{
+			return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer,
+					rx_length, rx_buffer);
+		}
+		else
+		{
+			DWORD ntestlength;
+
+			if (tx_length > (ISO_CMD_SIZE + ISO_LENGTH_SIZE))
+			{
+				/* Check length to see if it is a full APDU or a TPDU */
+				ntestlength = tx_buffer[ISO_OFFSET_LENGTH] +
+					ISO_CMD_SIZE + ISO_LENGTH_SIZE;
+
+				if (tx_length == (ntestlength + ISO_LENGTH_SIZE))
+				{
+					DWORD old_rx_length = *rx_length;
+
+					/* tx_buffer holds a proper APDU */
+					Le = tx_buffer[tx_length-1];
+					return_value = CmdXfrBlockShortAPDU(lun, tx_length,
+						tx_buffer, rx_length, rx_buffer);
+
+					if ((return_value == IFD_SUCCESS) && (*rx_length == 2))
+					{
+						/* Buffer for Get Response */
+						tx_buffer[0] = 0x00;
+						tx_buffer[1] = 0xC0;
+						tx_buffer[2] = 0x00;
+						tx_buffer[3] = 0x00;
+						tx_length = 5;
+						*rx_length = old_rx_length;
 
+						if (rx_buffer[0] == 0x61)
+						{
+							/* Get Response */
+							DEBUG_COMM2("TPDU: Automatic Get Response after 61%02X", rx_buffer[1]);
+							/* Card sent 61 xx
+							 * xx = 0 means 256 */
+							if (rx_buffer[1] == 0)
+								/* we want to receive what the APDU asked */
+								rx_buffer[1] = Le;
+							if (Le == 0)
+								/* we want what the card want to send */
+								Le = rx_buffer[1];
+
+							/* Get Response with P3 = min(Le, Lx) */
+							tx_buffer[4] = rx_buffer[1] < Le ? rx_buffer[1] : Le;
+
+							return_value = CmdXfrBlockShortAPDU(lun, tx_length,
+								tx_buffer, rx_length, rx_buffer);
+						}
+						if ((rx_buffer[0] == 0x90) && (rx_buffer[1] == 0x00))
+						{
+							/* Get Response */
+							DEBUG_COMM("TPDU: Automatic Get Response after 9000");
+							/* Card sent 90 00
+							 * Get Response with P3 = Le */
+							tx_buffer[4] = Le;
+
+							return_value = CmdXfrBlockShortAPDU(lun, tx_length,
+								tx_buffer, rx_length, rx_buffer);
+						}
+					}
+				}
+				else
+				{
+					if (tx_length > (ntestlength + ISO_LENGTH_SIZE))
+					{
+						/* Data are too long */
+						return_value = IFD_COMMUNICATION_ERROR;
+
+						goto clean_up_and_return;
+					}
+					else
+					{
+						/* tx_buffer holds a proper TPDU */
+						return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer, rx_length, rx_buffer);
+					}
+				}
+			}
+			else
+			{
+				/* tx_buffer holds too little data to form an APDU+length */
+				return_value = IFD_COMMUNICATION_ERROR;
+
+				goto clean_up_and_return;
+			}
+		}
+	}
+
+clean_up_and_return:
+	return return_value;
+} /* CmdXfrBlockTPDU */
+
+
+/*****************************************************************************
+ *
+ *					i2dw
+ *
+ ****************************************************************************/
 void i2dw(int value, unsigned char buffer[])
 {
 	buffer[0] = value & 0xFF;

Index: commands.h
===================================================================
RCS file: /cvsroot/pcsclite/Drivers/ccid/src/commands.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- commands.h	12 Aug 2003 12:40:25 -0000	1.1.1.1
+++ commands.h	10 Sep 2003 09:06:41 -0000	1.2
@@ -26,9 +26,15 @@
 #define ERROR_OFFSET 8
 
 RESPONSECODE CmdPowerOn(int lun, int * nlength, unsigned char buffer[]);
-RESPONSECODE Escape(int lun, int command);
+RESPONSECODE CmdEscape(int lun, int command);
 RESPONSECODE CmdPowerOff(int lun);
 RESPONSECODE CmdGetSlotStatus(int lun, unsigned char buffer[]);
 RESPONSECODE CmdXfrBlock(int lun, int tx_length, unsigned char tx_buffer[],
 	PDWORD rx_length, unsigned char rx_buffer[]);
+RESPONSECODE CmdXfrBlockShortAPDU(int lun, int tx_length,
+	unsigned char tx_buffer[], PDWORD rx_length, unsigned char rx_buffer[]);
+RESPONSECODE CmdXfrBlockTPDU(int lun, int tx_length, unsigned char tx_buffer[],
+	PDWORD rx_length, unsigned char rx_buffer[]);
+
+void i2dw(int value, unsigned char *buffer);