aboutsummaryrefslogtreecommitdiffstats
path: root/scd/ccid-driver.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scd/ccid-driver.c122
1 files changed, 119 insertions, 3 deletions
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 57e617ab5..08604131f 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -1,5 +1,6 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
- * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007
+ * 2008 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* This file is part of GnuPG.
@@ -262,6 +263,7 @@ static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno, int timeout,
int no_debug);
+static int abort_cmd (ccid_driver_t handle);
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
@@ -1069,7 +1071,10 @@ scan_or_find_devices (int readerno, const char *readerid,
/* Set the level of debugging to LEVEL and return the old level. -1
just returns the old level. A level of 0 disables debugging, 1
enables debugging, 2 enables additional tracing of the T=1
- protocol, other values are not yet defined. */
+ protocol, other values are not yet defined.
+
+ Note that libusb may provide its own debugging feature which is
+ enabled by setting the envvar USB_DEBUG. */
int
ccid_set_debug_level (int level)
{
@@ -1395,7 +1400,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
rc = usb_bulk_write (handle->idev,
handle->ep_bulk_out,
(char*)msg, msglen,
- 1000 /* ms timeout */);
+ 5000 /* ms timeout */);
if (rc == msglen)
return 0;
if (rc == -1)
@@ -1463,17 +1468,20 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
if (msglen < 10)
{
DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
+ abort_cmd (handle);
return CCID_DRIVER_ERR_INV_VALUE;
}
if (buffer[5] != 0)
{
DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
+ abort_cmd (handle);
return CCID_DRIVER_ERR_INV_VALUE;
}
if (buffer[6] != seqno)
{
DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
seqno, buffer[6]);
+ abort_cmd (handle);
return CCID_DRIVER_ERR_INV_VALUE;
}
@@ -1492,6 +1500,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
if (buffer[0] != expected_type)
{
DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+ abort_cmd (handle);
return CCID_DRIVER_ERR_INV_VALUE;
}
@@ -1521,6 +1530,113 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
}
+
+/* Send an abort sequence and wait until everything settled. */
+static int
+abort_cmd (ccid_driver_t handle)
+{
+ int rc;
+ char dummybuf[8];
+ unsigned char seqno;
+ unsigned char msg[100];
+ size_t msglen;
+ int i;
+
+ if (!handle->idev)
+ {
+ /* I don't know how to send an abort to non-USB devices. */
+ rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
+ }
+
+ DEBUGOUT ("sending abort sequence\n");
+ /* Send the abort command to the control pipe. Note that we don't
+ need to keep track of sent abort commands because there should
+ never be another thread using the same slot concurrently. */
+ seqno = (handle->seqno & 0xff);
+ rc = usb_control_msg (handle->idev,
+ 0x21,/* bmRequestType: host-to-device,
+ class specific, to interface. */
+ 1, /* ABORT */
+ (seqno << 8 | 0 /* slot */),
+ handle->ifc_no,
+ dummybuf, 0,
+ 1000 /* ms timeout */);
+ if (rc < 0)
+ {
+ DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ /* Now send the abort command to the bulk out pipe using the same
+ SEQNO and SLOT. */
+ msg[0] = PC_to_RDR_Abort;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msglen = 10;
+ set_msg_len (msg, 0);
+ handle->seqno++; /* Bumb up for the next use. */
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+ rc = usb_bulk_write (handle->idev,
+ handle->ep_bulk_out,
+ (char*)msg, msglen,
+ 5000 /* ms timeout */);
+ if (rc == msglen)
+ rc = 0;
+ else if (rc == -1)
+ DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", strerror (errno));
+ else
+ DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
+
+ if (rc)
+ return rc;
+
+ /* Wait for the expected response. */
+ do
+ {
+ rc = usb_bulk_read (handle->idev,
+ handle->ep_bulk_in,
+ (char*)msg, sizeof msg,
+ 5000 /*ms timeout*/);
+ if (rc < 0)
+ {
+ DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
+ strerror (errno));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ msglen = rc;
+
+ if (msglen < 10)
+ {
+ DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
+ (unsigned int)msglen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (msg[5] != 0)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n",
+ msg[7], msg[8], msg[9]);
+ if (CCID_COMMAND_FAILED (msg))
+ print_command_failed (msg);
+ }
+ while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
+
+ DEBUGOUT ("sending abort sequence succeeded\n");
+
+ return 0;
+}
+
+
/* Note that this function won't return the error codes NO_CARD or
CARD_INACTIVE. IF RESULT is not NULL, the result from the
operation will get returned in RESULT and its length in RESULTLEN.