* keyserver.c (parse_keyserver_uri): If there is a path present, set the
[gnupg.git] / g10 / apdu.c
index e58be59..5f8c579 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  *
  * $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
+   guaranteed to be defined true. */
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
 # include <unistd.h>
 # include <fcntl.h>
 #endif
-#ifdef HAVE_OPENSC
-# include <opensc/opensc.h>
-# ifdef USE_GNU_PTH
-# undef USE_GNU_PTH
-# endif
-#endif
+
 
 /* If requested include the definitions for the remote APDU protocol
    code. */
 #include "dynload.h"
 #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
+#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
 #define NEED_PCSC_WRAPPER 1
 #endif
+#endif
 
 
 #define MAX_READER 4 /* Number of readers we support concurrently. */
 
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
 #define DLSTDCALL __stdcall
 #else
 #define DLSTDCALL
 #define MAX_OPEN_FDS 20
 #endif
 
+/* Helper to pass patrameters 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. */
@@ -98,7 +111,8 @@ 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 *);
+                          unsigned char *, size_t *, struct pininfo_s *);
+  int (*check_keypad)(int, int, int, int, int, int);
   void (*dump_status_reader)(int);
 
   struct {
@@ -114,18 +128,15 @@ struct reader_table_s {
     pid_t pid;
 #endif /*NEED_PCSC_WRAPPER*/
   } pcsc;
-#ifdef HAVE_OPENSC
-  struct {
-    struct sc_context *ctx;
-    struct sc_card *scard;
-  } osc;
-#endif /*HAVE_OPENSC*/
 #ifdef USE_G10CODE_RAPDU
   struct {
     rapdu_t handle;
   } rapdu;
 #endif /*USE_G10CODE_RAPDU*/
+  char *rdrname;     /* Name of the connected reader or NULL if unknown. */
+  int last_status;
   int status;
+  int is_t0;         /* True if we know that we are running T=0. */
   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
@@ -151,14 +162,14 @@ static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad,
 static char (* DLSTDCALL CT_close) (unsigned short ctn);
 
 /* PC/SC constants and function pointer. */
-#define PCSC_SCOPE_USER      0 
-#define PCSC_SCOPE_TERMINAL  1 
-#define PCSC_SCOPE_SYSTEM    2 
-#define PCSC_SCOPE_GLOBAL    3 
+#define PCSC_SCOPE_USER      0
+#define PCSC_SCOPE_TERMINAL  1
+#define PCSC_SCOPE_SYSTEM    2
+#define PCSC_SCOPE_GLOBAL    3
 
-#define PCSC_PROTOCOL_T0     1 
-#define PCSC_PROTOCOL_T1     2 
-#define PCSC_PROTOCOL_RAW    4 
+#define PCSC_PROTOCOL_T0     1
+#define PCSC_PROTOCOL_T1     2
+#define PCSC_PROTOCOL_RAW    4
 
 #define PCSC_SHARE_EXCLUSIVE 1
 #define PCSC_SHARE_SHARED    2
@@ -169,13 +180,69 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_UNPOWER_CARD    2
 #define PCSC_EJECT_CARD      3
 
-struct pcsc_io_request_s {
-  unsigned long protocol; 
+#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.  */
+
+#define PCSC_STATE_UNAWARE     0x0000  /* Want status.  */
+#define PCSC_STATE_IGNORE      0x0001  /* Ignore this reader.  */
+#define PCSC_STATE_CHANGED     0x0002  /* State has changed.  */
+#define PCSC_STATE_UNKNOWN     0x0004  /* Reader unknown.  */
+#define PCSC_STATE_UNAVAILABLE 0x0008  /* Status unavailable.  */
+#define PCSC_STATE_EMPTY       0x0010  /* Card removed.  */
+#define PCSC_STATE_PRESENT     0x0020  /* Card inserted.  */
+#define PCSC_STATE_ATRMATCH    0x0040  /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE   0x0080  /* Exclusive Mode.  */
+#define PCSC_STATE_INUSE       0x0100  /* Shared mode.  */
+#define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
+
+/* Some PC/SC error codes.  */
+#define PCSC_E_CANCELLED               0x80100002
+#define PCSC_E_CANT_DISPOSE            0x8010000E
+#define PCSC_E_INSUFFICIENT_BUFFER     0x80100008
+#define PCSC_E_INVALID_ATR             0x80100015
+#define PCSC_E_INVALID_HANDLE          0x80100003
+#define PCSC_E_INVALID_PARAMETER       0x80100004
+#define PCSC_E_INVALID_TARGET          0x80100005
+#define PCSC_E_INVALID_VALUE           0x80100011
+#define PCSC_E_NO_MEMORY               0x80100006
+#define PCSC_E_UNKNOWN_READER          0x80100009
+#define PCSC_E_TIMEOUT                 0x8010000A
+#define PCSC_E_SHARING_VIOLATION       0x8010000B
+#define PCSC_E_NO_SMARTCARD            0x8010000C
+#define PCSC_E_UNKNOWN_CARD            0x8010000D
+#define PCSC_E_PROTO_MISMATCH          0x8010000F
+#define PCSC_E_NOT_READY               0x80100010
+#define PCSC_E_SYSTEM_CANCELLED        0x80100012
+#define PCSC_E_NOT_TRANSACTED          0x80100016
+#define PCSC_E_READER_UNAVAILABLE      0x80100017
+#define PCSC_W_REMOVED_CARD            0x80100069
+
+
+struct pcsc_io_request_s
+{
+  unsigned long protocol;
   unsigned long pci_len;
 };
 
 typedef struct pcsc_io_request_s *pcsc_io_request_t;
 
+struct pcsc_readerstate_s
+{
+  const char *reader;
+  void *user_data;
+  unsigned long current_state;
+  unsigned long event_state;
+  unsigned long atrlen;
+  unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
 long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
                                            const void *reserved1,
                                            const void *reserved2,
@@ -184,12 +251,21 @@ long (* DLSTDCALL pcsc_release_context) (unsigned long context);
 long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
                                       const char *groups,
                                       char *readers, unsigned long*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
+                                           unsigned long timeout,
+                                           pcsc_readerstate_t readerstates,
+                                           unsigned long nreaderstates);
 long (* DLSTDCALL pcsc_connect) (unsigned 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,
@@ -210,18 +286,20 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
                                      unsigned long timeout);
 
 
+/*  Prototypes.  */
+static int pcsc_get_status (int slot, unsigned int *status);
 
 
 \f
-/* 
+/*
       Helper
  */
+
 
 /* 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. */
-static int 
-new_reader_slot (void)    
+static int
+new_reader_slot (void)
 {
   int i, reader = -1;
 
@@ -251,14 +329,18 @@ new_reader_slot (void)
   reader_table[reader].reset_reader = NULL;
   reader_table[reader].get_status_reader = NULL;
   reader_table[reader].send_apdu_reader = NULL;
+  reader_table[reader].check_keypad = NULL;
   reader_table[reader].dump_status_reader = NULL;
 
   reader_table[reader].used = 1;
+  reader_table[reader].last_status = 0;
+  reader_table[reader].is_t0 = 1;
 #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
+
   return reader;
 }
 
@@ -300,6 +382,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"; 
     default: return "unknown host status error";
     }
 }
@@ -336,8 +419,8 @@ apdu_strerror (int rc)
 
 
 \f
-/* 
-       ct API Interface 
+/*
+       ct API Interface
  */
 
 static const char *
@@ -374,9 +457,9 @@ ct_activate_card (int slot)
   int rc;
   unsigned char dad[1], sad[1], cmd[11], buf[256];
   unsigned short buflen;
-  
+
   /* Check whether card has been inserted. */
-  dad[0] = 1;     /* Destination address: CT. */    
+  dad[0] = 1;     /* Destination address: CT. */
   sad[0] = 2;     /* Source address: Host. */
 
   cmd[0] = 0x20;  /* Class byte. */
@@ -395,8 +478,8 @@ ct_activate_card (int slot)
       return SW_HOST_CARD_IO_ERROR;
     }
 
-  /* Connected, now activate the card. */           
-  dad[0] = 1;    /* Destination address: CT. */    
+  /* Connected, now activate the card. */
+  dad[0] = 1;    /* Destination address: CT. */
   sad[0] = 2;    /* Source address: Host. */
 
   cmd[0] = 0x20;  /* Class byte. */
@@ -461,18 +544,18 @@ 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)
+              unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
 {
   int rc;
   unsigned char dad[1], sad[1];
   unsigned short ctbuflen;
-  
+
   /* If we don't have an ATR, we need to reset the reader first. */
   if (!reader_table[slot].atrlen
       && (rc = reset_ct_reader (slot)))
     return rc;
 
-  dad[0] = 0;     /* Destination address: Card. */    
+  dad[0] = 0;     /* Destination address: Card. */
   sad[0] = 2;     /* Source address: Host. */
   ctbuflen = *buflen;
   if (DBG_CARD_IO)
@@ -524,6 +607,7 @@ 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].dump_status_reader = ct_dump_reader_status;
 
   dump_reader_status (reader);
@@ -531,6 +615,10 @@ open_ct_reader (int port)
 }
 
 \f
+/*
+       PC/SC Interface
+ */
+
 #ifdef NEED_PCSC_WRAPPER
 static int
 writen (int fd, const void *buf, size_t nbytes)
@@ -573,7 +661,7 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
 #else
       n = read (fd, buf, nleft);
 #endif
-      if (n < 0 && errno == EINTR) 
+      if (n < 0 && errno == EINTR)
         continue;
       if (n < 0)
         return -1; /* read error. */
@@ -586,7 +674,7 @@ readn (int fd, void *buf, size_t buflen, size_t *nread)
     *nread = buflen - nleft;
 
 /*   log_printhex ("  readn:", orig_buf, *nread); */
-    
+
   return 0;
 }
 #endif /*NEED_PCSC_WRAPPER*/
@@ -605,48 +693,73 @@ pcsc_error_string (long err)
     {
     case 0x0002: s = "cancelled"; break;
     case 0x000e: s = "can't dispose"; break;
-    case 0x0008: s = "insufficient buffer"; break;   
+    case 0x0008: s = "insufficient buffer"; break;
     case 0x0015: s = "invalid ATR"; break;
     case 0x0003: s = "invalid handle"; break;
-    case 0x0004: s = "invalid parameter"; break; 
+    case 0x0004: s = "invalid parameter"; break;
     case 0x0005: s = "invalid target"; break;
-    case 0x0011: s = "invalid value"; break; 
-    case 0x0006: s = "no memory"; break;  
-    case 0x0013: s = "comm error"; break;      
-    case 0x0001: s = "internal error"; break;     
-    case 0x0014: s = "unknown error"; break; 
-    case 0x0007: s = "waited too long"; break;  
+    case 0x0011: s = "invalid value"; break;
+    case 0x0006: s = "no memory"; break;
+    case 0x0013: s = "comm error"; break;
+    case 0x0001: s = "internal error"; break;
+    case 0x0014: s = "unknown error"; break;
+    case 0x0007: s = "waited too long"; break;
     case 0x0009: s = "unknown reader"; break;
-    case 0x000a: s = "timeout"; break; 
-    case 0x000b: s = "sharing violation"; break;       
+    case 0x000a: s = "timeout"; break;
+    case 0x000b: s = "sharing violation"; break;
     case 0x000c: s = "no smartcard"; break;
-    case 0x000d: s = "unknown card"; break;   
-    case 0x000f: s = "proto mismatch"; break;          
-    case 0x0010: s = "not ready"; break;               
-    case 0x0012: s = "system cancelled"; break;        
+    case 0x000d: s = "unknown card"; break;
+    case 0x000f: s = "proto mismatch"; break;
+    case 0x0010: s = "not ready"; break;
+    case 0x0012: s = "system cancelled"; break;
     case 0x0016: s = "not transacted"; break;
-    case 0x0017: s = "reader unavailable"; break; 
-    case 0x0065: s = "unsupported card"; break;        
-    case 0x0066: s = "unresponsive card"; break;       
-    case 0x0067: s = "unpowered card"; break;          
-    case 0x0068: s = "reset card"; break;              
-    case 0x0069: s = "removed card"; break;            
-    case 0x006a: s = "inserted card"; break;           
-    case 0x001f: s = "unsupported feature"; break;     
-    case 0x0019: s = "PCI too small"; break;           
-    case 0x001a: s = "reader unsupported"; break;      
-    case 0x001b: s = "duplicate reader"; break;        
-    case 0x001c: s = "card unsupported"; break;        
-    case 0x001d: s = "no service"; break;              
-    case 0x001e: s = "service stopped"; break;      
+    case 0x0017: s = "reader unavailable"; break;
+    case 0x0065: s = "unsupported card"; break;
+    case 0x0066: s = "unresponsive card"; break;
+    case 0x0067: s = "unpowered card"; break;
+    case 0x0068: s = "reset card"; break;
+    case 0x0069: s = "removed card"; break;
+    case 0x006a: s = "inserted card"; break;
+    case 0x001f: s = "unsupported feature"; break;
+    case 0x0019: s = "PCI too small"; break;
+    case 0x001a: s = "reader unsupported"; break;
+    case 0x001b: s = "duplicate reader"; break;
+    case 0x001c: s = "card unsupported"; break;
+    case 0x001d: s = "no service"; break;
+    case 0x001e: s = "service stopped"; break;
     default:     s = "unknown PC/SC error code"; break;
     }
   return s;
 }
 
-/* 
-       PC/SC Interface
- */
+/* Map PC/SC error codes to our special host status words.  */
+static int
+pcsc_error_to_sw (long ec)
+{
+  int rc;
+
+  switch (ec)
+    {
+    case 0:  rc = 0; break;
+
+    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_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_PARAMETER:
+    case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
+
+    default:  rc = SW_HOST_GENERAL_ERROR; break;
+    }
+
+  return rc;
+}
 
 static void
 dump_pcsc_reader_status (int slot)
@@ -662,18 +775,317 @@ dump_pcsc_reader_status (int slot)
 }
 
 
-
+/* Send an PC/SC reset command and return a status word on error or 0
+   on success. */
 static int
-pcsc_get_status (int slot, unsigned int *status)
+reset_pcsc_reader (int slot)
 {
-  *status = 1|2|4;  /* FIXME!!!! */
+#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;
+    }
+
+  /* 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 = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  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;
+  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);
+    }
+
+
+  atrlen = 33;
+  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)
+    {
+      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);
+    }
+  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);
+
   return 0;
+#endif /* !NEED_PCSC_WRAPPER */
 }
 
+
 static int
-reset_pcsc_reader (int slot)
+pcsc_get_status (int slot, unsigned int *status)
 {
-  return SW_HOST_NOT_SUPPORTED;
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len, full_len;
+  int i, n;
+  unsigned char msgbuf[9];
+  unsigned char buffer[16];
+  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] = 0x04; /* STATUS 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 STATUS 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 STATUS 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. */
+  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  if (err)
+    {
+      log_error ("pcsc_status failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      /* This is a proper error code, so return immediately.  */
+      return pcsc_error_to_sw (err);
+    }
+
+  full_len = len;
+
+  /* The current version returns 3 words but we allow also for old
+     versions returning only 2 words. */
+  n = 12 < len ? 12 : len;
+  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len))
+      || (len != 8 && len != 12))
+    {
+      log_error ("error receiving PC/SC STATUS response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+
+  slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0));
+
+
+  full_len -= len;
+  /* Newer versions of the wrapper might send more status bytes.
+     Read them. */
+  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 TRANSMIT response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+      full_len -= n;
+    }
+
+  /* We are lucky: The wrapper already returns the data in the
+     required format. */
+  *status = buffer[3];
+
+  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;
+  struct pcsc_readerstate_s rdrstates[1];
+
+  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);
+    }
+
+
+  /*   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":"" ); */
+
+  *status = 0;
+  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+    *status |= 2;
+  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+    *status |= 4;
+  /* 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;
+
+  return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
 }
 
 
@@ -682,7 +1094,8 @@ reset_pcsc_reader (int slot)
    set to BUFLEN.  Returns: CT API error code. */
 static int
 pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+                unsigned char *buffer, size_t *buflen, 
+                struct pininfo_s *pininfo)
 {
 #ifdef NEED_PCSC_WRAPPER
   long err;
@@ -690,6 +1103,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   size_t len, full_len;
   int i, n;
   unsigned char msgbuf[9];
+  int sw = SW_HOST_CARD_IO_ERROR;
 
   if (!reader_table[slot].atrlen
       && (err = reset_pcsc_reader (slot)))
@@ -700,12 +1114,12 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 
   slotp = reader_table + slot;
 
-  if (slotp->pcsc.req_fd == -1 
-      || slotp->pcsc.rsp_fd == -1 
+  if (slotp->pcsc.req_fd == -1
+      || slotp->pcsc.rsp_fd == -1
       || slotp->pcsc.pid == (pid_t)(-1) )
     {
       log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
-      return SW_HOST_CARD_IO_ERROR;
+      return sw;
     }
 
   msgbuf[0] = 0x03; /* TRANSMIT command. */
@@ -741,11 +1155,11 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
     {
       log_error ("pcsc_transmit failed: %s (0x%lx)\n",
                  pcsc_error_string (err), err);
-      return SW_HOST_CARD_IO_ERROR;
+      return pcsc_error_to_sw (err);
     }
 
    full_len = len;
-   
+
    n = *buflen < len ? *buflen : len;
    if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
      {
@@ -787,14 +1201,14 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
-  return -1;
+  return sw;
 
 #else /*!NEED_PCSC_WRAPPER*/
 
   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;
@@ -815,8 +1229,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   if (err)
     log_error ("pcsc_transmit failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
-  
-  return err? SW_HOST_CARD_IO_ERROR:0; 
+
+  return pcsc_error_to_sw (err);
 #endif /*!NEED_PCSC_WRAPPER*/
 }
 
@@ -833,8 +1247,8 @@ close_pcsc_reader (int slot)
 
   slotp = reader_table + slot;
 
-  if (slotp->pcsc.req_fd == -1 
-      || slotp->pcsc.rsp_fd == -1 
+  if (slotp->pcsc.req_fd == -1
+      || slotp->pcsc.rsp_fd == -1
       || slotp->pcsc.pid == (pid_t)(-1) )
     {
       log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
@@ -872,10 +1286,10 @@ close_pcsc_reader (int slot)
   if (err)
     log_error ("pcsc_close failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
-  
-  /* We will the wrapper in any case - errors are merely
+
+  /* We will close the wrapper in any case - errors are merely
      informational. */
-  
+
  command_failed:
   close (slotp->pcsc.req_fd);
   close (slotp->pcsc.rsp_fd);
@@ -889,11 +1303,14 @@ close_pcsc_reader (int slot)
 #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;
   return 0;
 #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)
 {
@@ -909,6 +1326,8 @@ open_pcsc_reader (const char *portstr)
   size_t len;
   unsigned char msgbuf[9];
   int err;
+  unsigned int dummy_status;
+  int sw = SW_HOST_CARD_IO_ERROR;
 
   slot = new_reader_slot ();
   if (slot == -1)
@@ -918,7 +1337,7 @@ open_pcsc_reader (const char *portstr)
   /* 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
      may still be source copied. */
-  
+
   if (pipe (rp) == -1)
     {
       log_error ("error creating a pipe: %s\n", strerror (errno));
@@ -933,7 +1352,7 @@ open_pcsc_reader (const char *portstr)
       slotp->used = 0;
       return -1;
     }
-      
+
   pid = fork ();
   if (pid == -1)
     {
@@ -955,7 +1374,7 @@ open_pcsc_reader (const char *portstr)
       /* Double fork. */
       pid = fork ();
       if (pid == -1)
-        _exit (31); 
+        _exit (31);
       if (pid)
         _exit (0); /* Immediate exit this parent, so that the child
                       gets cleaned up by the init process. */
@@ -965,7 +1384,7 @@ open_pcsc_reader (const char *portstr)
         log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
       if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
         log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
-      
+
       /* Send stderr to the bit bucket. */
       fd = open ("/dev/null", O_WRONLY);
       if (fd == -1)
@@ -990,7 +1409,7 @@ open_pcsc_reader (const char *portstr)
       _exit (31);
     }
 
-  /* 
+  /*
      === Parent ===
    */
   close (wp[0]);
@@ -999,8 +1418,14 @@ open_pcsc_reader (const char *portstr)
   slotp->pcsc.rsp_fd = rp[0];
 
   /* Wait for the intermediate child to terminate. */
-  while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
+#ifdef USE_GNU_PTH
+#define WAIT pth_waitpid
+#else
+#define WAIT waitpid
+#endif
+  while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
     ;
+#undef X
 
   /* Now send the open request. */
   msgbuf[0] = 0x01; /* OPEN command. */
@@ -1032,21 +1457,34 @@ open_pcsc_reader (const char *portstr)
   len -= 4; /* Already read the error code. */
   if (len > DIM (slotp->atr))
     {
-      log_error ("PC/SC returned a too large ATR (len=%x)\n", len);
+      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+                 (unsigned long)len);
       goto command_failed;
     }
   err = (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));
+      sw = pcsc_error_to_sw (err);
       goto command_failed;
     }
+
+  slotp->last_status = 0;
+
+  /* The open request may return a zero for the ATR length to
+     indicate that no card is present.  */
   n = len;
-  if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+  if (n)
     {
-      log_error ("error receiving PC/SC OPEN response: %s\n",
-                 i? strerror (errno) : "premature EOF");
-      goto command_failed;
+      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC OPEN response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      slotp->last_status = (1|2|4| 0x8000);
     }
   slotp->atrlen = len;
 
@@ -1054,9 +1492,13 @@ 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;
 
-  dump_reader_status (slot); 
+  /* Read the status so that IS_T0 will be set. */
+  pcsc_get_status (slot, &dummy_status);
+
+  dump_reader_status (slot);
   return slot;
 
  command_failed:
@@ -1067,7 +1509,9 @@ open_pcsc_reader (const char *portstr)
   kill (slotp->pcsc.pid, SIGTERM);
   slotp->pcsc.pid = (pid_t)(-1);
   slotp->used = 0;
+  /* There is no way to return SW. */
   return -1;
+
 #else /*!NEED_PCSC_WRAPPER */
   long err;
   int slot;
@@ -1089,7 +1533,7 @@ open_pcsc_reader (const char *portstr)
       reader_table[slot].used = 0;
       return -1;
     }
-  
+
   err = pcsc_list_readers (reader_table[slot].pcsc.context,
                            NULL, NULL, &nreader);
   if (!err)
@@ -1100,7 +1544,7 @@ open_pcsc_reader (const char *portstr)
           log_error ("error allocating memory for reader list\n");
           pcsc_release_context (reader_table[slot].pcsc.context);
           reader_table[slot].used = 0;
-          return -1;
+          return -1 /*SW_HOST_OUT_OF_CORE*/;
         }
       err = pcsc_list_readers (reader_table[slot].pcsc.context,
                                NULL, list, &nreader);
@@ -1112,7 +1556,7 @@ open_pcsc_reader (const char *portstr)
       pcsc_release_context (reader_table[slot].pcsc.context);
       reader_table[slot].used = 0;
       xfree (list);
-      return -1;
+      return -1 /*pcsc_error_to_sw (err)*/;
     }
 
   listlen = nreader;
@@ -1132,52 +1576,75 @@ open_pcsc_reader (const char *portstr)
       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;
+      return -1 /*SW_HOST_OUT_OF_CORE*/;
+    }
+  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+  xfree (list);
+
   err = pcsc_connect (reader_table[slot].pcsc.context,
-                      portstr? portstr : list,
+                      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)
+  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;
-      xfree (list);
-      return -1;
-    }      
-  
-  atrlen = 32;
-  /* (We need to pass a dummy buffer.  We use LIST because it ought to
-     be large enough.) */
-  err = pcsc_status (reader_table[slot].pcsc.card,
-                     list, &listlen,
-                     &card_state, &card_protocol,
-                     reader_table[slot].atr, &atrlen);
-  xfree (list);
-  if (err)
+      return -1 /*pcsc_error_to_sw (err)*/;
+    }
+
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+  if (!err)
     {
-      log_error ("pcsc_status failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      reader_table[slot].used = 0;
-      return -1;
+      char reader[250];
+      unsigned long readerlen;
+
+      atrlen = 32;
+      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 (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].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;
 
 /*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
 /*   log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
 
-  dump_reader_status (slot); 
+  dump_reader_status (slot);
   return slot;
 #endif /*!NEED_PCSC_WRAPPER */
 }
@@ -1186,7 +1653,7 @@ open_pcsc_reader (const char *portstr)
 
 \f
 #ifdef HAVE_LIBUSB
-/* 
+/*
      Internal CCID driver interface.
  */
 
@@ -1203,16 +1670,16 @@ close_ccid_reader (int slot)
   ccid_close_reader (reader_table[slot].ccid.handle);
   reader_table[slot].used = 0;
   return 0;
-}                       
-  
+}
+
 
 static int
 shutdown_ccid_reader (int slot)
 {
   ccid_shutdown_reader (reader_table[slot].ccid.handle);
   return 0;
-}                       
-  
+}
+
 
 static int
 reset_ccid_reader (int slot)
@@ -1229,10 +1696,10 @@ reset_ccid_reader (int slot)
   assert (sizeof slotp->atr >= sizeof atr);
   slotp->atrlen = atrlen;
   memcpy (slotp->atr, atr, atrlen);
-  dump_reader_status (slot); 
+  dump_reader_status (slot);
   return 0;
-}                       
-  
+}
+
 
 static int
 get_status_ccid (int slot, unsigned int *status)
@@ -1248,7 +1715,7 @@ get_status_ccid (int slot, unsigned int *status)
     *status = 1|2|4;
   else if (bits == 1)
     *status = 2;
-  else 
+  else
     *status = 0;
 
   return 0;
@@ -1260,7 +1727,8 @@ get_status_ccid (int slot, unsigned int *status)
    set to BUFLEN.  Returns: Internal CCID driver error code. */
 static int
 send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+                unsigned char *buffer, size_t *buflen,
+                struct pininfo_s *pininfo)
 {
   long err;
   size_t maxbuflen;
@@ -1274,16 +1742,43 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
     log_printhex ("  APDU_data:", apdu, apdulen);
 
   maxbuflen = *buflen;
-  err = ccid_transceive (reader_table[slot].ccid.handle,
-                         apdu, apdulen,
-                         buffer, maxbuflen, buflen);
+  if (pininfo)
+    err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+                                  apdu, apdulen,
+                                  pininfo->mode,
+                                  pininfo->minlen,
+                                  pininfo->maxlen,
+                                  pininfo->padlen,
+                                  buffer, maxbuflen, buflen);
+  else
+    err = ccid_transceive (reader_table[slot].ccid.handle,
+                           apdu, apdulen,
+                           buffer, maxbuflen, buflen);
   if (err)
     log_error ("ccid_transceive failed: (0x%lx)\n",
                err);
-  
-  return err; 
+
+  return err;
 }
 
+
+/* Check whether the CCID reader supports the ISO command code COMMAND
+   on the keypad.  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)
+{
+  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);
+}
+
+
 /* Open the reader and try to read an ATR.  */
 static int
 open_ccid_reader (const char *portstr)
@@ -1311,15 +1806,22 @@ open_ccid_reader (const char *portstr)
       slotp->atrlen = 0;
       err = 0;
     }
+  else
+    {
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      reader_table[slot].last_status = (1|2|4| 0x8000);
+    }
 
   reader_table[slot].close_reader = close_ccid_reader;
   reader_table[slot].shutdown_reader = shutdown_ccid_reader;
   reader_table[slot].reset_reader = reset_ccid_reader;
   reader_table[slot].get_status_reader = get_status_ccid;
   reader_table[slot].send_apdu_reader = send_apdu_ccid;
+  reader_table[slot].check_keypad = check_ccid_keypad;
   reader_table[slot].dump_status_reader = dump_ccid_reader_status;
 
-  dump_reader_status (slot); 
+  dump_reader_status (slot);
   return slot;
 }
 
@@ -1329,226 +1831,8 @@ open_ccid_reader (const char *portstr)
 
 
 \f
-#ifdef HAVE_OPENSC
-/* 
-     OpenSC Interface.
-
-     This uses the OpenSC primitives to send APDUs.  We need this
-     because we can't mix OpenSC and native (i.e. ctAPI or PC/SC)
-     access to a card for resource conflict reasons.
- */
-
-
-static int
-close_osc_reader (int slot)
-{
-  /* FIXME: Implement. */
-  reader_table[slot].used = 0;
-  return 0;
-}
-
-static int
-reset_osc_reader (int slot)
-{
-  return SW_HOST_NOT_SUPPORTED;
-}
-
-
-static int
-osc_get_status (int slot, unsigned int *status)
-{
-  return SW_HOST_NOT_SUPPORTED;
-}
-
-
-/* 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: OpenSC error code. */
-static int
-osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
-{
-  long err;
-  struct sc_apdu a;
-  unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
-  unsigned char result[SC_MAX_APDU_BUFFER_SIZE];
-
-  if (DBG_CARD_IO)
-    log_printhex ("  APDU_data:", apdu, apdulen);
-
-  if (apdulen < 4)
-    {
-      log_error ("osc_send_apdu: APDU is too short\n");
-      return SW_HOST_INV_VALUE;
-    }
-
-  memset(&a, 0, sizeof a);
-  a.cla = *apdu++;
-  a.ins = *apdu++;
-  a.p1 = *apdu++;
-  a.p2 = *apdu++;
-  apdulen -= 4;
-
-  if (!apdulen)
-    a.cse = SC_APDU_CASE_1;
-  else if (apdulen == 1) 
-    {
-      a.le = *apdu? *apdu : 256;
-      apdu++; apdulen--;
-      a.cse = SC_APDU_CASE_2_SHORT;
-    }
-  else
-    {
-      a.lc = *apdu++; apdulen--;
-      if (apdulen < a.lc)
-        {
-          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
-          return SW_HOST_INV_VALUE;
-
-        }
-      memcpy(data, apdu, a.lc);
-      apdu += a.lc; apdulen -= a.lc;
-
-      a.data = data;
-      a.datalen = a.lc;
-      
-      if (!apdulen)
-        a.cse = SC_APDU_CASE_3_SHORT;
-      else
-        {
-          a.le = *apdu? *apdu : 256;
-          apdu++; apdulen--;
-          if (apdulen)
-            {
-              log_error ("osc_send_apdu: APDU larger than specified\n");
-              return SW_HOST_INV_VALUE;
-            }
-          a.cse = SC_APDU_CASE_4_SHORT;
-        }
-    }
-
-  a.resp = result;
-  a.resplen = DIM(result);
-
-  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
-  if (err)
-    {
-      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
-      return SW_HOST_CARD_IO_ERROR;
-    }
-
-  if (*buflen < 2 || a.resplen > *buflen - 2)
-    {
-      log_error ("osc_send_apdu: provided buffer too short to store result\n");
-      return SW_HOST_INV_VALUE;
-    }
-  memcpy (buffer, a.resp, a.resplen);
-  buffer[a.resplen] = a.sw1;
-  buffer[a.resplen+1] = a.sw2;
-  *buflen = a.resplen + 2;
-  return 0;
-}
-
-static int
-open_osc_reader (int portno)
-{
-  int err;
-  int slot;
-  reader_table_t slotp;
-
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
-  slotp = reader_table + slot;
-
-  err = sc_establish_context (&slotp->osc.ctx, "scdaemon");
-  if (err)
-    {
-      log_error ("failed to establish SC context: %s\n", sc_strerror (err));
-      slotp->used = 0;
-      return -1;
-    }
-  if (portno < 0 || portno >= slotp->osc.ctx->reader_count)
-    {
-      log_error ("no card reader available\n");
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  /* Redirect to our logging facility. */
-  slotp->osc.ctx->error_file = log_get_stream ();
-  slotp->osc.ctx->debug = opt.debug_sc;
-  slotp->osc.ctx->debug_file = log_get_stream ();
-
-  if (sc_detect_card_presence (slotp->osc.ctx->reader[portno], 0) != 1)
-    {
-      log_error ("no card present\n");
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-  
-  /* We want the standard ISO driver. */
-  /*FIXME: OpenSC does not like "iso7816", so we use EMV for now. */
-  err = sc_set_card_driver(slotp->osc.ctx, "emv");
-  if (err)
-    {
-      log_error ("failed to select the iso7816 driver: %s\n",
-                 sc_strerror (err));
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  /* Now connect the card and hope that OpenSC won't try to be too
-     smart. */
-  err = sc_connect_card (slotp->osc.ctx->reader[portno], 0,
-                         &slotp->osc.scard);
-  if (err)
-    {
-      log_error ("failed to connect card in reader %d: %s\n",
-                 portno, sc_strerror (err));
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-  if (opt.verbose)
-    log_info ("connected to card in opensc reader %d using driver `%s'\n",
-              portno, slotp->osc.scard->driver->name);
-
-  err = sc_lock (slotp->osc.scard);
-  if (err)
-    {
-      log_error ("can't lock card in reader %d: %s\n",
-                 portno, sc_strerror (err));
-      sc_disconnect_card (slotp->osc.scard, 0);
-      sc_release_context (slotp->osc.ctx);
-      slotp->used = 0;
-      return -1;
-    }
-
-  if (slotp->osc.scard->atr_len >= DIM (slotp->atr))
-    log_bug ("ATR returned by opensc is too large\n");
-  slotp->atrlen = slotp->osc.scard->atr_len;
-  memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen);
-
-  reader_table[slot].close_reader = close_osc_reader;
-  reader_table[slot].reset_reader = reset_osc_reader;
-  reader_table[slot].get_status_reader = osc_get_status;
-  reader_table[slot].send_apdu_reader = osc_send_apdu;
-  reader_table[slot].dump_status_reader = NULL;
-
-  dump_reader_status (slot); 
-  return slot;
-}
-
-#endif /* HAVE_OPENSC */
-
-
-\f
 #ifdef USE_G10CODE_RAPDU
-/* 
+/*
      The Remote APDU Interface.
 
      This uses the Remote APDU protocol to contact a reader.
@@ -1567,9 +1851,9 @@ rapdu_status_to_sw (int status)
     {
     case RAPDU_STATUS_SUCCESS:  rc = 0; break;
 
-    case RAPDU_STATUS_INVCMD:  
-    case RAPDU_STATUS_INVPROT:  
-    case RAPDU_STATUS_INVSEQ:  
+    case RAPDU_STATUS_INVCMD:
+    case RAPDU_STATUS_INVPROT:
+    case RAPDU_STATUS_INVSEQ:
     case RAPDU_STATUS_INVCOOKIE:
     case RAPDU_STATUS_INVREADER:  rc = SW_HOST_INV_VALUE;  break;
 
@@ -1634,7 +1918,7 @@ reset_rapdu_reader (int slot)
     {
       log_error ("ATR returned by the RAPDU layer is too large\n");
       rapdu_msg_release (msg);
-      return SW_HOST_INV_VALUE; 
+      return SW_HOST_INV_VALUE;
     }
   slotp->atrlen = msg->datalen;
   memcpy (slotp->atr, msg->data, msg->datalen);
@@ -1688,10 +1972,11 @@ my_rapdu_get_status (int slot, unsigned int *status)
 
 /* 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: OpenSC error code. */
+   set to BUFLEN.  Returns: APDU error code. */
 static int
 my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                    unsigned char *buffer, size_t *buflen)
+                    unsigned char *buffer, size_t *buflen,
+                    struct pininfo_s *pininfo)
 {
   int err;
   reader_table_t slotp;
@@ -1734,12 +2019,12 @@ my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
       rapdu_msg_release (msg);
       return sw;
     }
-  
+
   if (msg->datalen > maxlen)
     {
       log_error ("rapdu response apdu too large\n");
       rapdu_msg_release (msg);
-      return SW_HOST_INV_VALUE; 
+      return SW_HOST_INV_VALUE;
     }
 
   *buflen = msg->datalen;
@@ -1778,6 +2063,7 @@ open_rapdu_reader (int portno,
       return -1;
     }
 
+  rapdu_set_reader (slotp->rapdu.handle, portno);
 
   rapdu_set_iofunc (slotp->rapdu.handle,
                     readfnc, readfnc_value,
@@ -1821,13 +2107,14 @@ 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].dump_status_reader = NULL;
 
-  dump_reader_status (slot); 
+  dump_reader_status (slot);
   rapdu_msg_release (msg);
   return slot;
 
- failure:      
+ failure:
   rapdu_msg_release (msg);
   rapdu_release (slotp->rapdu.handle);
   slotp->used = 0;
@@ -1838,7 +2125,7 @@ open_rapdu_reader (int portno,
 
 
 \f
-/* 
+/*
        Driver Access
  */
 
@@ -1883,8 +2170,7 @@ unlock_slot (int slot)
 
 /* 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).  If
-   OpenSC support is compiled in, we first try to use OpenSC. */
+   the first USB reader.  For PC/SC the first listed reader). */
 int
 apdu_open_reader (const char *portstr)
 {
@@ -1910,16 +2196,6 @@ apdu_open_reader (const char *portstr)
 
 #endif /* HAVE_LIBUSB */
 
-#ifdef HAVE_OPENSC
-  if (!opt.disable_opensc)
-    {
-      int port = portstr? atoi (portstr) : 0;
-
-      return open_osc_reader (port);
-    }
-#endif /* HAVE_OPENSC */  
-
-
   if (opt.ctapi_driver && *opt.ctapi_driver)
     {
       int port = portstr? atoi (portstr) : 32768;
@@ -1927,7 +2203,7 @@ apdu_open_reader (const char *portstr)
       if (!ct_api_loaded)
         {
           void *handle;
-          
+
           handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
           if (!handle)
             {
@@ -1949,7 +2225,7 @@ apdu_open_reader (const char *portstr)
       return open_ct_reader (port);
     }
 
-  
+
   /* No ctAPI configured, so lets try the PC/SC API */
   if (!pcsc_api_loaded)
     {
@@ -1967,18 +2243,28 @@ apdu_open_reader (const char *portstr)
       pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
       pcsc_release_context   = dlsym (handle, "SCardReleaseContext");
       pcsc_list_readers      = dlsym (handle, "SCardListReaders");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_list_readers)
         pcsc_list_readers    = dlsym (handle, "SCardListReadersA");
 #endif
+      pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
+#if defined(_WIN32) || defined(__CYGWIN__)
+      if (!pcsc_get_status_change)
+        pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
+#endif
       pcsc_connect           = dlsym (handle, "SCardConnect");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_connect)
         pcsc_connect         = dlsym (handle, "SCardConnectA");
 #endif
+      pcsc_reconnect         = dlsym (handle, "SCardReconnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+      if (!pcsc_reconnect)
+        pcsc_reconnect       = dlsym (handle, "SCardReconnectA");
+#endif
       pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
       pcsc_status            = dlsym (handle, "SCardStatus");
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
       if (!pcsc_status)
         pcsc_status          = dlsym (handle, "SCardStatusA");
 #endif
@@ -1988,34 +2274,38 @@ apdu_open_reader (const char *portstr)
       pcsc_set_timeout       = dlsym (handle, "SCardSetTimeout");
 
       if (!pcsc_establish_context
-          || !pcsc_release_context  
-          || !pcsc_list_readers     
-          || !pcsc_connect          
+          || !pcsc_release_context
+          || !pcsc_list_readers
+          || !pcsc_get_status_change
+          || !pcsc_connect
+          || !pcsc_reconnect
           || !pcsc_disconnect
           || !pcsc_status
           || !pcsc_begin_transaction
           || !pcsc_end_transaction
-          || !pcsc_transmit         
+          || !pcsc_transmit
           /* || !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)\n",
+                     "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                      !!pcsc_establish_context,
-                     !!pcsc_release_context,  
-                     !!pcsc_list_readers,     
-                     !!pcsc_connect,          
+                     !!pcsc_release_context,
+                     !!pcsc_list_readers,
+                     !!pcsc_get_status_change,
+                     !!pcsc_connect,
+                     !!pcsc_reconnect,
                      !!pcsc_disconnect,
                      !!pcsc_status,
                      !!pcsc_begin_transaction,
                      !!pcsc_end_transaction,
-                     !!pcsc_transmit,         
+                     !!pcsc_transmit,
                      !!pcsc_set_timeout );
           dlclose (handle);
           return -1;
         }
-#endif /*!NEED_PCSC_WRAPPER*/  
+#endif /*!NEED_PCSC_WRAPPER*/
       pcsc_api_loaded = 1;
     }
 
@@ -2029,7 +2319,7 @@ apdu_open_reader (const char *portstr)
    only be called once and the slot will not be valid afther this.
 
    If PORTSTR is NULL we default to the first availabe port.
-*/  
+*/
 int
 apdu_open_remote_reader (const char *portstr,
                          const unsigned char *cookie, size_t length,
@@ -2049,7 +2339,7 @@ apdu_open_remote_reader (const char *portstr,
                             writefnc, writefnc_value,
                             closefnc, closefnc_value);
 #else
-#ifdef _WIN32 
+#ifdef _WIN32
   errno = ENOENT;
 #else
   errno = ENOSYS;
@@ -2102,13 +2392,21 @@ apdu_reset (int slot)
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  
+
   if ((sw = lock_slot (slot)))
     return sw;
 
+  reader_table[slot].last_status = 0;
   if (reader_table[slot].reset_reader)
     sw = reader_table[slot].reset_reader (slot);
 
+  if (!sw)
+    {
+      /* If 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);
+    }
+
   unlock_slot (slot);
   return sw;
 }
@@ -2126,7 +2424,7 @@ apdu_activate (int slot)
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  
+
   if ((sw = trylock_slot (slot)))
     return sw;
 
@@ -2143,24 +2441,33 @@ apdu_activate (int slot)
           /* We don't have an ATR or a card is present though inactive:
              do a reset now. */
           if (reader_table[slot].reset_reader)
-            sw = reader_table[slot].reset_reader (slot);
+            {
+              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);
+                }
+            }
         }
     }
-  
+
   unlock_slot (slot);
   return sw;
 }
 
-  
+
 
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
 {
-  char *buf;
+  unsigned char *buf;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return NULL;
-  
+
   buf = xtrymalloc (reader_table[slot].atrlen);
   if (!buf)
     return NULL;
@@ -2170,7 +2477,7 @@ apdu_get_atr (int slot, size_t *atrlen)
 }
 
 
-    
+
 /* Retrieve the status for SLOT. The function does only wait for the
    card to become available if HANG is set to true. On success the
    bits in STATUS will be set to
@@ -2205,7 +2512,22 @@ apdu_get_status (int slot, int hang,
   unlock_slot (slot);
 
   if (sw)
-    return sw;
+    {
+      reader_table[slot].last_status = 0;
+      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 )
+    {
+      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;
+    }
+  reader_table[slot].last_status = (s | 0x8000);
 
   if (status)
     *status = s;
@@ -2215,11 +2537,30 @@ apdu_get_status (int slot, int hang,
 }
 
 
+/* Check whether the reader supports the ISO command code COMMAND on
+   the keypad.  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)
+{
+  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);
+  else
+    return SW_HOST_NOT_SUPPORTED;
+}
+
+
 /* Dispatcher for the actual send_apdu function. Note, that this
    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)
+           unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
@@ -2227,24 +2568,20 @@ 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;
 }
 
-/* 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.  */
-int 
-apdu_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)
+
+/* Core APDU trabceiver function. Parameters are described at
+   apdu_send_le with the exception of PININFO which indicates keypad
+   related operations if not NULL. */
+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)
 {
 #define RESULTLEN 256
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
@@ -2263,9 +2600,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
                class, ins, p0, p1, lc, le);
 
   if (lc != -1 && (lc > 255 || lc < 0))
-    return SW_WRONG_LENGTH; 
-  if (le != -1 && (le > 256 || le < 1))
-    return SW_WRONG_LENGTH; 
+    return SW_WRONG_LENGTH;
+  if (le != -1 && (le > 256 || le < 0))
+    return SW_WRONG_LENGTH;
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
@@ -2282,14 +2619,18 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
       apdu[apdulen++] = lc;
       memcpy (apdu+apdulen, data, lc);
       apdulen += lc;
+      /* 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)
-    apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */
+    apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
   assert (sizeof (apdu) >= apdulen);
   /* As safeguard don't pass any garbage from the stack to the driver. */
   memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
   resultlen = RESULTLEN;
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_simple(%d) failed: %s\n",
@@ -2302,7 +2643,8 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   resultlen -= 2;
   if (DBG_CARD_IO)
     {
-      log_debug (" response: sw=%04X  datalen=%d\n", sw, resultlen);
+      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);
     }
@@ -2344,7 +2686,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
       do
         {
           int len = (sw & 0x00ff);
-          
+
           if (DBG_CARD_IO)
             log_debug ("apdu_send_simple(%d): %d more bytes available\n",
                        slot, len);
@@ -2353,10 +2695,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
-          apdu[apdulen++] = len; 
+          apdu[apdulen++] = len;
           memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
           resultlen = RESULTLEN;
-          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          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",
@@ -2368,7 +2710,8 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
           resultlen -= 2;
           if (DBG_CARD_IO)
             {
-              log_debug ("     more: sw=%04X  datalen=%d\n", sw, resultlen);
+              log_debug ("     more: sw=%04X  datalen=%d\n",
+                         sw, (unsigned int)resultlen);
               if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
                 log_printhex ("     dump: ", result, resultlen);
             }
@@ -2401,7 +2744,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
                       slot, sw);
         }
       while ((sw & 0xff00) == SW_MORE_DATA);
-      
+
       if (retbuf)
         {
           *retbuflen = p - *retbuf;
@@ -2415,12 +2758,33 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
 
   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.  */
+int
+apdu_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)
+{
+  return send_le (slot, class, ins, p0, p1,
+                  lc, data, le,
+                  retbuf, retbuflen,
+                  NULL);
+}
+
+
+/* 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
@@ -2429,12 +2793,12 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
    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 
+int
 apdu_send (int slot, int class, int ins, int p0, int p1,
            int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
 {
-  return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, 
-                       retbuf, retbuflen);
+  return send_le (slot, class, ins, p0, p1, lc, data, 256,
+                  retbuf, retbuflen, NULL);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -2443,11 +2807,29 @@ apdu_send (int slot, int class, int ins, int p0, int p1,
    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. */
-int 
+int
 apdu_send_simple (int slot, int class, int ins, int p0, int p1,
                   int lc, const char *data)
 {
-  return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL);
+  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+}
+
+
+/* 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);
 }
 
 
@@ -2459,7 +2841,7 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1,
    the end.  The function does not return a regular status word but 0
    on success.  If the slot is locked, the fucntion returns
    immediately.*/
-int 
+int
 apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                   int handle_more,
                   unsigned char **retbuf, size_t *retbuflen)
@@ -2480,15 +2862,15 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if ((sw = trylock_slot (slot)))
     return sw;
 
-  /* We simply trucntate a too long APDU.  */
+  /* We simply trunctate a too long APDU.  */
   if (apdudatalen > sizeof apdu)
     apdudatalen = sizeof apdu;
   apdulen = apdudatalen;
   memcpy (apdu, apdudata, apdudatalen);
   class = apdulen? *apdu : 0;
 
-
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  resultlen = RESULTLEN;
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_direct(%d) failed: %s\n",
@@ -2501,7 +2883,8 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   resultlen -= 2;
   if (DBG_CARD_IO)
     {
-      log_debug (" response: sw=%04X  datalen=%d\n", sw, resultlen);
+      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);
     }
@@ -2529,7 +2912,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
       do
         {
           int len = (sw & 0x00ff);
-          
+
           if (DBG_CARD_IO)
             log_debug ("apdu_send_direct(%d): %d more bytes available\n",
                        slot, len);
@@ -2538,10 +2921,10 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
-          apdu[apdulen++] = len; 
+          apdu[apdulen++] = len;
           memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
           resultlen = RESULTLEN;
-          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          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",
@@ -2553,7 +2936,8 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
           resultlen -= 2;
           if (DBG_CARD_IO)
             {
-              log_debug ("     more: sw=%04X  datalen=%d\n", sw, resultlen);
+              log_debug ("     more: sw=%04X  datalen=%d\n",
+                         sw, (unsigned int)resultlen);
               if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
                 log_printhex ("     dump: ", result, resultlen);
             }
@@ -2586,7 +2970,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                       slot, sw);
         }
       while ((sw & 0xff00) == SW_MORE_DATA);
-      
+
       if (retbuf)
         {
           *retbuflen = p - *retbuf;
@@ -2622,9 +3006,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
 
   if (DBG_CARD_IO && retbuf)
     log_printhex ("      dump: ", *retbuf, *retbuflen);
+
   return 0;
 #undef RESULTLEN
 }
-
-