SCD: Add option enable-keypad-varlen and support for GEMPC_PINPAD.
[gnupg.git] / scd / ccid-driver.c
index 0860413..6d135e0 100644 (file)
@@ -1,6 +1,6 @@
 /* ccid-driver.c - USB ChipCardInterfaceDevices driver
  * Copyright (C) 2003, 2004, 2005, 2006, 2007
- *               2008  Free Software Foundation, Inc.
+ *               2008, 2009  Free Software Foundation, Inc.
  * Written by Werner Koch.
  *
  * This file is part of GnuPG.
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_PTH
+# include <pth.h>
+#endif /*HAVE_PTH*/
 
 #include <usb.h>
 
 #endif /* This source not used by scdaemon. */
 
 
+#ifndef EAGAIN
+#define EAGAIN  EWOULDBLOCK
+#endif
+
+
 
 enum {
   RDR_to_PC_NotifySlotChange= 0x50,
@@ -200,9 +209,21 @@ enum {
   VENDOR_SCM    = 0x04e6,
   VENDOR_OMNIKEY= 0x076b,
   VENDOR_GEMPC  = 0x08e6,
-  VENDOR_KAAN   = 0x0d46
+  VENDOR_KAAN   = 0x0d46,
+  VENDOR_FSIJ   = 0x234b,
+  VENDOR_VASCO  = 0x1a44
 };
 
+/* Some product ids.  */
+#define SCM_SCR331      0xe001
+#define SCM_SCR331DI    0x5111
+#define SCM_SCR335      0x5115
+#define SCM_SCR3320     0x5117
+#define SCM_SPR532      0xe003
+#define CHERRY_ST2000   0x003e
+#define VASCO_920       0x0920
+#define GEMPC_PINPAD   0x3478
+
 /* A list and a table with special transport descriptions. */
 enum {
   TRANSPORT_USB    = 0, /* Standard USB transport. */
@@ -239,13 +260,27 @@ struct ccid_driver_s
   int seqno;
   unsigned char t1_ns;
   unsigned char t1_nr;
-  int nonnull_nad;
-  int auto_ifsd;
+  unsigned char nonnull_nad;
   int max_ifsd;
   int ifsd;
-  int powered_off;
-  int has_pinpad;
-  int apdu_level;     /* Reader supports short APDU level exchange.  */
+  int ifsc;
+  unsigned char apdu_level:2;     /* Reader supports short APDU level
+                                     exchange.  With a value of 2 short
+                                     and extended level is supported.*/
+  unsigned int auto_voltage:1;
+  unsigned int auto_param:1;
+  unsigned int auto_pps:1;
+  unsigned int auto_ifsd:1;
+  unsigned int powered_off:1;
+  unsigned int has_pinpad:2;
+  unsigned int enodev_seen:1;
+
+  time_t last_progress; /* Last time we sent progress line.  */
+
+  /* The progress callback and its first arg as supplied to
+     ccid_set_progress_cb.  */
+  void (*progress_cb)(void *, const char *, int, int, int);
+  void *progress_cb_arg;
 };
 
 
@@ -253,17 +288,19 @@ static int initialized_usb; /* Tracks whether USB has been initialized. */
 static int debug_level;     /* Flag to control the debug output. 
                                0 = No debugging
                                1 = USB I/O info
-                               2 = T=1 protocol tracing
+                               2 = Level 1 + T=1 protocol tracing
+                               3 = Level 2 + USB/I/O tracing of SlotStatus.
                               */
 
 
 static unsigned int compute_edc (const unsigned char *data, size_t datalen,
                                  int use_crc);
-static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
+static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+                     int no_debug);
 static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                     size_t *nread, int expected_type, int seqno, int timeout,
                     int no_debug);
-static int abort_cmd (ccid_driver_t handle);
+static int abort_cmd (ccid_driver_t handle, int seqno);
 
 /* Convert a little endian stored 4 byte value into an unsigned
    integer. */
@@ -273,6 +310,15 @@ convert_le_u32 (const unsigned char *buf)
   return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 
 }
 
+
+/* Convert a little endian stored 2 byte value into an unsigned
+   integer. */
+static unsigned int 
+convert_le_u16 (const unsigned char *buf)
+{
+  return buf[0] | (buf[1] << 8); 
+}
+
 static void
 set_msg_len (unsigned char *msg, unsigned int length)
 {
@@ -283,6 +329,47 @@ set_msg_len (unsigned char *msg, unsigned int length)
 }
 
 
+static void
+my_sleep (int seconds)
+{
+#ifdef HAVE_PTH
+  /* With Pth we also call the standard sleep(0) so that the process
+     may give up its timeslot.  */
+  if (!seconds)
+    {
+# ifdef HAVE_W32_SYSTEM    
+      Sleep (0);
+# else
+      sleep (0);
+# endif
+    }
+  pth_sleep (seconds);
+#else
+# ifdef HAVE_W32_SYSTEM    
+  Sleep (seconds*1000);
+# else
+  sleep (seconds);
+# endif
+#endif
+}
+
+static void
+print_progress (ccid_driver_t handle)
+{
+  time_t ct = time (NULL);
+
+  /* We don't want to print progress lines too often. */
+  if (ct == handle->last_progress)
+    return;
+
+  if (handle->progress_cb)
+    handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0);
+
+  handle->last_progress = ct;
+}
+
+
+
 /* Pint an error message for a failed CCID command including a textual
    error code.  MSG shall be the CCID message at a minimum of 10 bytes. */
 static void
@@ -328,8 +415,318 @@ print_command_failed (const unsigned char *msg)
     }
   DEBUGOUT_1 ("CCID command failed: %s\n", t);
 }
+
+
+static void
+print_pr_data (const unsigned char *data, size_t datalen, size_t off)
+{
+  int any = 0;
+
+  for (; off < datalen; off++)
+    {
+      if (!any || !(off % 16))
+        {
+          if (any)
+            DEBUGOUT_LF ();
+          DEBUGOUT_1 ("  [%04lu] ", (unsigned long) off);
+        }
+      DEBUGOUT_CONT_1 (" %02X", data[off]);
+      any = 1;
+    }
+  if (any && (off % 16))
+    DEBUGOUT_LF ();
+}
+
+static void
+print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+  DEBUGOUT_1 ("%s:\n", name);
+  if (msglen < 7)
+    return;
+  DEBUGOUT_1 ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
+  DEBUGOUT_1 ("  bSlot .............: %u\n", msg[5]);
+  DEBUGOUT_1 ("  bSeq ..............: %u\n", msg[6]);
+}
+
+
+static void
+print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_2 ("  bPowerSelect ......: 0x%02x (%s)\n", msg[7],
+              msg[7] == 0? "auto":
+              msg[7] == 1? "5.0 V":
+              msg[7] == 2? "3.0 V":
+              msg[7] == 3? "1.8 V":"");
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
+{
+  unsigned int val;
+
+  print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bBWI ..............: 0x%02x\n", msg[7]);
+  val = convert_le_u16 (msg+8);
+  DEBUGOUT_2 ("  wLevelParameter ...: 0x%04x%s\n", val,
+              val == 1? " (continued)":
+              val == 2? " (continues+ends)":
+              val == 3? " (continues+continued)":
+              val == 16? " (DataBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_getparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setparameters (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bProtocolNum ......: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_escape (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_iccclock (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bClockCommand .....: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bmChanges .........: 0x%02x\n", msg[7]);
+  DEBUGOUT_1 ("  bClassGetResponse .: 0x%02x\n", msg[8]);
+  DEBUGOUT_1 ("  bClassEnvelope ....: 0x%02x\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_secure (const unsigned char *msg, size_t msglen)
+{
+  unsigned int val;
+
+  print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bBMI ..............: 0x%02x\n", msg[7]);
+  val = convert_le_u16 (msg+8);
+  DEBUGOUT_2 ("  wLevelParameter ...: 0x%04x%s\n", val,
+              val == 1? " (continued)":
+              val == 2? " (continues+ends)":
+              val == 3? " (continues+continued)":
+              val == 16? " (DataBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_mechanical (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bFunction .........: 0x%02x\n", msg[7]);
+  print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_abort (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
+  if (msglen < 10)
+    return;
+  print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_unknown (const unsigned char *msg, size_t msglen)
+{
+  print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
+  if (msglen < 10)
+    return;
+  print_pr_data (msg, msglen, 0);
+}
+
+
+static void
+print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+  DEBUGOUT_1 ("%s:\n", name);
+  if (msglen < 9)
+    return;
+  DEBUGOUT_1 ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
+  DEBUGOUT_1 ("  bSlot .............: %u\n", msg[5]);
+  DEBUGOUT_1 ("  bSeq ..............: %u\n", msg[6]);
+  DEBUGOUT_1 ("  bStatus ...........: %u\n", msg[7]);
+  if (msg[8])
+    DEBUGOUT_1 ("  bError ............: %u\n", msg[8]);
+}
+
+
+static void
+print_r2p_datablock (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
+  if (msglen < 10)
+    return;
+  if (msg[9])
+    DEBUGOUT_2 ("  bChainParameter ...: 0x%02x%s\n", msg[9],
+                msg[9] == 1? " (continued)":
+                msg[9] == 2? " (continues+ends)":
+                msg[9] == 3? " (continues+continued)":
+                msg[9] == 16? " (XferBlock-expected)":"");
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_2 ("  bClockStatus ......: 0x%02x%s\n", msg[9],
+              msg[9] == 0? " (running)":
+              msg[9] == 1? " (stopped-L)":
+              msg[9] == 2? " (stopped-H)":
+              msg[9] == 3? " (stopped)":"");
+  print_pr_data (msg, msglen, 10);
+}
   
 
+static void
+print_r2p_parameters (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
+  if (msglen < 10)
+    return;
+
+  DEBUGOUT_1 ("  protocol ..........: T=%d\n", msg[9]);
+  if (msglen == 17 && msg[9] == 1)
+    {
+      /* Protocol T=1.  */
+      DEBUGOUT_1 ("  bmFindexDindex ....: %02X\n", msg[10]);
+      DEBUGOUT_1 ("  bmTCCKST1 .........: %02X\n", msg[11]);
+      DEBUGOUT_1 ("  bGuardTimeT1 ......: %02X\n", msg[12]);
+      DEBUGOUT_1 ("  bmWaitingIntegersT1: %02X\n", msg[13]);
+      DEBUGOUT_1 ("  bClockStop ........: %02X\n", msg[14]);
+      DEBUGOUT_1 ("  bIFSC .............: %d\n", msg[15]);
+      DEBUGOUT_1 ("  bNadValue .........: %d\n", msg[16]);
+    }
+  else
+    print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_escape (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  buffer[9] .........: %02X\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_datarate (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
+  if (msglen < 10)
+    return;
+  if (msglen >= 18)
+    {
+      DEBUGOUT_1 ("  dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
+      DEBUGOUT_1 ("  dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
+      print_pr_data (msg, msglen, 18);
+    }
+  else
+    print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_unknown (const unsigned char *msg, size_t msglen)
+{
+  print_r2p_header ("Unknown RDR_to_PC command", msg, msglen);
+  if (msglen < 10)
+    return;
+  DEBUGOUT_1 ("  bMessageType ......: %02X\n", msg[0]);
+  DEBUGOUT_1 ("  buffer[9] .........: %02X\n", msg[9]);
+  print_pr_data (msg, msglen, 10);
+}
+
+
 /* Given a handle used for special transport prepare it for use.  In
    particular setup all information in way that resembles what
    parse_cccid_descriptor does. */
@@ -367,7 +764,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
 {
   unsigned int i;
   unsigned int us;
-  int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
+  int have_t1 = 0, have_tpdu=0;
 
 
   handle->nonnull_nad = 0;
@@ -376,6 +773,9 @@ parse_ccid_descriptor (ccid_driver_t handle,
   handle->ifsd = 0;
   handle->has_pinpad = 0;
   handle->apdu_level = 0;
+  handle->auto_voltage = 0;
+  handle->auto_param = 0;
+  handle->auto_pps = 0;
   DEBUGOUT_3 ("idVendor: %04X  idProduct: %04X  bcdDevice: %04X\n",
               handle->id_vendor, handle->id_product, handle->bcd_device);
   if (buflen < 54 || buf[0] < 54)
@@ -451,23 +851,32 @@ parse_ccid_descriptor (ccid_driver_t handle,
   DEBUGOUT_1 ("  dwFeatures       %08X\n", us);
   if ((us & 0x0002))
     {
-      DEBUGOUT ("    Auto configuration based on ATR\n");
-      have_auto_conf = 1;
+      DEBUGOUT ("    Auto configuration based on ATR (assumes auto voltage)\n");
+      handle->auto_voltage = 1;
     }
   if ((us & 0x0004))
     DEBUGOUT ("    Auto activation on insert\n");
   if ((us & 0x0008))
-    DEBUGOUT ("    Auto voltage selection\n");
+    {
+      DEBUGOUT ("    Auto voltage selection\n");
+      handle->auto_voltage = 1;
+    }
   if ((us & 0x0010))
     DEBUGOUT ("    Auto clock change\n");
   if ((us & 0x0020))
     DEBUGOUT ("    Auto baud rate change\n");
   if ((us & 0x0040))
-    DEBUGOUT ("    Auto parameter negotation made by CCID\n");
+    {
+      DEBUGOUT ("    Auto parameter negotiation made by CCID\n");
+      handle->auto_param = 1;
+    }
   else if ((us & 0x0080))
-    DEBUGOUT ("    Auto PPS made by CCID\n");
-  else if ((us & (0x0040 | 0x0080)))
-    DEBUGOUT ("    WARNING: conflicting negotation features\n");
+    {
+      DEBUGOUT ("    Auto PPS made by CCID\n");
+      handle->auto_pps = 1;
+    }
+  if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
+    DEBUGOUT ("    WARNING: conflicting negotiation features\n");
 
   if ((us & 0x0100))
     DEBUGOUT ("    CCID can set ICC in clock stop mode\n");
@@ -495,7 +904,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
   else if ((us & 0x00040000))
     {
       DEBUGOUT ("    Short and extended APDU level exchange\n");
-      handle->apdu_level = 1;
+      handle->apdu_level = 2;
     }
   else if ((us & 0x00070000))
     DEBUGOUT ("    WARNING: conflicting exchange levels\n");
@@ -543,11 +952,10 @@ parse_ccid_descriptor (ccid_driver_t handle,
     DEBUGOUT_LF ();
   }
 
-  if (!have_t1 || !(have_tpdu  || handle->apdu_level) || !have_auto_conf)
+  if (!have_t1 || !(have_tpdu  || handle->apdu_level))
     {
       DEBUGOUT ("this drivers requires that the reader supports T=1, "
-                "TPDU or APDU level exchange and auto configuration - "
-                "this is not available\n");
+                "TPDU or APDU level exchange - this is not available\n");
       return -1;
     }
 
@@ -562,13 +970,19 @@ parse_ccid_descriptor (ccid_driver_t handle,
         0x5111 - SCR 331-DI 
         0x5115 - SCR 335 
         0xe003 - SPR 532 
+     The     
+         0x5117 - SCR 3320 USB ID-000 reader
+     seems to be very slow but enabling this workaround boosts the
+     performance to a a more or less acceptable level (tested by David). 
+         
   */
   if (handle->id_vendor == VENDOR_SCM
-      && handle->max_ifsd > 48      
-      && (  (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
-          ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
-          ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514)
-          ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
+      && handle->max_ifsd > 48
+      && (  (handle->id_product == SCM_SCR331   && handle->bcd_device < 0x0516)
+          ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
+          ||(handle->id_product == SCM_SCR335   && handle->bcd_device < 0x0514)
+          ||(handle->id_product == SCM_SPR532   && handle->bcd_device < 0x0504)
+          ||(handle->id_product == SCM_SCR3320  && handle->bcd_device < 0x0522)
           ))
     {
       DEBUGOUT ("enabling workaround for buggy SCM readers\n");
@@ -748,16 +1162,20 @@ scan_or_find_usb_device (int scan_mode,
             {
               ifcdesc = (interface->altsetting + set_no);
               /* The second condition is for older SCM SPR 532 who did
-                 not know about the assigned CCID class.  Instead of
-                 trying to interpret the strings we simply check the
-                 product ID. */
+                 not know about the assigned CCID class.  The third
+                 condition does the same for a Cherry SmartTerminal
+                 ST-2000.  Instead of trying to interpret the strings
+                 we simply check the product ID. */
               if (ifcdesc && ifcdesc->extra
                   && ((ifcdesc->bInterfaceClass == 11
                        && ifcdesc->bInterfaceSubClass == 0
                        && ifcdesc->bInterfaceProtocol == 0)
                       || (ifcdesc->bInterfaceClass == 255
                           && dev->descriptor.idVendor == VENDOR_SCM
-                          && dev->descriptor.idProduct == 0xe003)))
+                          && dev->descriptor.idProduct == SCM_SPR532)
+                      || (ifcdesc->bInterfaceClass == 255
+                          && dev->descriptor.idVendor == VENDOR_CHERRY
+                          && dev->descriptor.idProduct == CHERRY_ST2000)))
                 {
                   idev = usb_open (dev);
                   if (!idev)
@@ -1071,7 +1489,8 @@ scan_or_find_devices (int readerno, const char *readerid,
 /* Set the level of debugging to LEVEL and return the old level.  -1
    just returns the old level.  A level of 0 disables debugging, 1
    enables debugging, 2 enables additional tracing of the T=1
-   protocol, other values are not yet defined.
+   protocol, 3 additionally enables debugging for GetSlotStatus, other
+   values are not yet defined.
 
    Note that libusb may provide its own debugging feature which is
    enabled by setting the envvar USB_DEBUG.  */
@@ -1247,7 +1666,7 @@ do_close_reader (ccid_driver_t handle)
       set_msg_len (msg, 0);
       msglen = 10;
       
-      rc = bulk_out (handle, msg, msglen);
+      rc = bulk_out (handle, msg, msglen, 0);
       if (!rc)
         bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
                  seqno, 2000, 0);
@@ -1340,6 +1759,20 @@ ccid_shutdown_reader (ccid_driver_t handle)
 }
 
 
+int 
+ccid_set_progress_cb (ccid_driver_t handle, 
+                      void (*cb)(void *, const char *, int, int, int),
+                      void *cb_arg)
+{
+  if (!handle || !handle->rid)
+    return CCID_DRIVER_ERR_INV_VALUE;
+
+  handle->progress_cb = cb;
+  handle->progress_cb_arg = cb_arg;
+  return 0;
+}
+
+
 /* Close the reader HANDLE. */
 int 
 ccid_close_reader (ccid_driver_t handle)
@@ -1391,10 +1824,68 @@ writen (int fd, const void *buf, size_t nbytes)
 /* Write a MSG of length MSGLEN to the designated bulk out endpoint.
    Returns 0 on success. */
 static int
-bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
+bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+          int no_debug)
 {
   int rc;
 
+  /* No need to continue and clutter the log with USB write error
+     messages after we got the first ENODEV.  */
+  if (handle->enodev_seen)
+    return CCID_DRIVER_ERR_NO_READER;
+
+  if (debug_level && (!no_debug || debug_level >= 3))
+    {
+      switch (msglen? msg[0]:0)
+        {
+        case PC_to_RDR_IccPowerOn:
+          print_p2r_iccpoweron (msg, msglen);
+          break;
+        case PC_to_RDR_IccPowerOff:
+          print_p2r_iccpoweroff (msg, msglen);
+          break;
+       case PC_to_RDR_GetSlotStatus:
+          print_p2r_getslotstatus (msg, msglen);
+          break;
+        case PC_to_RDR_XfrBlock:
+          print_p2r_xfrblock (msg, msglen);
+          break;
+       case PC_to_RDR_GetParameters:
+          print_p2r_getparameters (msg, msglen);
+          break;
+        case PC_to_RDR_ResetParameters:
+          print_p2r_resetparameters (msg, msglen);
+          break;
+       case PC_to_RDR_SetParameters:
+          print_p2r_setparameters (msg, msglen);
+          break;
+        case PC_to_RDR_Escape:
+          print_p2r_escape (msg, msglen);
+          break;
+        case PC_to_RDR_IccClock:
+          print_p2r_iccclock (msg, msglen);
+          break;
+       case PC_to_RDR_T0APDU:
+          print_p2r_to0apdu (msg, msglen);
+          break;
+        case PC_to_RDR_Secure:
+          print_p2r_secure (msg, msglen);
+          break;
+        case PC_to_RDR_Mechanical:
+          print_p2r_mechanical (msg, msglen);
+          break;
+        case PC_to_RDR_Abort:
+          print_p2r_abort (msg, msglen);
+          break;
+        case PC_to_RDR_SetDataRate:
+          print_p2r_setdatarate (msg, msglen);
+          break;
+        default:
+          print_p2r_unknown (msg, msglen);
+          break;
+        }
+    }
+  
   if (handle->idev)
     {
       rc = usb_bulk_write (handle->idev, 
@@ -1403,8 +1894,27 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
                            5000 /* ms timeout */);
       if (rc == msglen)
         return 0;
+#ifdef ENODEV
+      if (rc == -(ENODEV))
+        {
+          /* The Linux libusb returns a negative error value.  Catch
+             the most important one.  */
+          errno = ENODEV;
+          rc = -1;
+        }
+#endif /*ENODEV*/
+
       if (rc == -1)
-        DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+        {
+          DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+#ifdef ENODEV
+          if (errno == ENODEV)
+            {
+              handle->enodev_seen = 1;
+              return CCID_DRIVER_ERR_NO_READER;
+            }
+#endif /*ENODEV*/
+        }
       else
         DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
     }
@@ -1426,14 +1936,16 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
    is the sequence number used to send the request and EXPECTED_TYPE
    the type of message we expect. Does checks on the ccid
    header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to
-   avoid debug messages in case of no error. Returns 0 on success. */
+   avoid debug messages in case of no error; this can be overriden
+   with a glibal debug level of at least 3. Returns 0 on success. */
 static int
 bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
          size_t *nread, int expected_type, int seqno, int timeout,
          int no_debug)
 {
-  int i, rc;
+  int rc;
   size_t msglen;
+  int eagain_retries = 0;
 
   /* Fixme: The next line for the current Valgrind without support
      for USB IOCTLs. */
@@ -1447,7 +1959,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                           timeout);
       if (rc < 0)
         {
-          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
+          rc = errno;
+          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 3)
+            {
+              my_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
@@ -1457,36 +1975,41 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       rc = read (handle->dev_fd, buffer, length);
       if (rc < 0)
         {
+          rc = errno;
           DEBUGOUT_2 ("read from %d failed: %s\n",
-                      handle->dev_fd, strerror (errno));
+                      handle->dev_fd, strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 5)
+            {
+              my_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
     }
-
+  eagain_retries = 0;
 
   if (msglen < 10)
     {
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
-      abort_cmd (handle);
+      abort_cmd (handle, seqno);
       return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[5] != 0)    
     {
       DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
-      abort_cmd (handle);
       return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[6] != seqno)    
     {
       DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
                   seqno, buffer[6]);
-      abort_cmd (handle);
-      return CCID_DRIVER_ERR_INV_VALUE;
+      /* Retry until we are synced again.  */
+      goto retry;
     }
 
   /* We need to handle the time extension request before we check that
-     we go the expected message type.  This is in particular required
+     we got the expected message type.  This is in particular required
      for the Cherry keyboard which sends a time extension request for
      each key hit.  */
   if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
@@ -1500,18 +2023,33 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   if (buffer[0] != expected_type)
     {
       DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
-      abort_cmd (handle);
+      abort_cmd (handle, seqno);
       return CCID_DRIVER_ERR_INV_VALUE;
     }
 
-
-  if (!no_debug)
+  if (debug_level && (!no_debug || debug_level >= 3))
     {
-      DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n"
-                  "               data:",  buffer[7], buffer[8], buffer[9] );
-      for (i=10; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", buffer[i]);
-      DEBUGOUT_LF ();
+      switch (buffer[0])
+        {
+        case RDR_to_PC_DataBlock:
+          print_r2p_datablock (buffer, msglen);
+          break;
+        case RDR_to_PC_SlotStatus:
+          print_r2p_slotstatus (buffer, msglen);
+          break;
+        case RDR_to_PC_Parameters:
+          print_r2p_parameters (buffer, msglen);
+          break;
+        case RDR_to_PC_Escape:
+          print_r2p_escape (buffer, msglen);
+          break;
+        case RDR_to_PC_DataRate:
+          print_r2p_datarate (buffer, msglen);
+          break;
+        default:
+          print_r2p_unknown (buffer, msglen);
+          break;
+        }
     }
   if (CCID_COMMAND_FAILED (buffer))
     print_command_failed (buffer);
@@ -1533,14 +2071,12 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
 
 /* Send an abort sequence and wait until everything settled.  */
 static int
-abort_cmd (ccid_driver_t handle)
+abort_cmd (ccid_driver_t handle, int seqno)
 {
   int rc;
   char dummybuf[8];
-  unsigned char seqno;
   unsigned char msg[100];
   size_t msglen;
-  int i;
 
   if (!handle->idev)
     {
@@ -1548,11 +2084,11 @@ abort_cmd (ccid_driver_t handle)
       rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
     }
   
-  DEBUGOUT ("sending abort sequence\n");
+  seqno &= 0xff;
+  DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
   /* Send the abort command to the control pipe.  Note that we don't
      need to keep track of sent abort commands because there should
      never be another thread using the same slot concurrently.  */
-  seqno = (handle->seqno & 0xff);
   rc = usb_control_msg (handle->idev, 
                         0x21,/* bmRequestType: host-to-device,
                                 class specific, to interface.  */
@@ -1568,38 +2104,36 @@ abort_cmd (ccid_driver_t handle)
     }
 
   /* Now send the abort command to the bulk out pipe using the same
-     SEQNO and SLOT. */
-  msg[0] = PC_to_RDR_Abort;
-  msg[5] = 0; /* slot */
-  msg[6] = seqno;
-  msg[7] = 0; /* RFU */
-  msg[8] = 0; /* RFU */
-  msg[9] = 0; /* RFU */
-  msglen = 10;
-  set_msg_len (msg, 0);
-  handle->seqno++;  /* Bumb up for the next use. */
-
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  rc = usb_bulk_write (handle->idev, 
-                       handle->ep_bulk_out,
-                       (char*)msg, msglen,
-                       5000 /* ms timeout */);
-  if (rc == msglen)
-    rc = 0;
-  else if (rc == -1)
-    DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", strerror (errno));
-  else
-    DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
-
-  if (rc)
-    return rc;
-
-  /* Wait for the expected response.  */
+     SEQNO and SLOT.  Do this in a loop to so that all seqno are
+     tried.  */
+  seqno--;  /* Adjust for next increment.  */
   do
     {
+      seqno++; 
+      msg[0] = PC_to_RDR_Abort;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno;
+      msg[7] = 0; /* RFU */
+      msg[8] = 0; /* RFU */
+      msg[9] = 0; /* RFU */
+      msglen = 10;
+      set_msg_len (msg, 0);
+
+      rc = usb_bulk_write (handle->idev, 
+                           handle->ep_bulk_out,
+                           (char*)msg, msglen,
+                           5000 /* ms timeout */);
+      if (rc == msglen)
+        rc = 0;
+      else if (rc == -1)
+        DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", 
+                    strerror (errno));
+      else
+        DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
+
+      if (rc)
+        return rc;
+      
       rc = usb_bulk_read (handle->idev, 
                           handle->ep_bulk_in,
                           (char*)msg, sizeof msg, 
@@ -1631,6 +2165,7 @@ abort_cmd (ccid_driver_t handle)
     }
   while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
 
+  handle->seqno = ((seqno + 1) & 0xff);
   DEBUGOUT ("sending abort sequence succeeded\n");
 
   return 0;
@@ -1647,7 +2182,7 @@ send_escape_cmd (ccid_driver_t handle,
                  const unsigned char *data, size_t datalen,
                  unsigned char *result, size_t resultmax, size_t *resultlen)
 {
-  int i, rc;
+  int rc;
   unsigned char msg[100];
   size_t msglen;
   unsigned char seqno;
@@ -1668,11 +2203,7 @@ send_escape_cmd (ccid_driver_t handle,
   msglen = 10 + datalen;
   set_msg_len (msg, datalen);
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape,
@@ -1773,7 +2304,7 @@ ccid_poll (ccid_driver_t handle)
 }
 
 
-/* Note that this fucntion won't return the error codes NO_CARD or
+/* Note that this function won't return the error codes NO_CARD or
    CARD_INACTIVE */
 int 
 ccid_slot_status (ccid_driver_t handle, int *statusbits)
@@ -1793,7 +2324,7 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
   msg[9] = 0; /* RFU */
   set_msg_len (msg, 0);
 
-  rc = bulk_out (handle, msg, 10);
+  rc = bulk_out (handle, msg, 10, 1);
   if (rc)
     return rc;
   /* Note that we set the NO_DEBUG flag here, so that the logs won't
@@ -1823,6 +2354,151 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
 }
 
 
+/* Parse ATR string (of ATRLEN) and update parameters at PARAM.
+   Calling this routine, it should prepare default values at PARAM
+   beforehand.  This routine assumes that card is accessed by T=1
+   protocol.  It doesn't analyze historical bytes at all.
+
+   Returns < 0 value on error:
+     -1 for parse error or integrity check error
+     -2 for card doesn't support T=1 protocol
+     -3 for parameters are nod explicitly defined by ATR
+     -4 for this driver doesn't support CRC
+
+   Returns >= 0 on success:
+      0 for card is negotiable mode
+      1 for card is specific mode (and not negotiable)
+ */
+static int
+update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen)
+{
+  int i = -1;
+  int t, y, chk;
+  int historical_bytes_num, negotiable = 1;
+
+#define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0)
+
+  NEXTBYTE ();
+
+  if (atr[i] == 0x3F)
+    param[1] |= 0x02;          /* Convention is inverse.  */
+  NEXTBYTE ();
+
+  y = (atr[i] >> 4);
+  historical_bytes_num = atr[i] & 0x0f;
+  NEXTBYTE ();
+
+  if ((y & 1))
+    {
+      param[0] = atr[i];       /* TA1 - Fi & Di */
+      NEXTBYTE ();
+    }
+
+  if ((y & 2))
+    NEXTBYTE ();               /* TB1 - ignore */
+
+  if ((y & 4))
+    {
+      param[2] = atr[i];       /* TC1 - Guard Time */
+      NEXTBYTE ();
+    }
+
+  if ((y & 8))
+    {
+      y = (atr[i] >> 4);       /* TD1 */
+      t = atr[i] & 0x0f;
+      NEXTBYTE ();
+
+      if ((y & 1))
+       {                       /* TA2 - PPS mode */
+         if ((atr[i] & 0x0f) != 1)
+           return -2;          /* Wrong card protocol (!= 1).  */
+
+         if ((atr[i] & 0x10) != 0x10)
+           return -3; /* Transmission parameters are implicitly defined. */
+
+         negotiable = 0;       /* TA2 means specific mode.  */
+         NEXTBYTE ();
+       }
+
+      if ((y & 2))
+       NEXTBYTE ();            /* TB2 - ignore */
+
+      if ((y & 4))
+       NEXTBYTE ();            /* TC2 - ignore */
+
+      if ((y & 8))
+       {
+         y = (atr[i] >> 4);    /* TD2 */
+         t = atr[i] & 0x0f;
+         NEXTBYTE ();
+       }
+      else
+       y = 0;
+
+      while (y)
+       {
+         if ((y & 1))
+           {                   /* TAx */
+             if (t == 1)
+               param[5] = atr[i]; /* IFSC */
+             else if (t == 15)
+               /* XXX: check voltage? */
+               param[4] = (atr[i] >> 6); /* ClockStop */
+
+             NEXTBYTE ();
+           }
+
+         if ((y & 2))
+           {
+             if (t == 1)
+               param[3] = atr[i]; /* TBx - BWI & CWI */
+             NEXTBYTE ();
+           }
+
+         if ((y & 4))
+           {
+             if (t == 1)
+               param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */
+             NEXTBYTE ();
+
+             if (param[1] & 0x01)
+               return -4;      /* CRC not supported yet.  */
+           }
+
+         if ((y & 8))
+           {
+             y = (atr[i] >> 4); /* TDx */
+             t = atr[i] & 0x0f;
+             NEXTBYTE ();
+           }
+         else
+           y = 0;
+       }
+    }
+
+  i += historical_bytes_num - 1;
+  NEXTBYTE ();
+  if (atrlen != i+1)
+    return -1;
+
+#undef NEXTBYTE
+
+  chk = 0;
+  do
+    {
+      chk ^= atr[i];
+      i--;
+    }
+  while (i > 0);
+
+  if (chk != 0)
+    return -1;
+
+  return negotiable;
+}
+
+
 /* Return the ATR of the card.  This is not a cached value and thus an
    actual reset is done.  */
 int 
@@ -1837,9 +2513,17 @@ ccid_get_atr (ccid_driver_t handle,
   unsigned char seqno;
   int use_crc = 0;
   unsigned int edc;
-  int i;
   int tried_iso = 0;
   int got_param;
+  unsigned char param[7] = { /* For Protocol T=1 */
+    0x11, /* bmFindexDindex */
+    0x10, /* bmTCCKST1 */
+    0x00, /* bGuardTimeT1 */
+    0x4d, /* bmWaitingIntegersT1 */
+    0x00, /* bClockStop */
+    0x20, /* bIFSC */
+    0x00  /* bNadValue */
+  };
 
   /* First check whether a card is available.  */
   rc = ccid_slot_status (handle, &statusbits);
@@ -1854,13 +2538,14 @@ ccid_get_atr (ccid_driver_t handle,
   msg[0] = PC_to_RDR_IccPowerOn;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
-  msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+  /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+  msg[7] = handle->auto_voltage ? 0 : 1;
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
   set_msg_len (msg, 0);
   msglen = 10;
 
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
@@ -1896,42 +2581,72 @@ ccid_get_atr (ccid_driver_t handle,
       *atrlen = n;
     }
 
+  param[6] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+  rc = update_param_by_atr (param, msg+10, msglen - 10);
+  if (rc < 0)
+    {
+      DEBUGOUT_1 ("update_param_by_atr failed: %d\n", rc);
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
+    }
+
   got_param = 0;
-  msg[0] = PC_to_RDR_GetParameters;
-  msg[5] = 0; /* slot */
-  msg[6] = seqno = handle->seqno++;
-  msg[7] = 0; /* RFU */
-  msg[8] = 0; /* RFU */
-  msg[9] = 0; /* RFU */
-  set_msg_len (msg, 0);
-  msglen = 10;
-  rc = bulk_out (handle, msg, msglen);
-  if (!rc)
-    rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
-                  seqno, 2000, 0);
-  if (rc)
-    DEBUGOUT ("GetParameters failed\n");
-  else
+
+  if (handle->auto_param)
     {
-      DEBUGOUT ("GetParametes returned");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
-      if (msglen >= 10)
-        {
-          DEBUGOUT_1 ("  protocol ..........: T=%d\n", msg[9]);
-          if (msglen == 17 && msg[9] == 1)
-            {
-              DEBUGOUT_1 ("  bmFindexDindex ....: %02X\n", msg[10]);
-              DEBUGOUT_1 ("  bmTCCKST1 .........: %02X\n", msg[11]);
-              DEBUGOUT_1 ("  bGuardTimeT1 ......: %02X\n", msg[12]);
-              DEBUGOUT_1 ("  bmWaitingIntegersT1: %02X\n", msg[13]);
-              DEBUGOUT_1 ("  bClockStop ........: %02X\n", msg[14]);
-              DEBUGOUT_1 ("  bIFSC .............: %d\n", msg[15]);
-              DEBUGOUT_1 ("  bNadValue .........: %d\n", msg[16]);
-              got_param = 1;
-            }
-        }
+      msg[0] = PC_to_RDR_GetParameters;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno = handle->seqno++;
+      msg[7] = 0; /* RFU */
+      msg[8] = 0; /* RFU */
+      msg[9] = 0; /* RFU */
+      set_msg_len (msg, 0);
+      msglen = 10;
+      rc = bulk_out (handle, msg, msglen, 0);
+      if (!rc)
+       rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+                     seqno, 2000, 0);
+      if (rc)
+       DEBUGOUT ("GetParameters failed\n");
+      else if (msglen == 17 && msg[9] == 1)
+       got_param = 1;
+    }
+  else if (handle->auto_pps)
+    ;
+  else if (rc == 1)            /* It's negotiable, send PPS.  */
+    {
+      msg[0] = PC_to_RDR_XfrBlock;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno = handle->seqno++;
+      msg[7] = 0;
+      msg[8] = 0;
+      msg[9] = 0;
+      msg[10] = 0xff;          /* PPSS */
+      msg[11] = 0x11;          /* PPS0: PPS1, Protocol T=1 */
+      msg[12] = param[0];      /* PPS1: Fi / Di */
+      msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */
+      set_msg_len (msg, 4);
+      msglen = 10 + 4;
+
+      rc = bulk_out (handle, msg, msglen, 0);
+      if (rc)
+       return rc;
+
+      rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
+                   seqno, 5000, 0);
+      if (rc)
+       return rc;
+
+      if (msglen != 10 + 4)
+       {
+         DEBUGOUT_1 ("Setting PPS failed: %d\n", msglen);
+         return CCID_DRIVER_ERR_CARD_IO_ERROR;
+       }
+
+      if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0])
+       {
+         DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]);
+         return CCID_DRIVER_ERR_CARD_IO_ERROR;
+       }
     }
 
   /* Setup parameters to select T=1. */
@@ -1943,25 +2658,11 @@ ccid_get_atr (ccid_driver_t handle,
   msg[9] = 0; /* RFU */
 
   if (!got_param)
-    {
-      /* FIXME: Get those values from the ATR. */
-      msg[10]= 0x01; /* Fi/Di */
-      msg[11]= 0x10; /* LRC, direct convention. */
-      msg[12]= 0;    /* Extra guardtime. */
-      msg[13]= 0x41; /* BWI/CWI */
-      msg[14]= 0;    /* No clock stoppping. */
-      msg[15]= 254;  /* IFSC */
-      msg[16]= 0;    /* Does not support non default NAD values. */
-    }
+    memcpy (&msg[10], param, 7);
   set_msg_len (msg, 7);
   msglen = 10 + 7;
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
@@ -1969,6 +2670,17 @@ ccid_get_atr (ccid_driver_t handle,
   if (rc)
     DEBUGOUT ("SetParameters failed (ignored)\n");
 
+  if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
+    handle->ifsc = msg[15];
+  else
+    handle->ifsc = 128; /* Something went wrong, assume 128 bytes.  */
+
+  if (handle->nonnull_nad && msglen > 16 && msg[16] == 0)
+    {
+      DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n");
+      handle->nonnull_nad = 0;
+    }
+
   handle->t1_ns = 0;
   handle->t1_nr = 0;
 
@@ -1996,11 +2708,6 @@ ccid_get_atr (ccid_driver_t handle,
       set_msg_len (msg, tpdulen);
       msglen = 10 + tpdulen;
 
-      DEBUGOUT ("sending");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
-
       if (debug_level > 1)
         DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
                       ((msg[11] & 0xc0) == 0x80)? 'R' :
@@ -2009,7 +2716,7 @@ ccid_get_atr (ccid_driver_t handle,
                                        : !!(msg[11] & 0x40)),
                     (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
 
-      rc = bulk_out (handle, msg, msglen);
+      rc = bulk_out (handle, msg, msglen, 0);
       if (rc)
         return rc;
 
@@ -2066,6 +2773,16 @@ compute_edc (const unsigned char *data, size_t datalen, int use_crc)
 }
 
 
+/* Return true if APDU is an extended length one.  */
+static int
+is_exlen_apdu (const unsigned char *apdu, size_t apdulen)
+{
+  if (apdulen < 7 || apdu[4])
+    return 0;  /* Too short or no Z byte.  */
+  return 1;
+}
+
+
 /* Helper for ccid_transceive used for APDU level exchanges.  */
 static int
 ccid_transceive_apdu_level (ccid_driver_t handle,
@@ -2074,13 +2791,13 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
                             size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+261], recv_buffer[10+261];
+  unsigned char send_buffer[10+261+300], recv_buffer[10+261+300];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg;
   size_t msglen;
   unsigned char seqno;
-  int i;
+  int bwi = 4;
 
   msg = send_buffer;
 
@@ -2090,26 +2807,21 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
 
   /* The maximum length for a short APDU T=1 block is 261.  For an
      extended APDU T=1 block the maximum length 65544; however
-     extended APDU exchange levele is not yet supported.  */
-  if (apdulen > 261)
+     extended APDU exchange level is not fully supported yet.  */
+  if (apdulen > 289)
     return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
+  
   msg[0] = PC_to_RDR_XfrBlock;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
-  msg[7] = 4; /* bBWI */
+  msg[7] = bwi; /* bBWI */
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
   memcpy (msg+10, apdu, apdulen);
   set_msg_len (msg, apdulen);
   msglen = 10 + apdulen;
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
 
@@ -2118,10 +2830,53 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
                 RDR_to_PC_DataBlock, seqno, 5000, 0);
   if (rc)
     return rc;
-      
-  apdu = msg + 10;
-  apdulen = msglen - 10;
-      
+
+  if (msg[9] == 1)
+    {
+      size_t total_msglen = msglen;
+
+      while (1)
+        {
+          unsigned char status;
+
+          msg = recv_buffer + total_msglen;
+
+          msg[0] = PC_to_RDR_XfrBlock;
+          msg[5] = 0; /* slot */
+          msg[6] = seqno = handle->seqno++;
+          msg[7] = bwi; /* bBWI */
+          msg[8] = 0x10;                /* Request next data block */
+          msg[9] = 0;
+          set_msg_len (msg, 0);
+          msglen = 10;
+
+          rc = bulk_out (handle, msg, msglen, 0);
+          if (rc)
+            return rc;
+
+          rc = bulk_in (handle, msg, sizeof recv_buffer - total_msglen, &msglen,
+                        RDR_to_PC_DataBlock, seqno, 5000, 0);
+          if (rc)
+            return rc;
+          status = msg[9];
+          memmove (msg, msg+10, msglen - 10);
+          total_msglen += msglen - 10;
+          if (total_msglen >= sizeof recv_buffer)
+            return CCID_DRIVER_ERR_OUT_OF_CORE;
+
+          if (status == 0x02)
+            break;
+        }
+
+      apdu = recv_buffer + 10;
+      apdulen = total_msglen - 10;
+    }
+  else
+    {
+      apdu = msg + 10;
+      apdulen = msglen - 10;
+    }
+
   if (resp)
     {
       if (apdulen > maxresplen)
@@ -2199,19 +2954,24 @@ ccid_transceive (ccid_driver_t handle,
                  unsigned char *resp, size_t maxresplen, size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+259], recv_buffer[10+259];
+  /* The size of the buffer used to be 10+259.  For the via_escape
+     hack we need one extra byte, thus 11+259.  */
+  unsigned char send_buffer[11+259], recv_buffer[11+259];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg, *tpdu, *p;
   size_t msglen, tpdulen, last_tpdulen, n;
   unsigned char seqno;
-  int i;
   unsigned int edc;
   int use_crc = 0;
+  int hdrlen, pcboff;
   size_t dummy_nresp;
+  int via_escape = 0;
   int next_chunk = 1;
   int sending = 1;
   int retries = 0;
+  int resyncing = 0;
+  int nad_byte;
 
   if (!nresp)
     nresp = &dummy_nresp;
@@ -2219,13 +2979,37 @@ ccid_transceive (ccid_driver_t handle,
 
   /* Smarter readers allow to send APDUs directly; divert here. */
   if (handle->apdu_level)
-    return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
-                                       resp, maxresplen, nresp);
+    {
+      /* We employ a hack for Omnikey readers which are able to send
+         TPDUs using an escape sequence.  There is no documentation
+         but the Windows driver does it this way.  Tested using a
+         CM6121.  This method works also for the Cherry XX44
+         keyboards; however there are problems with the
+         ccid_tranceive_secure which leads to a loss of sync on the
+         CCID level.  If Cherry wants to make their keyboard work
+         again, they should hand over some docs. */
+      if ((handle->id_vendor == VENDOR_OMNIKEY
+           || (!handle->idev && handle->id_product == TRANSPORT_CM4040))
+          && handle->apdu_level < 2
+          && is_exlen_apdu (apdu_buf, apdu_buflen))
+        via_escape = 1;
+      else
+        return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
+                                           resp, maxresplen, nresp);
+    }
 
   /* The other readers we support require sending TPDUs.  */
 
   tpdulen = 0; /* Avoid compiler warning about no initialization. */
   msg = send_buffer;
+  hdrlen = via_escape? 11 : 10;
+
+  /* NAD: DAD=1, SAD=0 */
+  nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0;
+  if (via_escape)
+    nad_byte = 0;
+
+  last_tpdulen = 0;  /* Avoid gcc warning (controlled by RESYNCING). */
   for (;;)
     {
       if (next_chunk)
@@ -2237,22 +3021,14 @@ ccid_transceive (ccid_driver_t handle,
           assert (apdulen);
 
           /* Construct an I-Block. */
-          /* Fixme: I am not sure whether limiting the length to 259
-             as per CCID spec is required.  The code blow chops the
-             APDU anyway into 128 byte blocks.  Needs to be addressed
-             when supporting extended length APDUs. */
-          if (apdulen > 259)
-            return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
-          tpdu = msg+10;
-          /* NAD: DAD=1, SAD=0 */
-          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+          tpdu = msg + hdrlen;
+          tpdu[0] = nad_byte;
           tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
-          if (apdulen > 128 /* fixme: replace by ifsc */)
+          if (apdulen > handle->ifsc )
             {
-              apdulen = 128;
-              apdu_buf += 128;  
-              apdu_buflen -= 128;
+              apdulen = handle->ifsc;
+              apdu_buf += handle->ifsc;  
+              apdu_buflen -= handle->ifsc;
               tpdu[1] |= (1 << 5); /* Set more bit. */
             }
           tpdu[2] = apdulen;
@@ -2264,42 +3040,56 @@ ccid_transceive (ccid_driver_t handle,
           tpdu[tpdulen++] = edc;
         }
 
-      msg[0] = PC_to_RDR_XfrBlock;
-      msg[5] = 0; /* slot */
-      msg[6] = seqno = handle->seqno++;
-      msg[7] = 4; /* bBWI */
-      msg[8] = 0; /* RFU */
-      msg[9] = 0; /* RFU */
-      set_msg_len (msg, tpdulen);
-      msglen = 10 + tpdulen;
-      last_tpdulen = tpdulen;
-
-      DEBUGOUT ("sending");
-      for (i=0; i < msglen; i++)
-        DEBUGOUT_CONT_1 (" %02X", msg[i]);
-      DEBUGOUT_LF ();
+      if (via_escape)
+        {
+          msg[0] = PC_to_RDR_Escape;
+          msg[5] = 0; /* slot */
+          msg[6] = seqno = handle->seqno++;
+          msg[7] = 0; /* RFU */
+          msg[8] = 0; /* RFU */
+          msg[9] = 0; /* RFU */
+          msg[10] = 0x1a; /* Omnikey command to send a TPDU.  */
+          set_msg_len (msg, 1 + tpdulen);
+        }
+      else
+        {
+          msg[0] = PC_to_RDR_XfrBlock;
+          msg[5] = 0; /* slot */
+          msg[6] = seqno = handle->seqno++;
+          msg[7] = 4; /* bBWI */
+          msg[8] = 0; /* RFU */
+          msg[9] = 0; /* RFU */
+          set_msg_len (msg, tpdulen);
+        }
+      msglen = hdrlen + tpdulen;
+      if (!resyncing)
+        last_tpdulen = tpdulen;
+      pcboff = hdrlen+1;
 
       if (debug_level > 1)
-          DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
-                      ((msg[11] & 0xc0) == 0x80)? 'R' :
-                                (msg[11] & 0x80)? 'S' : 'I',
-                      ((msg[11] & 0x80)? !!(msg[11]& 0x10)
-                                       : !!(msg[11] & 0x40)),
-                      (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
-
-      rc = bulk_out (handle, msg, msglen);
+        DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+                    ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+                    (msg[pcboff] & 0x80)? 'S' : 'I',
+                    ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+                     : !!(msg[pcboff] & 0x40)),
+                    (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+                     " [more]":""));
+      
+      rc = bulk_out (handle, msg, msglen, 0);
       if (rc)
         return rc;
 
       msg = recv_buffer;
       rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
-                    RDR_to_PC_DataBlock, seqno, 5000, 0);
+                    via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, 
+                    seqno, 5000, 0);
       if (rc)
         return rc;
-      
-      tpdu = msg + 10;
-      tpdulen = msglen - 10;
-      
+
+      tpdu = msg + hdrlen;
+      tpdulen = msglen - hdrlen;
+      resyncing = 0;
+            
       if (tpdulen < 4) 
         {
           usb_clear_halt (handle->idev, handle->ep_bulk_in);
@@ -2308,11 +3098,13 @@ ccid_transceive (ccid_driver_t handle,
 
       if (debug_level > 1)
         DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
-                    ((msg[11] & 0xc0) == 0x80)? 'R' :
-                              (msg[11] & 0x80)? 'S' : 'I',
-                    ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
-                    ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
-                    (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+                    ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+                              (msg[pcboff] & 0x80)? 'S' : 'I',
+                    ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+                     : !!(msg[pcboff] & 0x40)),
+                    ((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0,
+                    (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+                     " [more]":""));
 
       if (!(tpdu[1] & 0x80))
         { /* This is an I-block. */
@@ -2326,9 +3118,8 @@ ccid_transceive (ccid_driver_t handle,
           if (!!(tpdu[1] & 0x40) != handle->t1_nr)
             { /* Reponse does not match our sequence number. */
               msg = send_buffer;
-              tpdu = msg+10;
-              /* NAD: DAD=1, SAD=0 */
-              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
               tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
               tpdu[2] = 0;
               tpdulen = 3;
@@ -2365,9 +3156,8 @@ ccid_transceive (ccid_driver_t handle,
             return 0; /* No chaining requested - ready. */
           
           msg = send_buffer;
-          tpdu = msg+10;
-          /* NAD: DAD=1, SAD=0 */
-          tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+          tpdu = msg + hdrlen;
+          tpdu[0] = nad_byte;
           tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
           tpdu[2] = 0;
           tpdulen = 3;
@@ -2379,14 +3169,37 @@ ccid_transceive (ccid_driver_t handle,
       else if ((tpdu[1] & 0xc0) == 0x80)
         { /* This is a R-block. */
           if ( (tpdu[1] & 0x0f)) 
-            { /* Error: repeat last block */
-              if (++retries > 3)
+            { 
+              retries++;
+              if (via_escape && retries == 1 && (msg[pcboff] & 0x0f))
+                {
+                  /* Error probably due to switching to TPDU.  Send a
+                     resync request.  We use the recv_buffer so that
+                     we don't corrupt the send_buffer.  */
+                  msg = recv_buffer;
+                  tpdu = msg + hdrlen;
+                  tpdu[0] = nad_byte;
+                  tpdu[1] = 0xc0; /* S-block resync request. */
+                  tpdu[2] = 0;
+                  tpdulen = 3;
+                  edc = compute_edc (tpdu, tpdulen, use_crc);
+                  if (use_crc)
+                    tpdu[tpdulen++] = (edc >> 8);
+                  tpdu[tpdulen++] = edc;
+                  resyncing = 1;
+                  DEBUGOUT ("T=1: requesting resync\n");
+                }
+              else if (retries > 3)
                 {
-                  DEBUGOUT ("3 failed retries\n");
+                  DEBUGOUT ("T=1: 3 failed retries\n");
                   return CCID_DRIVER_ERR_CARD_IO_ERROR;
                 }
-              msg = send_buffer;
-              tpdulen = last_tpdulen;
+              else
+                {
+                  /* Error: repeat last block */
+                  msg = send_buffer;
+                  tpdulen = last_tpdulen;
+                }
             }
           else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
             { /* Response does not match our sequence number. */
@@ -2409,16 +3222,37 @@ ccid_transceive (ccid_driver_t handle,
       else 
         { /* This is a S-block. */
           retries = 0;
-          DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
+          DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n",
                       (tpdu[1] & 0x20)? "response": "request",
                       (tpdu[1] & 0x1f));
-          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
-            { /* Wait time extension request. */
+          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
+            {
+              /* Information field size request.  */
+              unsigned char ifsc = tpdu[3];
+
+              if (ifsc < 16 || ifsc > 254)
+                return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+              msg = send_buffer;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
+              tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
+              tpdu[2] = 1;
+              tpdu[3] = ifsc;
+              tpdulen = 4;
+              edc = compute_edc (tpdu, tpdulen, use_crc);
+              if (use_crc)
+                tpdu[tpdulen++] = (edc >> 8);
+              tpdu[tpdulen++] = edc;
+              DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc);
+            }
+          else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+            {
+              /* Wait time extension request. */
               unsigned char bwi = tpdu[3];
               msg = send_buffer;
-              tpdu = msg+10;
-              /* NAD: DAD=1, SAD=0 */
-              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+              tpdu = msg + hdrlen;
+              tpdu[0] = nad_byte;
               tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
               tpdu[2] = 1;
               tpdu[3] = bwi;
@@ -2427,7 +3261,15 @@ ccid_transceive (ccid_driver_t handle,
               if (use_crc)
                 tpdu[tpdulen++] = (edc >> 8);
               tpdu[tpdulen++] = edc;
-              DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi);
+              DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi);
+              print_progress (handle);
+            }
+          else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2])
+            {
+              DEBUGOUT ("T=1: resync ack from reader\n");
+              /* Repeat previous block.  */
+              msg = send_buffer;
+              tpdulen = last_tpdulen;
             }
           else
             return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2464,10 +3306,10 @@ ccid_transceive_secure (ccid_driver_t handle,
   unsigned char *msg, *tpdu, *p;
   size_t msglen, tpdulen, n;
   unsigned char seqno;
-  int i;
   size_t dummy_nresp;
   int testmode;
   int cherry_mode = 0;
+  int enable_varlen = opt.enable_keypad_varlen;
 
   testmode = !resp && !nresp;
 
@@ -2478,7 +3320,7 @@ ccid_transceive_secure (ccid_driver_t handle,
   if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1))
     ;
   else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2))
-    return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */
+    ;
   else
     return CCID_DRIVER_ERR_NO_KEYPAD;
     
@@ -2505,8 +3347,15 @@ ccid_transceive_secure (ccid_driver_t handle,
     {
     case VENDOR_SCM:  /* Tested with SPR 532. */
     case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
+    case VENDOR_FSIJ: /* Tested with Gnuk (0.21). */
+      enable_varlen = 1;
+      break;
+    case VENDOR_VASCO: /* Tested with DIGIPASS 920 */
+      enable_varlen = 1;
+      pinlen_max = 15;
       break;
     case VENDOR_CHERRY:
+      enable_varlen = 1;
       /* The CHERRY XX44 keyboard echos an asterisk for each entered
          character on the keyboard channel.  We use a special variant
          of PC_to_RDR_Secure which directs these characters to the
@@ -2514,8 +3363,14 @@ ccid_transceive_secure (ccid_driver_t handle,
          Lc byte to the APDU.  It seems that it will be replaced with
          the actual length instead of being appended before the APDU
          is send to the card. */
-      cherry_mode = 1;
+      if (handle->id_product != CHERRY_ST2000)
+        cherry_mode = 1;
       break;
+    case VENDOR_GEMPC:
+      enable_varlen = 0;
+      if (handle->id_product == GEMPC_PINPAD)
+       break;
+      /* fall through */
     default:
      return CCID_DRIVER_ERR_NOT_SUPPORTED;
     }
@@ -2539,7 +3394,8 @@ ccid_transceive_secure (ccid_driver_t handle,
   msg[7] = 0; /* bBWI */
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
-  msg[10] = 0; /* Perform PIN verification. */
+  msg[10] = apdu_buf[1] == 0x20 ? 0 : 1;
+               /* Perform PIN verification or PIN modification. */
   msg[11] = 0; /* Timeout in seconds. */
   msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
   if (handle->id_vendor == VENDOR_SCM)
@@ -2558,39 +3414,64 @@ ccid_transceive_secure (ccid_driver_t handle,
                          Units are bytes, position is 0. */
     }
 
-  /* The following is a little endian word. */
-  msg[15] = pinlen_max;   /* wPINMaxExtraDigit-Maximum.  */
-  msg[16] = pinlen_min;   /* wPINMaxExtraDigit-Minimum.  */
+  msglen = 15;
+  if (apdu_buf[1] == 0x24)
+    {
+      msg[msglen++] = 0;    /* bInsertionOffsetOld */
+      msg[msglen++] = 0;    /* bInsertionOffsetNew */
+    }
 
-  msg[17] = 0x02; /* bEntryValidationCondition:
-                     Validation key pressed */
+  /* The following is a little endian word. */
+  msg[msglen++] = pinlen_max;   /* wPINMaxExtraDigit-Maximum.  */
+  msg[msglen++] = pinlen_min;   /* wPINMaxExtraDigit-Minimum.  */
+
+  if (apdu_buf[1] == 0x24)
+    msg[msglen++] = apdu_buf[2] == 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)
+               */
+
+  msg[msglen] = 0x02; /* bEntryValidationCondition:
+                         Validation key pressed */
   if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
-    msg[17] |= 0x01; /* Max size reached.  */
-  msg[18] = 0xff; /* bNumberMessage: Default. */
-  msg[19] = 0x04; /* wLangId-High. */
-  msg[20] = 0x09; /* wLangId-Low:  English FIXME: use the first entry. */
-  msg[21] = 0;    /* bMsgIndex. */
+    msg[msglen] |= 0x01; /* Max size reached.  */
+  msglen++;
+
+  if (apdu_buf[1] == 0x20)
+    msg[msglen++] = 0xff; /* bNumberMessage: Default. */
+  else
+    msg[msglen++] = 0x03; /* bNumberMessage. */
+
+  msg[msglen++] = 0x09; /* wLangId-Low:  English FIXME: use the first entry. */
+  msg[msglen++] = 0x04; /* wLangId-High. */
+
+  if (apdu_buf[1] == 0x20)
+    msg[msglen++] = 0;    /* bMsgIndex. */
+  else
+    {
+      msg[msglen++] = 0;    /* bMsgIndex1. */
+      msg[msglen++] = 1;    /* bMsgIndex2. */
+      msg[msglen++] = 2;    /* bMsgIndex3. */
+    }
+
   /* bTeoProlog follows: */
-  msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
-  msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
-  msg[24] = 0; /* The apdulen will be filled in by the reader.  */
+  msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+  msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */
+  msg[msglen++] = 0; /* The apdulen will be filled in by the reader.  */
   /* APDU follows:  */
-  msg[25] = apdu_buf[0]; /* CLA */
-  msg[26] = apdu_buf[1]; /* INS */
-  msg[27] = apdu_buf[2]; /* P1 */
-  msg[28] = apdu_buf[3]; /* P2 */
-  msglen = 29;
+  msg[msglen++] = apdu_buf[0]; /* CLA */
+  msg[msglen++] = apdu_buf[1]; /* INS */
+  msg[msglen++] = apdu_buf[2]; /* P1 */
+  msg[msglen++] = apdu_buf[3]; /* P2 */
   if (cherry_mode)
     msg[msglen++] = 0;
   /* An EDC is not required. */
   set_msg_len (msg, msglen - 10);
 
-  DEBUGOUT ("sending");
-  for (i=0; i < msglen; i++)
-    DEBUGOUT_CONT_1 (" %02X", msg[i]);
-  DEBUGOUT_LF ();
-  
-  rc = bulk_out (handle, msg, msglen);
+  rc = bulk_out (handle, msg, msglen, 0);
   if (rc)
     return rc;
   
@@ -2692,7 +3573,7 @@ ccid_transceive_secure (ccid_driver_t handle,
     }
   else 
     { /* This is a S-block. */
-      DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n",
+      DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n",
                   (tpdu[1] & 0x20)? "response": "request",
                   (tpdu[1] & 0x1f));
       return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2733,6 +3614,7 @@ print_error (int err)
   fprintf (stderr, "operation failed: %s\n", p);
 }
 
+
 static void
 print_data (const unsigned char *data, size_t length)
 {