scd: change default value of pinpad maxlen.
[gnupg.git] / scd / apdu.c
index 380450d..e920678 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 /* 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 "exechelp.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
+#include "iso7816.h"
 #include "apdu.h"
 #include "ccid-driver.h"
-#include "iso7816.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
+#ifdef USE_NPTH
 #if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
 #define NEED_PCSC_WRAPPER 1
 #endif
 #define DLSTDCALL
 #endif
 
-
-/* Helper to pass parameters related to keypad based operations. */
-struct pininfo_s
-{
-  int mode;
-  int minlen;
-  int maxlen;
-  int padlen;
-};
-
 /* A structure to collect information pertaining to one reader
    slot. */
 struct reader_table_s {
@@ -107,12 +96,12 @@ struct reader_table_s {
   int (*reset_reader)(int);
   int (*get_status_reader)(int, unsigned int *);
   int (*send_apdu_reader)(int,unsigned char *,size_t,
-                          unsigned char *, size_t *, struct pininfo_s *);
-  int (*check_keypad)(int, int, int, int, int, int);
+                          unsigned char *, size_t *, pininfo_t *);
+  int (*check_pinpad)(int, int, pininfo_t *);
   void (*dump_status_reader)(int);
   int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
-  int (*keypad_verify)(int, int, int, int, int, struct pininfo_s *);
-  int (*keypad_modify)(int, int, int, int, int, struct pininfo_s *);
+  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;
@@ -144,9 +133,9 @@ struct reader_table_s {
                               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;
@@ -239,6 +228,7 @@ 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
 
 #define CM_IOCTL_GET_FEATURE_REQUEST (0x42000000 + 3400)
@@ -322,9 +312,6 @@ long (* DLSTDCALL pcsc_control) (unsigned long card,
                                  unsigned long recv_len,
                                  unsigned long *bytes_returned);
 
-/* Flag set if PC/SC returned the no-service error.  */
-static int pcsc_no_service;
-
 
 /*  Prototypes.  */
 static int pcsc_get_status (int slot, unsigned int *status);
@@ -332,12 +319,11 @@ 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_keypad (int slot, int command, int pin_mode,
-                              int pinlen_min, int pinlen_max, int pin_padlen);
-static int pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
-                               struct pininfo_s *pininfo);
-static int pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
-                               struct pininfo_s *pininfo);
+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
@@ -352,6 +338,7 @@ static int
 new_reader_slot (void)
 {
   int i, reader = -1;
+  int err;
 
   for (i=0; i < MAX_READER; i++)
     {
@@ -363,17 +350,18 @@ 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*/
   reader_table[reader].connect_card = NULL;
   reader_table[reader].disconnect_card = NULL;
   reader_table[reader].close_reader = NULL;
@@ -381,11 +369,11 @@ new_reader_slot (void)
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
-  reader_table[reader].check_keypad = check_pcsc_keypad;
+  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].keypad_verify = pcsc_keypad_verify;
-  reader_table[reader].keypad_modify = pcsc_keypad_modify;
+  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;
@@ -440,7 +428,7 @@ 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";
     }
@@ -463,8 +451,11 @@ apdu_strerror (int rc)
     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_LC         : return "bad Lc";
-    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";
@@ -605,7 +596,7 @@ ct_get_status (int slot, unsigned int *status)
    set to BUFLEN.  Returns: CT API error code. */
 static int
 ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-              unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+              unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
 {
   int rc;
   unsigned char dad[1], sad[1];
@@ -670,10 +661,10 @@ 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].keypad_verify = NULL;
-  reader_table[reader].keypad_modify = NULL;
+  reader_table[reader].pinpad_verify = NULL;
+  reader_table[reader].pinpad_modify = NULL;
 
   dump_reader_status (reader);
   return reader;
@@ -695,8 +686,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
@@ -721,11 +712,11 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
 
   while (nleft > 0)
     {
-#ifdef USE_GNU_PTH
+#ifdef USE_NPTH
 # ifdef HAVE_W32_SYSTEM
-#  error Cannot use pth_read here because it expects a system HANDLE.
+#  error Cannot use npth_read here because it expects a system HANDLE.
 # endif
-      n = pth_read (fd, buf, nleft);
+      n = npth_read (fd, buf, nleft);
 #else
       n = read (fd, buf, nleft);
 #endif
@@ -813,6 +804,7 @@ 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;
@@ -1011,7 +1003,8 @@ pcsc_get_status_wrapped (int slot, unsigned int *status)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -1034,7 +1027,7 @@ pcsc_get_status (int slot, unsigned int *status)
 static int
 pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
                        unsigned char *buffer, size_t *buflen,
-                       struct pininfo_s *pininfo)
+                       pininfo_t *pininfo)
 {
   long err;
   struct pcsc_io_request_s send_pci;
@@ -1070,7 +1063,7 @@ pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
 static int
 pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
                         unsigned char *buffer, size_t *buflen,
-                        struct pininfo_s *pininfo)
+                        pininfo_t *pininfo)
 {
   long err;
   reader_table_t slotp;
@@ -1175,7 +1168,8 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -1189,7 +1183,7 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
 static int
 pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                 unsigned char *buffer, size_t *buflen,
-                struct pininfo_s *pininfo)
+                pininfo_t *pininfo)
 {
 #ifdef NEED_PCSC_WRAPPER
   return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
@@ -1316,7 +1310,8 @@ control_pcsc_wrapped (int slot, unsigned long ioctl_code,
   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 pcsc_error_to_sw (err);
@@ -1417,7 +1412,8 @@ close_pcsc_reader_wrapped (int slot)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return 0;
@@ -1635,7 +1631,8 @@ reset_pcsc_reader_wrapped (int slot)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   return sw;
@@ -1681,11 +1678,8 @@ open_pcsc_reader_direct (const char *portstr)
       log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
                  pcsc_error_string (err), err);
       reader_table[slot].used = 0;
-      if (err == 0x8010001d)
-        pcsc_no_service = 1;
       return -1;
     }
-  pcsc_no_service = 0;
 
   err = pcsc_list_readers (reader_table[slot].pcsc.context,
                            NULL, NULL, &nreader);
@@ -1719,7 +1713,7 @@ open_pcsc_reader_direct (const char *portstr)
       if (!*p && !p[1])
         break;
       if (*p)
-        log_info ("detected reader `%s'\n", p);
+        log_info ("detected reader '%s'\n", p);
       if (nreader < (strlen (p)+1))
         {
           log_error ("invalid response from pcsc_list_readers\n");
@@ -1782,7 +1776,7 @@ open_pcsc_reader_wrapped (const char *portstr)
 
   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;
     }
@@ -1846,7 +1840,7 @@ open_pcsc_reader_wrapped (const char *portstr)
       /* Send stderr to the bit bucket. */
       fd = open ("/dev/null", O_WRONLY);
       if (fd == -1)
-        log_fatal ("can't open `/dev/null': %s", strerror (errno));
+        log_fatal ("can't open '/dev/null': %s", strerror (errno));
       if (fd != 2 && dup2 (fd, 2) == -1)
         log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
 
@@ -1871,8 +1865,8 @@ open_pcsc_reader_wrapped (const char *portstr)
   slotp->pcsc.rsp_fd = rp[0];
 
   /* Wait for the intermediate child to terminate. */
-#ifdef USE_GNU_PTH
-#define WAIT pth_waitpid
+#ifdef USE_NPTH
+#define WAIT npth_waitpid
 #else
 #define WAIT waitpid
 #endif
@@ -1916,6 +1910,7 @@ open_pcsc_reader_wrapped (const char *portstr)
     }
   err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
                        | (msgbuf[7] << 8 ) | msgbuf[8]);
+
   if (err)
     {
       log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
@@ -1960,7 +1955,8 @@ open_pcsc_reader_wrapped (const char *portstr)
   close (slotp->pcsc.rsp_fd);
   slotp->pcsc.req_fd = -1;
   slotp->pcsc.rsp_fd = -1;
-  kill (slotp->pcsc.pid, SIGTERM);
+  if (slotp->pcsc.pid != -1)
+    kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
   /* There is no way to return SW. */
@@ -1982,19 +1978,15 @@ open_pcsc_reader (const char *portstr)
 
 
 /* Check whether the reader supports the ISO command code COMMAND
-   on the keypad.  Return 0 on success.  */
+   on the pinpad.  Return 0 on success.  */
 static int
-check_pcsc_keypad (int slot, int command, int pin_mode,
-                   int pinlen_min, int pinlen_max, int pin_padlen)
+check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
 {
   unsigned char buf[256];
   size_t len = 256;
   int sw;
 
-  (void)pin_mode;
-  (void)pinlen_min;
-  (void)pinlen_max;
-  (void)pin_padlen;
+  (void)pininfo;      /* XXX: Identify reader and set pininfo->fixedlen.  */
 
  check_again:
   if (command == ISO7816_VERIFY)
@@ -2043,14 +2035,14 @@ check_pcsc_keypad (int slot, int command, int pin_mode,
 }
 
 
-#define PIN_VERIFY_STRUCTURE_SIZE 23
+#define PIN_VERIFY_STRUCTURE_SIZE 24
 static int
-pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
-                    struct pininfo_s *pininfo)
+pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+                    pininfo_t *pininfo)
 {
   int sw;
   unsigned char *pin_verify;
-  unsigned long len = PIN_VERIFY_STRUCTURE_SIZE;
+  int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
   unsigned char result[2];
   size_t resultlen = 2;
 
@@ -2058,16 +2050,13 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
       && (sw = reset_pcsc_reader (slot)))
     return sw;
 
-  if (pininfo->mode != 1)
-    return SW_NOT_SUPPORTED;
-
-  if (pininfo->padlen != 0)
+  if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
     return SW_NOT_SUPPORTED;
 
   if (!pininfo->minlen)
     pininfo->minlen = 1;
   if (!pininfo->maxlen)
-    pininfo->maxlen = 25;
+    pininfo->maxlen = 15;
 
   /* Note that the 25 is the maximum value the SPR532 allows.  */
   if (pininfo->minlen < 1 || pininfo->minlen > 25
@@ -2082,7 +2071,7 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   pin_verify[0] = 0x00; /* bTimerOut */
   pin_verify[1] = 0x00; /* bTimerOut2 */
   pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
-  pin_verify[3] = 0x00; /* bmPINBlockString */
+  pin_verify[3] = pininfo->fixedlen; /* bmPINBlockString */
   pin_verify[4] = 0x00; /* bmPINLengthFormat */
   pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
   pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
@@ -2095,8 +2084,8 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   pin_verify[11] = 0x00; /* bMsgIndex */
   pin_verify[12] = 0x00; /* bTeoPrologue[0] */
   pin_verify[13] = 0x00; /* bTeoPrologue[1] */
-  pin_verify[14] = 0x00; /* bTeoPrologue[2] */
-  pin_verify[15] = 0x04; /* ulDataLength */
+  pin_verify[14] = pininfo->fixedlen + 0x05; /* bTeoPrologue[2] */
+  pin_verify[15] = pininfo->fixedlen + 0x05; /* ulDataLength */
   pin_verify[16] = 0x00; /* ulDataLength */
   pin_verify[17] = 0x00; /* ulDataLength */
   pin_verify[18] = 0x00; /* ulDataLength */
@@ -2104,6 +2093,13 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   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);
+
+  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);
@@ -2111,18 +2107,25 @@ pcsc_keypad_verify (int slot, int class, int ins, int p0, int p1,
   if (sw || resultlen < 2)
     return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
+    {
+      log_error ("control_pcsc failed: %d\n", sw);
+      return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  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 28
+#define PIN_MODIFY_STRUCTURE_SIZE 29
 static int
-pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
-                    struct pininfo_s *pininfo)
+pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+                    pininfo_t *pininfo)
 {
   int sw;
   unsigned char *pin_modify;
-  unsigned long len = PIN_MODIFY_STRUCTURE_SIZE;
+  int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
   unsigned char result[2];
   size_t resultlen = 2;
 
@@ -2130,16 +2133,13 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
       && (sw = reset_pcsc_reader (slot)))
     return sw;
 
-  if (pininfo->mode != 1)
-    return SW_NOT_SUPPORTED;
-
-  if (pininfo->padlen != 0)
+  if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
     return SW_NOT_SUPPORTED;
 
   if (!pininfo->minlen)
     pininfo->minlen = 1;
   if (!pininfo->maxlen)
-    pininfo->maxlen = 25;
+    pininfo->maxlen = 15;
 
   /* Note that the 25 is the maximum value the SPR532 allows.  */
   if (pininfo->minlen < 1 || pininfo->minlen > 25
@@ -2154,18 +2154,19 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[0] = 0x00; /* bTimerOut */
   pin_modify[1] = 0x00; /* bTimerOut2 */
   pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
-  pin_modify[3] = 0x00; /* bmPINBlockString */
+  pin_modify[3] = pininfo->fixedlen; /* bmPINBlockString */
   pin_modify[4] = 0x00; /* bmPINLengthFormat */
   pin_modify[5] = 0x00; /* bInsertionOffsetOld */
-  pin_modify[6] = 0x00; /* bInsertionOffsetNew */
+  pin_modify[6] = pininfo->fixedlen; /* bInsertionOffsetNew */
   pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
   pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
-  pin_modify[9] = 0x03;  /* 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[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.  */
@@ -2177,8 +2178,8 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   pin_modify[16] = 0x00; /* bMsgIndex3 */
   pin_modify[17] = 0x00; /* bTeoPrologue[0] */
   pin_modify[18] = 0x00; /* bTeoPrologue[1] */
-  pin_modify[19] = 0x00; /* bTeoPrologue[2] */
-  pin_modify[20] = 0x04; /* ulDataLength */
+  pin_modify[19] = 2 * pininfo->fixedlen + 0x05; /* bTeoPrologue[2] */
+  pin_modify[20] = 2 * pininfo->fixedlen + 0x05; /* ulDataLength */
   pin_modify[21] = 0x00; /* ulDataLength */
   pin_modify[22] = 0x00; /* ulDataLength */
   pin_modify[23] = 0x00; /* ulDataLength */
@@ -2186,13 +2187,25 @@ pcsc_keypad_modify (int slot, int class, int ins, int p0, int p1,
   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);
+
+  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)
-    return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    {
+      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
@@ -2281,7 +2294,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;
@@ -2297,11 +2310,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t 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,
@@ -2316,25 +2325,22 @@ 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_keypad_verify (int slot, int class, int ins, int p0, int p1,
-                    struct pininfo_s *pininfo)
+ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
+                      pininfo_t *pininfo)
 {
   unsigned char apdu[4];
   int err, sw;
@@ -2346,9 +2352,7 @@ ccid_keypad_verify (int slot, int class, int ins, int p0, int p1,
   apdu[2] = p0;
   apdu[3] = p1;
   err = ccid_transceive_secure (reader_table[slot].ccid.handle,
-                                apdu, sizeof apdu,
-                                pininfo->mode, pininfo->minlen, pininfo->maxlen,
-                                pininfo->padlen,
+                                apdu, sizeof apdu, pininfo,
                                 result, 2, &resultlen);
   if (err)
     return err;
@@ -2402,11 +2406,11 @@ 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].keypad_verify = ccid_keypad_verify;
-  reader_table[slot].keypad_modify = NULL;
+  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;
@@ -2566,7 +2570,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;
@@ -2697,10 +2701,10 @@ 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].keypad_verify = NULL;
-  reader_table[slot].keypad_modify = NULL;
+  reader_table[slot].pinpad_verify = NULL;
+  reader_table[slot].pinpad_modify = NULL;
 
   dump_reader_status (slot);
   rapdu_msg_release (msg);
@@ -2725,38 +2729,47 @@ 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))
+#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 (errno));
+      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
       return SW_HOST_LOCKING_FAILED;
     }
-#endif /*USE_GNU_PTH*/
+#endif /*USE_NPTH*/
   return 0;
 }
 
 static int
 trylock_slot (int slot)
 {
-#ifdef USE_GNU_PTH
-  if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
+#ifdef USE_NPTH
+  int err;
+
+  err = npth_mutex_trylock (&reader_table[slot].lock);
+  if (err == EBUSY)
+    return SW_HOST_BUSY;
+  else if (err)
     {
-      if (errno == EBUSY)
-        return SW_HOST_BUSY;
-      log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+      log_error ("failed to acquire apdu lock: %s\n", strerror (err));
       return SW_HOST_LOCKING_FAILED;
     }
-#endif /*USE_GNU_PTH*/
+#endif /*USE_NPTH*/
   return 0;
 }
 
 static void
 unlock_slot (int slot)
 {
-#ifdef USE_GNU_PTH
-  if (!pth_mutex_release (&reader_table[slot].lock))
+#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_GNU_PTH*/
+#endif /*USE_NPTH*/
 }
 
 
@@ -2764,13 +2777,13 @@ unlock_slot (int slot)
    error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
    the first USB reader.  For PC/SC the first listed reader). */
 int
-apdu_open_reader (const char *portstr, int *r_no_service)
+apdu_open_reader (const char *portstr)
 {
   static int pcsc_api_loaded, ct_api_loaded;
   int slot;
 
-  if (r_no_service)
-    *r_no_service = 0;
+  if (DBG_READER)
+    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);
 
 #ifdef HAVE_LIBUSB
   if (!opt.disable_ccid)
@@ -2783,6 +2796,8 @@ apdu_open_reader (const char *portstr, int *r_no_service)
       if (slot != -1)
         {
           once_available = 1;
+          if (DBG_READER)
+            log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
           return slot; /* got one */
         }
 
@@ -2793,14 +2808,22 @@ apdu_open_reader (const char *portstr, int *r_no_service)
          and over again.  To reset this flag "gpgconf --kill scdaemon"
          can be used.  */
       if (once_available)
-        return -1;
+        {
+          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 */
@@ -2844,7 +2867,7 @@ apdu_open_reader (const char *portstr, int *r_no_service)
       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;
         }
@@ -2922,9 +2945,9 @@ apdu_open_reader (const char *portstr, int *r_no_service)
     }
 
   slot = open_pcsc_reader (portstr);
-  if (slot == -1 && r_no_service && pcsc_no_service)
-    *r_no_service = 1;
 
+  if (DBG_READER)
+    log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
   return slot;
 }
 
@@ -2979,13 +3002,31 @@ apdu_close_reader (int slot)
 {
   int sw;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_close_reader: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
   sw = apdu_disconnect (slot);
   if (sw)
-    return sw;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
+      return sw;
+    }
   if (reader_table[slot].close_reader)
-    return reader_table[slot].close_reader (slot);
+    {
+      sw = reader_table[slot].close_reader (slot);
+      if (DBG_READER)
+        log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
+      return sw;
+    }
+  if (DBG_READER)
+    log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
   return SW_HOST_NOT_SUPPORTED;
 }
 
@@ -3024,13 +3065,32 @@ apdu_shutdown_reader (int slot)
 {
   int sw;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_shutdown_reader: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_shutdown_reader => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
   sw = apdu_disconnect (slot);
   if (sw)
-    return sw;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_shutdown_reader => 0x%x (apdu_disconnect)\n",
+                   sw);
+      return sw;
+    }
   if (reader_table[slot].shutdown_reader)
-    return reader_table[slot].shutdown_reader (slot);
+    {
+      sw = reader_table[slot].shutdown_reader (slot);
+      if (DBG_READER)
+        log_debug ("leave: apdu_shutdown_reader => 0x%x (close_reader)\n", sw);
+      return sw;
+    }
+  if (DBG_READER)
+    log_debug ("leave: apdu_shutdown_reader => SW_HOST_NOT_SUPPORTED\n");
   return SW_HOST_NOT_SUPPORTED;
 }
 
@@ -3048,14 +3108,24 @@ apdu_enum_reader (int slot, int *used)
 
 
 /* Connect a card.  This is used to power up the card and make sure
-   that an ATR is available.  */
+   that an ATR is available.  Depending on the reader backend it may
+   return an error for an inactive card or if no card is
+   available.  */
 int
 apdu_connect (int slot)
 {
   int sw;
+  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;
+    }
 
   /* Only if the access method provides a connect function we use it.
      If not, we expect that the card has been implicitly connected by
@@ -3077,7 +3147,16 @@ apdu_connect (int slot)
      scdaemon is fired up and apdu_get_status has not yet been called.
      Without that we would force a reset of the card with the next
      call to apdu_get_status.  */
-  apdu_get_status_internal (slot, 1, 1, NULL, NULL);
+  apdu_get_status_internal (slot, 1, 1, &status, NULL);
+  if (sw)
+    ;
+  else if (!(status & APDU_CARD_PRESENT))
+    sw = SW_HOST_NO_CARD;
+  else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
+    sw = SW_HOST_CARD_INACTIVE;
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
 
   return sw;
 }
@@ -3088,8 +3167,15 @@ apdu_disconnect (int slot)
 {
   int sw;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_disconnect: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
 
   if (reader_table[slot].disconnect_card)
     {
@@ -3102,6 +3188,9 @@ apdu_disconnect (int slot)
     }
   else
     sw = 0;
+
+  if (DBG_READER)
+    log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
   return sw;
 }
 
@@ -3137,11 +3226,22 @@ 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 )
-    return SW_HOST_NO_DRIVER;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
+      return SW_HOST_NO_DRIVER;
+    }
 
   if ((sw = lock_slot (slot)))
-    return sw;
+    {
+      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)
@@ -3157,73 +3257,47 @@ apdu_reset (int slot)
     }
 
   unlock_slot (slot);
+  if (DBG_READER)
+    log_debug ("leave: apdu_reset => 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. */
-int
-apdu_activate (int slot)
-{
-  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].get_status_reader)
-    sw = reader_table[slot].get_status_reader (slot, &s);
-
-  if (!sw)
-    {
-      if (!(s & 2))  /* Card not present.  */
-        sw = SW_HOST_NO_CARD;
-      else if ( ((s & 2) && !(s & 4))
-                || !reader_table[slot].atrlen )
-        {
-          /* We don't have an ATR or a card is present though inactive:
-             do a reset now. */
-          if (reader_table[slot].reset_reader)
-            {
-              reader_table[slot].last_status = 0;
-              sw = reader_table[slot].reset_reader (slot);
-              if (!sw)
-                {
-                  /* If we got to here we know that a card is present
-                     and usable.  Thus remember this.  */
-                  reader_table[slot].last_status = (APDU_CARD_USABLE
-                                                    | APDU_CARD_PRESENT
-                                                    | APDU_CARD_ACTIVE);
-                }
-            }
-        }
-    }
-
-  unlock_slot (slot);
-  return sw;
-}
-
-
+/* Return the ATR or NULL if none is available.  On success the length
+   of the ATR is stored at ATRLEN.  The caller must free the returned
+   value.  */
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
 {
   unsigned char *buf;
 
+  if (DBG_READER)
+    log_debug ("enter: apdu_get_atr: slot=%d\n", slot);
+
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
+      return NULL;
+    }
   if (!reader_table[slot].atrlen)
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
+      return NULL;
+    }
+
   buf = xtrymalloc (reader_table[slot].atrlen);
   if (!buf)
-    return NULL;
+    {
+      if (DBG_READER)
+        log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
+      return NULL;
+    }
   memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
   *atrlen = reader_table[slot].atrlen;
+  if (DBG_READER)
+    log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
   return buf;
 }
 
@@ -3294,68 +3368,100 @@ int
 apdu_get_status (int slot, int hang,
                  unsigned int *status, unsigned int *changed)
 {
-  return apdu_get_status_internal (slot, hang, 0, status, changed);
+  int sw;
+
+  if (DBG_READER)
+    log_debug ("enter: apdu_get_status: slot=%d hang=%d\n", slot, hang);
+  sw = apdu_get_status_internal (slot, hang, 0, status, changed);
+  if (DBG_READER)
+    {
+      if (status && changed)
+        log_debug ("leave: apdu_get_status => sw=0x%x status=%u changecnt=%u\n",
+                   sw, *status, *changed);
+      else if (status)
+        log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n",
+                   sw, *status);
+      else if (changed)
+        log_debug ("leave: apdu_get_status => sw=0x%x changed=%u\n",
+                   sw, *changed);
+      else
+        log_debug ("leave: apdu_get_status => sw=0x%x\n", sw);
+    }
+  return sw;
 }
 
 
 /* Check whether the reader supports the ISO command code COMMAND on
-   the keypad.  Return 0 on success.  For a description of the pin
+   the pinpad.  Return 0 on success.  For a description of the pin
    parameters, see ccid-driver.c */
 int
-apdu_check_keypad (int slot, int command, int pin_mode,
-                   int pinlen_min, int pinlen_max, int pin_padlen)
+apdu_check_pinpad (int slot, int command, pininfo_t *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if (reader_table[slot].check_keypad)
-    return reader_table[slot].check_keypad (slot, command,
-                                            pin_mode, pinlen_min, pinlen_max,
-                                            pin_padlen);
+  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_keypad_verify (int slot, int class, int ins, int p0, int p1, int pin_mode,
-                    int pinlen_min, int pinlen_max, int pin_padlen)
+apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+                   pininfo_t *pininfo)
 {
-  struct pininfo_s pininfo;
-
-  pininfo.mode = pin_mode;
-  pininfo.minlen = pinlen_min;
-  pininfo.maxlen = pinlen_max;
-  pininfo.padlen = pin_padlen;
-
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if (reader_table[slot].keypad_verify)
-    return reader_table[slot].keypad_verify (slot, class, ins, p0, p1,
-                                             &pininfo);
+  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_keypad_modify (int slot, int class, int ins, int p0, int p1, int pin_mode,
-                    int pinlen_min, int pinlen_max, int pin_padlen)
+apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+                   pininfo_t *pininfo)
 {
-  struct pininfo_s pininfo;
-
-  pininfo.mode = pin_mode;
-  pininfo.minlen = pinlen_min;
-  pininfo.maxlen = pinlen_max;
-  pininfo.padlen = pin_padlen;
-
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
-  if (reader_table[slot].keypad_modify)
-    return reader_table[slot].keypad_modify (slot, class, ins, p0, p1,
-                                             &pininfo);
+  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;
 }
@@ -3365,7 +3471,7 @@ apdu_keypad_modify (int slot, int class, int ins, int p0, int p1, 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;
@@ -3381,7 +3487,7 @@ 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
+   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:
@@ -3397,7 +3503,7 @@ 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 SHORT_RESULT_BUFFER_SIZE 258
   /* We allocate 8 extra bytes as a safety margin towards a driver bug.  */
@@ -3794,24 +3900,6 @@ apdu_send_simple (int slot, int extended_mode,
 }
 
 
-/* Same as apdu_send_simple but uses the keypad of the reader. */
-int
-apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
-                     int lc, const char *data,
-                     int pin_mode,
-                     int pinlen_min, int pinlen_max, int pin_padlen)
-{
-  struct pininfo_s pininfo;
-
-  pininfo.mode = pin_mode;
-  pininfo.minlen = pinlen_min;
-  pininfo.maxlen = pinlen_max;
-  pininfo.padlen = pin_padlen;
-  return send_le (slot, class, ins, p0, p1, lc, data, -1,
-                  NULL, NULL, &pininfo, 0);
-}
-
-
 /* This is a more generic version of the apdu sending routine.  It
    takes an already formatted APDU in APDUDATA or length APDUDATALEN
    and returns with an APDU including the status word.  With