scd: Support multiple readers by CCID driver.
authorNIIBE Yutaka <gniibe@fsij.org>
Fri, 6 Jan 2017 00:14:13 +0000 (09:14 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Fri, 6 Jan 2017 00:47:31 +0000 (09:47 +0900)
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--

With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG.  Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
scd/apdu.c
scd/apdu.h
scd/app-common.h
scd/app.c
scd/ccid-driver.c
scd/ccid-driver.h

index d0b75c8..50363ce 100644 (file)
 #define CCID_DRIVER_INCLUDE_USB_IDS 1
 #include "ccid-driver.h"
 
+struct dev_list {
+  struct ccid_dev_table *ccid_table;
+  const char *portstr;
+  int idx;
+  int idx_max;
+};
+
 /* Due to conflicting use of threading libraries we usually can't link
    against libpcsclite if we are using Pth.  Instead we use a wrapper
    program.  Note that with nPth there is no need for a wrapper. */
@@ -428,7 +435,6 @@ new_reader_slot (void)
 {
   int i, reader = -1;
 
-  npth_mutex_lock (&reader_table_lock);
   for (i=0; i < MAX_READER; i++)
     if (!reader_table[i].used)
       {
@@ -436,7 +442,6 @@ new_reader_slot (void)
         reader_table[reader].used = 1;
         break;
       }
-  npth_mutex_unlock (&reader_table_lock);
 
   if (reader == -1)
     {
@@ -1428,8 +1433,6 @@ static int
 close_pcsc_reader_direct (int slot)
 {
   pcsc_release_context (reader_table[slot].pcsc.context);
-  xfree (reader_table[slot].rdrname);
-  reader_table[slot].rdrname = NULL;
   return 0;
 }
 #endif /*!NEED_PCSC_WRAPPER*/
@@ -2432,7 +2435,6 @@ static int
 close_ccid_reader (int slot)
 {
   ccid_close_reader (reader_table[slot].ccid.handle);
-  reader_table[slot].rdrname = NULL;
   return 0;
 }
 
@@ -2566,7 +2568,7 @@ ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
 
 /* Open the reader and try to read an ATR.  */
 static int
-open_ccid_reader (const char *portstr)
+open_ccid_reader (struct dev_list *dl)
 {
   int err;
   int slot;
@@ -2577,8 +2579,8 @@ open_ccid_reader (const char *portstr)
     return -1;
   slotp = reader_table + slot;
 
-  err = ccid_open_reader (&slotp->ccid.handle, portstr,
-                          (const char **)&slotp->rdrname);
+  err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
+                          &slotp->ccid.handle, &slotp->rdrname);
   if (err)
     {
       slotp->used = 0;
@@ -2611,12 +2613,7 @@ open_ccid_reader (const char *portstr)
   unlock_slot (slot);
   return slot;
 }
-
-
-
 #endif /* HAVE_LIBUSB */
-
-
 \f
 #ifdef USE_G10CODE_RAPDU
 /*
@@ -2919,63 +2916,80 @@ open_rapdu_reader (int portno,
 /*
        Driver Access
  */
+gpg_error_t
+apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
+{
+  gpg_error_t err;
+  struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
 
+  *l_p = NULL;
+  if (!dl)
+    return gpg_error_from_syserror ();
 
-/* Open the reader and return an internal slot number or -1 on
-   error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
-   the first USB reader.  For PC/SC the first listed reader). */
-int
-apdu_open_reader (const char *portstr)
-{
-  static int pcsc_api_loaded, ct_api_loaded;
-  int slot;
+  dl->portstr = portstr;
+  dl->idx = 0;
 
-  if (DBG_READER)
-    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+  npth_mutex_lock (&reader_table_lock);
 
 #ifdef HAVE_LIBUSB
-  if (!opt.disable_ccid)
+  if (opt.disable_ccid)
     {
-      static int once_available;
-      int i;
-      const char *s;
-
-      slot = open_ccid_reader (portstr);
-      if (slot != -1)
-        {
-          once_available = 1;
-          if (DBG_READER)
-            log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
-          return slot; /* got one */
-        }
+      dl->ccid_table = NULL;
+      dl->idx_max = 1;
+    }
+  else
+    {
+      err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table);
+      if (err)
+        return err;
 
-      /* If we ever loaded successfully loaded a CCID reader we never
-         want to fallback to another driver.  This solves a problem
-         where ccid was used, the card unplugged and then scdaemon
-         tries to find a new reader and will eventually try PC/SC over
-         and over again.  To reset this flag "gpgconf --kill scdaemon"
-         can be used.  */
-      if (once_available)
+      if (dl->idx_max == 0)
         {
-          if (DBG_READER)
-            log_debug ("leave: apdu_open_reader => slot=-1 (once_avail)\n");
-          return -1;
-        }
-
-      /* If a CCID reader specification has been given, the user does
-         not want a fallback to other drivers. */
-      if (portstr)
-        for (s=portstr, i=0; *s; s++)
-          if (*s == ':' && (++i == 3))
+          /* If a CCID reader specification has been given, the user does
+             not want a fallback to other drivers. */
+          if (portstr && strlen (portstr) > 5 && portstr[4] == ':')
             {
               if (DBG_READER)
                 log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
-              return -1;
+
+              xfree (dl);
+              npth_mutex_unlock (&reader_table_lock);
+              return gpg_error (GPG_ERR_ENODEV);
             }
+          else
+            dl->idx_max = 1;
+        }
     }
-
+#else
+  dl->ccid_table = NULL;
+  dl->idx_max = 1;
 #endif /* HAVE_LIBUSB */
 
+  *l_p = dl;
+  return 0;
+}
+
+void
+apdu_dev_list_finish (struct dev_list *dl)
+{
+  ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
+  xfree (dl);
+  npth_mutex_unlock (&reader_table_lock);
+}
+
+
+/* Open the reader and return an internal slot number or -1 on
+   error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
+   the first USB reader.  For PC/SC the first listed reader). */
+static int
+apdu_open_one_reader (const char *portstr)
+{
+  static int pcsc_api_loaded, ct_api_loaded;
+  int slot;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
+
   if (opt.ctapi_driver && *opt.ctapi_driver)
     {
       int port = portstr? atoi (portstr) : 32768;
@@ -3005,7 +3019,6 @@ apdu_open_reader (const char *portstr)
       return open_ct_reader (port);
     }
 
-
   /* No ctAPI configured, so lets try the PC/SC API */
   if (!pcsc_api_loaded)
     {
@@ -3099,6 +3112,96 @@ apdu_open_reader (const char *portstr)
   return slot;
 }
 
+int
+apdu_open_reader (struct dev_list *dl)
+{
+  int slot;
+
+  if (dl->ccid_table)
+    { /* CCID readers.  */
+      int readerno;
+
+      /* See whether we want to use the reader ID string or a reader
+         number. A readerno of -1 indicates that the reader ID string is
+         to be used. */
+      if (dl->portstr && strchr (dl->portstr, ':'))
+        readerno = -1; /* We want to use the readerid.  */
+      else if (dl->portstr)
+        {
+          readerno = atoi (dl->portstr);
+          if (readerno < 0)
+            {
+              return -1;
+            }
+        }
+      else
+        readerno = 0;  /* Default. */
+
+      if (readerno > 0)
+        { /* Use single, the specific reader.  */
+          if (readerno >= dl->idx_max)
+            return -1;
+
+          dl->idx = readerno;
+          dl->portstr = NULL;
+          slot = open_ccid_reader (dl);
+          dl->idx = dl->idx_max;
+          if (slot >= 0)
+            return slot;
+          else
+            return -1;
+        }
+
+      while (dl->idx < dl->idx_max)
+        {
+          unsigned int bai = ccid_get_BAI (dl->idx, dl->ccid_table);
+
+          if (DBG_READER)
+            log_debug ("apdu_open_reader: BAI=%x\n", bai);
+
+          /* Check identity by BAI against already opened HANDLEs.  */
+          for (slot = 0; slot < MAX_READER; slot++)
+            if (reader_table[slot].used
+                && ccid_compare_BAI (reader_table[slot].ccid.handle, bai))
+              break;
+
+          if (slot == MAX_READER)
+            { /* Found a new device.  */
+              if (DBG_READER)
+                log_debug ("apdu_open_reader: new device=%x\n", bai);
+
+              slot = open_ccid_reader (dl);
+
+              dl->idx++;
+              if (slot >= 0)
+                return slot;
+              else
+                {
+                  /* Skip this reader.  */
+                  log_error ("ccid open error: skip\n");
+                  continue;
+                }
+            }
+          else
+            dl->idx++;
+        }
+
+      slot = -1;
+    }
+  else
+    { /* PC/SC readers.  */
+      if (dl->idx++ == 0)
+        slot = apdu_open_one_reader (dl->portstr);
+      else
+        slot = -1;
+    }
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
+
+  return slot;
+}
+
 
 /* Open an remote reader and return an internal slot number or -1 on
    error. This function is an alternative to apdu_open_reader and used
@@ -3177,6 +3280,8 @@ apdu_close_reader (int slot)
         log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
       return sw;
     }
+  xfree (reader_table[slot].rdrname);
+  reader_table[slot].rdrname = NULL;
   reader_table[slot].used = 0;
   if (DBG_READER)
     log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
@@ -3204,6 +3309,8 @@ apdu_prepare_exit (void)
             apdu_disconnect (slot);
             if (reader_table[slot].close_reader)
               reader_table[slot].close_reader (slot);
+            xfree (reader_table[slot].rdrname);
+            reader_table[slot].rdrname = NULL;
             reader_table[slot].used = 0;
           }
       npth_mutex_unlock (&reader_table_lock);
index 3021cf7..473def5 100644 (file)
@@ -74,6 +74,7 @@ enum {
   SW_HOST_ALREADY_CONNECTED = 0x1000f
 };
 
+struct dev_list;
 
 #define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
 
@@ -86,8 +87,11 @@ enum {
 
 gpg_error_t apdu_init (void);
 
+gpg_error_t apdu_dev_list_start (const char *portstr, struct dev_list **l_p);
+void apdu_dev_list_finish (struct dev_list *l);
+
 /* Note, that apdu_open_reader returns no status word but -1 on error. */
-int apdu_open_reader (const char *portstr);
+int apdu_open_reader (struct dev_list *l);
 int apdu_open_remote_reader (const char *portstr,
                              const unsigned char *cookie, size_t length,
                              int (*readfnc) (void *opaque,
@@ -117,9 +121,9 @@ int apdu_reset (int slot);
 int apdu_get_status (int slot, int hang, unsigned int *status);
 int apdu_check_pinpad (int slot, int command, pininfo_t *pininfo);
 int apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
-                       pininfo_t *pininfo);
+                        pininfo_t *pininfo);
 int apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
-                       pininfo_t *pininfo);
+                        pininfo_t *pininfo);
 int apdu_send_simple (int slot, int extended_mode,
                       int class, int ins, int p0, int p1,
                       int lc, const char *data);
index 781bf46..2371818 100644 (file)
@@ -121,6 +121,9 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
 
 
 /*-- app.c --*/
+app_t app_list_start (void);
+void app_list_finish (void);
+
 void app_dump_state (void);
 void application_notify_card_reset (int slot);
 gpg_error_t check_application_conflict (const char *name, app_t app);
index a499724..6db9e27 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -319,22 +319,30 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
 gpg_error_t
 select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   app_t app;
-  int slot;
 
   *r_app = NULL;
 
-  if ((scan && !app_top)
-      /* FIXME: Here, we can change code to support multiple readers.
-         For now, we only open a single reader.
-      */
-      || !app_top)
+  if (scan || !app_top)
     {
-      slot = apdu_open_reader (opt.reader_port);
-      if (slot >= 0)
+      struct dev_list *l;
+
+      err = apdu_dev_list_start (opt.reader_port, &l);
+      if (err)
+        return err;
+
+      while (1)
         {
-          int sw = apdu_connect (slot);
+          int slot;
+          int sw;
+
+          slot = apdu_open_reader (l);
+          if (slot < 0)
+            break;
+
+          err = 0;
+          sw = apdu_connect (slot);
 
           if (sw == SW_HOST_CARD_INACTIVE)
             {
@@ -346,23 +354,17 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan)
             err = 0;
           else
             err = gpg_error (GPG_ERR_ENODEV);
+
+          if (!err)
+            err = app_new_register (slot, ctrl, name);
+          else
+            apdu_close_reader (slot);
         }
-      else
-        err = gpg_error (GPG_ERR_ENODEV);
 
-      if (!err)
-        err = app_new_register (slot, ctrl, name);
-      else
-        apdu_close_reader (slot);
+      apdu_dev_list_finish (l);
     }
-  else
-    err = 0;
-
-  if (!err)
-    app = app_top;
-  else
-    app = NULL;
 
+  app = app_top;
   if (app)
     {
       lock_app (app, ctrl);
@@ -552,8 +554,6 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 
   if (!app)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (!app->ref_count)
-    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
@@ -1071,3 +1071,16 @@ initialize_module_command (void)
 
   return apdu_init ();
 }
+
+app_t
+app_list_start (void)
+{
+  npth_mutex_lock (&app_list_lock);
+  return app_top;
+}
+
+void
+app_list_finish (void)
+{
+  npth_mutex_unlock (&app_list_lock);
+}
index 6d81122..5e02628 100644 (file)
@@ -239,12 +239,11 @@ static struct
 struct ccid_driver_s
 {
   libusb_device_handle *idev;
-  char *rid;
   int dev_fd;  /* -1 for USB transport or file descriptor of the
                    transport device. */
+  unsigned int bai;
   unsigned short id_vendor;
   unsigned short id_product;
-  unsigned short bcd_device;
   int ifc_no;
   int ep_bulk_out;
   int ep_bulk_in;
@@ -744,14 +743,13 @@ prepare_special_transport (ccid_driver_t handle)
    Note, that this code is based on the one in lsusb.c of the
    usb-utils package, I wrote on 2003-09-01. -wk. */
 static int
-parse_ccid_descriptor (ccid_driver_t handle,
+parse_ccid_descriptor (ccid_driver_t handle, unsigned short bcd_device,
                        const unsigned char *buf, size_t buflen)
 {
   unsigned int i;
   unsigned int us;
   int have_t1 = 0, have_tpdu=0;
 
-
   handle->nonnull_nad = 0;
   handle->auto_ifsd = 0;
   handle->max_ifsd = 32;
@@ -761,7 +759,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
   handle->auto_param = 0;
   handle->auto_pps = 0;
   DEBUGOUT_3 ("idVendor: %04X  idProduct: %04X  bcdDevice: %04X\n",
-              handle->id_vendor, handle->id_product, handle->bcd_device);
+              handle->id_vendor, handle->id_product, bcd_device);
   if (buflen < 54 || buf[0] < 54)
     {
       DEBUGOUT ("CCID device descriptor is too short\n");
@@ -964,11 +962,11 @@ parse_ccid_descriptor (ccid_driver_t handle,
   */
   if (handle->id_vendor == VENDOR_SCM
       && handle->max_ifsd > 48
-      && (  (handle->id_product == SCM_SCR331   && handle->bcd_device < 0x0516)
-          ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
-          ||(handle->id_product == SCM_SCR335   && handle->bcd_device < 0x0514)
-          ||(handle->id_product == SCM_SPR532   && handle->bcd_device < 0x0504)
-          ||(handle->id_product == SCM_SCR3320  && handle->bcd_device < 0x0522)
+      && (  (handle->id_product == SCM_SCR331   && bcd_device < 0x0516)
+          ||(handle->id_product == SCM_SCR331DI && bcd_device < 0x0620)
+          ||(handle->id_product == SCM_SCR335   && bcd_device < 0x0514)
+          ||(handle->id_product == SCM_SPR532   && bcd_device < 0x0504)
+          ||(handle->id_product == SCM_SCR3320  && bcd_device < 0x0522)
           ))
     {
       DEBUGOUT ("enabling workaround for buggy SCM readers\n");
@@ -1534,23 +1532,33 @@ ccid_vendor_specific_init (ccid_driver_t handle)
 }
 
 
-/* Open the reader with the internal number READERNO and return a
-   pointer to be used as handle in HANDLE.  Returns 0 on success. */
-int
-ccid_open_reader (ccid_driver_t *handle, const char *readerid,
-                  const char **rdrname_p)
-{
-  int rc = 0;
-  libusb_device_handle *idev = NULL;
-  int dev_fd = -1;
-  char *rid = NULL;
-  unsigned char *ifcdesc_extra = NULL;
+#define MAX_DEVICE 4 /* See MAX_READER in apdu.c.  */
+
+struct ccid_dev_table {
+  int n;                        /* Index to ccid_usb_dev_list */
+  int transport;
+  int interface_number;
+  int setting_number;
+  unsigned char *ifcdesc_extra;
+  int ep_bulk_out;
+  int ep_bulk_in;
+  int ep_intr;
   size_t ifcdesc_extra_len;
-  int readerno;
-  int ifc_no, set_no, ep_bulk_out, ep_bulk_in, ep_intr;
-  struct libusb_device_descriptor desc;
+};
 
-  *handle = NULL;
+static libusb_device **ccid_usb_dev_list;
+static struct ccid_dev_table ccid_dev_table[MAX_DEVICE];
+
+gpg_error_t
+ccid_dev_scan (int *idx_max_p, struct ccid_dev_table **t_p)
+{
+  ssize_t n;
+  libusb_device *dev;
+  int i;
+  int ifc_no;
+  int set_no;
+  int idx = 0;
+  int err = 0;
 
   if (!initialized_usb)
     {
@@ -1558,122 +1566,379 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid,
       initialized_usb = 1;
     }
 
-  /* See whether we want to use the reader ID string or a reader
-     number. A readerno of -1 indicates that the reader ID string is
-     to be used. */
-  if (readerid && strchr (readerid, ':'))
-    readerno = -1; /* We want to use the readerid.  */
-  else if (readerid)
+  n = libusb_get_device_list (NULL, &ccid_usb_dev_list);
+  for (i = 0; i < n; i++)
+    {
+      struct libusb_config_descriptor *config;
+      struct libusb_device_descriptor desc;
+
+      dev = ccid_usb_dev_list[i];
+
+      if (libusb_get_device_descriptor (dev, &desc))
+        continue;
+
+      if (libusb_get_active_config_descriptor (dev, &config))
+        continue;
+
+      for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+        for (set_no=0; set_no < config->interface[ifc_no].num_altsetting;
+             set_no++)
+          {
+            const struct libusb_interface_descriptor *ifcdesc;
+
+            ifcdesc = &config->interface[ifc_no].altsetting[set_no];
+            /* The second condition is for older SCM SPR 532 who did
+               not know about the assigned CCID class.  The third
+               condition does the same for a Cherry SmartTerminal
+               ST-2000.  Instead of trying to interpret the strings
+               we simply check the product ID. */
+            if (ifcdesc && ifcdesc->extra
+                && ((ifcdesc->bInterfaceClass == 11
+                     && ifcdesc->bInterfaceSubClass == 0
+                     && ifcdesc->bInterfaceProtocol == 0)
+                    || (ifcdesc->bInterfaceClass == 255
+                        && desc.idVendor == VENDOR_SCM
+                        && desc.idProduct == SCM_SPR532)
+                    || (ifcdesc->bInterfaceClass == 255
+                        && desc.idVendor == VENDOR_CHERRY
+                        && desc.idProduct == CHERRY_ST2000)))
+              {
+                /* Found a reader.  */
+                unsigned char *ifcdesc_extra;
+
+                ifcdesc_extra = malloc (ifcdesc->extra_length);
+                if (!ifcdesc_extra)
+                  {
+                    err = gpg_error_from_syserror ();
+                    libusb_free_config_descriptor (config);
+                    goto scan_finish;
+                  }
+                memcpy (ifcdesc_extra, ifcdesc->extra, ifcdesc->extra_length);
+
+                ccid_dev_table[idx].transport = TRANSPORT_USB;
+                ccid_dev_table[idx].n = i;
+                ccid_dev_table[idx].interface_number = ifc_no;
+                ccid_dev_table[idx].setting_number = set_no;
+                ccid_dev_table[idx].ifcdesc_extra = ifcdesc_extra;
+                ccid_dev_table[idx].ifcdesc_extra_len = ifcdesc->extra_length;
+                ccid_dev_table[idx].ep_bulk_out = find_endpoint (ifcdesc, 0);
+                ccid_dev_table[idx].ep_bulk_in = find_endpoint (ifcdesc, 1);
+                ccid_dev_table[idx].ep_intr = find_endpoint (ifcdesc, 2);
+
+                idx++;
+                if (idx >= MAX_DEVICE)
+                  {
+                    libusb_free_config_descriptor (config);
+                    err = 0;
+                    goto scan_finish;
+                  }
+              }
+          }
+
+      libusb_free_config_descriptor (config);
+    }
+
+  /* Now check whether there are any devices with special transport types. */
+  for (i=0; transports[i].name; i++)
     {
-      readerno = atoi (readerid);
-      if (readerno < 0)
+      if (access (transports[i].name, (R_OK|W_OK)) == 0)
         {
-          DEBUGOUT ("no CCID readers found\n");
-          rc = CCID_DRIVER_ERR_NO_READER;
-          goto leave;
+          /* Found a device. */
+          DEBUGOUT_1 ("Found CCID reader %d\n", idx);
+
+          ccid_dev_table[idx].transport = TRANSPORT_CM4040;
+          ccid_dev_table[idx].n = i;
+          ccid_dev_table[idx].interface_number = 0;
+          ccid_dev_table[idx].setting_number = 0;
+          ccid_dev_table[idx].ifcdesc_extra = NULL;
+          ccid_dev_table[idx].ifcdesc_extra_len = 0;
+          ccid_dev_table[idx].ep_bulk_out = 0;
+          ccid_dev_table[idx].ep_bulk_in = 0;
+          ccid_dev_table[idx].ep_intr = 0;
+
+          idx++;
+          if (idx >= MAX_DEVICE)
+            goto scan_finish;
         }
     }
-  else
-    readerno = 0;  /* Default. */
 
-  if (scan_or_find_devices (readerno, readerid, &rid, &desc, &ifcdesc_extra,
-                            &ifcdesc_extra_len, &ifc_no, &set_no, &ep_bulk_out,
-                            &ep_bulk_in, &ep_intr, &idev, &dev_fd))
+ scan_finish:
+
+  if (err)
+    {
+      *idx_max_p = 0;
+      *t_p = NULL;
+      for (i = 0; i < idx; i++)
+        {
+          free (ccid_dev_table[idx].ifcdesc_extra);
+          ccid_dev_table[idx].transport = 0;
+          ccid_dev_table[idx].n = 0;
+          ccid_dev_table[idx].interface_number = 0;
+          ccid_dev_table[idx].setting_number = 0;
+          ccid_dev_table[idx].ifcdesc_extra = NULL;
+          ccid_dev_table[idx].ifcdesc_extra_len = 0;
+          ccid_dev_table[idx].ep_bulk_out = 0;
+          ccid_dev_table[idx].ep_bulk_in = 0;
+          ccid_dev_table[idx].ep_intr = 0;
+        }
+      libusb_free_device_list (ccid_usb_dev_list, 1);
+      ccid_usb_dev_list = NULL;
+    }
+  else
     {
-      if (readerno == -1)
-        DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid );
+      *idx_max_p = idx;
+      if (idx)
+        *t_p = ccid_dev_table;
       else
-        DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
-      rc = CCID_DRIVER_ERR_NO_READER;
-      goto leave;
+        *t_p = NULL;
     }
 
-  /* Okay, this is a CCID reader. */
-  *handle = calloc (1, sizeof **handle);
-  if (!*handle)
+  return err;
+}
+
+void
+ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
+{
+  int i;
+
+  for (i = 0; i < max; i++)
     {
-      DEBUGOUT ("out of memory\n");
-      rc = CCID_DRIVER_ERR_OUT_OF_CORE;
-      goto leave;
+      free (tbl[i].ifcdesc_extra);
+      tbl[i].transport = 0;
+      tbl[i].n = 0;
+      tbl[i].interface_number = 0;
+      tbl[i].setting_number = 0;
+      tbl[i].ifcdesc_extra = NULL;
+      tbl[i].ifcdesc_extra_len = 0;
+      tbl[i].ep_bulk_out = 0;
+      tbl[i].ep_bulk_in = 0;
+      tbl[i].ep_intr = 0;
     }
-  (*handle)->rid = rid;
-  if (idev) /* Regular USB transport. */
+  libusb_free_device_list (ccid_usb_dev_list, 1);
+  ccid_usb_dev_list = NULL;
+}
+
+unsigned int
+ccid_get_BAI (int idx, struct ccid_dev_table *tbl)
+{
+  int n;
+  int bus, addr, intf;
+  unsigned int bai;
+
+  if (tbl[idx].transport == TRANSPORT_USB)
     {
-      (*handle)->idev = idev;
-      (*handle)->dev_fd = -1;
-      (*handle)->id_vendor = desc.idVendor;
-      (*handle)->id_product = desc.idProduct;
-      (*handle)->bcd_device = desc.bcdDevice;
-      (*handle)->ifc_no = ifc_no;
-      (*handle)->ep_bulk_out = ep_bulk_out;
-      (*handle)->ep_bulk_in = ep_bulk_in;
-      (*handle)->ep_intr = ep_intr;
+      libusb_device *dev;
+
+      n = tbl[idx].n;
+      dev = ccid_usb_dev_list[n];
+
+      bus = libusb_get_bus_number (dev);
+      addr = libusb_get_device_address (dev);
+      intf = tbl[idx].interface_number;
+      bai = (bus << 16) | (addr << 8) | intf;
     }
-  else if (dev_fd != -1) /* Device transport. */
+  else
     {
-      (*handle)->idev = NULL;
-      (*handle)->dev_fd = dev_fd;
-      (*handle)->id_vendor = 0;  /* Magic vendor for special transport. */
-      (*handle)->id_product = ifc_no; /* Transport type */
-      prepare_special_transport (*handle);
+      n = tbl[idx].n;
+      bai = 0xFFFF0000 | n;
     }
-  else
+
+  return bai;
+}
+
+int
+ccid_compare_BAI (ccid_driver_t handle, unsigned int bai)
+{
+  return handle->bai == bai;
+}
+
+static int
+ccid_open_usb_reader (const char *spec_reader_name,
+                      int idx, struct ccid_dev_table *ccid_table,
+                      ccid_driver_t *handle, char **rdrname_p)
+{
+  libusb_device *dev;
+  libusb_device_handle *idev = NULL;
+  char *rid;
+  int rc = 0;
+  int ifc_no, set_no;
+  struct libusb_device_descriptor desc;
+  int n;
+  int bus, addr;
+  unsigned int bai;
+
+  n = ccid_table[idx].n;
+  ifc_no = ccid_table[idx].interface_number;
+  set_no = ccid_table[idx].setting_number;
+
+  dev = ccid_usb_dev_list[n];
+  bus = libusb_get_bus_number (dev);
+  addr = libusb_get_device_address (dev);
+  bai = (bus << 16) | (addr << 8) | ifc_no;
+
+  rc = libusb_open (dev, &idev);
+  if (rc)
     {
-      assert (!"no transport"); /* Bug. */
+      DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (rc));
+      free (*handle);
+      *handle = NULL;
+      return rc;
     }
 
-  DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n",  readerno, rid );
+  rc = libusb_get_device_descriptor (dev, &desc);
+  if (rc)
+    {
+      libusb_close (idev);
+      free (*handle);
+      *handle = NULL;
+      return rc;
+    }
 
-  if (idev)
+  rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
+                        desc.iSerialNumber);
+
+  /* Check to see if reader name matches the spec.  */
+  if (spec_reader_name
+      && strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
     {
-      if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
-        {
-          DEBUGOUT ("device not supported\n");
-          rc = CCID_DRIVER_ERR_NO_READER;
-          goto leave;
-        }
+      DEBUGOUT ("device not matched\n");
+      rc = CCID_DRIVER_ERR_NO_READER;
+      goto leave;
+    }
 
-      rc = libusb_claim_interface (idev, ifc_no);
+  (*handle)->id_vendor = desc.idVendor;
+  (*handle)->id_product = desc.idProduct;
+  (*handle)->idev = idev;
+  (*handle)->dev_fd = -1;
+  (*handle)->bai = bai;
+  (*handle)->ifc_no = ifc_no;
+  (*handle)->ep_bulk_out = ccid_table[idx].ep_bulk_out;
+  (*handle)->ep_bulk_in = ccid_table[idx].ep_bulk_in;
+  (*handle)->ep_intr = ccid_table[idx].ep_intr;
+
+  DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", idx, rid);
+
+  if (parse_ccid_descriptor (*handle, desc.bcdDevice,
+                             ccid_table[idx].ifcdesc_extra,
+                             ccid_table[idx].ifcdesc_extra_len))
+    {
+      DEBUGOUT ("device not supported\n");
+      rc = CCID_DRIVER_ERR_NO_READER;
+      goto leave;
+    }
+
+  rc = libusb_claim_interface (idev, ifc_no);
+  if (rc)
+    {
+      DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+      rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+      goto leave;
+    }
+
+  if (set_no != 0)
+    {
+      rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
       if (rc)
         {
-          DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+          DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
           rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
           goto leave;
         }
-
-      if (set_no != 0)
-        {
-          rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
-          if (rc)
-            {
-              DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
-              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
-              goto leave;
-            }
-        }
     }
 
   rc = ccid_vendor_specific_init (*handle);
 
  leave:
-  free (ifcdesc_extra);
   if (rc)
     {
       free (rid);
-      if (idev)
-        libusb_close (idev);
-      if (dev_fd != -1)
-        close (dev_fd);
+      libusb_close (idev);
       free (*handle);
       *handle = NULL;
     }
   else
-    if (rdrname_p)
-      *rdrname_p = (*handle)->rid;
+    {
+      if (rdrname_p)
+        *rdrname_p = rid;
+      else
+        free (rid);
+    }
 
   return rc;
 }
 
+/* Open the reader with the internal number READERNO and return a
+   pointer to be used as handle in HANDLE.  Returns 0 on success. */
+int
+ccid_open_reader (const char *spec_reader_name,
+                  int idx, struct ccid_dev_table *ccid_table,
+                  ccid_driver_t *handle, char **rdrname_p)
+{
+  int n;
+  int fd;
+  char *rid;
+
+  *handle = calloc (1, sizeof **handle);
+  if (!*handle)
+    {
+      DEBUGOUT ("out of memory\n");
+      return CCID_DRIVER_ERR_OUT_OF_CORE;
+    }
+
+  if (ccid_table[idx].transport == TRANSPORT_USB)
+    return ccid_open_usb_reader (spec_reader_name, idx, ccid_table,
+                                 handle, rdrname_p);
+
+  /* Special transport support.  */
+
+  n = ccid_table[idx].n;
+  fd = open (transports[n].name, O_RDWR);
+  if (fd < 0)
+    {
+      DEBUGOUT_2 ("failed to open '%s': %s\n",
+                  transports[n].name, strerror (errno));
+      free (*handle);
+      *handle = NULL;
+      return -1;
+    }
+
+  rid = malloc (strlen (transports[n].name) + 30 + 10);
+  if (!rid)
+    {
+      close (fd);
+      free (*handle);
+      *handle = NULL;
+      return -1; /* Error. */
+    }
+
+  sprintf (rid, "0000:%04X:%s:0", transports[n].type, transports[n].name);
+
+  /* Check to see if reader name matches the spec.  */
+  if (spec_reader_name
+      && strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
+    {
+      DEBUGOUT ("device not matched\n");
+      free (rid);
+      close (fd);
+      free (*handle);
+      *handle = NULL;
+      return -1;
+    }
+
+  (*handle)->id_vendor = 0;
+  (*handle)->id_product = transports[n].type;
+  (*handle)->idev = NULL;
+  (*handle)->dev_fd = fd;
+  (*handle)->bai = 0xFFFF0000 | n;
+  prepare_special_transport (*handle);
+  if (rdrname_p)
+    *rdrname_p = rid;
+  else
+    free (rid);
+
+  return 0;
+}
+
 
 static void
 do_close_reader (ccid_driver_t handle)
@@ -1719,7 +1984,7 @@ ccid_set_progress_cb (ccid_driver_t handle,
                       void (*cb)(void *, const char *, int, int, int),
                       void *cb_arg)
 {
-  if (!handle || !handle->rid)
+  if (!handle)
     return CCID_DRIVER_ERR_INV_VALUE;
 
   handle->progress_cb = cb;
@@ -1736,7 +2001,6 @@ ccid_close_reader (ccid_driver_t handle)
     return 0;
 
   do_close_reader (handle);
-  free (handle->rid);
   free (handle);
   return 0;
 }
index e3aed9f..9e71f5e 100644 (file)
@@ -1,5 +1,5 @@
-/* ccid-driver.c - USB ChipCardInterfaceDevices driver
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+/* ccid-driver.h - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -109,10 +109,18 @@ enum {
 struct ccid_driver_s;
 typedef struct ccid_driver_s *ccid_driver_t;
 
+struct ccid_dev_table;
+
 int ccid_set_debug_level (int level);
 char *ccid_get_reader_list (void);
-int ccid_open_reader (ccid_driver_t *handle, const char *readerid,
-                      const char **rdrname_p);
+
+gpg_error_t ccid_dev_scan (int *idx_max, struct ccid_dev_table **t_p);
+void ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max);
+unsigned int ccid_get_BAI (int, struct ccid_dev_table *tbl);
+int ccid_compare_BAI (ccid_driver_t handle, unsigned int);
+int ccid_open_reader (const char *spec_reader_name,
+                      int idx, struct ccid_dev_table *ccid_table,
+                      ccid_driver_t *handle, char **rdrname_p);
 int ccid_set_progress_cb (ccid_driver_t handle,
                           void (*cb)(void *, const char *, int, int, int),
                           void *cb_arg);
@@ -126,7 +134,7 @@ int ccid_transceive (ccid_driver_t handle,
                      unsigned char *resp, size_t maxresplen, size_t *nresp);
 int ccid_transceive_secure (ccid_driver_t handle,
                      const unsigned char *apdu, size_t apdulen,
-                    pininfo_t *pininfo,
+                     pininfo_t *pininfo,
                      unsigned char *resp, size_t maxresplen, size_t *nresp);
 int ccid_transceive_escape (ccid_driver_t handle,
                             const unsigned char *data, size_t datalen,