gpg: Print ownertrust in TOFU+PGP trust model.
[gnupg.git] / scd / apdu.c
index b12b7e9..268a2c6 100644 (file)
@@ -1,5 +1,6 @@
 /* apdu.c - ISO 7816 APDU functions and low level I/O
- * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009, 2010,
+ *               2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * $Id$
  */
 
 /* NOTE: This module is also used by other software, thus the use of
-   the macro USE_GNU_PTH is mandatory.  For GnuPG this macro is
+   the macro USE_NPTH is mandatory.  For GnuPG this macro is
    guaranteed to be defined true. */
 
 #include <config.h>
 #include <string.h>
 #include <assert.h>
 #include <signal.h>
-#ifdef USE_GNU_PTH
+#ifdef USE_NPTH
 # include <unistd.h>
 # include <fcntl.h>
-# include <pth.h>
+# include <npth.h>
 #endif
 
 
 #include "memory.h"
 #include "util.h"
 #include "i18n.h"
+#include "dynload.h"
 #include "cardglue.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #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"
 
-
 /* Due to conflicting use of threading libraries we usually can't link
-   against libpcsclite.   Instead we use a wrapper program.  */
-#ifdef USE_GNU_PTH
+   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
 #define DLSTDCALL
 #endif
 
-
-/* Helper to pass parameters related to keypad based operations. */
-struct pininfo_s
-{
-  int mode;
-  int minlen;
-  int maxlen;
-  int padlen;
-};
+#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
+typedef unsigned int pcsc_dword_t;
+#else
+typedef unsigned long pcsc_dword_t;
+#endif
 
 /* A structure to collect information pertaining to one reader
    slot. */
@@ -106,17 +105,24 @@ struct reader_table_s {
   int (*reset_reader)(int);
   int (*get_status_reader)(int, unsigned int *);
   int (*send_apdu_reader)(int,unsigned char *,size_t,
-                          unsigned char *, size_t *, struct pininfo_s *);
-  int (*check_keypad)(int, int, int, int, int, int);
+                          unsigned char *, size_t *, pininfo_t *);
+  int (*check_pinpad)(int, int, pininfo_t *);
   void (*dump_status_reader)(int);
+  int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
+  int (*pinpad_verify)(int, int, int, int, int, pininfo_t *);
+  int (*pinpad_modify)(int, int, int, int, int, pininfo_t *);
 
   struct {
     ccid_driver_t handle;
   } ccid;
   struct {
-    unsigned long context;
-    unsigned long card;
-    unsigned long protocol;
+    long context;
+    long card;
+    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;
@@ -133,14 +139,18 @@ struct reader_table_s {
   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_GNU_PTH
+#ifdef USE_NPTH
   int lock_initialized;
-  pth_mutex_t lock;
+  npth_mutex_t lock;
 #endif
 };
 typedef struct reader_table_s *reader_table_t;
@@ -165,7 +175,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 
 #define PCSC_PROTOCOL_T0     1
 #define PCSC_PROTOCOL_T1     2
-#define PCSC_PROTOCOL_RAW    4
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_PROTOCOL_RAW   0x00010000  /* The active protocol.  */
+#else
+# define PCSC_PROTOCOL_RAW   4
+#endif
 
 #define PCSC_SHARE_EXCLUSIVE 1
 #define PCSC_SHARE_SHARED    2
@@ -176,13 +190,23 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_UNPOWER_CARD    2
 #define PCSC_EJECT_CARD      3
 
-#define PCSC_UNKNOWN    0x0001
-#define PCSC_ABSENT     0x0002  /* Card is absent.  */
-#define PCSC_PRESENT    0x0004  /* Card is present.  */
-#define PCSC_SWALLOWED  0x0008  /* Card is present and electrical connected. */
-#define PCSC_POWERED    0x0010  /* Card is powered.  */
-#define PCSC_NEGOTIABLE 0x0020  /* Card is awaiting PTS.  */
-#define PCSC_SPECIFIC   0x0040  /* Card is ready for use.  */
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_UNKNOWN    0x0000  /* The driver is not aware of the status.  */
+# define PCSC_ABSENT     0x0001  /* Card is absent.  */
+# define PCSC_PRESENT    0x0002  /* Card is present.  */
+# define PCSC_SWALLOWED  0x0003  /* Card is present and electrical connected. */
+# define PCSC_POWERED    0x0004  /* Card is powered.  */
+# define PCSC_NEGOTIABLE 0x0005  /* Card is awaiting PTS.  */
+# define PCSC_SPECIFIC   0x0006  /* Card is ready for use.  */
+#else
+# define PCSC_UNKNOWN    0x0001
+# define PCSC_ABSENT     0x0002  /* Card is absent.  */
+# define PCSC_PRESENT    0x0004  /* Card is present.  */
+# define PCSC_SWALLOWED  0x0008  /* Card is present and electrical connected. */
+# define PCSC_POWERED    0x0010  /* Card is powered.  */
+# define PCSC_NEGOTIABLE 0x0020  /* Card is awaiting PTS.  */
+# define PCSC_SPECIFIC   0x0040  /* Card is ready for use.  */
+#endif
 
 #define PCSC_STATE_UNAWARE     0x0000  /* Want status.  */
 #define PCSC_STATE_IGNORE      0x0001  /* Ignore this reader.  */
@@ -195,6 +219,9 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_STATE_EXCLUSIVE   0x0080  /* Exclusive Mode.  */
 #define PCSC_STATE_INUSE       0x0100  /* Shared mode.  */
 #define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_STATE_UNPOWERED  0x0400  /* Card not powerred up.  */
+#endif
 
 /* Some PC/SC error codes.  */
 #define PCSC_E_CANCELLED               0x80100002
@@ -216,8 +243,35 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_E_SYSTEM_CANCELLED        0x80100012
 #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
+#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
    shifts bit 31 will get sign extended.  We use this mask to fix
    it. */
@@ -232,68 +286,89 @@ struct pcsc_io_request_s
 
 typedef struct pcsc_io_request_s *pcsc_io_request_t;
 
+#ifdef __APPLE__
+#pragma pack(1)
+#endif
+
 struct pcsc_readerstate_s
 {
   const char *reader;
   void *user_data;
-  unsigned long current_state;
-  unsigned long event_state;
-  unsigned long atrlen;
+  pcsc_dword_t current_state;
+  pcsc_dword_t event_state;
+  pcsc_dword_t atrlen;
   unsigned char atr[33];
 };
 
+#ifdef __APPLE__
+#pragma pack()
+#endif
+
 typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
 
-long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
+long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope,
                                            const void *reserved1,
                                            const void *reserved2,
-                                           unsigned long *r_context);
-long (* DLSTDCALL pcsc_release_context) (unsigned long context);
-long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
+                                           long *r_context);
+long (* DLSTDCALL pcsc_release_context) (long context);
+long (* DLSTDCALL pcsc_list_readers) (long context,
                                       const char *groups,
-                                      char *readers, unsigned long*readerslen);
-long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
-                                           unsigned long timeout,
+                                      char *readers, pcsc_dword_t*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (long context,
+                                           pcsc_dword_t timeout,
                                            pcsc_readerstate_t readerstates,
-                                           unsigned long nreaderstates);
-long (* DLSTDCALL pcsc_connect) (unsigned long context,
+                                           pcsc_dword_t nreaderstates);
+long (* DLSTDCALL pcsc_connect) (long context,
                                  const char *reader,
-                                 unsigned long share_mode,
-                                 unsigned long preferred_protocols,
-                                 unsigned long *r_card,
-                                 unsigned long *r_active_protocol);
-long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
-                                   unsigned long share_mode,
-                                   unsigned long preferred_protocols,
-                                   unsigned long initialization,
-                                   unsigned long *r_active_protocol);
-long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
-                                    unsigned long disposition);
-long (* DLSTDCALL pcsc_status) (unsigned long card,
-                                char *reader, unsigned long *readerlen,
-                                unsigned long *r_state,
-                                unsigned long *r_protocol,
-                                unsigned char *atr, unsigned long *atrlen);
-long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
-long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
-                                         unsigned long disposition);
-long (* DLSTDCALL pcsc_transmit) (unsigned long card,
+                                 pcsc_dword_t share_mode,
+                                 pcsc_dword_t preferred_protocols,
+                                 long *r_card,
+                                 pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (long card,
+                                   pcsc_dword_t share_mode,
+                                   pcsc_dword_t preferred_protocols,
+                                   pcsc_dword_t initialization,
+                                   pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_disconnect) (long card,
+                                    pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_status) (long card,
+                                char *reader, pcsc_dword_t *readerlen,
+                                pcsc_dword_t *r_state,
+                                pcsc_dword_t *r_protocol,
+                                unsigned char *atr, pcsc_dword_t *atrlen);
+long (* DLSTDCALL pcsc_begin_transaction) (long card);
+long (* DLSTDCALL pcsc_end_transaction) (long card,
+                                         pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_transmit) (long card,
                                   const pcsc_io_request_t send_pci,
                                   const unsigned char *send_buffer,
-                                  unsigned long send_len,
+                                  pcsc_dword_t send_len,
                                   pcsc_io_request_t recv_pci,
                                   unsigned char *recv_buffer,
-                                  unsigned long *recv_len);
-long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
-                                     unsigned long timeout);
+                                  pcsc_dword_t *recv_len);
+long (* DLSTDCALL pcsc_set_timeout) (long context,
+                                     pcsc_dword_t timeout);
+long (* DLSTDCALL pcsc_control) (long card,
+                                 pcsc_dword_t control_code,
+                                 const void *send_buffer,
+                                 pcsc_dword_t send_len,
+                                 void *recv_buffer,
+                                 pcsc_dword_t recv_len,
+                                 pcsc_dword_t *bytes_returned);
 
 
 /*  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);
+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);
+static int pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+                               pininfo_t *pininfo);
 
 
 \f
@@ -301,13 +376,61 @@ static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
       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++)
     {
@@ -319,17 +442,23 @@ new_reader_slot (void)
       log_error ("new_reader_slot: out of slots\n");
       return -1;
     }
-#ifdef USE_GNU_PTH
+#ifdef USE_NPTH
   if (!reader_table[reader].lock_initialized)
     {
-      if (!pth_mutex_init (&reader_table[reader].lock))
+      err = npth_mutex_init (&reader_table[reader].lock, NULL);
+      if (err)
         {
-          log_error ("error initializing mutex: %s\n", strerror (errno));
+          log_error ("error initializing mutex: %s\n", strerror (err));
           return -1;
         }
       reader_table[reader].lock_initialized = 1;
     }
-#endif /*USE_GNU_PTH*/
+#endif /*USE_NPTH*/
+  if (lock_slot (reader))
+    {
+      log_error ("error locking mutex: %s\n", strerror (errno));
+      return -1;
+    }
   reader_table[reader].connect_card = NULL;
   reader_table[reader].disconnect_card = NULL;
   reader_table[reader].close_reader = NULL;
@@ -337,18 +466,27 @@ new_reader_slot (void)
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
-  reader_table[reader].check_keypad = NULL;
+  reader_table[reader].check_pinpad = check_pcsc_pinpad;
   reader_table[reader].dump_status_reader = NULL;
+  reader_table[reader].set_progress_cb = NULL;
+  reader_table[reader].pinpad_verify = pcsc_pinpad_verify;
+  reader_table[reader].pinpad_modify = pcsc_pinpad_modify;
 
-  reader_table[reader].used = 1;  
+  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;
   reader_table[reader].pcsc.pid = (pid_t)(-1);
 #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;
 }
@@ -391,8 +529,8 @@ host_sw_string (long err)
     case SW_HOST_GENERAL_ERROR: return "general error";
     case SW_HOST_NO_READER: return "no reader";
     case SW_HOST_ABORTED: return "aborted";
-    case SW_HOST_NO_KEYPAD: return "no keypad"; 
-    case SW_HOST_ALREADY_CONNECTED: return "already connected"; 
+    case SW_HOST_NO_PINPAD: return "no pinpad";
+    case SW_HOST_ALREADY_CONNECTED: return "already connected";
     default: return "unknown host status error";
     }
 }
@@ -408,13 +546,18 @@ 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";
     case SW_FILE_NOT_FOUND : return "file not found";
     case SW_RECORD_NOT_FOUND:return "record not found";
     case SW_REF_NOT_FOUND  : return "reference not found";
-    case SW_BAD_P0_P1      : return "bad P0 or P1";
+    case SW_NOT_ENOUGH_MEMORY: return "not enough memory space in the file";
+    case SW_INCONSISTENT_LC: return "Lc inconsistent with TLV structure.";
+    case SW_INCORRECT_P0_P1: return "incorrect parameters P0,P1";
+    case SW_BAD_LC         : return "Lc inconsistent with P0,P1";
+    case SW_BAD_P0_P1      : return "bad P0,P1";
     case SW_INS_NOT_SUP    : return "instruction not supported";
     case SW_CLA_NOT_SUP    : return "class not supported";
     case SW_SUCCESS        : return "success";
@@ -544,7 +687,7 @@ 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;
@@ -555,7 +698,7 @@ ct_get_status (int slot, unsigned int *status)
    set to BUFLEN.  Returns: CT API error code. */
 static int
 ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-              unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+              unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
 {
   int rc;
   unsigned char dad[1], sad[1];
@@ -605,6 +748,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;
     }
 
@@ -620,10 +764,13 @@ open_ct_reader (int port)
   reader_table[reader].reset_reader = reset_ct_reader;
   reader_table[reader].get_status_reader = ct_get_status;
   reader_table[reader].send_apdu_reader = ct_send_apdu;
-  reader_table[reader].check_keypad = NULL;
+  reader_table[reader].check_pinpad = NULL;
   reader_table[reader].dump_status_reader = ct_dump_reader_status;
+  reader_table[reader].pinpad_verify = NULL;
+  reader_table[reader].pinpad_modify = NULL;
 
   dump_reader_status (reader);
+  unlock_slot (reader);
   return reader;
 }
 
@@ -643,8 +790,8 @@ writen (int fd, const void *buf, size_t nbytes)
 
   while (nleft > 0)
     {
-#ifdef USE_GNU_PTH
-      nwritten = pth_write (fd, buf, nleft);
+#ifdef USE_NPTH
+      nwritten = npth_write (fd, buf, nleft);
 #else
       nwritten = write (fd, buf, nleft);
 #endif
@@ -669,8 +816,11 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
 
   while (nleft > 0)
     {
-#ifdef USE_GNU_PTH
-      n = pth_read (fd, buf, nleft);
+#ifdef USE_NPTH
+# ifdef HAVE_W32_SYSTEM
+#  error Cannot use npth_read here because it expects a system HANDLE.
+# endif
+      n = npth_read (fd, buf, nleft);
 #else
       n = read (fd, buf, nleft);
 #endif
@@ -758,13 +908,16 @@ 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;
     case PCSC_W_REMOVED_CARD:        rc = SW_HOST_NO_CARD; break;
 
     case PCSC_E_INVALID_TARGET:
     case PCSC_E_INVALID_VALUE:
-    case PCSC_E_INVALID_HANDLE: 
+    case PCSC_E_INVALID_HANDLE:
     case PCSC_E_INVALID_PARAMETER:
     case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
 
@@ -830,9 +983,11 @@ pcsc_get_status_direct (int slot, unsigned int *status)
 
   *status = 0;
   if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
-    *status |= APDU_CARD_PRESENT;
-  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
-    *status |= APDU_CARD_ACTIVE;
+    {
+      *status |= APDU_CARD_PRESENT;
+      if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+       *status |= APDU_CARD_ACTIVE;
+    }
 #ifndef HAVE_W32_SYSTEM
   /* We indicate a useful card if it is not in use by another
      application.  This is because we only use exclusive access
@@ -896,15 +1051,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",
@@ -956,7 +1110,8 @@ pcsc_get_status_wrapped (int slot, unsigned int *status)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -978,12 +1133,14 @@ pcsc_get_status (int slot, unsigned int *status)
 #ifndef NEED_PCSC_WRAPPER
 static int
 pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
-                       unsigned char *buffer, size_t *buflen, 
-                       struct pininfo_s *pininfo)
+                       unsigned char *buffer, size_t *buflen,
+                       pininfo_t *pininfo)
 {
   long err;
   struct pcsc_io_request_s send_pci;
-  unsigned long recv_len;
+  pcsc_dword_t recv_len;
+
+  (void)pininfo;
 
   if (!reader_table[slot].atrlen
       && (err = reset_pcsc_reader (slot)))
@@ -1014,8 +1171,8 @@ pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
 #ifdef NEED_PCSC_WRAPPER
 static int
 pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
-                        unsigned char *buffer, size_t *buflen, 
-                        struct pininfo_s *pininfo)
+                        unsigned char *buffer, size_t *buflen,
+                        pininfo_t *pininfo)
 {
   long err;
   reader_table_t slotp;
@@ -1064,15 +1221,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",
@@ -1120,7 +1276,8 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -1133,8 +1290,8 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
    BUFLEN.  Returns: A status word. */
 static int
 pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen, 
-                struct pininfo_s *pininfo)
+                unsigned char *buffer, size_t *buflen,
+                pininfo_t *pininfo)
 {
 #ifdef NEED_PCSC_WRAPPER
   return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
@@ -1146,6 +1303,154 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 
 #ifndef NEED_PCSC_WRAPPER
 static int
+control_pcsc_direct (int slot, pcsc_dword_t ioctl_code,
+                     const unsigned char *cntlbuf, size_t len,
+                     unsigned char *buffer, pcsc_dword_t *buflen)
+{
+  long err;
+
+  err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
+                      cntlbuf, len, buffer, buflen? *buflen:0, buflen);
+  if (err)
+    {
+      log_error ("pcsc_control failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return pcsc_error_to_sw (err);
+    }
+
+  return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+control_pcsc_wrapped (int slot, pcsc_dword_t ioctl_code,
+                      const unsigned char *cntlbuf, size_t len,
+                      unsigned char *buffer, pcsc_dword_t *buflen)
+{
+  long err = PCSC_E_NOT_TRANSACTED;
+  reader_table_t slotp;
+  unsigned char msgbuf[9];
+  int i, n;
+  size_t full_len;
+
+  slotp = reader_table + slot;
+
+  msgbuf[0] = 0x06; /* CONTROL command. */
+  msgbuf[1] = ((len + 4) >> 24);
+  msgbuf[2] = ((len + 4) >> 16);
+  msgbuf[3] = ((len + 4) >>  8);
+  msgbuf[4] = ((len + 4)      );
+  msgbuf[5] = (ioctl_code >> 24);
+  msgbuf[6] = (ioctl_code >> 16);
+  msgbuf[7] = (ioctl_code >>  8);
+  msgbuf[8] = (ioctl_code      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 9)
+       || writen (slotp->pcsc.req_fd, cntlbuf, len))
+    {
+      log_error ("error sending PC/SC CONTROL request: %s\n",
+                 strerror (errno));
+      goto command_failed;
+    }
+
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+    {
+      log_error ("error receiving PC/SC CONTROL response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+  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 (buf32_to_ulong (msgbuf+5));
+  if (err)
+    {
+      log_error ("pcsc_control failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return pcsc_error_to_sw (err);
+    }
+
+  full_len = 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;
+    }
+  if (buflen)
+    *buflen = n;
+
+  full_len -= len;
+  if (full_len)
+    {
+      log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+      err = PCSC_E_INVALID_VALUE;
+    }
+  /* We need to read any rest of the response, to keep the
+     protocol running.  */
+  while (full_len)
+    {
+      unsigned char dummybuf[128];
+
+      n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+      if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC CONTROL response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+      full_len -= n;
+    }
+
+  if (!err)
+    return 0;
+
+ command_failed:
+  close (slotp->pcsc.req_fd);
+  close (slotp->pcsc.rsp_fd);
+  slotp->pcsc.req_fd = -1;
+  slotp->pcsc.rsp_fd = -1;
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
+  slotp->pcsc.pid = (pid_t)(-1);
+  slotp->used = 0;
+  return pcsc_error_to_sw (err);
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+
+/* Do some control with the value of IOCTL_CODE to the card inserted
+   to SLOT.  Input buffer is specified by CNTLBUF of length LEN.
+   Output buffer is specified by BUFFER of length *BUFLEN, and the
+   actual output size will be stored at BUFLEN.  Returns: A status word.
+   This routine is used for PIN pad input support.  */
+static int
+control_pcsc (int slot, pcsc_dword_t ioctl_code,
+              const unsigned char *cntlbuf, size_t len,
+              unsigned char *buffer, pcsc_dword_t *buflen)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return control_pcsc_wrapped (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#else
+  return control_pcsc_direct (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#endif
+}
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
 close_pcsc_reader_direct (int slot)
 {
   pcsc_release_context (reader_table[slot].pcsc.context);
@@ -1197,15 +1502,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);
@@ -1218,7 +1522,8 @@ close_pcsc_reader_wrapped (int slot)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return 0;
@@ -1262,15 +1567,17 @@ connect_pcsc_card (int slot)
   if (err)
     {
       reader_table[slot].pcsc.card = 0;
-      if (err != PCSC_E_NO_SMARTCARD) 
+      if (err != PCSC_E_NO_SMARTCARD)
         log_error ("pcsc_connect failed: %s (0x%lx)\n",
                    pcsc_error_string (err), err);
     }
   else
     {
       char reader[250];
-      unsigned long readerlen, atrlen;
-      unsigned long card_state, card_protocol;
+      pcsc_dword_t readerlen, atrlen;
+      pcsc_dword_t card_state, card_protocol;
+
+      pcsc_vendor_specific_init (slot);
 
       atrlen = DIM (reader_table[0].atr);
       readerlen = sizeof reader -1 ;
@@ -1280,7 +1587,7 @@ 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))
@@ -1312,7 +1619,7 @@ disconnect_pcsc_card (int slot)
   assert (slot >= 0 && slot < MAX_READER);
 
   if (!reader_table[slot].pcsc.card)
-    return 0; 
+    return 0;
 
   err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
   if (err)
@@ -1384,7 +1691,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");
@@ -1398,8 +1705,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",
@@ -1436,7 +1742,8 @@ reset_pcsc_reader_wrapped (int slot)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -1457,6 +1764,155 @@ 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 && product == 0x003e) /* Cherry ST-2xxx */
+    {
+      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
@@ -1466,7 +1922,8 @@ open_pcsc_reader_direct (const char *portstr)
   long err;
   int slot;
   char *list = NULL;
-  unsigned long nreader, listlen;
+  char *rdrname = NULL;
+  pcsc_dword_t nreader;
   char *p;
 
   slot = new_reader_slot ();
@@ -1482,6 +1939,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;
     }
 
@@ -1495,6 +1953,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,
@@ -1507,35 +1966,39 @@ 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;
 
@@ -1552,6 +2015,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 */
@@ -1573,14 +2037,14 @@ open_pcsc_reader_wrapped (const char *portstr)
   unsigned char msgbuf[9];
   int err;
   unsigned int dummy_status;
-  int sw = SW_HOST_CARD_IO_ERROR;
-  /* 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";
 
   if (access (wrapperpgm, X_OK))
     {
-      log_error ("can't run PC/SC access module `%s': %s\n",
+      log_error ("can't run PC/SC access module '%s': %s\n",
                  wrapperpgm, strerror (errno));
       return -1;
     }
@@ -1598,6 +2062,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)
@@ -1606,6 +2071,7 @@ open_pcsc_reader_wrapped (const char *portstr)
       close (rp[0]);
       close (rp[1]);
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1618,6 +2084,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;
@@ -1644,7 +2111,7 @@ open_pcsc_reader_wrapped (const char *portstr)
       /* Send stderr to the bit bucket. */
       fd = open ("/dev/null", O_WRONLY);
       if (fd == -1)
-        log_fatal ("can't open `/dev/null': %s", strerror (errno));
+        log_fatal ("can't open '/dev/null': %s", strerror (errno));
       if (fd != 2 && dup2 (fd, 2) == -1)
         log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
 
@@ -1669,8 +2136,8 @@ open_pcsc_reader_wrapped (const char *portstr)
   slotp->pcsc.rsp_fd = rp[0];
 
   /* Wait for the intermediate child to terminate. */
-#ifdef USE_GNU_PTH
-#define WAIT pth_waitpid
+#ifdef USE_NPTH
+#define WAIT npth_waitpid
 #else
 #define WAIT waitpid
 #endif
@@ -1699,7 +2166,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");
@@ -1712,12 +2179,10 @@ 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));
-      sw = pcsc_error_to_sw (err);
       goto command_failed;
     }
 
@@ -1748,10 +2213,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:
@@ -1759,9 +2227,11 @@ open_pcsc_reader_wrapped (const char *portstr)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    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;
 
@@ -1780,42 +2250,245 @@ open_pcsc_reader (const char *portstr)
 }
 
 
-\f
-#ifdef HAVE_LIBUSB
-/*
-     Internal CCID driver interface.
- */
+/* Check whether the reader supports the ISO command code COMMAND
+   on the pinpad.  Return 0 on success.  */
+static int
+check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+  int r;
 
+  if (reader_table[slot].pcsc.pinmin >= 0)
+    pininfo->minlen = reader_table[slot].pcsc.pinmin;
 
-static void
-dump_ccid_reader_status (int slot)
-{
-  log_info ("reader slot %d: using ccid driver\n", slot);
-}
+  if (reader_table[slot].pcsc.pinmax >= 0)
+    pininfo->maxlen = reader_table[slot].pcsc.pinmax;
 
-static int
-close_ccid_reader (int slot)
-{
-  ccid_close_reader (reader_table[slot].ccid.handle);
-  reader_table[slot].used = 0;
-  return 0;
-}
+  if (!pininfo->minlen)
+    pininfo->minlen = 1;
+  if (!pininfo->maxlen)
+    pininfo->maxlen = 15;
 
+  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
+    r = SW_NOT_SUPPORTED;
 
-static int
-shutdown_ccid_reader (int slot)
-{
-  ccid_shutdown_reader (reader_table[slot].ccid.handle);
-  return 0;
-}
+  if (DBG_CARD_IO)
+    log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
+               (unsigned int)command, r);
 
+  if (reader_table[slot].pinpad_varlen_supported)
+    pininfo->fixedlen = 0;
+
+  return r;
+}
 
+#define PIN_VERIFY_STRUCTURE_SIZE 24
 static int
-reset_ccid_reader (int slot)
+pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+                    pininfo_t *pininfo)
 {
-  int err;
-  reader_table_t slotp = reader_table + slot;
-  unsigned char atr[33];
+  int sw;
+  unsigned char *pin_verify;
+  int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
+  /*
+   * 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
+      && (sw = reset_pcsc_reader (slot)))
+    return sw;
+
+  if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+    return SW_NOT_SUPPORTED;
+
+  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; /* 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 */
+  pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
+  pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
+  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] = 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 */
+  pin_verify[12] = 0x00; /* bTeoPrologue[0] */
+  pin_verify[13] = 0x00; /* bTeoPrologue[1] */
+  pin_verify[14] = pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
+  pin_verify[15] = pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+  pin_verify[16] = 0x00; /* ulDataLength */
+  pin_verify[17] = 0x00; /* ulDataLength */
+  pin_verify[18] = 0x00; /* ulDataLength */
+  pin_verify[19] = class; /* abData[0] */
+  pin_verify[20] = ins; /* abData[1] */
+  pin_verify[21] = p0; /* abData[2] */
+  pin_verify[22] = p1; /* abData[3] */
+  pin_verify[23] = pininfo->fixedlen; /* abData[4] */
+  if (pininfo->fixedlen)
+    memset (&pin_verify[24], 0xff, pininfo->fixedlen);
+  else if (no_lc)
+    len--;
+
+  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);
+
+  sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
+                     pin_verify, len, result, &resultlen);
+  xfree (pin_verify);
+  if (sw || resultlen < 2)
+    {
+      log_error ("control_pcsc failed: %d\n", sw);
+      return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  if (DBG_CARD_IO)
+    log_debug (" response: sw=%04X  datalen=%d\n", sw, (unsigned int)resultlen);
+  return sw;
+}
+
+
+#define PIN_MODIFY_STRUCTURE_SIZE 29
+static int
+pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+                    pininfo_t *pininfo)
+{
+  int sw;
+  unsigned char *pin_modify;
+  int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
+  unsigned char result[6];      /* See the comment at pinpad_verify.  */
+  pcsc_dword_t resultlen = 6;
+  int no_lc;
+
+  if (!reader_table[slot].atrlen
+      && (sw = reset_pcsc_reader (slot)))
+    return sw;
+
+  if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+    return SW_NOT_SUPPORTED;
+
+  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; /* 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 */
+  pin_modify[5] = 0x00; /* bInsertionOffsetOld */
+  pin_modify[6] = pininfo->fixedlen; /* bInsertionOffsetNew */
+  pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
+  pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
+  pin_modify[9] = (p0 == 0 ? 0x03 : 0x01);
+                  /* bConfirmPIN
+                   *    0x00: new PIN once
+                   *    0x01: new PIN twice (confirmation)
+                   *    0x02: old PIN and new PIN once
+                   *    0x03: old PIN and new PIN twice (confirmation)
+                   */
+  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] = 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] = 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] */
+  pin_modify[20] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+  pin_modify[21] = 0x00; /* ulDataLength */
+  pin_modify[22] = 0x00; /* ulDataLength */
+  pin_modify[23] = 0x00; /* ulDataLength */
+  pin_modify[24] = class; /* abData[0] */
+  pin_modify[25] = ins; /* abData[1] */
+  pin_modify[26] = p0; /* abData[2] */
+  pin_modify[27] = p1; /* abData[3] */
+  pin_modify[28] = 2 * pininfo->fixedlen; /* abData[4] */
+  if (pininfo->fixedlen)
+    memset (&pin_modify[29], 0xff, 2 * pininfo->fixedlen);
+  else if (no_lc)
+    len--;
+
+  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);
+
+  sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
+                     pin_modify, len, result, &resultlen);
+  xfree (pin_modify);
+  if (sw || resultlen < 2)
+    {
+      log_error ("control_pcsc failed: %d\n", sw);
+      return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  if (DBG_CARD_IO)
+    log_debug (" response: sw=%04X  datalen=%d\n", sw, (unsigned int)resultlen);
+  return sw;
+}
+\f
+#ifdef HAVE_LIBUSB
+/*
+     Internal CCID driver interface.
+ */
+
+
+static void
+dump_ccid_reader_status (int slot)
+{
+  log_info ("reader slot %d: using ccid driver\n", slot);
+}
+
+static int
+close_ccid_reader (int slot)
+{
+  ccid_close_reader (reader_table[slot].ccid.handle);
+  reader_table[slot].rdrname = NULL;
+  reader_table[slot].used = 0;
+  return 0;
+}
+
+
+static int
+shutdown_ccid_reader (int slot)
+{
+  ccid_shutdown_reader (reader_table[slot].ccid.handle);
+  return 0;
+}
+
+
+static int
+reset_ccid_reader (int slot)
+{
+  int err;
+  reader_table_t slotp = reader_table + slot;
+  unsigned char atr[33];
   size_t atrlen;
 
   err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
@@ -1831,6 +2504,15 @@ reset_ccid_reader (int slot)
 
 
 static int
+set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+  reader_table_t slotp = reader_table + slot;
+
+  return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
+}
+
+
+static int
 get_status_ccid (int slot, unsigned int *status)
 {
   int rc;
@@ -1838,7 +2520,7 @@ get_status_ccid (int slot, unsigned int *status)
 
   rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
   if (rc)
-    return -1;
+    return rc;
 
   if (bits == 0)
     *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
@@ -1857,7 +2539,7 @@ get_status_ccid (int slot, unsigned int *status)
 static int
 send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
                 unsigned char *buffer, size_t *buflen,
-                struct pininfo_s *pininfo)
+                pininfo_t *pininfo)
 {
   long err;
   size_t maxbuflen;
@@ -1868,16 +2550,12 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
     return err;
 
   if (DBG_CARD_IO)
-    log_printhex ("  APDU_data:", apdu, apdulen);
+    log_printhex (" raw apdu:", apdu, apdulen);
 
   maxbuflen = *buflen;
   if (pininfo)
     err = ccid_transceive_secure (reader_table[slot].ccid.handle,
-                                  apdu, apdulen,
-                                  pininfo->mode,
-                                  pininfo->minlen,
-                                  pininfo->maxlen,
-                                  pininfo->padlen,
+                                  apdu, apdulen, pininfo,
                                   buffer, maxbuflen, buflen);
   else
     err = ccid_transceive (reader_table[slot].ccid.handle,
@@ -1892,19 +2570,43 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
 
 
 /* Check whether the CCID reader supports the ISO command code COMMAND
-   on the keypad.  Return 0 on success.  For a description of the pin
+   on the pinpad.  Return 0 on success.  For a description of the pin
    parameters, see ccid-driver.c */
 static int
-check_ccid_keypad (int slot, int command, int pin_mode,
-                   int pinlen_min, int pinlen_max, int pin_padlen)
+check_ccid_pinpad (int slot, int command, pininfo_t *pininfo)
 {
   unsigned char apdu[] = { 0, 0, 0, 0x81 };
 
   apdu[1] = command;
-  return ccid_transceive_secure (reader_table[slot].ccid.handle,
-                                 apdu, sizeof apdu,
-                                 pin_mode, pinlen_min, pinlen_max, pin_padlen,
-                                 NULL, 0, NULL);
+  return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu,
+                                sizeof apdu, pininfo, NULL, 0, NULL);
+}
+
+
+static int
+ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
+                      pininfo_t *pininfo)
+{
+  unsigned char apdu[4];
+  int err, sw;
+  unsigned char result[2];
+  size_t resultlen = 2;
+
+  apdu[0] = class;
+  apdu[1] = ins;
+  apdu[2] = p0;
+  apdu[3] = p1;
+  err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+                                apdu, sizeof apdu, pininfo,
+                                result, 2, &resultlen);
+  if (err)
+    return err;
+
+  if (resultlen < 2)
+    return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  return sw;
 }
 
 
@@ -1921,10 +2623,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 (&slotp->ccid.handle, portstr,
+                          (const char **)&slotp->rdrname);
   if (err)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1949,13 +2653,17 @@ open_ccid_reader (const char *portstr)
   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;
-  reader_table[slot].check_keypad = check_ccid_keypad;
+  reader_table[slot].check_pinpad = check_ccid_pinpad;
   reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+  reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+  reader_table[slot].pinpad_verify = ccid_pinpad_operation;
+  reader_table[slot].pinpad_modify = ccid_pinpad_operation;
   /* Our CCID reader code does not support T=0 at all, thus reset the
      flag.  */
   reader_table[slot].is_t0 = 0;
 
   dump_reader_status (slot);
+  unlock_slot (slot);
   return slot;
 }
 
@@ -2110,7 +2818,7 @@ my_rapdu_get_status (int slot, unsigned int *status)
 static int
 my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                     unsigned char *buffer, size_t *buflen,
-                    struct pininfo_s *pininfo)
+                    pininfo_t *pininfo)
 {
   int err;
   reader_table_t slotp;
@@ -2194,6 +2902,7 @@ open_rapdu_reader (int portno,
   if (!slotp->rapdu.handle)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -2241,17 +2950,21 @@ open_rapdu_reader (int portno,
   reader_table[slot].reset_reader = reset_rapdu_reader;
   reader_table[slot].get_status_reader = my_rapdu_get_status;
   reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
-  reader_table[slot].check_keypad = NULL;
+  reader_table[slot].check_pinpad = NULL;
   reader_table[slot].dump_status_reader = NULL;
+  reader_table[slot].pinpad_verify = NULL;
+  reader_table[slot].pinpad_modify = NULL;
 
   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;
 }
 
@@ -2264,44 +2977,6 @@ open_rapdu_reader (int portno,
  */
 
 
-static int
-lock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
-  if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
-    {
-      log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
-      return SW_HOST_LOCKING_FAILED;
-    }
-#endif /*USE_GNU_PTH*/
-  return 0;
-}
-
-static int
-trylock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
-  if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
-    {
-      if (errno == EBUSY)
-        return SW_HOST_BUSY;
-      log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
-      return SW_HOST_LOCKING_FAILED;
-    }
-#endif /*USE_GNU_PTH*/
-  return 0;
-}
-
-static void
-unlock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
-  if (!pth_mutex_release (&reader_table[slot].lock))
-    log_error ("failed to release apdu lock: %s\n", strerror (errno));
-#endif /*USE_GNU_PTH*/
-}
-
-
 /* 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). */
@@ -2309,23 +2984,50 @@ int
 apdu_open_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);
 
 #ifdef HAVE_LIBUSB
   if (!opt.disable_ccid)
     {
-      int slot, i;
+      static int once_available;
+      int i;
       const char *s;
 
       slot = open_ccid_reader (portstr);
       if (slot != -1)
-        return slot; /* got one */
+        {
+          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))
-            return -1;
+            {
+              if (DBG_READER)
+                log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
+              return -1;
+            }
     }
 
 #endif /* HAVE_LIBUSB */
@@ -2369,7 +3071,7 @@ apdu_open_reader (const char *portstr)
       handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
       if (!handle)
         {
-          log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
+          log_error ("apdu_open_reader: failed to open driver '%s': %s\n",
                      opt.pcsc_driver, dlerror ());
           return -1;
         }
@@ -2406,6 +3108,7 @@ apdu_open_reader (const char *portstr)
       pcsc_end_transaction   = dlsym (handle, "SCardEndTransaction");
       pcsc_transmit          = dlsym (handle, "SCardTransmit");
       pcsc_set_timeout       = dlsym (handle, "SCardSetTimeout");
+      pcsc_control           = dlsym (handle, "SCardControl");
 
       if (!pcsc_establish_context
           || !pcsc_release_context
@@ -2418,12 +3121,13 @@ apdu_open_reader (const char *portstr)
           || !pcsc_begin_transaction
           || !pcsc_end_transaction
           || !pcsc_transmit
+          || !pcsc_control
           /* || !pcsc_set_timeout */)
         {
           /* Note that set_timeout is currently not used and also not
              available under Windows. */
           log_error ("apdu_open_reader: invalid PC/SC driver "
-                     "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+                     "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                      !!pcsc_establish_context,
                      !!pcsc_release_context,
                      !!pcsc_list_readers,
@@ -2435,7 +3139,8 @@ apdu_open_reader (const char *portstr)
                      !!pcsc_begin_transaction,
                      !!pcsc_end_transaction,
                      !!pcsc_transmit,
-                     !!pcsc_set_timeout );
+                     !!pcsc_set_timeout,
+                     !!pcsc_control );
           dlclose (handle);
           return -1;
         }
@@ -2443,7 +3148,11 @@ apdu_open_reader (const char *portstr)
       pcsc_api_loaded = 1;
     }
 
-  return open_pcsc_reader (portstr);
+  slot = open_pcsc_reader (portstr);
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
+  return slot;
 }
 
 
@@ -2452,7 +3161,7 @@ apdu_open_reader (const char *portstr)
    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,
@@ -2497,16 +3206,64 @@ apdu_close_reader (int slot)
 {
   int sw;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_close_reader: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
   sw = apdu_disconnect (slot);
   if (sw)
-    return 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 ("apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
+    }
   if (reader_table[slot].close_reader)
-    return reader_table[slot].close_reader (slot);
+    {
+      sw = reader_table[slot].close_reader (slot);
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
+      return sw;
+    }
+  if (DBG_READER)
+    log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
   return SW_HOST_NOT_SUPPORTED;
 }
 
+
+/* Function suitable for a cleanup function to close all reader.  It
+   should not be used if the reader will be opened again.  The reason
+   for implementing this to properly close USB devices so that they
+   will startup the next time without error. */
+void
+apdu_prepare_exit (void)
+{
+  static int sentinel;
+  int slot;
+
+  if (!sentinel)
+    {
+      sentinel = 1;
+      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);
+            reader_table[slot].used = 0;
+          }
+      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. */
@@ -2515,13 +3272,32 @@ 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 )
-    return SW_HOST_NO_DRIVER;
+    {
+      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)
-    return sw;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_shutdown_reader => 0x%x (apdu_disconnect)\n",
+                   sw);
+      return sw;
+    }
   if (reader_table[slot].shutdown_reader)
-    return reader_table[slot].shutdown_reader (slot);
+    {
+      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;
 }
 
@@ -2539,14 +3315,24 @@ apdu_enum_reader (int slot, int *used)
 
 
 /* Connect a card.  This is used to power up the card and make sure
-   that an ATR is available.  */
+   that an ATR is available.  Depending on the reader backend it may
+   return an error for an inactive card or if no card is
+   available.  */
 int
 apdu_connect (int slot)
 {
-  int sw;
+  int sw = 0;
+  unsigned int status = 0;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_connect: slot=%d\n", slot);
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_connect => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
 
   /* Only if the access method provides a connect function we use it.
      If not, we expect that the card has been implicitly connected by
@@ -2560,15 +3346,24 @@ 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, NULL, NULL);
+  if (!sw)
+    sw = apdu_get_status_internal (slot, 1, 1, &status, NULL);
+
+  if (sw)
+    ;
+  else if (!(status & APDU_CARD_PRESENT))
+    sw = SW_HOST_NO_CARD;
+  else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
+    sw = SW_HOST_CARD_INACTIVE;
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
 
   return sw;
 }
@@ -2579,8 +3374,15 @@ apdu_disconnect (int slot)
 {
   int sw;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_disconnect: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
 
   if (reader_table[slot].disconnect_card)
     {
@@ -2593,105 +3395,116 @@ apdu_disconnect (int slot)
     }
   else
     sw = 0;
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
   return sw;
 }
 
 
-
-/* Do a reset for the card in reader at SLOT. */
+/* Set the progress callback of SLOT to CB and its args to CB_ARG.  If
+   CB is NULL the progress callback is removed.  */
 int
-apdu_reset (int slot)
+apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
 {
   int sw;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if ((sw = lock_slot (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 (reader_table[slot].set_progress_cb)
     {
-      /* 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);
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
+          unlock_slot (slot);
+        }
     }
-
-  unlock_slot (slot);
+  else
+    sw = 0;
   return sw;
 }
 
 
-/* Activate a card if it has not yet been done.  This is a kind of
-   reset-if-required.  It is useful to test for presence of a card
-   before issuing a bunch of apdu commands.  It does not wait on a
-   locked card. */
+/* Do a reset for the card in reader at SLOT. */
 int
-apdu_activate (int slot)
+apdu_reset (int slot)
 {
   int sw;
-  unsigned int s;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_reset: slot=%d\n", slot);
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
 
-  if ((sw = trylock_slot (slot)))
-    return sw;
+  if ((sw = lock_slot (slot)))
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
+      return sw;
+    }
 
-  if (reader_table[slot].get_status_reader)
-    sw = reader_table[slot].get_status_reader (slot, &s);
+  reader_table[slot].last_status = 0;
+  if (reader_table[slot].reset_reader)
+    sw = reader_table[slot].reset_reader (slot);
 
   if (!sw)
     {
-      if (!(s & 2))  /* Card not present.  */
-        sw = SW_HOST_NO_CARD;
-      else if ( ((s & 2) && !(s & 4))
-                || !reader_table[slot].atrlen )
-        {
-          /* We don't have an ATR or a card is present though inactive:
-             do a reset now. */
-          if (reader_table[slot].reset_reader)
-            {
-              reader_table[slot].last_status = 0;
-              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);
-                }
-            }
-        }
+      /* 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);
   return sw;
 }
 
 
-
+/* Return the ATR or NULL if none is available.  On success the length
+   of the ATR is stored at ATRLEN.  The caller must free the returned
+   value.  */
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
 {
   unsigned char *buf;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_get_atr: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
+      return NULL;
+    }
   if (!reader_table[slot].atrlen)
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
+      return NULL;
+    }
+
   buf = xtrymalloc (reader_table[slot].atrlen);
   if (!buf)
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
+      return NULL;
+    }
   memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
   *atrlen = reader_table[slot].atrlen;
+  if (DBG_READER)
+    log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
   return buf;
 }
 
@@ -2762,24 +3575,100 @@ int
 apdu_get_status (int slot, int hang,
                  unsigned int *status, unsigned int *changed)
 {
-  return apdu_get_status_internal (slot, hang, 0, status, changed);
+  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);
+  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)
+        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);
+    }
+  return sw;
 }
 
 
 /* Check whether the reader supports the ISO command code COMMAND on
-   the keypad.  Return 0 on success.  For a description of the pin
+   the pinpad.  Return 0 on success.  For a description of the pin
    parameters, see ccid-driver.c */
 int
-apdu_check_keypad (int slot, int command, int pin_mode,
-                   int pinlen_min, int pinlen_max, int pin_padlen)
+apdu_check_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if (opt.enable_pinpad_varlen)
+    pininfo->fixedlen = 0;
+
+  if (reader_table[slot].check_pinpad)
+    {
+      int sw;
+
+      if ((sw = lock_slot (slot)))
+        return sw;
+
+      sw = reader_table[slot].check_pinpad (slot, command, pininfo);
+      unlock_slot (slot);
+      return sw;
+    }
+  else
+    return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+                   pininfo_t *pininfo)
+{
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if (reader_table[slot].pinpad_verify)
+    {
+      int sw;
+
+      if ((sw = lock_slot (slot)))
+        return sw;
+
+      sw = reader_table[slot].pinpad_verify (slot, class, ins, p0, p1,
+                                            pininfo);
+      unlock_slot (slot);
+      return sw;
+    }
+  else
+    return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+                   pininfo_t *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if (reader_table[slot].check_keypad)
-    return reader_table[slot].check_keypad (slot, command,
-                                            pin_mode, pinlen_min, pinlen_max,
-                                            pin_padlen);
+  if (reader_table[slot].pinpad_modify)
+    {
+      int sw;
+
+      if ((sw = lock_slot (slot)))
+        return sw;
+
+      sw = reader_table[slot].pinpad_modify (slot, class, ins, p0, p1,
+                                             pininfo);
+      unlock_slot (slot);
+      return sw;
+    }
   else
     return SW_HOST_NOT_SUPPORTED;
 }
@@ -2789,7 +3678,7 @@ apdu_check_keypad (int slot, int command, int pin_mode,
    function should be called in locked state. */
 static int
 send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-           unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+           unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
@@ -2797,7 +3686,7 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   if (reader_table[slot].send_apdu_reader)
     return reader_table[slot].send_apdu_reader (slot,
                                                 apdu, apdulen,
-                                                buffer, buflen, 
+                                                buffer, buflen,
                                                 pininfo);
   else
     return SW_HOST_NOT_SUPPORTED;
@@ -2805,39 +3694,48 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 
 
 /* Core APDU tranceiver function. Parameters are described at
-   apdu_send_le with the exception of PININFO which indicates keypad
-   related operations if not NULL.  If EXTENDED_MODE is not NULL
+   apdu_send_le with the exception of PININFO which indicates pinpad
+   related operations if not NULL.  If EXTENDED_MODE is not 0
    command chaining or extended length will be used according to these
    values:
        n < 0 := Use command chaining with the data part limited to -n
                 in each chunk.  If -1 is used a default value is used.
+      n == 0 := No extended mode or command chaining.
       n == 1 := Use extended length for input and output without a
                 length limit.
        n > 1 := Use extended length with up to N bytes.
+
 */
 static int
 send_le (int slot, int class, int ins, int p0, int p1,
          int lc, const char *data, int le,
          unsigned char **retbuf, size_t *retbuflen,
-         struct pininfo_s *pininfo, int extended_mode)
+         pininfo_t *pininfo, int extended_mode)
 {
-#define RESULTLEN 258
-  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
-                                         the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+  /* We allocate 8 extra bytes as a safety margin towards a driver bug.  */
+  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+  unsigned char *result_buffer = NULL;
+  size_t result_buffer_size;
+  unsigned char *result;
   size_t resultlen;
-  unsigned char apdu[5+256+1];
+  unsigned char short_apdu_buffer[5+256+1];
+  unsigned char *apdu_buffer = NULL;
+  size_t apdu_buffer_size;
+  unsigned char *apdu;
   size_t apdulen;
   int sw;
   long rc; /* We need a long here due to PC/SC. */
   int did_exact_length_hack = 0;
   int use_chaining = 0;
+  int use_extended_length = 0;
   int lc_chunk;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
   if (DBG_CARD_IO)
-    log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n",
+    log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
                class, ins, p0, p1, lc, le, extended_mode);
 
   if (lc != -1 && (lc > 255 || lc < 0))
@@ -2847,76 +3745,165 @@ send_le (int slot, int class, int ins, int p0, int p1,
       if (!extended_mode)
         return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
       else if (extended_mode > 0)
-        return SW_HOST_NOT_SUPPORTED; /* FIXME.  */
+        use_extended_length = 1;
       else if (extended_mode < 0)
         {
           /* Send APDU using chaining mode.  */
           if (lc > 16384)
             return SW_WRONG_LENGTH;   /* Sanity check.  */
           if ((class&0xf0) != 0)
-            return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0.  */ 
-          use_chaining = extended_mode == -1? 255 : -extended_mode;  
+            return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0.  */
+          use_chaining = extended_mode == -1? 255 : -extended_mode;
           use_chaining &= 0xff;
         }
-      else 
+      else
+        return SW_HOST_INV_VALUE;
+    }
+  else if (lc == -1 && extended_mode > 0)
+    use_extended_length = 1;
+
+  if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
+    {
+      /* Expected Data does not fit into an APDU.  What we do now
+         depends on the EXTENDED_MODE parameter.  Note that a check
+         for command chaining does not make sense because we are
+         looking at Le.  */
+      if (!extended_mode)
+        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
+      else if (use_extended_length)
+        ; /* We are already using extended length.  */
+      else if (extended_mode > 0)
+        use_extended_length = 1;
+      else
         return SW_HOST_INV_VALUE;
     }
-  if (le != -1 && (le > 256 || le < 0))
-    return SW_WRONG_LENGTH;
+
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
-  if ((sw = lock_slot (slot)))
-    return sw;
+  if (use_extended_length)
+    {
+      if (reader_table[slot].is_t0)
+        return SW_HOST_NOT_SUPPORTED;
+
+      /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le.  */
+      apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
+      apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
+      if (!apdu_buffer)
+        return SW_HOST_OUT_OF_CORE;
+      apdu = apdu_buffer;
+    }
+  else
+    {
+      apdu_buffer_size = sizeof short_apdu_buffer;
+      apdu = short_apdu_buffer;
+    }
 
-  do
+  if (use_extended_length && (le > 256 || le < 0))
     {
-      apdulen = 0;
-      apdu[apdulen] = class;
-      if (use_chaining && lc > 255)
+      result_buffer_size = le < 0? 4096 : le;
+      result_buffer = xtrymalloc (result_buffer_size + 10);
+      if (!result_buffer)
         {
-          apdu[apdulen] |= 0x10;
-          assert (use_chaining < 256);
-          lc_chunk = use_chaining;
-          lc -= use_chaining;
+          xfree (apdu_buffer);
+          return SW_HOST_OUT_OF_CORE;
         }
-      else
+      result = result_buffer;
+    }
+  else
+    {
+      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+      result = short_result_buffer;
+    }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+  if ((sw = lock_slot (slot)))
+    {
+      xfree (apdu_buffer);
+      xfree (result_buffer);
+      return sw;
+    }
+
+  do
+    {
+      if (use_extended_length)
         {
           use_chaining = 0;
-          lc_chunk = lc;
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          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);
+              data += lc;
+              apdulen += lc;
+            }
+          if (le != -1)
+            {
+              if (lc <= 0)
+                apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
+              apdu[apdulen++] = ((le >> 8) & 0xff);
+              apdu[apdulen++] = (le & 0xff);
+            }
         }
-      apdulen++;
-      apdu[apdulen++] = ins;
-      apdu[apdulen++] = p0;
-      apdu[apdulen++] = p1;
-      if (lc_chunk != -1)
+      else
         {
-          apdu[apdulen++] = lc_chunk;
-          memcpy (apdu+apdulen, data, lc_chunk);
-          data += lc_chunk;
-          apdulen += lc_chunk;
-          /* T=0 does not allow the use of Lc together with Le; thus
-             disable Le in this case.  */
-          if (reader_table[slot].is_t0)
-            le = -1;
+          apdulen = 0;
+          apdu[apdulen] = class;
+          if (use_chaining && lc > 255)
+            {
+              apdu[apdulen] |= 0x10;
+              assert (use_chaining < 256);
+              lc_chunk = use_chaining;
+              lc -= use_chaining;
+            }
+          else
+            {
+              use_chaining = 0;
+              lc_chunk = lc;
+            }
+          apdulen++;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          if (lc_chunk != -1)
+            {
+              apdu[apdulen++] = lc_chunk;
+              memcpy (apdu+apdulen, data, lc_chunk);
+              data += lc_chunk;
+              apdulen += lc_chunk;
+              /* T=0 does not allow the use of Lc together with Le;
+                 thus disable Le in this case.  */
+              if (reader_table[slot].is_t0)
+                le = -1;
+            }
+          if (le != -1 && !use_chaining)
+            apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
         }
-      if (le != -1)
-        apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
-      /* As safeguard don't pass any garbage from the stack to the driver. */
-      assert (sizeof (apdu) >= apdulen);
-      memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+
     exact_length_hack:
-      resultlen = RESULTLEN;
+      /* As a safeguard don't pass any garbage to the driver.  */
+      assert (apdulen <= apdu_buffer_size);
+      memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+      resultlen = result_buffer_size;
       rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
       if (rc || resultlen < 2)
         {
-          log_error ("apdu_send_simple(%d) failed: %s\n",
-                     slot, apdu_strerror (rc));
+          log_info ("apdu_send_simple(%d) failed: %s\n",
+                    slot, apdu_strerror (rc));
           unlock_slot (slot);
+          xfree (apdu_buffer);
+          xfree (result_buffer);
           return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
         }
       sw = (result[resultlen-2] << 8) | result[resultlen-1];
-      if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
+      if (!use_extended_length
+          && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
         {
           apdu[apdulen-1] = (sw & 0x00ff);
           did_exact_length_hack = 1;
@@ -2925,6 +3912,13 @@ send_le (int slot, int class, int ins, int p0, int p1,
     }
   while (use_chaining && sw == SW_SUCCESS);
 
+  if (apdu_buffer)
+    {
+      xfree (apdu_buffer);
+      apdu_buffer = NULL;
+      apdu_buffer_size = 0;
+    }
+
   /* Store away the returned data but strip the statusword. */
   resultlen -= 2;
   if (DBG_CARD_IO)
@@ -2932,7 +3926,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
       log_debug (" response: sw=%04X  datalen=%d\n",
                  sw, (unsigned int)resultlen);
       if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
-        log_printhex ("     dump: ", result, resultlen);
+        log_printhex ("    dump: ", result, resultlen);
     }
 
   if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
@@ -2943,6 +3937,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           *retbuflen = resultlen;
@@ -2962,6 +3957,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           assert (resultlen < bufsize);
@@ -2976,20 +3972,24 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (DBG_CARD_IO)
             log_debug ("apdu_send_simple(%d): %d more bytes available\n",
                        slot, len);
+          apdu_buffer_size = sizeof short_apdu_buffer;
+          apdu = short_apdu_buffer;
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = len;
-          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
-          resultlen = RESULTLEN;
+          assert (apdulen <= apdu_buffer_size);
+          memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+          resultlen = result_buffer_size;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
                          slot, apdu_strerror (rc));
               unlock_slot (slot);
+              xfree (result_buffer);
               return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -3015,6 +4015,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
                       if (!tmp)
                         {
                           unlock_slot (slot);
+                          xfree (result_buffer);
                           return SW_HOST_OUT_OF_CORE;
                         }
                       p = tmp + (p - *retbuf);
@@ -3041,105 +4042,99 @@ send_le (int slot, int class, int ins, int p0, int p1,
     }
 
   unlock_slot (slot);
+  xfree (result_buffer);
 
   if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return sw;
-#undef RESULTLEN
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
    for LC won't sent this field and the data field; in this case DATA
-   must also be passed as NULL.  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 for releasing the buffer even
-   in case of errors.  */
+   must also be passed as NULL.  If EXTENDED_MODE is not 0 command
+   chaining or extended length will be used; see send_le for details.
+   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 responsible
+   for releasing the buffer even in case of errors.  */
 int
-apdu_send_le(int slot, int class, int ins, int p0, int p1,
+apdu_send_le(int slot, int extended_mode,
+             int class, int ins, int p0, int p1,
              int lc, const char *data, int le,
              unsigned char **retbuf, size_t *retbuflen)
 {
   return send_le (slot, class, ins, p0, p1,
                   lc, data, le,
                   retbuf, retbuflen,
-                  NULL, 0);
+                  NULL, extended_mode);
 }
 
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. 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 for releasing the buffer even in case of
-   errors.  */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  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 responsible for
+   releasing the buffer even in case of errors.  */
 int
-apdu_send (int slot, int class, int ins, int p0, int p1,
+apdu_send (int slot, int extended_mode,
+           int class, int ins, int p0, int p1,
            int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
 {
   return send_le (slot, class, ins, p0, p1, lc, data, 256,
-                  retbuf, retbuflen, NULL, 0);
+                  retbuf, retbuflen, NULL, extended_mode);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. The return value is the status word or -1
-   for an invalid SLOT or other non card related error.  No data will be
-   returned. */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  The
+   return value is the status word or -1 for an invalid SLOT or other
+   non card related error.  No data will be returned.  */
 int
 apdu_send_simple (int slot, int extended_mode,
                   int class, int ins, int p0, int p1,
                   int lc, const char *data)
 {
-  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL, 
+  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
                   extended_mode);
 }
 
 
-/* Same as apdu_send_simple but uses the keypad of the reader. */
-int
-apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
-                     int lc, const char *data,  
-                     int pin_mode,
-                     int pinlen_min, int pinlen_max, int pin_padlen)
-{
-  struct pininfo_s pininfo;
-
-  pininfo.mode = pin_mode;
-  pininfo.minlen = pinlen_min;
-  pininfo.maxlen = pinlen_max;
-  pininfo.padlen = pin_padlen;
-  return send_le (slot, class, ins, p0, p1, lc, data, -1,
-                  NULL, NULL, &pininfo, 0);
-}
-
-
 /* This is a more generic version of the apdu sending routine.  It
    takes an already formatted APDU in APDUDATA or length APDUDATALEN
    and returns with an APDU including the status word.  With
    HANDLE_MORE set to true this function will handle the MORE DATA
    status and return all APDUs concatenated with one status word at
-   the end.  The function does not return a regular status word but 0
-   on success.  If the slot is locked, the function returns
-   immediately with an error.  */
+   the end.  If EXTENDED_LENGTH is != 0 extended lengths are allowed
+   with a max. result data length of EXTENDED_LENGTH bytes.  The
+   function does not return a regular status word but 0 on success.
+   If the slot is locked, the function returns immediately with an
+   error.  */
 int
-apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+apdu_send_direct (int slot, size_t extended_length,
+                  const unsigned char *apdudata, size_t apdudatalen,
                   int handle_more,
                   unsigned char **retbuf, size_t *retbuflen)
 {
-#define RESULTLEN 258
-  unsigned char apdu[5+256+1];
-  size_t apdulen;
-  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
-                                         the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+  unsigned char *result_buffer = NULL;
+  size_t result_buffer_size;
+  unsigned char *result;
   size_t resultlen;
+  unsigned char short_apdu_buffer[5+256+10];
+  unsigned char *apdu_buffer = NULL;
+  unsigned char *apdu;
+  size_t apdulen;
   int sw;
   long rc; /* we need a long here due to PC/SC. */
   int class;
@@ -3147,23 +4142,59 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if ((sw = trylock_slot (slot)))
-    return sw;
+  if (apdudatalen > 65535)
+    return SW_HOST_INV_VALUE;
 
-  /* We simply trunctate a too long APDU.  */
-  if (apdudatalen > sizeof apdu)
-    apdudatalen = sizeof apdu;
+  if (apdudatalen > sizeof short_apdu_buffer - 5)
+    {
+      apdu_buffer = xtrymalloc (apdudatalen + 5);
+      if (!apdu_buffer)
+        return SW_HOST_OUT_OF_CORE;
+      apdu = apdu_buffer;
+    }
+  else
+    {
+      apdu = short_apdu_buffer;
+    }
   apdulen = apdudatalen;
   memcpy (apdu, apdudata, apdudatalen);
   class = apdulen? *apdu : 0;
 
-  resultlen = RESULTLEN;
+  if (extended_length >= 256 && extended_length <= 65536)
+    {
+      result_buffer_size = extended_length;
+      result_buffer = xtrymalloc (result_buffer_size + 10);
+      if (!result_buffer)
+        {
+          xfree (apdu_buffer);
+          return SW_HOST_OUT_OF_CORE;
+        }
+      result = result_buffer;
+    }
+  else
+    {
+      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+      result = short_result_buffer;
+    }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+  if ((sw = trylock_slot (slot)))
+    {
+      xfree (apdu_buffer);
+      xfree (result_buffer);
+      return sw;
+    }
+
+  resultlen = result_buffer_size;
   rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+  xfree (apdu_buffer);
+  apdu_buffer = NULL;
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_direct(%d) failed: %s\n",
                  slot, apdu_strerror (rc));
       unlock_slot (slot);
+      xfree (result_buffer);
       return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
     }
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -3190,6 +4221,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           assert (resultlen < bufsize);
@@ -3204,20 +4236,22 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (DBG_CARD_IO)
             log_debug ("apdu_send_direct(%d): %d more bytes available\n",
                        slot, len);
+          apdu = short_apdu_buffer;
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = len;
-          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
-          resultlen = RESULTLEN;
+          memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen);
+          resultlen = result_buffer_size;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_direct(%d) for get response failed: %s\n",
                          slot, apdu_strerror (rc));
               unlock_slot (slot);
+              xfree (result_buffer);
               return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
@@ -3243,6 +4277,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                       if (!tmp)
                         {
                           unlock_slot (slot);
+                          xfree (result_buffer);
                           return SW_HOST_OUT_OF_CORE;
                         }
                       p = tmp + (p - *retbuf);
@@ -3275,6 +4310,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           if (!*retbuf)
             {
               unlock_slot (slot);
+              xfree (result_buffer);
               return SW_HOST_OUT_OF_CORE;
             }
           *retbuflen = resultlen;
@@ -3283,6 +4319,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     }
 
   unlock_slot (slot);
+  xfree (result_buffer);
 
   /* Append the status word.  Note that we reserved the two extra
      bytes while allocating the buffer.  */
@@ -3296,5 +4333,11 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return 0;
-#undef RESULTLEN
+}
+
+
+const char *
+apdu_get_reader_name (int slot)
+{
+  return reader_table[slot].rdrname;
 }