scd: Fix possible NULL deref in apdu.c
[gnupg.git] / scd / apdu.c
index 5c1c699..53cc4b9 100644 (file)
@@ -1,5 +1,6 @@
 /* apdu.c - ISO 7816 APDU functions and low level I/O
- * Copyright (C) 2003, 2004, 2008 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
 
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
+typedef unsigned int pcsc_dword_t;
 #else
-#define MAX_OPEN_FDS 20
+typedef unsigned long pcsc_dword_t;
 #endif
 
-/* Helper to pass parameters related to keypad based operations. */
-struct pininfo_s
-{
-  int mode;
-  int minlen;
-  int maxlen;
-  int padlen;
-};
-
 /* A structure to collect information pertaining to one reader
    slot. */
 struct reader_table_s {
@@ -103,22 +98,31 @@ struct reader_table_s {
   unsigned short port; /* Port number:  0 = unused, 1 - dev/tty */
 
   /* Function pointers intialized to the various backends.  */
+  int (*connect_card)(int);
+  int (*disconnect_card)(int);
   int (*close_reader)(int);
   int (*shutdown_reader)(int);
   int (*reset_reader)(int);
   int (*get_status_reader)(int, unsigned int *);
   int (*send_apdu_reader)(int,unsigned char *,size_t,
-                          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;
@@ -131,17 +135,22 @@ struct reader_table_s {
   } rapdu;
 #endif /*USE_G10CODE_RAPDU*/
   char *rdrname;     /* Name of the connected reader or NULL if unknown. */
+  int any_status;    /* True if we have seen any status.  */
   int last_status;
   int status;
   int is_t0;         /* True if we know that we are running T=0. */
+  int is_spr532;     /* True if we know that the reader is a SPR532.  */
+  int pinpad_varlen_supported;  /* True if we know that the reader
+                                   supports variable length pinpad
+                                   input.  */
   unsigned char atr[33];
   size_t atrlen;           /* A zero length indicates that the ATR has
                               not yet been read; i.e. the card is not
                               ready for use. */
   unsigned int change_counter;
-#ifdef USE_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;
@@ -166,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
@@ -177,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.  */
@@ -196,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
@@ -217,8 +243,34 @@ 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_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. */
@@ -233,64 +285,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
@@ -298,13 +375,61 @@ static int pcsc_get_status (int slot, unsigned int *status);
       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++)
     {
@@ -316,33 +441,51 @@ 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;
   reader_table[reader].shutdown_reader = NULL;
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
-  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].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;
 }
@@ -385,7 +528,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_NO_PINPAD: return "no pinpad";
+    case SW_HOST_ALREADY_CONNECTED: return "already connected";
     default: return "unknown host status error";
     }
 }
@@ -401,13 +545,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";
@@ -536,10 +685,11 @@ reset_ct_reader (int slot)
 static int
 ct_get_status (int slot, unsigned int *status)
 {
-  *status = 1|2|4;  /* FIXME */
+  (void)slot;
+  /* The status we returned is wrong but we don't care becuase ctAPI
+     is not anymore required.  */
+  *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE;
   return 0;
-
-  return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
@@ -547,12 +697,14 @@ 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];
   unsigned short ctbuflen;
 
+  (void)pininfo;
+
   /* If we don't have an ATR, we need to reset the reader first. */
   if (!reader_table[slot].atrlen
       && (rc = reset_ct_reader (slot)))
@@ -595,6 +747,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;
     }
 
@@ -610,10 +763,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;
 }
 
@@ -633,8 +789,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
@@ -659,8 +815,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
@@ -748,13 +907,14 @@ 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_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;
 
@@ -767,178 +927,89 @@ pcsc_error_to_sw (long ec)
 static void
 dump_pcsc_reader_status (int slot)
 {
-  log_info ("reader slot %d: active protocol:", slot);
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
-    log_printf (" T0");
-  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-    log_printf (" T1");
-  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
-    log_printf (" raw");
-  log_printf ("\n");
+  if (reader_table[slot].pcsc.card)
+    {
+      log_info ("reader slot %d: active protocol:", slot);
+      if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+        log_printf (" T0");
+      else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+        log_printf (" T1");
+      else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+        log_printf (" raw");
+      log_printf ("\n");
+    }
+  else
+    log_info ("reader slot %d: not connected\n", slot);
 }
 
 
-/* Send an PC/SC reset command and return a status word on error or 0
-   on success. */
+#ifndef NEED_PCSC_WRAPPER
 static int
-reset_pcsc_reader (int slot)
+pcsc_get_status_direct (int slot, unsigned int *status)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
-  reader_table_t slotp;
-  size_t len;
-  int i, n;
-  unsigned char msgbuf[9];
-  unsigned int dummy_status;
-  int sw = SW_HOST_CARD_IO_ERROR;
-
-  slotp = reader_table + slot;
-
-  if (slotp->pcsc.req_fd == -1
-      || slotp->pcsc.rsp_fd == -1
-      || slotp->pcsc.pid == (pid_t)(-1) )
-    {
-      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
-      return sw;
-    }
-
-  msgbuf[0] = 0x05; /* RESET command. */
-  len = 0;
-  msgbuf[1] = (len >> 24);
-  msgbuf[2] = (len >> 16);
-  msgbuf[3] = (len >>  8);
-  msgbuf[4] = (len      );
-  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
-    {
-      log_error ("error sending PC/SC RESET request: %s\n",
-                 strerror (errno));
-      goto command_failed;
-    }
+  struct pcsc_readerstate_s rdrstates[1];
 
-  /* Read the response. */
-  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
-    {
-      log_error ("error receiving PC/SC RESET response: %s\n",
-                 i? strerror (errno) : "premature EOF");
-      goto command_failed;
-    }
-  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
-  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. */
-  if (len > DIM (slotp->atr))
-    {
-      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
-                 (unsigned long)len);
-      sw = SW_HOST_GENERAL_ERROR;
-      goto command_failed;
-    }
-  err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
-                       | (msgbuf[7] << 8 ) | msgbuf[8]);
+  memset (rdrstates, 0, sizeof *rdrstates);
+  rdrstates[0].reader = reader_table[slot].rdrname;
+  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
+                                0,
+                                rdrstates, 1);
+  if (err == PCSC_E_TIMEOUT)
+    err = 0; /* Timeout is no error error here. */
   if (err)
     {
-      log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
                  pcsc_error_string (err), err);
-      /* If the error code is no smart card, we should not considere
-         this a major error and close the wrapper.  */
-      sw = pcsc_error_to_sw (err);
-      if (err == PCSC_E_NO_SMARTCARD)
-        return sw;
-      goto command_failed;
-    }
-
-  /* The open function may return a zero for the ATR length to
-     indicate that no card is present.  */
-  n = len;
-  if (n)
-    {
-      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
-        {
-          log_error ("error receiving PC/SC RESET response: %s\n",
-                     i? strerror (errno) : "premature EOF");
-          goto command_failed;
-        }
-    }
-  slotp->atrlen = len;
-
-  /* Read the status so that IS_T0 will be set. */
-  pcsc_get_status (slot, &dummy_status);
-
-  return 0;
-
- command_failed:
-  close (slotp->pcsc.req_fd);
-  close (slotp->pcsc.rsp_fd);
-  slotp->pcsc.req_fd = -1;
-  slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
-  slotp->pcsc.pid = (pid_t)(-1);
-  slotp->used = 0;
-  return sw;
-
-#else /* !NEED_PCSC_WRAPPER */
-  long err;
-  char reader[250];
-  unsigned long nreader, atrlen;
-  unsigned long card_state, card_protocol;
-
-  if (reader_table[slot].pcsc.card)
-    {
-      err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
-      if (err)
-        {
-          log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
-                     pcsc_error_string (err), err);
-          return SW_HOST_CARD_IO_ERROR;
-        }
-      reader_table[slot].pcsc.card = 0;
-    }
-
-  err = pcsc_connect (reader_table[slot].pcsc.context,
-                      reader_table[slot].rdrname,
-                      PCSC_SHARE_EXCLUSIVE,
-                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
-                      &reader_table[slot].pcsc.card,
-                      &reader_table[slot].pcsc.protocol);
-  if (err)
-    {
-      log_error ("pcsc_connect failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      reader_table[slot].pcsc.card = 0;
       return pcsc_error_to_sw (err);
     }
 
+  /*   log_debug  */
+  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
 
-  atrlen = DIM(reader_table[0].atr);
-  nreader = sizeof reader - 1;
-  err = pcsc_status (reader_table[slot].pcsc.card,
-                     reader, &nreader,
-                     &card_state, &card_protocol,
-                     reader_table[slot].atr, &atrlen);
-  if (err)
+  *status = 0;
+  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
     {
-      log_error ("pcsc_status failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      reader_table[slot].atrlen = 0;
-      return pcsc_error_to_sw (err);
+      *status |= APDU_CARD_PRESENT;
+      if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+       *status |= APDU_CARD_ACTIVE;
     }
-  if (atrlen > DIM (reader_table[0].atr))
-    log_bug ("ATR returned by pcsc_status is too large\n");
-  reader_table[slot].atrlen = atrlen;
-  reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+#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
+     mode.  */
+  if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+       == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
+       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+    *status |= APDU_CARD_USABLE;
+#else
+  /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
+     time when we are the only user (SCM SCR335) under Windows.  */
+  if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+      == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+    *status |= APDU_CARD_USABLE;
+#endif
 
   return 0;
-#endif /* !NEED_PCSC_WRAPPER */
 }
+#endif /*!NEED_PCSC_WRAPPER*/
 
 
+#ifdef NEED_PCSC_WRAPPER
 static int
-pcsc_get_status (int slot, unsigned int *status)
+pcsc_get_status_wrapped (int slot, unsigned int *status)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
   reader_table_t slotp;
   size_t len, full_len;
@@ -977,15 +1048,14 @@ pcsc_get_status (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",
@@ -1030,7 +1100,6 @@ pcsc_get_status (int slot, unsigned int *status)
   /* We are lucky: The wrapper already returns the data in the
      required format. */
   *status = buffer[3];
-
   return 0;
 
  command_failed:
@@ -1038,78 +1107,70 @@ pcsc_get_status (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;
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return pcsc_get_status_wrapped (slot, status);
+#else
+  return pcsc_get_status_direct (slot, status);
+#endif
+}
 
-#else /*!NEED_PCSC_WRAPPER*/
 
+#ifndef NEED_PCSC_WRAPPER
+static int
+pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
+                       unsigned char *buffer, size_t *buflen,
+                       pininfo_t *pininfo)
+{
   long err;
-  struct pcsc_readerstate_s rdrstates[1];
+  struct pcsc_io_request_s send_pci;
+  pcsc_dword_t recv_len;
 
-  memset (rdrstates, 0, sizeof *rdrstates);
-  rdrstates[0].reader = reader_table[slot].rdrname;
-  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
-  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
-                                0,
-                                rdrstates, 1);
-  if (err == PCSC_E_TIMEOUT)
-    err = 0; /* Timeout is no error error here. */
-  if (err)
-    {
-      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      return pcsc_error_to_sw (err);
-    }
+  (void)pininfo;
 
+  if (!reader_table[slot].atrlen
+      && (err = reset_pcsc_reader (slot)))
+    return err;
 
-  /*   log_debug  */
-  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
-  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
 
-  *status = 0;
-  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
-    *status |= 2;
-  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
-    *status |= 4;
-#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
-     mode.  */
-  if ( (*status & 6) == 6
-       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
-    *status |= 1;
-#else
-  /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
-     time when we are the only user (SCM SCR335) under Windows.  */
-  if ((status & 6) == 6)
-    status |= 1;
-#endif
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+      send_pci.protocol = PCSC_PROTOCOL_T1;
+  else
+      send_pci.protocol = PCSC_PROTOCOL_T0;
+  send_pci.pci_len = sizeof send_pci;
+  recv_len = *buflen;
+  err = pcsc_transmit (reader_table[slot].pcsc.card,
+                       &send_pci, apdu, apdulen,
+                       NULL, buffer, &recv_len);
+  *buflen = recv_len;
+  if (err)
+    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
 
-  return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
+  return pcsc_error_to_sw (err);
 }
+#endif /*!NEED_PCSC_WRAPPER*/
 
 
-/* Actually send the APDU of length APDULEN to SLOT and return a
-   maximum of *BUFLEN data in BUFFER, the actual returned size will be
-   set to BUFLEN.  Returns: CT API error code. */
+#ifdef NEED_PCSC_WRAPPER
 static int
-pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen, 
-                struct pininfo_s *pininfo)
+pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
+                        unsigned char *buffer, size_t *buflen,
+                        pininfo_t *pininfo)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
   reader_table_t slotp;
   size_t len, full_len;
@@ -1117,6 +1178,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   unsigned char msgbuf[9];
   int sw = SW_HOST_CARD_IO_ERROR;
 
+  (void)pininfo;
+
   if (!reader_table[slot].atrlen
       && (err = reset_pcsc_reader (slot)))
     return err;
@@ -1155,15 +1218,14 @@ pcsc_send_apdu (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",
@@ -1211,47 +1273,196 @@ pcsc_send_apdu (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;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-#else /*!NEED_PCSC_WRAPPER*/
 
+/* Send the APDU of length APDULEN to SLOT and return a maximum of
+   *BUFLEN data in BUFFER, the actual returned size will be stored at
+   BUFLEN.  Returns: A status word. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen,
+                pininfo_t *pininfo)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
+#else
+  return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo);
+#endif
+}
+
+
+#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;
-  struct pcsc_io_request_s send_pci;
-  unsigned long recv_len;
 
-  if (!reader_table[slot].atrlen
-      && (err = reset_pcsc_reader (slot)))
-    return 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);
+    }
 
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
+  return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
 
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-      send_pci.protocol = PCSC_PROTOCOL_T1;
-  else
-      send_pci.protocol = PCSC_PROTOCOL_T0;
-  send_pci.pci_len = sizeof send_pci;
-  recv_len = *buflen;
-  err = pcsc_transmit (reader_table[slot].pcsc.card,
-                       &send_pci, apdu, apdulen,
-                       NULL, buffer, &recv_len);
-  *buflen = recv_len;
+
+#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_transmit failed: %s (0x%lx)\n",
-               pcsc_error_string (err), 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*/
 }
+#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
-close_pcsc_reader (int slot)
+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);
+  xfree (reader_table[slot].rdrname);
+  reader_table[slot].rdrname = NULL;
+  reader_table[slot].used = 0;
+  return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+close_pcsc_reader_wrapped (int slot)
+{
   long err;
   reader_table_t slotp;
   size_t len;
@@ -1288,15 +1499,14 @@ close_pcsc_reader (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);
@@ -1309,29 +1519,500 @@ close_pcsc_reader (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;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-#else /*!NEED_PCSC_WRAPPER*/
 
-  pcsc_release_context (reader_table[slot].pcsc.context);
-  xfree (reader_table[slot].rdrname);
-  reader_table[slot].rdrname = NULL;
-  reader_table[slot].used = 0;
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return close_pcsc_reader_wrapped (slot);
+#else
+  return close_pcsc_reader_direct (slot);
+#endif
+}
+
+
+/* Connect a PC/SC card.  */
+#ifndef NEED_PCSC_WRAPPER
+static int
+connect_pcsc_card (int slot)
+{
+  long err;
+
+  assert (slot >= 0 && slot < MAX_READER);
+
+  if (reader_table[slot].pcsc.card)
+    return SW_HOST_ALREADY_CONNECTED;
+
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+  reader_table[slot].is_t0 = 0;
+
+  err = pcsc_connect (reader_table[slot].pcsc.context,
+                      reader_table[slot].rdrname,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &reader_table[slot].pcsc.card,
+                      &reader_table[slot].pcsc.protocol);
+  if (err)
+    {
+      reader_table[slot].pcsc.card = 0;
+      if (err != PCSC_E_NO_SMARTCARD)
+        log_error ("pcsc_connect failed: %s (0x%lx)\n",
+                   pcsc_error_string (err), err);
+    }
+  else
+    {
+      char reader[250];
+      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 ;
+      err = pcsc_status (reader_table[slot].pcsc.card,
+                         reader, &readerlen,
+                         &card_state, &card_protocol,
+                         reader_table[slot].atr, &atrlen);
+      if (err)
+        log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+                   pcsc_error_string (err), err, (long unsigned int)readerlen);
+      else
+        {
+          if (atrlen > DIM (reader_table[0].atr))
+            log_bug ("ATR returned by pcsc_status is too large\n");
+          reader_table[slot].atrlen = atrlen;
+          /* If we got to here we know that a card is present
+             and usable.  Remember this.  */
+          reader_table[slot].last_status = (   APDU_CARD_USABLE
+                                             | APDU_CARD_PRESENT
+                                             | APDU_CARD_ACTIVE);
+          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+        }
+    }
+
+  dump_reader_status (slot);
+  return pcsc_error_to_sw (err);
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+/* Disconnect a PC/SC card.  Note that this succeeds even if the card
+   is not connected.  */
+#ifndef NEED_PCSC_WRAPPER
+static int
+disconnect_pcsc_card (int slot)
+{
+  long err;
+
+  assert (slot >= 0 && slot < MAX_READER);
+
+  if (!reader_table[slot].pcsc.card)
+    return 0;
+
+  err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+  if (err)
+    {
+      log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
+    }
+  reader_table[slot].pcsc.card = 0;
   return 0;
+}
 #endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
+reset_pcsc_reader_direct (int slot)
+{
+  int sw;
+
+  sw = disconnect_pcsc_card (slot);
+  if (!sw)
+    sw = connect_pcsc_card (slot);
+
+  return sw;
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+reset_pcsc_reader_wrapped (int slot)
+{
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i, n;
+  unsigned char msgbuf[9];
+  unsigned int dummy_status;
+  int sw = SW_HOST_CARD_IO_ERROR;
+
+  slotp = reader_table + slot;
+
+  if (slotp->pcsc.req_fd == -1
+      || slotp->pcsc.rsp_fd == -1
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+      return sw;
+    }
+
+  msgbuf[0] = 0x05; /* RESET command. */
+  len = 0;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+    {
+      log_error ("error sending PC/SC RESET 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 RESET 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. */
+  if (len > DIM (slotp->atr))
+    {
+      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+                 (unsigned long)len);
+      sw = SW_HOST_GENERAL_ERROR;
+      goto command_failed;
+    }
+  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
+  if (err)
+    {
+      log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      /* If the error code is no smart card, we should not considere
+         this a major error and close the wrapper.  */
+      sw = pcsc_error_to_sw (err);
+      if (err == PCSC_E_NO_SMARTCARD)
+        return sw;
+      goto command_failed;
+    }
+
+  /* The open function may return a zero for the ATR length to
+     indicate that no card is present.  */
+  n = len;
+  if (n)
+    {
+      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC RESET response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+    }
+  slotp->atrlen = len;
+
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
+  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 sw;
+}
+#endif /* !NEED_PCSC_WRAPPER */
+
+
+/* Send an PC/SC reset command and return a status word on error or 0
+   on success. */
+static int
+reset_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return reset_pcsc_reader_wrapped (slot);
+#else
+  return reset_pcsc_reader_direct (slot);
+#endif
+}
+
+
+/* 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")
+                   || 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 */
+           || 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
+static int
+open_pcsc_reader_direct (const char *portstr)
+{
+  long err;
+  int slot;
+  char *list = NULL;
+  pcsc_dword_t nreader;
+  char *p;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+
+  /* Fixme: Allocating a context for each slot is not required.  One
+     global context should be sufficient.  */
+  err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
+                                &reader_table[slot].pcsc.context);
+  if (err)
+    {
+      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;
+    }
+
+  err = pcsc_list_readers (reader_table[slot].pcsc.context,
+                           NULL, NULL, &nreader);
+  if (!err)
+    {
+      list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
+      if (!list)
+        {
+          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,
+                               NULL, list, &nreader);
+    }
+  if (err)
+    {
+      log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      xfree (list);
+      unlock_slot (slot);
+      return -1;
+    }
+
+  p = list;
+  while (nreader)
+    {
+      if (!*p && !p[1])
+        break;
+      if (*p)
+        log_info ("detected reader '%s'\n", p);
+      if (nreader < (strlen (p)+1))
+        {
+          log_error ("invalid response from pcsc_list_readers\n");
+          break;
+        }
+      nreader -= strlen (p)+1;
+      p += strlen (p) + 1;
+    }
+
+  reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+  if (!reader_table[slot].rdrname)
+    {
+      log_error ("error allocating memory for reader name\n");
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      unlock_slot (slot);
+      return -1;
+    }
+  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+  xfree (list);
+  list = NULL;
+
+  reader_table[slot].pcsc.card = 0;
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+
+  reader_table[slot].connect_card = connect_pcsc_card;
+  reader_table[slot].disconnect_card = disconnect_pcsc_card;
+  reader_table[slot].close_reader = close_pcsc_reader;
+  reader_table[slot].reset_reader = reset_pcsc_reader;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+  dump_reader_status (slot);
+  unlock_slot (slot);
+  return slot;
 }
+#endif /*!NEED_PCSC_WRAPPER */
+
 
-/* Note:  It is a pitty that we can't return proper error codes.  */
-static int
-open_pcsc_reader (const char *portstr)
-{
-#ifdef NEED_PCSC_WRAPPER
 /* Open the PC/SC reader using the pcsc_wrapper program.  This is
    needed to cope with different thread models and other peculiarities
    of libpcsclite. */
+#ifdef NEED_PCSC_WRAPPER
+static int
+open_pcsc_reader_wrapped (const char *portstr)
+{
   int slot;
   reader_table_t slotp;
   int fd, rp[2], wp[2];
@@ -1341,14 +2022,14 @@ open_pcsc_reader (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
      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;
     }
@@ -1358,14 +2039,15 @@ open_pcsc_reader (const char *portstr)
     return -1;
   slotp = reader_table + slot;
 
-  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
-     the common directy but implement it direclty so that this file
+  /* Fire up the PC/SCc wrapper.  We don't use any fork/exec code from
+     the common directy but implement it directly so that this file
      may still be source copied. */
 
   if (pipe (rp) == -1)
     {
       log_error ("error creating a pipe: %s\n", strerror (errno));
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
   if (pipe (wp) == -1)
@@ -1374,6 +2056,7 @@ open_pcsc_reader (const char *portstr)
       close (rp[0]);
       close (rp[1]);
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1386,6 +2069,7 @@ open_pcsc_reader (const char *portstr)
       close (wp[0]);
       close (wp[1]);
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
   slotp->pcsc.pid = pid;
@@ -1412,17 +2096,12 @@ open_pcsc_reader (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));
 
       /* Close all other files. */
-      n = sysconf (_SC_OPEN_MAX);
-      if (n < 0)
-        n = MAX_OPEN_FDS;
-      for (i=3; i < n; i++)
-        close(i);
-      errno = 0;
+      close_all_fds (3, NULL);
 
       execl (wrapperpgm,
              "pcsc-wrapper",
@@ -1442,14 +2121,14 @@ open_pcsc_reader (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
   while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
     ;
-#undef X
+#undef WAIT
 
   /* Now send the open request. */
   msgbuf[0] = 0x01; /* OPEN command. */
@@ -1472,7 +2151,7 @@ open_pcsc_reader (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");
@@ -1485,12 +2164,10 @@ open_pcsc_reader (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;
     }
 
@@ -1509,7 +2186,9 @@ open_pcsc_reader (const char *portstr)
         }
       /* If we got to here we know that a card is present
          and usable.  Thus remember this.  */
-      slotp->last_status = (1|2|4| 0x8000);
+      slotp->last_status = (  APDU_CARD_USABLE
+                            | APDU_CARD_PRESENT
+                            | APDU_CARD_ACTIVE);
     }
   slotp->atrlen = len;
 
@@ -1517,13 +2196,15 @@ open_pcsc_reader (const char *portstr)
   reader_table[slot].reset_reader = reset_pcsc_reader;
   reader_table[slot].get_status_reader = pcsc_get_status;
   reader_table[slot].send_apdu_reader = pcsc_send_apdu;
-  reader_table[slot].check_keypad = NULL;
   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:
@@ -1531,152 +2212,223 @@ open_pcsc_reader (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;
 
-#else /*!NEED_PCSC_WRAPPER */
-  long err;
-  int slot;
-  char *list = NULL;
-  unsigned long nreader, listlen, atrlen;
-  char *p;
-  unsigned long card_state, card_protocol;
+}
+#endif /*NEED_PCSC_WRAPPER*/
 
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
 
-  err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
-                                &reader_table[slot].pcsc.context);
-  if (err)
-    {
-      log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      reader_table[slot].used = 0;
-      return -1;
-    }
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+  return open_pcsc_reader_wrapped (portstr);
+#else
+  return open_pcsc_reader_direct (portstr);
+#endif
+}
 
-  err = pcsc_list_readers (reader_table[slot].pcsc.context,
-                           NULL, NULL, &nreader);
-  if (!err)
-    {
-      list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
-      if (!list)
-        {
-          log_error ("error allocating memory for reader list\n");
-          pcsc_release_context (reader_table[slot].pcsc.context);
-          reader_table[slot].used = 0;
-          return -1 /*SW_HOST_OUT_OF_CORE*/;
-        }
-      err = pcsc_list_readers (reader_table[slot].pcsc.context,
-                               NULL, list, &nreader);
-    }
-  if (err)
-    {
-      log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      reader_table[slot].used = 0;
-      xfree (list);
-      return -1 /*pcsc_error_to_sw (err)*/;
-    }
 
-  listlen = nreader;
-  p = list;
-  while (nreader)
-    {
-      if (!*p && !p[1])
-        break;
-      if (*p)
-        log_info ("detected reader `%s'\n", p);
-      if (nreader < (strlen (p)+1))
-        {
-          log_error ("invalid response from pcsc_list_readers\n");
-          break;
-        }
-      nreader -= strlen (p)+1;
-      p += strlen (p) + 1;
-    }
+/* 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;
 
-  reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
-  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;
-      return -1 /*SW_HOST_OUT_OF_CORE*/;
-    }
-  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
-  xfree (list);
-  list = NULL;
+  if (reader_table[slot].pcsc.pinmin >= 0)
+    pininfo->minlen = reader_table[slot].pcsc.pinmin;
 
-  err = pcsc_connect (reader_table[slot].pcsc.context,
-                      reader_table[slot].rdrname,
-                      PCSC_SHARE_EXCLUSIVE,
-                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
-                      &reader_table[slot].pcsc.card,
-                      &reader_table[slot].pcsc.protocol);
-  if (err == PCSC_E_NO_SMARTCARD) 
-    reader_table[slot].pcsc.card = 0;
-  else if (err)
-    {
-      log_error ("pcsc_connect failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      xfree (reader_table[slot].rdrname);
-      reader_table[slot].rdrname = NULL;
-      reader_table[slot].used = 0;
-      return -1 /*pcsc_error_to_sw (err)*/;
-    }
+  if (reader_table[slot].pcsc.pinmax >= 0)
+    pininfo->maxlen = reader_table[slot].pcsc.pinmax;
 
-  reader_table[slot].atrlen = 0;
-  reader_table[slot].last_status = 0;
-  if (!err)
-    {
-      char reader[250];
-      unsigned long readerlen;
+  if (!pininfo->minlen)
+    pininfo->minlen = 1;
+  if (!pininfo->maxlen)
+    pininfo->maxlen = 15;
 
-      atrlen = DIM (reader_table[0].atr);
-      readerlen = sizeof reader -1 ;
-      err = pcsc_status (reader_table[slot].pcsc.card,
-                         reader, &readerlen,
-                         &card_state, &card_protocol,
-                         reader_table[slot].atr, &atrlen);
-      if (err)
-        log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
-                   pcsc_error_string (err), err, readerlen);
-      else
-        {
-          if (atrlen > DIM (reader_table[0].atr))
-            log_bug ("ATR returned by pcsc_status is too large\n");
-          reader_table[slot].atrlen = atrlen;
-          /* If we got to here we know that a card is present
-             and usable.  Thus remember this.  */
-          reader_table[slot].last_status = (1|2|4| 0x8000);
-          reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
-        }
-    }
+  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;
 
-  reader_table[slot].close_reader = close_pcsc_reader;
-  reader_table[slot].reset_reader = reset_pcsc_reader;
-  reader_table[slot].get_status_reader = pcsc_get_status;
-  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
-  reader_table[slot].check_keypad = NULL;
-  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+  if (DBG_CARD_IO)
+    log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
+               (unsigned int)command, r);
 
-/*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
-/*   log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
+  if (reader_table[slot].pinpad_varlen_supported)
+    pininfo->fixedlen = 0;
 
-  dump_reader_status (slot);
-  return slot;
-#endif /*!NEED_PCSC_WRAPPER */
+  return r;
+}
+
+#define PIN_VERIFY_STRUCTURE_SIZE 24
+static int
+pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+                    pininfo_t *pininfo)
+{
+  int sw;
+  unsigned char *pin_verify;
+  int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
+  unsigned char result[2];
+  pcsc_dword_t resultlen = 2;
+  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[2];
+  pcsc_dword_t resultlen = 2;
+  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
 /*
@@ -1728,6 +2480,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;
@@ -1735,12 +2496,12 @@ 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 = 1|2|4;
+    *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
   else if (bits == 1)
-    *status = 2;
+    *status = APDU_CARD_PRESENT;
   else
     *status = 0;
 
@@ -1754,7 +2515,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;
@@ -1765,16 +2526,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,
@@ -1789,19 +2546,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;
 }
 
 
@@ -1822,6 +2603,7 @@ open_ccid_reader (const char *portstr)
   if (err)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -1836,7 +2618,9 @@ open_ccid_reader (const char *portstr)
     {
       /* If we got to here we know that a card is present
          and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (1|2|4| 0x8000);
+      reader_table[slot].last_status = (APDU_CARD_USABLE
+                                        | APDU_CARD_PRESENT
+                                        | APDU_CARD_ACTIVE);
     }
 
   reader_table[slot].close_reader = close_ccid_reader;
@@ -1844,10 +2628,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;
 }
 
@@ -2002,7 +2793,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;
@@ -2086,6 +2877,7 @@ open_rapdu_reader (int portno,
   if (!slotp->rapdu.handle)
     {
       slotp->used = 0;
+      unlock_slot (slot);
       return -1;
     }
 
@@ -2133,17 +2925,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;
 }
 
@@ -2156,44 +2952,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). */
@@ -2201,23 +2959,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 */
@@ -2261,7 +3046,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;
         }
@@ -2298,6 +3083,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
@@ -2310,12 +3096,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,
@@ -2327,7 +3114,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;
         }
@@ -2335,7 +3123,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;
 }
 
 
@@ -2365,6 +3157,15 @@ apdu_open_remote_reader (const char *portstr,
                             writefnc, writefnc_value,
                             closefnc, closefnc_value);
 #else
+  (void)portstr;
+  (void)cookie;
+  (void)length;
+  (void)readfnc;
+  (void)readfnc_value;
+  (void)writefnc;
+  (void)writefnc_value;
+  (void)closefnc;
+  (void)closefnc_value;
 #ifdef _WIN32
   errno = ENOENT;
 #else
@@ -2375,26 +3176,100 @@ apdu_open_remote_reader (const char *portstr,
 }
 
 
-int
-apdu_close_reader (int slot)
+int
+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 )
+    {
+      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)
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
+      return sw;
+    }
+  if (reader_table[slot].close_reader)
+    {
+      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)
 {
-  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
-  if (reader_table[slot].close_reader)
-    return reader_table[slot].close_reader (slot);
-  return SW_HOST_NOT_SUPPORTED;
+  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 should be used
-   to get it active again. */
+   the handle ready for later use. A apdu_reset_reader or apdu_connect
+   should be used to get it active again. */
 int
 apdu_shutdown_reader (int slot)
 {
+  int sw;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_shutdown_reader: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    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)
+    {
+      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;
 }
 
@@ -2410,95 +3285,198 @@ apdu_enum_reader (int slot, int *used)
   return 0;
 }
 
-/* Do a reset for the card in reader at SLOT. */
+
+/* Connect a card.  This is used to power up the card and make sure
+   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_reset (int slot)
+apdu_connect (int slot)
 {
   int sw;
+  unsigned int status;
+
+  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;
+    }
 
-  if ((sw = lock_slot (slot)))
-    return sw;
+  /* Only if the access method provides a connect function we use it.
+     If not, we expect that the card has been implicitly connected by
+     apdu_open_reader.  */
+  if (reader_table[slot].connect_card)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].connect_card (slot);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+
+  /* We need to call apdu_get_status_internal, so that the last-status
+     machinery gets setup properly even if a card is inserted while
+     scdaemon is fired up and apdu_get_status has not yet been called.
+     Without that we would force a reset of the card with the next
+     call to apdu_get_status.  */
+  apdu_get_status_internal (slot, 1, 1, &status, NULL);
+  if (sw)
+    ;
+  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;
 
-  reader_table[slot].last_status = 0;
-  if (reader_table[slot].reset_reader)
-    sw = reader_table[slot].reset_reader (slot);
+  if (DBG_READER)
+    log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
 
-  if (!sw)
+  return sw;
+}
+
+
+int
+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 )
     {
-      /* If we got to here we know that a card is present
-         and usable.  Thus remember this.  */
-      reader_table[slot].last_status = (1|2|4| 0x8000);
+      if (DBG_READER)
+        log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
     }
 
-  unlock_slot (slot);
+  if (reader_table[slot].disconnect_card)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].disconnect_card (slot);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
   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. */
+/* 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_activate (int slot)
+apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
 {
   int sw;
-  unsigned int s;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if ((sw = trylock_slot (slot)))
-    return sw;
+  if (reader_table[slot].set_progress_cb)
+    {
+      sw = lock_slot (slot);
+      if (!sw)
+        {
+          sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
+          unlock_slot (slot);
+        }
+    }
+  else
+    sw = 0;
+  return sw;
+}
 
-  if (reader_table[slot].get_status_reader)
-    sw = reader_table[slot].get_status_reader (slot, &s);
+
+/* Do a reset for the card in reader at SLOT. */
+int
+apdu_reset (int slot)
+{
+  int sw;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_reset: slot=%d\n", slot);
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
+
+  if ((sw = lock_slot (slot)))
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
+      return sw;
+    }
+
+  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 = (1|2|4| 0x8000);
-                }
-            }
-        }
+      /* 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)
+    {
+      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;
 }
 
@@ -2508,20 +3486,20 @@ apdu_get_atr (int slot, size_t *atrlen)
    card to become available if HANG is set to true. On success the
    bits in STATUS will be set to
 
-     bit 0 = card present and usable
-     bit 1 = card present
-     bit 2 = card active
-     bit 3 = card access locked [not yet implemented]
+     APDU_CARD_USABLE  (bit 0) = card present and usable
+     APDU_CARD_PRESENT (bit 1) = card present
+     APDU_CARD_ACTIVE  (bit 2) = card active
+                       (bit 3) = card access locked [not yet implemented]
 
-   For must application, testing bit 0 is sufficient.
+   For must applications, testing bit 0 is sufficient.
 
    CHANGED will receive the value of the counter tracking the number
    of card insertions.  This value may be used to detect a card
    change.
 */
-int
-apdu_get_status (int slot, int hang,
-                 unsigned int *status, unsigned int *changed)
+static int
+apdu_get_status_internal (int slot, int hang, int no_atr_reset,
+                          unsigned int *status, unsigned int *changed)
 {
   int sw;
   unsigned int s;
@@ -2543,17 +3521,18 @@ apdu_get_status (int slot, int hang,
       return sw;
     }
 
-  /* Keep track of changes.  We use one extra bit to test whether we
-     have checked the status at least once. */
-  if ( s != (reader_table[slot].last_status & 0x07ff)
-       || !reader_table[slot].last_status )
+  /* Keep track of changes.  */
+  if (s != reader_table[slot].last_status
+      || !reader_table[slot].any_status )
     {
       reader_table[slot].change_counter++;
-      /* Make sure that the ATR is invalid so that a reset will be by
-         activate.  */
-      reader_table[slot].atrlen = 0;
+      /* Make sure that the ATR is invalid so that a reset will be
+         triggered by apdu_activate.  */
+      if (!no_atr_reset)
+        reader_table[slot].atrlen = 0;
     }
-  reader_table[slot].last_status = (s | 0x8000);
+  reader_table[slot].any_status = 1;
+  reader_table[slot].last_status = s;
 
   if (status)
     *status = s;
@@ -2563,20 +3542,105 @@ apdu_get_status (int slot, int hang,
 }
 
 
+/* See above for a description.  */
+int
+apdu_get_status (int slot, int hang,
+                 unsigned int *status, unsigned int *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;
 }
@@ -2586,7 +3650,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;
@@ -2594,7 +3658,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;
@@ -2602,118 +3666,214 @@ 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 without the data part limited to -n
+       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 == 1 := Use extended length for input and output with out a
+      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))
     {
-      /* Data does not fit into an APDU.  What we do now dependes on
+      /* Data does not fit into an APDU.  What we do now depends on
          the EXTENDED_MODE parameter.  */
       if (!extended_mode)
-        return SW_WRONG_LENGTH; /* No way. to send such an APDU. */
+        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;
+          apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
+          if (lc >= 0)
+            {
+              apdu[apdulen++] = ((lc >> 8) & 0xff);
+              apdu[apdulen++] = (lc & 0xff);
+              memcpy (apdu+apdulen, data, lc);
+              data += lc;
+              apdulen += lc;
+            }
+          if (le != -1)
+            {
+              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;
@@ -2722,6 +3882,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)
@@ -2729,7 +3896,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)
@@ -2740,6 +3907,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;
@@ -2759,6 +3927,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);
@@ -2773,20 +3942,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];
@@ -2812,6 +3985,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);
@@ -2838,105 +4012,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 reponsible
+   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 reponsible 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 the with an APDU including the status word.  With
+   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;
@@ -2944,23 +4112,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];
@@ -2987,6 +4191,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);
@@ -3001,20 +4206,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];
@@ -3040,6 +4247,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);
@@ -3050,7 +4258,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                 }
             }
           else
-            log_info ("apdu_send_sdirect(%d) "
+            log_info ("apdu_send_direct(%d) "
                       "got unexpected status %04X from get response\n",
                       slot, sw);
         }
@@ -3072,6 +4280,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;
@@ -3080,9 +4289,10 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     }
 
   unlock_slot (slot);
+  xfree (result_buffer);
 
-  /* Append the status word - we reseved the two extra bytes while
-     allocating the buffer. */
+  /* Append the status word.  Note that we reserved the two extra
+     bytes while allocating the buffer.  */
   if (retbuf)
     {
       (*retbuf)[(*retbuflen)++] = (sw >> 8);
@@ -3093,5 +4303,4 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return 0;
-#undef RESULTLEN
 }