(find_endpoint): New.

(scan_or_find_devices): Add new args to return endpoint info and
interface number.
(ccid_open_reader, ccid_shutdown_reader): Take care of these new
args.
(bulk_in, bulk_out): Use the correct endpoints.
(ccid_transceive_apdu_level): New.
(ccid_transceive): Divert to above.
(parse_ccid_descriptor): Allow APDU level exchange mode.
This commit is contained in:
Werner Koch 2004-12-28 07:13:24 +00:00
parent b85cf46a42
commit 6a899889bc
2 changed files with 171 additions and 21 deletions

View File

@ -1,3 +1,15 @@
2004-12-28 Werner Koch <wk@g10code.com>
* ccid-driver.c (find_endpoint): New.
(scan_or_find_devices): Add new args to return endpoint info and
interface number.
(ccid_open_reader, ccid_shutdown_reader): Take care of these new
args.
(bulk_in, bulk_out): Use the correct endpoints.
(ccid_transceive_apdu_level): New.
(ccid_transceive): Divert to above.
(parse_ccid_descriptor): Allow APDU level exchange mode.
2004-12-21 Werner Koch <wk@g10code.com>
* scdaemon.c (main): Use default_homedir().

View File

@ -198,6 +198,9 @@ struct ccid_driver_s
unsigned short id_vendor;
unsigned short id_product;
unsigned short bcd_device;
int ep_bulk_out;
int ep_bulk_in;
int ep_intr;
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
@ -207,6 +210,7 @@ struct ccid_driver_s
int ifsd;
int powered_off;
int has_pinpad;
int apdu_level; /* Reader supports short APDU level exchange. */
};
@ -260,6 +264,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->max_ifsd = 32;
handle->ifsd = 0;
handle->has_pinpad = 0;
handle->apdu_level = 0;
DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
handle->id_vendor, handle->id_product, handle->bcd_device);
if (buflen < 54 || buf[0] < 54)
@ -372,9 +377,15 @@ parse_ccid_descriptor (ccid_driver_t handle,
have_tpdu = 1;
}
else if ((us & 0x00020000))
DEBUGOUT (" Short APDU level exchange\n");
{
DEBUGOUT (" Short APDU level exchange\n");
handle->apdu_level = 1;
}
else if ((us & 0x00040000))
DEBUGOUT (" Short and extended APDU level exchange\n");
{
DEBUGOUT (" Short and extended APDU level exchange\n");
handle->apdu_level = 1;
}
else if ((us & 0x00070000))
DEBUGOUT (" WARNING: conflicting exchange levels\n");
@ -421,10 +432,10 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_LF ();
}
if (!have_t1 || !have_tpdu || !have_auto_conf)
if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf)
{
DEBUGOUT ("this drivers requires that the reader supports T=1, "
"TPDU level exchange and auto configuration - "
"TPDU or APDU level exchange and auto configuration - "
"this is not available\n");
return -1;
}
@ -546,6 +557,36 @@ make_reader_id (usb_dev_handle *idev,
}
/* Helper to find the endpoint from an interface descriptor. */
static int
find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode)
{
int no;
int want_bulk_in = 0;
if (mode == 1)
want_bulk_in = 0x80;
for (no=0; no < ifcdesc->bNumEndpoints; no++)
{
struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no;
if (ep->bDescriptorType != USB_DT_ENDPOINT)
;
else if (mode == 2
&& ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK)
== USB_ENDPOINT_TYPE_INTERRUPT)
&& (ep->bEndpointAddress & 0x80))
return (ep->bEndpointAddress & 0x0f);
else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK)
== USB_ENDPOINT_TYPE_BULK)
&& (ep->bEndpointAddress & 0x80) == want_bulk_in)
return (ep->bEndpointAddress & 0x0f);
}
/* Should never happen. */
return mode == 2? 0x83 : mode == 1? 0x82 :1;
}
/* Combination function to either scan all CCID devices or to find and
open one specific device.
@ -579,7 +620,9 @@ scan_or_find_devices (int readerno, const char *readerid,
char **r_rid,
struct usb_device **r_dev,
unsigned char **ifcdesc_extra,
size_t *ifcdesc_extra_len)
size_t *ifcdesc_extra_len,
int *interface_number,
int *ep_bulk_out, int *ep_bulk_in, int *ep_intr)
{
char *rid_list = NULL;
int count = 0;
@ -597,6 +640,8 @@ scan_or_find_devices (int readerno, const char *readerid,
*ifcdesc_extra = NULL;
if (ifcdesc_extra_len)
*ifcdesc_extra_len = 0;
if (interface_number)
*interface_number = 0;
/* See whether we want scan or find mode. */
if (scan_mode)
@ -721,6 +766,16 @@ scan_or_find_devices (int readerno, const char *readerid,
ifcdesc->extralen);
*ifcdesc_extra_len = ifcdesc->extralen;
}
if (interface_number)
*interface_number = (ifcdesc->
bInterfaceNumber);
if (ep_bulk_out)
*ep_bulk_out = find_endpoint (ifcdesc, 0);
if (ep_bulk_in)
*ep_bulk_in = find_endpoint (ifcdesc, 1);
if (ep_intr)
*ep_intr = find_endpoint (ifcdesc, 2);
if (r_dev)
*r_dev = dev;
@ -787,7 +842,8 @@ ccid_get_reader_list (void)
initialized_usb = 1;
}
scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL);
scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL, NULL,
NULL, NULL, NULL);
return reader_list;
}
@ -804,6 +860,7 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
unsigned char *ifcdesc_extra = NULL;
size_t ifcdesc_extra_len;
int readerno;
int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;
*handle = NULL;
@ -832,7 +889,8 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
readerno = 0; /* Default. */
idev = scan_or_find_devices (readerno, readerid, &rid, &dev,
&ifcdesc_extra, &ifcdesc_extra_len);
&ifcdesc_extra, &ifcdesc_extra_len,
&ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr);
if (!idev)
{
if (readerno == -1)
@ -856,6 +914,9 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
(*handle)->id_vendor = dev->descriptor.idVendor;
(*handle)->id_product = dev->descriptor.idProduct;
(*handle)->bcd_device = dev->descriptor.bcdDevice;
(*handle)->ep_bulk_out = ep_bulk_out;
(*handle)->ep_bulk_in = ep_bulk_in;
(*handle)->ep_intr = ep_intr;
DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid );
@ -867,9 +928,7 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
goto leave;
}
/* fixme: Do we need to claim and set the interface as
determined above? */
rc = usb_claim_interface (idev, 0);
rc = usb_claim_interface (idev, ifc_no);
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
@ -877,9 +936,6 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
goto leave;
}
/* FIXME: Do we need to get the endpoint addresses from the
structure and store them with the handle? */
leave:
free (ifcdesc_extra);
if (rc)
@ -944,6 +1000,7 @@ ccid_shutdown_reader (ccid_driver_t handle)
usb_dev_handle *idev = NULL;
unsigned char *ifcdesc_extra = NULL;
size_t ifcdesc_extra_len;
int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;
if (!handle || !handle->rid)
return CCID_DRIVER_ERR_INV_VALUE;
@ -951,7 +1008,8 @@ ccid_shutdown_reader (ccid_driver_t handle)
do_close_reader (handle);
idev = scan_or_find_devices (-1, handle->rid, NULL, &dev,
&ifcdesc_extra, &ifcdesc_extra_len);
&ifcdesc_extra, &ifcdesc_extra_len,
&ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr);
if (!idev)
{
DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid);
@ -960,6 +1018,9 @@ ccid_shutdown_reader (ccid_driver_t handle)
handle->idev = idev;
handle->ep_bulk_out = ep_bulk_out;
handle->ep_bulk_in = ep_bulk_in;
handle->ep_intr = ep_intr;
if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len))
{
@ -968,9 +1029,7 @@ ccid_shutdown_reader (ccid_driver_t handle)
goto leave;
}
/* fixme: Do we need to claim and set the interface as
determined above? */
rc = usb_claim_interface (idev, 0);
rc = usb_claim_interface (idev, ifc_no);
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
@ -1022,7 +1081,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
int rc;
rc = usb_bulk_write (handle->idev,
1, /*endpoint */
handle->ep_bulk_out,
msg, msglen,
1000 /* ms timeout */);
if (rc == msglen)
@ -1053,7 +1112,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
memset (buffer, 0, length);
retry:
rc = usb_bulk_read (handle->idev,
0x82,
handle->ep_bulk_in,
buffer, length,
10000 /* ms timeout */ );
/* Fixme: instead of using a 10 second timeout we should better
@ -1160,7 +1219,7 @@ ccid_poll (ccid_driver_t handle)
int i, j;
rc = usb_bulk_read (handle->idev,
0x83,
handle->ep_intr,
msg, sizeof msg,
0 /* ms timeout */ );
if (rc < 0 && errno == ETIMEDOUT)
@ -1402,6 +1461,78 @@ compute_edc (const unsigned char *data, size_t datalen, int use_crc)
}
/* Helper for ccid_transceive used for APDU level exchanges. */
static int
ccid_transceive_apdu_level (ccid_driver_t handle,
const unsigned char *apdu_buf, size_t apdu_buflen,
unsigned char *resp, size_t maxresplen,
size_t *nresp)
{
int rc;
unsigned char send_buffer[10+259], recv_buffer[10+259];
const unsigned char *apdu;
size_t apdulen;
unsigned char *msg;
size_t msglen;
unsigned char seqno;
int i;
msg = send_buffer;
apdu = apdu_buf;
apdulen = apdu_buflen;
assert (apdulen);
if (apdulen > 254)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 4; /* bBWI */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
memcpy (msg+10, apdu, apdulen);
set_msg_len (msg, apdulen);
msglen = 10 + apdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
apdu = msg + 10;
apdulen = msglen - 10;
if (resp)
{
if (apdulen > maxresplen)
{
DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
(unsigned int)apdulen, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
memcpy (resp, apdu, apdulen);
*nresp = apdulen;
}
return 0;
}
/*
Protocol T=1 overview
@ -1478,6 +1609,13 @@ ccid_transceive (ccid_driver_t handle,
nresp = &dummy_nresp;
*nresp = 0;
/* Smarter readers allow to send APDUs directly; divert here. */
if (handle->apdu_level)
return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
resp, maxresplen, nresp);
/* The other readers we support require sending TPDUs. */
tpdulen = 0; /* Avoid compiler warning about no initialization. */
msg = send_buffer;
for (;;)
@ -1828,7 +1966,7 @@ ccid_transceive_secure (ccid_driver_t handle,
if (tpdulen < 4)
{
usb_clear_halt (handle->idev, 0x82);
usb_clear_halt (handle->idev, handle->ep_bulk_in);
return CCID_DRIVER_ERR_ABORTED;
}
#ifdef DEBUG_T1