scd: Only submit apdu_get_status when needed.
[gnupg.git] / scd / apdu.c
index e05869f..f88d970 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 /* NOTE: This module is also used by other software, thus the use of
 #include "scdaemon.h"
 #include "exechelp.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
+#include "host2net.h"
 
 #include "iso7816.h"
 #include "apdu.h"
+#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.   Instead we use a wrapper program.  */
-#ifdef USE_NPTH
+   against libpcsclite if we are using Pth.  Instead we use a wrapper
+   program.  Note that with nPth there is no need for a wrapper. */
+#ifdef USE_PTH  /* Right, plain old Pth.  */
 #if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
 #define NEED_PCSC_WRAPPER 1
 #endif
@@ -98,7 +108,6 @@ struct reader_table_s {
   int (*connect_card)(int);
   int (*disconnect_card)(int);
   int (*close_reader)(int);
-  int (*shutdown_reader)(int);
   int (*reset_reader)(int);
   int (*get_status_reader)(int, unsigned int *);
   int (*send_apdu_reader)(int,unsigned char *,size_t,
@@ -118,6 +127,8 @@ struct reader_table_s {
     pcsc_dword_t protocol;
     pcsc_dword_t verify_ioctl;
     pcsc_dword_t modify_ioctl;
+    int pinmin;
+    int pinmax;
 #ifdef NEED_PCSC_WRAPPER
     int req_fd;
     int rsp_fd;
@@ -130,18 +141,16 @@ struct reader_table_s {
   } rapdu;
 #endif /*USE_G10CODE_RAPDU*/
   char *rdrname;     /* Name of the connected reader or NULL if unknown. */
-  int any_status;    /* True if we have seen any status.  */
-  int last_status;
-  int status;
   int is_t0;         /* True if we know that we are running T=0. */
   int is_spr532;     /* True if we know that the reader is a SPR532.  */
+  int pinpad_varlen_supported;  /* True if we know that the reader
+                                   supports variable length pinpad
+                                   input.  */
   unsigned char atr[33];
   size_t atrlen;           /* A zero length indicates that the ATR has
                               not yet been read; i.e. the card is not
                               ready for use. */
-  unsigned int change_counter;
 #ifdef USE_NPTH
-  int lock_initialized;
   npth_mutex_t lock;
 #endif
 };
@@ -150,6 +159,10 @@ typedef struct reader_table_s *reader_table_t;
 /* A global table to keep track of active readers. */
 static struct reader_table_s reader_table[MAX_READER];
 
+#ifdef USE_NPTH
+static npth_mutex_t reader_table_lock;
+#endif
+
 
 /* ct API function pointer. */
 static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn);
@@ -210,7 +223,7 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_STATE_ATRMATCH    0x0040  /* ATR matches card. */
 #define PCSC_STATE_EXCLUSIVE   0x0080  /* Exclusive Mode.  */
 #define PCSC_STATE_INUSE       0x0100  /* Shared mode.  */
-#define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
+#define PCSC_STATE_MUTE        0x0200  /* Unresponsive card.  */
 #ifdef HAVE_W32_SYSTEM
 # define PCSC_STATE_UNPOWERED  0x0400  /* Card not powerred up.  */
 #endif
@@ -236,16 +249,32 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_E_NOT_TRANSACTED          0x80100016
 #define PCSC_E_READER_UNAVAILABLE      0x80100017
 #define PCSC_E_NO_SERVICE              0x8010001D
+#define PCSC_E_SERVICE_STOPPED         0x8010001E
 #define PCSC_W_REMOVED_CARD            0x80100069
 
 /* Fix pcsc-lite ABI incompatibilty.  */
 #ifndef SCARD_CTL_CODE
-# define SCARD_CTL_CODE(code) (0x42000000 + (code))
+#ifdef _WIN32
+#include <winioctl.h>
+#define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD, (code), \
+                                      METHOD_BUFFERED, FILE_ANY_ACCESS)
+#else
+#define SCARD_CTL_CODE(code) (0x42000000 + (code))
+#endif
 #endif
 
 #define CM_IOCTL_GET_FEATURE_REQUEST     SCARD_CTL_CODE(3400)
+#define CM_IOCTL_VENDOR_IFD_EXCHANGE     SCARD_CTL_CODE(1)
 #define FEATURE_VERIFY_PIN_DIRECT        0x06
 #define FEATURE_MODIFY_PIN_DIRECT        0x07
+#define FEATURE_GET_TLV_PROPERTIES       0x12
+
+#define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2
+#define PCSCv2_PART10_PROPERTY_bTimeOut2                 3
+#define PCSCv2_PART10_PROPERTY_bMinPINSize               6
+#define PCSCv2_PART10_PROPERTY_bMaxPINSize               7
+#define PCSCv2_PART10_PROPERTY_wIdVendor                11
+#define PCSCv2_PART10_PROPERTY_wIdProduct               12
 
 
 /* The PC/SC error is defined as a long as per specs.  Due to left
@@ -334,11 +363,11 @@ long (* DLSTDCALL pcsc_control) (long card,
 
 
 /*  Prototypes.  */
+static int pcsc_vendor_specific_init (int slot);
 static int pcsc_get_status (int slot, unsigned int *status);
 static int reset_pcsc_reader (int slot);
 static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
-                                     unsigned int *status,
-                                     unsigned int *changed);
+                                     unsigned int *status);
 static int check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo);
 static int pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
                                pininfo_t *pininfo);
@@ -351,41 +380,84 @@ static int pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
       Helper
  */
 
+static int
+lock_slot (int slot)
+{
+#ifdef USE_NPTH
+  int err;
+
+  err = npth_mutex_lock (&reader_table[slot].lock);
+  if (err)
+    {
+      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
+      return SW_HOST_LOCKING_FAILED;
+    }
+#endif /*USE_NPTH*/
+  return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_NPTH
+  int err;
+
+  err = npth_mutex_trylock (&reader_table[slot].lock);
+  if (err == EBUSY)
+    return SW_HOST_BUSY;
+  else if (err)
+    {
+      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
+      return SW_HOST_LOCKING_FAILED;
+    }
+#endif /*USE_NPTH*/
+  return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_NPTH
+  int err;
+
+  err = npth_mutex_unlock (&reader_table[slot].lock);
+  if (err)
+    log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_NPTH*/
+}
+
 
 /* Find an unused reader slot for PORTSTR and put it into the reader
-   table.  Return -1 on error or the index into the reader table. */
+   table.  Return -1 on error or the index into the reader table.
+   Acquire slot's lock on successful return.  Caller needs to unlock it.  */
 static int
 new_reader_slot (void)
 {
   int i, reader = -1;
-  int err;
 
   for (i=0; i < MAX_READER; i++)
-    {
-      if (!reader_table[i].used && reader == -1)
+    if (!reader_table[i].used)
+      {
         reader = i;
-    }
+        reader_table[reader].used = 1;
+        break;
+      }
+
   if (reader == -1)
     {
       log_error ("new_reader_slot: out of slots\n");
       return -1;
     }
-#ifdef USE_NPTH
-  if (!reader_table[reader].lock_initialized)
+
+  if (lock_slot (reader))
     {
-      err = npth_mutex_init (&reader_table[reader].lock, NULL);
-      if (err)
-        {
-          log_error ("error initializing mutex: %s\n", strerror (err));
-          return -1;
-        }
-      reader_table[reader].lock_initialized = 1;
+      reader_table[reader].used = 0;
+      return -1;
     }
-#endif /*USE_NPTH*/
+
   reader_table[reader].connect_card = NULL;
   reader_table[reader].disconnect_card = NULL;
   reader_table[reader].close_reader = NULL;
-  reader_table[reader].shutdown_reader = NULL;
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
@@ -395,11 +467,9 @@ new_reader_slot (void)
   reader_table[reader].pinpad_verify = pcsc_pinpad_verify;
   reader_table[reader].pinpad_modify = pcsc_pinpad_modify;
 
-  reader_table[reader].used = 1;
-  reader_table[reader].any_status = 0;
-  reader_table[reader].last_status = 0;
   reader_table[reader].is_t0 = 1;
   reader_table[reader].is_spr532 = 0;
+  reader_table[reader].pinpad_varlen_supported = 0;
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
@@ -407,6 +477,8 @@ new_reader_slot (void)
 #endif
   reader_table[reader].pcsc.verify_ioctl = 0;
   reader_table[reader].pcsc.modify_ioctl = 0;
+  reader_table[reader].pcsc.pinmin = -1;
+  reader_table[reader].pcsc.pinmax = -1;
 
   return reader;
 }
@@ -421,8 +493,7 @@ dump_reader_status (int slot)
   if (reader_table[slot].dump_status_reader)
     reader_table[slot].dump_status_reader (slot);
 
-  if (reader_table[slot].status != -1
-      && reader_table[slot].atrlen)
+  if (reader_table[slot].atrlen)
     {
       log_info ("slot %d: ATR=", slot);
       log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
@@ -466,6 +537,7 @@ apdu_strerror (int rc)
     case SW_WRONG_LENGTH   : return "wrong length";
     case SW_CHV_WRONG      : return "CHV wrong";
     case SW_CHV_BLOCKED    : return "CHV blocked";
+    case SW_REF_DATA_INV   : return "referenced data invalidated";
     case SW_USE_CONDITIONS : return "use conditions not satisfied";
     case SW_BAD_PARAMETER  : return "bad parameter";
     case SW_NOT_SUPPORTED  : return "not supported";
@@ -511,16 +583,6 @@ ct_error_string (long err)
 }
 
 
-static void
-ct_dump_reader_status (int slot)
-{
-  log_info ("reader slot %d: %s\n", slot,
-            reader_table[slot].status == 1? "Processor ICC present" :
-            reader_table[slot].status == 0? "Memory ICC present" :
-            "ICC not present" );
-}
-
-
 /* Wait for the card in SLOT and activate it.  Return a status word
    error or 0 on success. */
 static int
@@ -579,7 +641,6 @@ ct_activate_card (int slot)
       return SW_HOST_CARD_IO_ERROR;
     }
 
-  reader_table[slot].status = buf[buflen - 1];
   memcpy (reader_table[slot].atr, buf, buflen - 2);
   reader_table[slot].atrlen = buflen - 2;
   return 0;
@@ -590,7 +651,6 @@ static int
 close_ct_reader (int slot)
 {
   CT_close (slot);
-  reader_table[slot].used = 0;
   return 0;
 }
 
@@ -606,14 +666,14 @@ static int
 ct_get_status (int slot, unsigned int *status)
 {
   (void)slot;
-  /* The status we returned is wrong but we don't care becuase ctAPI
+  /* The status we returned is wrong but we don't care because ctAPI
      is not anymore required.  */
   *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE;
   return 0;
 }
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
-   maximum of *BUFLEN data in BUFFER, the actual retruned size will be
+   maximum of *BUFLEN data in BUFFER, the actual returned size will be
    set to BUFLEN.  Returns: CT API error code. */
 static int
 ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
@@ -667,6 +727,7 @@ open_ct_reader (int port)
       log_error ("apdu_open_ct_reader failed on port %d: %s\n",
                  port, ct_error_string (rc));
       reader_table[reader].used = 0;
+      unlock_slot (reader);
       return -1;
     }
 
@@ -683,11 +744,12 @@ open_ct_reader (int port)
   reader_table[reader].get_status_reader = ct_get_status;
   reader_table[reader].send_apdu_reader = ct_send_apdu;
   reader_table[reader].check_pinpad = NULL;
-  reader_table[reader].dump_status_reader = ct_dump_reader_status;
+  reader_table[reader].dump_status_reader = NULL;
   reader_table[reader].pinpad_verify = NULL;
   reader_table[reader].pinpad_modify = NULL;
 
   dump_reader_status (reader);
+  unlock_slot (reader);
   return reader;
 }
 
@@ -825,6 +887,8 @@ pcsc_error_to_sw (long ec)
     case PCSC_E_CANCELLED:           rc = SW_HOST_ABORTED; break;
     case PCSC_E_NO_MEMORY:           rc = SW_HOST_OUT_OF_CORE; break;
     case PCSC_E_TIMEOUT:             rc = SW_HOST_CARD_IO_ERROR; break;
+    case PCSC_E_NO_SERVICE:
+    case PCSC_E_SERVICE_STOPPED:
     case PCSC_E_UNKNOWN_READER:      rc = SW_HOST_NO_READER; break;
     case PCSC_E_SHARING_VIOLATION:   rc = SW_HOST_LOCKING_FAILED; break;
     case PCSC_E_NO_SMARTCARD:        rc = SW_HOST_NO_CARD; break;
@@ -901,7 +965,7 @@ pcsc_get_status_direct (int slot, unsigned int *status)
     {
       *status |= APDU_CARD_PRESENT;
       if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
-       *status |= APDU_CARD_ACTIVE;
+        *status |= APDU_CARD_ACTIVE;
     }
 #ifndef HAVE_W32_SYSTEM
   /* We indicate a useful card if it is not in use by another
@@ -966,15 +1030,14 @@ pcsc_get_status_wrapped (int slot, unsigned int *status)
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     {
       log_error ("pcsc_status failed: %s (0x%lx)\n",
@@ -1056,6 +1119,8 @@ pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
   struct pcsc_io_request_s send_pci;
   pcsc_dword_t recv_len;
 
+  (void)pininfo;
+
   if (!reader_table[slot].atrlen
       && (err = reset_pcsc_reader (slot)))
     return err;
@@ -1135,15 +1200,14 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     {
       log_error ("pcsc_transmit failed: %s (0x%lx)\n",
@@ -1225,7 +1289,7 @@ control_pcsc_direct (int slot, pcsc_dword_t ioctl_code,
   long err;
 
   err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
-                      cntlbuf, len, buffer, *buflen, buflen);
+                      cntlbuf, len, buffer, buflen? *buflen:0, buflen);
   if (err)
     {
       log_error ("pcsc_control failed: %s (0x%lx)\n",
@@ -1276,15 +1340,14 @@ control_pcsc_wrapped (int slot, pcsc_dword_t ioctl_code,
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf32_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     {
       log_error ("pcsc_control failed: %s (0x%lx)\n",
@@ -1294,14 +1357,18 @@ control_pcsc_wrapped (int slot, pcsc_dword_t ioctl_code,
 
   full_len = len;
 
-  n = *buflen < len ? *buflen : len;
+  if (buflen)
+    n = *buflen < len ? *buflen : len;
+  else
+    n = 0;
   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
     {
       log_error ("error receiving PC/SC CONTROL response: %s\n",
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  *buflen = n;
+  if (buflen)
+    *buflen = n;
 
   full_len -= len;
   if (full_len)
@@ -1366,9 +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;
-  reader_table[slot].used = 0;
   return 0;
 }
 #endif /*!NEED_PCSC_WRAPPER*/
@@ -1414,15 +1478,14 @@ close_pcsc_reader_wrapped (int slot)
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf32_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
       goto command_failed;
     }
   len -= 4; /* Already read the error code. */
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     log_error ("pcsc_close failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
@@ -1468,7 +1531,6 @@ connect_pcsc_card (int slot)
     return SW_HOST_ALREADY_CONNECTED;
 
   reader_table[slot].atrlen = 0;
-  reader_table[slot].last_status = 0;
   reader_table[slot].is_t0 = 0;
 
   err = pcsc_connect (reader_table[slot].pcsc.context,
@@ -1488,7 +1550,9 @@ connect_pcsc_card (int slot)
     {
       char reader[250];
       pcsc_dword_t readerlen, atrlen;
-      long card_state, card_protocol;
+      pcsc_dword_t card_state, card_protocol;
+
+      pcsc_vendor_specific_init (slot);
 
       atrlen = DIM (reader_table[0].atr);
       readerlen = sizeof reader -1 ;
@@ -1498,17 +1562,12 @@ connect_pcsc_card (int slot)
                          reader_table[slot].atr, &atrlen);
       if (err)
         log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
-                   pcsc_error_string (err), err, readerlen);
+                   pcsc_error_string (err), err, (long unsigned int)readerlen);
       else
         {
           if (atrlen > DIM (reader_table[0].atr))
             log_bug ("ATR returned by pcsc_status is too large\n");
           reader_table[slot].atrlen = atrlen;
-          /* If we got to here we know that a card is present
-             and usable.  Remember this.  */
-          reader_table[slot].last_status = (   APDU_CARD_USABLE
-                                             | APDU_CARD_PRESENT
-                                             | APDU_CARD_ACTIVE);
           reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
         }
     }
@@ -1602,7 +1661,7 @@ reset_pcsc_reader_wrapped (int slot)
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf32_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
@@ -1616,8 +1675,7 @@ reset_pcsc_reader_wrapped (int slot)
       sw = SW_HOST_GENERAL_ERROR;
       goto command_failed;
     }
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     {
       log_error ("PC/SC RESET failed: %s (0x%lx)\n",
@@ -1676,6 +1734,159 @@ reset_pcsc_reader (int slot)
 }
 
 
+/* Examine reader specific parameters and initialize.  This is mostly
+   for pinpad input.  Called at opening the connection to the reader.  */
+static int
+pcsc_vendor_specific_init (int slot)
+{
+  unsigned char buf[256];
+  pcsc_dword_t len;
+  int sw;
+  int vendor = 0;
+  int product = 0;
+  pcsc_dword_t get_tlv_ioctl = (pcsc_dword_t)-1;
+  unsigned char *p;
+
+  len = sizeof (buf);
+  sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
+  if (sw)
+    {
+      log_error ("pcsc_vendor_specific_init: GET_FEATURE_REQUEST failed: %d\n",
+                 sw);
+      return SW_NOT_SUPPORTED;
+    }
+  else
+    {
+      p = buf;
+      while (p < buf + len)
+        {
+          unsigned char code = *p++;
+          int l = *p++;
+          unsigned int v = 0;
+
+          if (l == 1)
+            v = p[0];
+          else if (l == 2)
+            v = buf16_to_uint (p);
+          else if (l == 4)
+            v = buf32_to_uint (p);
+
+          if (code == FEATURE_VERIFY_PIN_DIRECT)
+            reader_table[slot].pcsc.verify_ioctl = v;
+          else if (code == FEATURE_MODIFY_PIN_DIRECT)
+            reader_table[slot].pcsc.modify_ioctl = v;
+          else if (code == FEATURE_GET_TLV_PROPERTIES)
+            get_tlv_ioctl = v;
+
+          if (DBG_CARD_IO)
+            log_debug ("feature: code=%02X, len=%d, v=%02X\n", code, l, v);
+
+          p += l;
+        }
+    }
+
+  if (get_tlv_ioctl == (pcsc_dword_t)-1)
+    {
+      /*
+       * For system which doesn't support GET_TLV_PROPERTIES,
+       * we put some heuristics here.
+       */
+      if (reader_table[slot].rdrname)
+        {
+          if (strstr (reader_table[slot].rdrname, "SPRx32"))
+            {
+              reader_table[slot].is_spr532 = 1;
+              reader_table[slot].pinpad_varlen_supported = 1;
+            }
+          else if (strstr (reader_table[slot].rdrname, "ST-2xxx"))
+            {
+              reader_table[slot].pcsc.pinmax = 15;
+              reader_table[slot].pinpad_varlen_supported = 1;
+            }
+          else if (strstr (reader_table[slot].rdrname, "cyberJack")
+                   || strstr (reader_table[slot].rdrname, "DIGIPASS")
+                   || strstr (reader_table[slot].rdrname, "Gnuk")
+                   || strstr (reader_table[slot].rdrname, "KAAN"))
+            reader_table[slot].pinpad_varlen_supported = 1;
+        }
+
+      return 0;
+    }
+
+  len = sizeof (buf);
+  sw = control_pcsc (slot, get_tlv_ioctl, NULL, 0, buf, &len);
+  if (sw)
+    {
+      log_error ("pcsc_vendor_specific_init: GET_TLV_IOCTL failed: %d\n", sw);
+      return SW_NOT_SUPPORTED;
+    }
+
+  p = buf;
+  while (p < buf + len)
+    {
+      unsigned char tag = *p++;
+      int l = *p++;
+      unsigned int v = 0;
+
+      /* Umm... here is little endian, while the encoding above is big.  */
+      if (l == 1)
+        v = p[0];
+      else if (l == 2)
+        v = (((unsigned int)p[1] << 8) | p[0]);
+      else if (l == 4)
+        v = (((unsigned int)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+
+      if (tag == PCSCv2_PART10_PROPERTY_bMinPINSize)
+        reader_table[slot].pcsc.pinmin = v;
+      else if (tag == PCSCv2_PART10_PROPERTY_bMaxPINSize)
+        reader_table[slot].pcsc.pinmax = v;
+      else if (tag == PCSCv2_PART10_PROPERTY_wIdVendor)
+        vendor = v;
+      else if (tag == PCSCv2_PART10_PROPERTY_wIdProduct)
+        product = v;
+
+      if (DBG_CARD_IO)
+        log_debug ("TLV properties: tag=%02X, len=%d, v=%08X\n", tag, l, v);
+
+      p += l;
+    }
+
+  if (vendor == VENDOR_VEGA && product == VEGA_ALPHA)
+    {
+      /*
+       * Please read the comment of ccid_vendor_specific_init in
+       * ccid-driver.c.
+       */
+      const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
+      sw = control_pcsc (slot, CM_IOCTL_VENDOR_IFD_EXCHANGE,
+                         cmd, sizeof (cmd), NULL, 0);
+      if (sw)
+        return SW_NOT_SUPPORTED;
+    }
+  else if (vendor == VENDOR_SCM && product == SCM_SPR532) /* SCM SPR532 */
+    {
+      reader_table[slot].is_spr532 = 1;
+      reader_table[slot].pinpad_varlen_supported = 1;
+    }
+  else if (vendor == 0x046a)
+    {
+      /* Cherry ST-2xxx (product == 0x003e) supports TPDU level
+       * exchange.  Other products which only support short APDU level
+       * exchange only work with shorter keys like RSA 1024.
+       */
+      reader_table[slot].pcsc.pinmax = 15;
+      reader_table[slot].pinpad_varlen_supported = 1;
+    }
+  else if (vendor == 0x0c4b /* Tested with Reiner cyberJack GO */
+           || vendor == 0x1a44 /* Tested with Vasco DIGIPASS 920 */
+           || vendor == 0x234b /* Tested with FSIJ Gnuk Token */
+           || vendor == 0x0d46 /* Tested with KAAN Advanced??? */)
+    reader_table[slot].pinpad_varlen_supported = 1;
+
+  return 0;
+}
+
+
 /* Open the PC/SC reader without using the wrapper.  Returns -1 on
    error or a slot number for the reader.  */
 #ifndef NEED_PCSC_WRAPPER
@@ -1685,7 +1896,8 @@ open_pcsc_reader_direct (const char *portstr)
   long err;
   int slot;
   char *list = NULL;
-  pcsc_dword_t nreader, listlen;
+  char *rdrname = NULL;
+  pcsc_dword_t nreader;
   char *p;
 
   slot = new_reader_slot ();
@@ -1701,6 +1913,7 @@ open_pcsc_reader_direct (const char *portstr)
       log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
                  pcsc_error_string (err), err);
       reader_table[slot].used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1714,6 +1927,7 @@ open_pcsc_reader_direct (const char *portstr)
           log_error ("error allocating memory for reader list\n");
           pcsc_release_context (reader_table[slot].pcsc.context);
           reader_table[slot].used = 0;
+          unlock_slot (slot);
           return -1 /*SW_HOST_OUT_OF_CORE*/;
         }
       err = pcsc_list_readers (reader_table[slot].pcsc.context,
@@ -1726,41 +1940,44 @@ open_pcsc_reader_direct (const char *portstr)
       pcsc_release_context (reader_table[slot].pcsc.context);
       reader_table[slot].used = 0;
       xfree (list);
+      unlock_slot (slot);
       return -1;
     }
 
-  listlen = nreader;
   p = list;
   while (nreader)
     {
       if (!*p && !p[1])
         break;
-      if (*p)
-        log_info ("detected reader '%s'\n", p);
+      log_info ("detected reader '%s'\n", p);
       if (nreader < (strlen (p)+1))
         {
           log_error ("invalid response from pcsc_list_readers\n");
           break;
         }
+      if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr)))
+        rdrname = p;
       nreader -= strlen (p)+1;
       p += strlen (p) + 1;
     }
 
-  reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+  if (!rdrname)
+    rdrname = list;
+
+  reader_table[slot].rdrname = xtrystrdup (rdrname);
   if (!reader_table[slot].rdrname)
     {
       log_error ("error allocating memory for reader name\n");
       pcsc_release_context (reader_table[slot].pcsc.context);
       reader_table[slot].used = 0;
+      unlock_slot (slot);
       return -1;
     }
-  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
   xfree (list);
   list = NULL;
 
   reader_table[slot].pcsc.card = 0;
   reader_table[slot].atrlen = 0;
-  reader_table[slot].last_status = 0;
 
   reader_table[slot].connect_card = connect_pcsc_card;
   reader_table[slot].disconnect_card = disconnect_pcsc_card;
@@ -1771,6 +1988,7 @@ open_pcsc_reader_direct (const char *portstr)
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
   dump_reader_status (slot);
+  unlock_slot (slot);
   return slot;
 }
 #endif /*!NEED_PCSC_WRAPPER */
@@ -1793,7 +2011,7 @@ open_pcsc_reader_wrapped (const char *portstr)
   int err;
   unsigned int dummy_status;
 
-  /* Note that we use the constant and not the fucntion because this
+  /* Note that we use the constant and not the function because this
      code won't be be used under Windows.  */
   const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";
 
@@ -1817,6 +2035,7 @@ open_pcsc_reader_wrapped (const char *portstr)
     {
       log_error ("error creating a pipe: %s\n", strerror (errno));
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
   if (pipe (wp) == -1)
@@ -1825,6 +2044,7 @@ open_pcsc_reader_wrapped (const char *portstr)
       close (rp[0]);
       close (rp[1]);
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1837,6 +2057,7 @@ open_pcsc_reader_wrapped (const char *portstr)
       close (wp[0]);
       close (wp[1]);
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
   slotp->pcsc.pid = pid;
@@ -1918,7 +2139,7 @@ open_pcsc_reader_wrapped (const char *portstr)
                  i? strerror (errno) : "premature EOF");
       goto command_failed;
     }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  len = buf32_to_size_t (msgbuf+1);
   if (msgbuf[0] != 0x81 || len < 4)
     {
       log_error ("invalid response header from PC/SC received\n");
@@ -1931,17 +2152,13 @@ open_pcsc_reader_wrapped (const char *portstr)
                  (unsigned long)len);
       goto command_failed;
     }
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
-
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
   if (err)
     {
       log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
       goto command_failed;
     }
 
-  slotp->last_status = 0;
-
   /* The open request may return a zero for the ATR length to
      indicate that no card is present.  */
   n = len;
@@ -1953,11 +2170,6 @@ open_pcsc_reader_wrapped (const char *portstr)
                      i? strerror (errno) : "premature EOF");
           goto command_failed;
         }
-      /* If we got to here we know that a card is present
-         and usable.  Thus remember this.  */
-      slotp->last_status = (  APDU_CARD_USABLE
-                            | APDU_CARD_PRESENT
-                            | APDU_CARD_ACTIVE);
     }
   slotp->atrlen = len;
 
@@ -1967,10 +2179,13 @@ open_pcsc_reader_wrapped (const char *portstr)
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
   reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
 
+  pcsc_vendor_specific_init (slot);
+
   /* Read the status so that IS_T0 will be set. */
   pcsc_get_status (slot, &dummy_status);
 
   dump_reader_status (slot);
+  unlock_slot (slot);
   return slot;
 
  command_failed:
@@ -1982,6 +2197,7 @@ open_pcsc_reader_wrapped (const char *portstr)
     kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
+  unlock_slot (slot);
   /* There is no way to return SW. */
   return -1;
 
@@ -2005,68 +2221,36 @@ open_pcsc_reader (const char *portstr)
 static int
 check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
 {
-  unsigned char buf[256];
-  pcsc_dword_t len = 256;
-  int sw;
+  int r;
 
-  /* Hack to identify the SCM SPR532 and SPR332 readers which support
-     variable length PIN input.
-     FIXME: Figure out whether there is a feature attribute for this.
-     Alternatively use the USB ids to detect known readers.  */
-  if (reader_table[slot].rdrname
-      && strstr (reader_table[slot].rdrname, "SPRx32"))
-    {
-      reader_table[slot].is_spr532 = 1;
-      pininfo->fixedlen = 0;
-    }
+  if (reader_table[slot].pcsc.pinmin >= 0)
+    pininfo->minlen = reader_table[slot].pcsc.pinmin;
 
- check_again:
-  if (command == ISO7816_VERIFY)
-    {
-      if (reader_table[slot].pcsc.verify_ioctl == (pcsc_dword_t)-1)
-        return SW_NOT_SUPPORTED;
-      else if (reader_table[slot].pcsc.verify_ioctl != 0)
-        return 0;                       /* Success */
-    }
-  else if (command == ISO7816_CHANGE_REFERENCE_DATA)
-    {
-      if (reader_table[slot].pcsc.modify_ioctl == (pcsc_dword_t)-1)
-        return SW_NOT_SUPPORTED;
-      else if (reader_table[slot].pcsc.modify_ioctl != 0)
-        return 0;                       /* Success */
-    }
-  else
-    return SW_NOT_SUPPORTED;
+  if (reader_table[slot].pcsc.pinmax >= 0)
+    pininfo->maxlen = reader_table[slot].pcsc.pinmax;
 
-  reader_table[slot].pcsc.verify_ioctl = (pcsc_dword_t)-1;
-  reader_table[slot].pcsc.modify_ioctl = (pcsc_dword_t)-1;
+  if (!pininfo->minlen)
+    pininfo->minlen = 1;
+  if (!pininfo->maxlen)
+    pininfo->maxlen = 15;
 
-  sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
-  if (sw)
-    return SW_NOT_SUPPORTED;
+  if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
+      || (command == ISO7816_CHANGE_REFERENCE_DATA
+          && reader_table[slot].pcsc.modify_ioctl != 0))
+    r = 0;                       /* Success */
   else
-    {
-      unsigned char *p = buf;
+    r = SW_NOT_SUPPORTED;
 
-      while (p < buf + len)
-        {
-          unsigned char code = *p++;
+  if (DBG_CARD_IO)
+    log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
+               (unsigned int)command, r);
 
-          p++;                  /* Skip length */
-          if (code == FEATURE_VERIFY_PIN_DIRECT)
-            reader_table[slot].pcsc.verify_ioctl
-              = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-          else if (code == FEATURE_MODIFY_PIN_DIRECT)
-            reader_table[slot].pcsc.modify_ioctl
-              = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-          p += 4;
-        }
-    }
+  if (reader_table[slot].pinpad_varlen_supported)
+    pininfo->fixedlen = 0;
 
-  goto check_again;
+  return r;
 }
 
-
 #define PIN_VERIFY_STRUCTURE_SIZE 24
 static int
 pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
@@ -2075,8 +2259,16 @@ pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
   int sw;
   unsigned char *pin_verify;
   int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
-  unsigned char result[2];
-  pcsc_dword_t resultlen = 2;
+  /*
+   * The result buffer is only expected to have two-byte result on
+   * return.  However, some implementation uses this buffer for lower
+   * layer too and it assumes that there is enough space for lower
+   * layer communication.  Such an implementation fails for TPDU
+   * readers with "insufficient buffer", as it needs header and
+   * trailer.  Six is the number for header + result + trailer (TPDU).
+   */
+  unsigned char result[6];
+  pcsc_dword_t resultlen = 6;
   int no_lc;
 
   if (!reader_table[slot].atrlen
@@ -2086,25 +2278,14 @@ pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
   if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
     return SW_NOT_SUPPORTED;
 
-  if (!pininfo->minlen)
-    pininfo->minlen = 1;
-  if (!pininfo->maxlen)
-    pininfo->maxlen = 15;
-
-  /* Note that the 25 is the maximum value the SPR532 allows.  */
-  if (pininfo->minlen < 1 || pininfo->minlen > 25
-      || pininfo->maxlen < 1 || pininfo->maxlen > 25
-      || pininfo->minlen > pininfo->maxlen)
-    return SW_HOST_INV_VALUE;
-
   pin_verify = xtrymalloc (len);
   if (!pin_verify)
     return SW_HOST_OUT_OF_CORE;
 
   no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
 
-  pin_verify[0] = 0x00; /* bTimerOut */
-  pin_verify[1] = 0x00; /* bTimerOut2 */
+  pin_verify[0] = 0x00; /* bTimeOut */
+  pin_verify[1] = 0x00; /* bTimeOut2 */
   pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
   pin_verify[3] = pininfo->fixedlen; /* bmPINBlockString */
   pin_verify[4] = 0x00; /* bmPINLengthFormat */
@@ -2113,7 +2294,7 @@ pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
   pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */
   if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
     pin_verify[7] |= 0x01; /* Max size reached.  */
-  pin_verify[8] = 0xff; /* bNumberMessage: Default */
+  pin_verify[8] = 0x01; /* bNumberMessage: One message */
   pin_verify[9] =  0x09; /* wLangId: 0x0409: US English */
   pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */
   pin_verify[11] = 0x00; /* bMsgIndex */
@@ -2136,14 +2317,12 @@ pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
 
   if (DBG_CARD_IO)
     log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
-              class, ins, p0, p1, len, pininfo->maxlen);
+               class, ins, p0, p1, len, pininfo->maxlen);
 
   sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
                      pin_verify, len, result, &resultlen);
   xfree (pin_verify);
   if (sw || resultlen < 2)
-    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
-  sw = (result[resultlen-2] << 8) | result[resultlen-1];
     {
       log_error ("control_pcsc failed: %d\n", sw);
       return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
@@ -2163,8 +2342,8 @@ pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
   int sw;
   unsigned char *pin_modify;
   int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
-  unsigned char result[2];
-  pcsc_dword_t resultlen = 2;
+  unsigned char result[6];      /* See the comment at pinpad_verify.  */
+  pcsc_dword_t resultlen = 6;
   int no_lc;
 
   if (!reader_table[slot].atrlen
@@ -2174,25 +2353,14 @@ pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
   if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
     return SW_NOT_SUPPORTED;
 
-  if (!pininfo->minlen)
-    pininfo->minlen = 1;
-  if (!pininfo->maxlen)
-    pininfo->maxlen = 15;
-
-  /* Note that the 25 is the maximum value the SPR532 allows.  */
-  if (pininfo->minlen < 1 || pininfo->minlen > 25
-      || pininfo->maxlen < 1 || pininfo->maxlen > 25
-      || pininfo->minlen > pininfo->maxlen)
-    return SW_HOST_INV_VALUE;
-
   pin_modify = xtrymalloc (len);
   if (!pin_modify)
     return SW_HOST_OUT_OF_CORE;
 
   no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
 
-  pin_modify[0] = 0x00; /* bTimerOut */
-  pin_modify[1] = 0x00; /* bTimerOut2 */
+  pin_modify[0] = 0x00; /* bTimeOut */
+  pin_modify[1] = 0x00; /* bTimeOut2 */
   pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
   pin_modify[3] = pininfo->fixedlen; /* bmPINBlockString */
   pin_modify[4] = 0x00; /* bmPINLengthFormat */
@@ -2210,12 +2378,12 @@ pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[10] = 0x02; /* bEntryValidationCondition: Validation key pressed */
   if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
     pin_modify[10] |= 0x01; /* Max size reached.  */
-  pin_modify[11] = 0xff; /* bNumberMessage: Default */
+  pin_modify[11] = 0x03; /* bNumberMessage: Three messages */
   pin_modify[12] = 0x09; /* wLangId: 0x0409: US English */
   pin_modify[13] = 0x04; /* wLangId: 0x0409: US English */
   pin_modify[14] = 0x00; /* bMsgIndex1 */
-  pin_modify[15] = 0x00; /* bMsgIndex2 */
-  pin_modify[16] = 0x00; /* bMsgIndex3 */
+  pin_modify[15] = 0x01; /* bMsgIndex2 */
+  pin_modify[16] = 0x02; /* bMsgIndex3 */
   pin_modify[17] = 0x00; /* bTeoPrologue[0] */
   pin_modify[18] = 0x00; /* bTeoPrologue[1] */
   pin_modify[19] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
@@ -2235,7 +2403,7 @@ pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
 
   if (DBG_CARD_IO)
     log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
-              class, ins, p0, p1, len, (int)pininfo->maxlen);
+               class, ins, p0, p1, len, (int)pininfo->maxlen);
 
   sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
                      pin_modify, len, result, &resultlen);
@@ -2267,15 +2435,6 @@ static int
 close_ccid_reader (int slot)
 {
   ccid_close_reader (reader_table[slot].ccid.handle);
-  reader_table[slot].used = 0;
-  return 0;
-}
-
-
-static int
-shutdown_ccid_reader (int slot)
-{
-  ccid_shutdown_reader (reader_table[slot].ccid.handle);
   return 0;
 }
 
@@ -2376,13 +2535,13 @@ check_ccid_pinpad (int slot, int command, pininfo_t *pininfo)
 
   apdu[1] = command;
   return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu,
-                                sizeof apdu, pininfo, NULL, 0, NULL);
+                                 sizeof apdu, pininfo, NULL, 0, NULL);
 }
 
 
 static int
 ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
-                      pininfo_t *pininfo)
+                       pininfo_t *pininfo)
 {
   unsigned char apdu[4];
   int err, sw;
@@ -2409,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;
@@ -2420,10 +2579,12 @@ open_ccid_reader (const char *portstr)
     return -1;
   slotp = reader_table + slot;
 
-  err = ccid_open_reader (&slotp->ccid.handle, portstr);
+  err = ccid_open_reader (dl->portstr, dl->idx, dl->ccid_table,
+                          &slotp->ccid.handle, &slotp->rdrname);
   if (err)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -2434,17 +2595,8 @@ open_ccid_reader (const char *portstr)
       slotp->atrlen = 0;
       err = 0;
     }
-  else
-    {
-      /* If we got to here we know that a card is present
-         and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (APDU_CARD_USABLE
-                                        | APDU_CARD_PRESENT
-                                        | APDU_CARD_ACTIVE);
-    }
 
   reader_table[slot].close_reader = close_ccid_reader;
-  reader_table[slot].shutdown_reader = shutdown_ccid_reader;
   reader_table[slot].reset_reader = reset_ccid_reader;
   reader_table[slot].get_status_reader = get_status_ccid;
   reader_table[slot].send_apdu_reader = send_apdu_ccid;
@@ -2458,14 +2610,10 @@ open_ccid_reader (const char *portstr)
   reader_table[slot].is_t0 = 0;
 
   dump_reader_status (slot);
+  unlock_slot (slot);
   return slot;
 }
-
-
-
 #endif /* HAVE_LIBUSB */
-
-
 \f
 #ifdef USE_G10CODE_RAPDU
 /*
@@ -2512,7 +2660,6 @@ static int
 close_rapdu_reader (int slot)
 {
   rapdu_release (reader_table[slot].rapdu.handle);
-  reader_table[slot].used = 0;
   return 0;
 }
 
@@ -2696,6 +2843,7 @@ open_rapdu_reader (int portno,
   if (!slotp->rapdu.handle)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -2750,12 +2898,14 @@ open_rapdu_reader (int portno,
 
   dump_reader_status (slot);
   rapdu_msg_release (msg);
+  unlock_slot (slot);
   return slot;
 
  failure:
   rapdu_msg_release (msg);
   rapdu_release (slotp->rapdu.handle);
   slotp->used = 0;
+  unlock_slot (slot);
   return -1;
 }
 
@@ -2766,60 +2916,84 @@ open_rapdu_reader (int portno,
 /*
        Driver Access
  */
+gpg_error_t
+apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
+{
+  struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
 
+  *l_p = NULL;
+  if (!dl)
+    return gpg_error_from_syserror ();
 
-static int
-lock_slot (int slot)
-{
-#ifdef USE_NPTH
-  int err;
+  dl->portstr = portstr;
+  dl->idx = 0;
 
-  err = npth_mutex_lock (&reader_table[slot].lock);
-  if (err)
+  npth_mutex_lock (&reader_table_lock);
+
+#ifdef HAVE_LIBUSB
+  if (opt.disable_ccid)
     {
-      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
-      return SW_HOST_LOCKING_FAILED;
+      dl->ccid_table = NULL;
+      dl->idx_max = 1;
     }
-#endif /*USE_NPTH*/
-  return 0;
-}
+  else
+    {
+      gpg_error_t err;
 
-static int
-trylock_slot (int slot)
-{
-#ifdef USE_NPTH
-  int err;
+      err = ccid_dev_scan (&dl->idx_max, &dl->ccid_table);
+      if (err)
+        return err;
 
-  err = npth_mutex_trylock (&reader_table[slot].lock);
-  if (err == EBUSY)
-    return SW_HOST_BUSY;
-  else if (err)
-    {
-      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
-      return SW_HOST_LOCKING_FAILED;
+      if (dl->idx_max == 0)
+        {
+          /* 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");
+
+              xfree (dl);
+              npth_mutex_unlock (&reader_table_lock);
+              return gpg_error (GPG_ERR_ENODEV);
+            }
+          else
+            dl->idx_max = 1;
+        }
     }
-#endif /*USE_NPTH*/
+#else
+  dl->ccid_table = NULL;
+  dl->idx_max = 1;
+#endif /* HAVE_LIBUSB */
+
+  *l_p = dl;
   return 0;
 }
 
-static void
-unlock_slot (int slot)
+int
+apdu_dev_list_finish (struct dev_list *dl)
 {
-#ifdef USE_NPTH
-  int err;
+  int all_have_intr_endp;
 
-  err = npth_mutex_unlock (&reader_table[slot].lock);
-  if (err)
-    log_error ("failed to release apdu lock: %s\n", strerror (errno));
-#endif /*USE_NPTH*/
+#ifdef HAVE_LIBUSB
+  if (dl->ccid_table)
+    all_have_intr_endp = ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
+  else
+    all_have_intr_endp = 0;
+#else
+  all_have_intr_endp = 0;
+#endif
+  xfree (dl);
+  npth_mutex_unlock (&reader_table_lock);
+  return all_have_intr_endp;
 }
 
 
 /* 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
+apdu_open_one_reader (const char *portstr)
 {
   static int pcsc_api_loaded, ct_api_loaded;
   int slot;
@@ -2827,49 +3001,6 @@ apdu_open_reader (const char *portstr)
   if (DBG_READER)
     log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
 
-#ifdef HAVE_LIBUSB
-  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 */
-        }
-
-      /* 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 (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 (DBG_READER)
-                log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
-              return -1;
-            }
-    }
-
-#endif /* HAVE_LIBUSB */
-
   if (opt.ctapi_driver && *opt.ctapi_driver)
     {
       int port = portstr? atoi (portstr) : 32768;
@@ -2899,7 +3030,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)
     {
@@ -2993,13 +3123,105 @@ apdu_open_reader (const char *portstr)
   return slot;
 }
 
+int
+apdu_open_reader (struct dev_list *dl)
+{
+  int slot;
+
+#ifdef HAVE_LIBUSB
+  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
+#endif
+    { /* PC/SC readers.  */
+      if (dl->idx == 0)
+        {
+          dl->idx++;
+          slot = apdu_open_one_reader (dl->portstr);
+        }
+      else
+        slot = -1;
+    }
+
+  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
    with remote readers only.  Note that the supplied CLOSEFNC will
    only be called once and the slot will not be valid afther this.
 
-   If PORTSTR is NULL we default to the first availabe port.
+   If PORTSTR is NULL we default to the first available port.
 */
 int
 apdu_open_remote_reader (const char *portstr,
@@ -3056,17 +3278,24 @@ apdu_close_reader (int slot)
   sw = apdu_disconnect (slot);
   if (sw)
     {
+      /*
+       * When the reader/token was removed it might come here.
+       * It should go through to call CLOSE_READER even if we got an error.
+       */
       if (DBG_READER)
-        log_debug ("leave: apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
-      return sw;
+        log_debug ("apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
     }
   if (reader_table[slot].close_reader)
     {
       sw = reader_table[slot].close_reader (slot);
+      reader_table[slot].used = 0;
       if (DBG_READER)
         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");
   return SW_HOST_NOT_SUPPORTED;
@@ -3086,56 +3315,23 @@ apdu_prepare_exit (void)
   if (!sentinel)
     {
       sentinel = 1;
+      npth_mutex_lock (&reader_table_lock);
       for (slot = 0; slot < MAX_READER; slot++)
         if (reader_table[slot].used)
           {
             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);
       sentinel = 0;
     }
 }
 
 
-/* Shutdown a reader; that is basically the same as a close but keeps
-   the handle ready for later use. A apdu_reset_reader or apdu_connect
-   should be used to get it active again. */
-int
-apdu_shutdown_reader (int slot)
-{
-  int sw;
-
-  if (DBG_READER)
-    log_debug ("enter: apdu_shutdown_reader: slot=%d\n", slot);
-
-  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    {
-      if (DBG_READER)
-        log_debug ("leave: apdu_shutdown_reader => SW_HOST_NO_DRIVER\n");
-      return SW_HOST_NO_DRIVER;
-    }
-  sw = apdu_disconnect (slot);
-  if (sw)
-    {
-      if (DBG_READER)
-        log_debug ("leave: apdu_shutdown_reader => 0x%x (apdu_disconnect)\n",
-                   sw);
-      return sw;
-    }
-  if (reader_table[slot].shutdown_reader)
-    {
-      sw = reader_table[slot].shutdown_reader (slot);
-      if (DBG_READER)
-        log_debug ("leave: apdu_shutdown_reader => 0x%x (close_reader)\n", sw);
-      return sw;
-    }
-  if (DBG_READER)
-    log_debug ("leave: apdu_shutdown_reader => SW_HOST_NOT_SUPPORTED\n");
-  return SW_HOST_NOT_SUPPORTED;
-}
-
 /* Enumerate all readers and return information on whether this reader
    is in use.  The caller should start with SLOT set to 0 and
    increment it with each call until an error is returned. */
@@ -3156,8 +3352,8 @@ apdu_enum_reader (int slot, int *used)
 int
 apdu_connect (int slot)
 {
-  int sw;
-  unsigned int status;
+  int sw = 0;
+  unsigned int status = 0;
 
   if (DBG_READER)
     log_debug ("enter: apdu_connect: slot=%d\n", slot);
@@ -3181,15 +3377,15 @@ apdu_connect (int slot)
           unlock_slot (slot);
         }
     }
-  else
-    sw = 0;
 
   /* We need to call apdu_get_status_internal, so that the last-status
      machinery gets setup properly even if a card is inserted while
      scdaemon is fired up and apdu_get_status has not yet been called.
      Without that we would force a reset of the card with the next
      call to apdu_get_status.  */
-  apdu_get_status_internal (slot, 1, 1, &status, NULL);
+  if (!sw)
+    sw = apdu_get_status_internal (slot, 1, 1, &status);
+
   if (sw)
     ;
   else if (!(status & APDU_CARD_PRESENT))
@@ -3285,19 +3481,9 @@ apdu_reset (int slot)
       return sw;
     }
 
-  reader_table[slot].last_status = 0;
   if (reader_table[slot].reset_reader)
     sw = reader_table[slot].reset_reader (slot);
 
-  if (!sw)
-    {
-      /* If we got to here we know that a card is present
-         and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (APDU_CARD_USABLE
-                                        | APDU_CARD_PRESENT
-                                        | APDU_CARD_ACTIVE);
-    }
-
   unlock_slot (slot);
   if (DBG_READER)
     log_debug ("leave: apdu_reset => sw=0x%x\n", sw);
@@ -3355,14 +3541,10 @@ apdu_get_atr (int slot, size_t *atrlen)
                        (bit 3) = card access locked [not yet implemented]
 
    For must applications, testing bit 0 is sufficient.
-
-   CHANGED will receive the value of the counter tracking the number
-   of card insertions.  This value may be used to detect a card
-   change.
 */
 static int
 apdu_get_status_internal (int slot, int hang, int no_atr_reset,
-                          unsigned int *status, unsigned int *changed)
+                          unsigned int *status)
 {
   int sw;
   unsigned int s;
@@ -3380,52 +3562,31 @@ apdu_get_status_internal (int slot, int hang, int no_atr_reset,
 
   if (sw)
     {
-      reader_table[slot].last_status = 0;
-      return sw;
-    }
-
-  /* Keep track of changes.  */
-  if (s != reader_table[slot].last_status
-      || !reader_table[slot].any_status )
-    {
-      reader_table[slot].change_counter++;
-      /* Make sure that the ATR is invalid so that a reset will be
-         triggered by apdu_activate.  */
       if (!no_atr_reset)
         reader_table[slot].atrlen = 0;
+      s = 0;
     }
-  reader_table[slot].any_status = 1;
-  reader_table[slot].last_status = s;
 
   if (status)
     *status = s;
-  if (changed)
-    *changed = reader_table[slot].change_counter;
-  return 0;
+  return sw;
 }
 
 
 /* See above for a description.  */
 int
-apdu_get_status (int slot, int hang,
-                 unsigned int *status, unsigned int *changed)
+apdu_get_status (int slot, int hang, unsigned int *status)
 {
   int sw;
 
   if (DBG_READER)
     log_debug ("enter: apdu_get_status: slot=%d hang=%d\n", slot, hang);
-  sw = apdu_get_status_internal (slot, hang, 0, status, changed);
+  sw = apdu_get_status_internal (slot, hang, 0, status);
   if (DBG_READER)
     {
-      if (status && changed)
-        log_debug ("leave: apdu_get_status => sw=0x%x status=%u changecnt=%u\n",
-                   sw, *status, *changed);
-      else if (status)
+      if (status)
         log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n",
                    sw, *status);
-      else if (changed)
-        log_debug ("leave: apdu_get_status => sw=0x%x changed=%u\n",
-                   sw, *changed);
       else
         log_debug ("leave: apdu_get_status => sw=0x%x\n", sw);
     }
@@ -3463,7 +3624,7 @@ 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)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
@@ -3476,7 +3637,7 @@ apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
         return sw;
 
       sw = reader_table[slot].pinpad_verify (slot, class, ins, p0, p1,
-                                            pininfo);
+                                             pininfo);
       unlock_slot (slot);
       return sw;
     }
@@ -3487,7 +3648,7 @@ apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
 
 int
 apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
-                   pininfo_t *pininfo)
+                    pininfo_t *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
@@ -3636,8 +3797,9 @@ send_le (int slot, int class, int ins, int p0, int p1,
 
   if (use_extended_length && (le > 256 || le < 0))
     {
-      result_buffer_size = le < 0? 4096 : le;
-      result_buffer = xtrymalloc (result_buffer_size + 10);
+      /* Two more bytes are needed for status bytes.  */
+      result_buffer_size = le < 0? 4096 : (le + 2);
+      result_buffer = xtrymalloc (result_buffer_size);
       if (!result_buffer)
         {
           xfree (apdu_buffer);
@@ -3669,9 +3831,9 @@ send_le (int slot, int class, int ins, int p0, int p1,
           apdu[apdulen++] = ins;
           apdu[apdulen++] = p0;
           apdu[apdulen++] = p1;
-          apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
-          if (lc >= 0)
+          if (lc > 0)
             {
+              apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
               apdu[apdulen++] = ((lc >> 8) & 0xff);
               apdu[apdulen++] = (lc & 0xff);
               memcpy (apdu+apdulen, data, lc);
@@ -3680,6 +3842,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
             }
           if (le != -1)
             {
+              if (lc <= 0)
+                apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
               apdu[apdulen++] = ((le >> 8) & 0xff);
               apdu[apdulen++] = (le & 0xff);
             }
@@ -3891,7 +4055,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
    The return value is the status word or -1 for an invalid SLOT or
    other non card related error.  If RETBUF is not NULL, it will
    receive an allocated buffer with the returned data.  The length of
-   that data will be put into *RETBUFLEN.  The caller is reponsible
+   that data will be put into *RETBUFLEN.  The caller is responsible
    for releasing the buffer even in case of errors.  */
 int
 apdu_send_le(int slot, int extended_mode,
@@ -3914,7 +4078,7 @@ apdu_send_le(int slot, int extended_mode,
    return value is the status word or -1 for an invalid SLOT or other
    non card related error.  If RETBUF is not NULL, it will receive an
    allocated buffer with the returned data.  The length of that data
-   will be put into *RETBUFLEN.  The caller is reponsible for
+   will be put into *RETBUFLEN.  The caller is responsible for
    releasing the buffer even in case of errors.  */
 int
 apdu_send (int slot, int extended_mode,
@@ -4167,3 +4331,35 @@ apdu_send_direct (int slot, size_t extended_length,
 
   return 0;
 }
+
+
+const char *
+apdu_get_reader_name (int slot)
+{
+  return reader_table[slot].rdrname;
+}
+
+gpg_error_t
+apdu_init (void)
+{
+#ifdef USE_NPTH
+  gpg_error_t err;
+  int i;
+
+  if (npth_mutex_init (&reader_table_lock, NULL))
+    goto leave;
+
+  for (i = 0; i < MAX_READER; i++)
+    if (npth_mutex_init (&reader_table[i].lock, NULL))
+      goto leave;
+
+  /* All done well.  */
+  return 0;
+
+ leave:
+  err = gpg_error_from_syserror ();
+  log_error ("apdu: error initializing mutex: %s\n", gpg_strerror (err));
+  return err;
+#endif /*USE_NPTH*/
+  return 0;
+}