(reader_table_s): Add function pointers for the backends.
authorWerner Koch <wk@gnupg.org>
Fri, 16 Jul 2004 15:45:25 +0000 (15:45 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 16 Jul 2004 15:45:25 +0000 (15:45 +0000)
(apdu_close_reader, apdu_get_status, apdu_activate)
(send_apdu): Make use of them.
(new_reader_slot): Intialize them to NULL.
(dump_ccid_reader_status, ct_dump_reader_status): New.
(dump_pcsc_reader_status): New.
(open_ct_reader, open_pcsc_reader, open_ccid_reader)
(open_osc_reader, open_rapdu_reader): Intialize function pointers.
(ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu)
(error_string): Removed.  Replaced by apdu_strerror.
(get_ccid_error_string): Removed.
(ct_activate_card): Remove the unused loop.
(reset_ct_reader): Implemented.
(ct_send_apdu): Activate the card if not yet done.
(pcsc_send_apdu): Ditto.

scd/ChangeLog
scd/apdu.c
scd/apdu.h
scd/ccid-driver.c
scd/ccid-driver.h
scd/iso7816.c

index f594fdd..1d3cff2 100644 (file)
@@ -1,3 +1,46 @@
+2004-07-16  Werner Koch  <wk@gnupg.org>
+
+       * apdu.c (reader_table_s):  Add function pointers for the backends.
+       (apdu_close_reader, apdu_get_status, apdu_activate) 
+       (send_apdu): Make use of them.
+       (new_reader_slot): Intialize them to NULL.
+       (dump_ccid_reader_status, ct_dump_reader_status): New.
+       (dump_pcsc_reader_status): New.
+       (open_ct_reader, open_pcsc_reader, open_ccid_reader) 
+       (open_osc_reader, open_rapdu_reader): Intialize function pointers.
+       (ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu) 
+       (error_string): Removed.  Replaced by apdu_strerror.
+       (get_ccid_error_string): Removed.
+       (ct_activate_card): Remove the unused loop.
+       (reset_ct_reader): Implemented.
+       (ct_send_apdu): Activate the card if not yet done.
+       (pcsc_send_apdu): Ditto.
+
+2004-07-15  Werner Koch  <wk@gnupg.org>
+
+       * ccid-driver.h: Add error codes.
+       * ccid-driver.c: Implement more or less proper error codes all
+       over the place.
+
+       * apdu.c (apdu_send_direct): New.
+       (get_ccid_error_string): Add some error code mappings.
+       (send_apdu): Pass error codes along for drivers already supporting
+       them.
+       (host_sw_string): New.
+       (get_ccid_error_string): Use above.
+       (send_apdu_ccid): Reset the reader if it has not yet been done.
+       (open_ccid_reader): Don't care if the ATR can't be read.
+       (apdu_activate_card): New.
+       (apdu_strerror): New.
+       (dump_reader_status): Only enable it with opt.VERBOSE.
+       * iso7816.c (map_sw): Add mappings for the new error codes.
+
+2004-07-02  Werner Koch  <wk@gnupg.org>
+
+       * apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+       (reset_ccid_reader, open_osc_reader): Call dump_reader_status only
+       in verbose mode.
+
 2004-07-01  Werner Koch  <wk@gnupg.org>
 
        * sc-investigate.c: Initialize Pth which is now required.
index 9742be7..cc9b213 100644 (file)
 # include <opensc/opensc.h>
 #endif
 
+/* If requested include the definitions for the remote APDU protocol
+   code. */
+#ifdef USE_G10CODE_RAPDU
+#include "rapdu.h"
+#endif /*USE_G10CODE_RAPDU*/
+
 #if defined(GNUPG_SCD_MAIN_HEADER)
 #include GNUPG_SCD_MAIN_HEADER
 #elif GNUPG_MAJOR_VERSION == 1
@@ -59,8 +65,6 @@
 
 
 #define MAX_READER 4 /* Number of readers we support concurrently. */
-#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
-                                  insertion of the card (1 = don't wait). */
 
 
 #ifdef _WIN32
 struct reader_table_s {
   int used;            /* True if slot is used. */
   unsigned short port; /* Port number:  0 = unused, 1 - dev/tty */
-  int is_ccid;         /* Uses the internal CCID driver. */
+
+  /* Function pointers intialized to the various backends.  */
+  int (*close_reader)(int);
+  int (*reset_reader)(int);
+  int (*get_status_reader)(int, unsigned int *);
+  int (*send_apdu_reader)(int,unsigned char *,size_t,
+                          unsigned char *, size_t *);
+  void (*dump_status_reader)(int);
+
   struct {
     ccid_driver_t handle;
   } ccid;
-  int is_ctapi;        /* This is a ctAPI driver. */
   struct {
     unsigned long context;
     unsigned long card;
@@ -97,15 +108,21 @@ struct reader_table_s {
 #endif /*NEED_PCSC_WRAPPER*/
   } pcsc;
 #ifdef HAVE_OPENSC
-  int is_osc;          /* We are using the OpenSC driver layer. */
   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*/
   int status;
   unsigned char atr[33];
-  size_t atrlen;
+  size_t atrlen;           /* A zero length indicates that the ATR has
+                              not yet been read; i.e. the card is not
+                              ready for use. */
   unsigned int change_counter;
 #ifdef USE_GNU_PTH
   int lock_initialized;
@@ -222,12 +239,13 @@ new_reader_slot (void)
       reader_table[reader].lock_initialized = 1;
     }
 #endif /*USE_GNU_PTH*/
+  reader_table[reader].close_reader = NULL;
+  reader_table[reader].reset_reader = NULL;
+  reader_table[reader].get_status_reader = NULL;
+  reader_table[reader].send_apdu_reader = NULL;
+  reader_table[reader].dump_status_reader = NULL;
+
   reader_table[reader].used = 1;
-  reader_table[reader].is_ccid = 0;
-  reader_table[reader].is_ctapi = 0;
-#ifdef HAVE_OPENSC
-  reader_table[reader].is_osc = 0;
-#endif
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
@@ -238,34 +256,72 @@ new_reader_slot (void)
 
 
 static void
-dump_reader_status (int reader)
+dump_reader_status (int slot)
 {
-  if (reader_table[reader].is_ccid)
-    log_info ("reader slot %d: using ccid driver\n", reader);
-  else if (reader_table[reader].is_ctapi)
+  if (!opt.verbose)
+    return;
+
+  if (reader_table[slot].dump_status_reader)
+    reader_table[slot].dump_status_reader (slot);
+
+  if (reader_table[slot].status != -1
+      && reader_table[slot].atrlen)
     {
-      log_info ("reader slot %d: %s\n", reader,
-                reader_table[reader].status == 1? "Processor ICC present" :
-                reader_table[reader].status == 0? "Memory ICC present" :
-                "ICC not present" );
+      log_info ("slot %d: ATR=", slot);
+      log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
     }
-  else
+}
+
+
+
+static const char *
+host_sw_string (long err)
+{
+  switch (err)
     {
-      log_info ("reader slot %d: active protocol:", reader);
-      if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T0))
-        log_printf (" T0");
-      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T1))
-        log_printf (" T1");
-      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_RAW))
-        log_printf (" raw");
-      log_printf ("\n");
+    case 0: return "okay";
+    case SW_HOST_OUT_OF_CORE: return "out of core";
+    case SW_HOST_INV_VALUE: return "invalid value";
+    case SW_HOST_NO_DRIVER: return "no driver";
+    case SW_HOST_NOT_SUPPORTED: return "not supported";
+    case SW_HOST_LOCKING_FAILED: return "locking failed";
+    case SW_HOST_BUSY: return "busy";
+    case SW_HOST_NO_CARD: return "no card";
+    case SW_HOST_CARD_INACTIVE: return "card inactive";
+    case SW_HOST_CARD_IO_ERROR: return "card I/O error";
+    case SW_HOST_GENERAL_ERROR: return "general error";
+    case SW_HOST_NO_READER: return "no reader";
+    default: return "unknown host status error";
     }
+}
 
-  if (reader_table[reader].status != -1)
+
+const char *
+apdu_strerror (int rc)
+{
+  switch (rc)
     {
-      log_info ("reader %d: ATR=", reader);
-      log_printhex ("", reader_table[reader].atr,
-                    reader_table[reader].atrlen);
+    case SW_EOF_REACHED    : return "eof reached";
+    case SW_EEPROM_FAILURE : return "eeprom failure";
+    case SW_WRONG_LENGTH   : return "wrong length";
+    case SW_CHV_WRONG      : return "CHV wrong";
+    case SW_CHV_BLOCKED    : return "CHV blocked";
+    case SW_USE_CONDITIONS : return "use conditions not satisfied";
+    case SW_BAD_PARAMETER  : return "bad parameter";
+    case SW_NOT_SUPPORTED  : return "not supported";
+    case SW_FILE_NOT_FOUND : return "file not found";
+    case SW_RECORD_NOT_FOUND:return "record not found";
+    case SW_REF_NOT_FOUND  : return "reference not found";
+    case SW_BAD_P0_P1      : return "bad P0 or P1";
+    case SW_INS_NOT_SUP    : return "instruction not supported";
+    case SW_CLA_NOT_SUP    : return "class not supported";
+    case SW_SUCCESS        : return "success";
+    default:
+      if ((rc & ~0x00ff) == SW_MORE_DATA)
+        return "more data available";
+      if ( (rc & 0x10000) )
+        return host_sw_string (rc);
+      return "unknown status error";
     }
 }
 
@@ -290,120 +346,82 @@ ct_error_string (long err)
     }
 }
 
-/* Wait for the card in READER and activate it.  Return -1 on error or
-   0 on success. */
-static int
-ct_activate_card (int reader)
-{
-  int rc, count;
-
-  for (count = 0; count < CARD_CONNECT_TIMEOUT; count++)
-    {
-      unsigned char dad[1], sad[1], cmd[11], buf[256];
-      unsigned short buflen;
-
-      if (count)
-        ; /* FIXME: we should use a more reliable timer than sleep. */
-
-      /* Check whether card has been inserted. */
-      dad[0] = 1;     /* Destination address: CT. */    
-      sad[0] = 2;     /* Source address: Host. */
-
-      cmd[0] = 0x20;  /* Class byte. */
-      cmd[1] = 0x13;  /* Request status. */
-      cmd[2] = 0x00;  /* From kernel. */
-      cmd[3] = 0x80;  /* Return card's DO. */
-      cmd[4] = 0x00;
-
-      buflen = DIM(buf);
-
-      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
-      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
-        {
-          log_error ("ct_activate_card: can't get status of reader %d: %s\n",
-                     reader, ct_error_string (rc));
-          return -1;
-        }
 
-      /* Connected, now activate the card. */           
-      dad[0] = 1;    /* Destination address: CT. */    
-      sad[0] = 2;    /* Source address: Host. */
+static void
+ct_dump_reader_status (int slot)
+{
+  log_info ("reader slot %d: %s\n", slot,
+            reader_table[slot].status == 1? "Processor ICC present" :
+            reader_table[slot].status == 0? "Memory ICC present" :
+            "ICC not present" );
+}
 
-      cmd[0] = 0x20;  /* Class byte. */
-      cmd[1] = 0x12;  /* Request ICC. */
-      cmd[2] = 0x01;  /* From first interface. */
-      cmd[3] = 0x01;  /* Return card's ATR. */
-      cmd[4] = 0x00;
 
-      buflen = DIM(buf);
+/* Wait for the card in SLOT and activate it.  Return a status word
+   error or 0 on success. */
+static int
+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. */    
+  sad[0] = 2;     /* Source address: Host. */
 
-      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
-      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
-        {
-          log_error ("ct_activate_card(%d): activation failed: %s\n",
-                     reader, ct_error_string (rc));
-          if (!rc)
-            log_printhex ("  received data:", buf, buflen);
-          return -1;
-        }
+  cmd[0] = 0x20;  /* Class byte. */
+  cmd[1] = 0x13;  /* Request status. */
+  cmd[2] = 0x00;  /* From kernel. */
+  cmd[3] = 0x80;  /* Return card's DO. */
+  cmd[4] = 0x00;
 
-      /* Store the type and the ATR. */
-      if (buflen - 2 > DIM (reader_table[0].atr))
-        {
-          log_error ("ct_activate_card(%d): ATR too long\n", reader);
-          return -1;
-        }
+  buflen = DIM(buf);
 
-      reader_table[reader].status = buf[buflen - 1];
-      memcpy (reader_table[reader].atr, buf, buflen - 2);
-      reader_table[reader].atrlen = buflen - 2;
-      return 0;
+  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+    {
+      log_error ("ct_activate_card: can't get status of reader %d: %s\n",
+                 slot, ct_error_string (rc));
+      return SW_HOST_CARD_IO_ERROR;
     }
-  log_info ("ct_activate_card(%d): timeout waiting for card\n", reader);
-  return -1;
-}
 
+  /* Connected, now activate the card. */           
+  dad[0] = 1;    /* Destination address: CT. */    
+  sad[0] = 2;    /* Source address: Host. */
 
-/* Open a reader and return an internal handle for it.  PORT is a
-   non-negative value with the port number of the reader. USB readers
-   do have port numbers starting at 32769. */
-static int
-open_ct_reader (int port)
-{
-  int rc, reader;
+  cmd[0] = 0x20;  /* Class byte. */
+  cmd[1] = 0x12;  /* Request ICC. */
+  cmd[2] = 0x01;  /* From first interface. */
+  cmd[3] = 0x01;  /* Return card's ATR. */
+  cmd[4] = 0x00;
 
-  if (port < 0 || port > 0xffff)
-    {
-      log_error ("open_ct_reader: invalid port %d requested\n", port);
-      return -1;
-    }
-  reader = new_reader_slot ();
-  if (reader == -1)
-    return reader;
-  reader_table[reader].port = port;
+  buflen = DIM(buf);
 
-  rc = CT_init (reader, (unsigned short)port);
-  if (rc)
+  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
     {
-      log_error ("apdu_open_ct_reader failed on port %d: %s\n",
-                 port, ct_error_string (rc));
-      reader_table[reader].used = 0;
-      return -1;
+      log_error ("ct_activate_card(%d): activation failed: %s\n",
+                 slot, ct_error_string (rc));
+      if (!rc)
+        log_printhex ("  received data:", buf, buflen);
+      return SW_HOST_CARD_IO_ERROR;
     }
 
-  rc = ct_activate_card (reader);
-  if (rc)
+  /* Store the type and the ATR. */
+  if (buflen - 2 > DIM (reader_table[0].atr))
     {
-      reader_table[reader].used = 0;
-      return -1;
+      log_error ("ct_activate_card(%d): ATR too long\n", slot);
+      return SW_HOST_CARD_IO_ERROR;
     }
 
-  reader_table[reader].is_ctapi = 1;
-  dump_reader_status (reader);
-  return reader;
+  reader_table[slot].status = buf[buflen - 1];
+  memcpy (reader_table[slot].atr, buf, buflen - 2);
+  reader_table[slot].atrlen = buflen - 2;
+  return 0;
 }
 
+
 static int
 close_ct_reader (int slot)
 {
@@ -415,13 +433,17 @@ close_ct_reader (int slot)
 static int
 reset_ct_reader (int slot)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  /* FIXME: Check is this is sufficient do do a reset. */
+  return ct_activate_card (slot);
 }
 
 
 static int
 ct_get_status (int slot, unsigned int *status)
 {
+  *status = 1|2|4;  /* FIXME */
+  return 0;
+
   return SW_HOST_NOT_SUPPORTED;
 }
 
@@ -436,6 +458,11 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   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. */    
   sad[0] = 2;     /* Source address: Host. */
   ctbuflen = *buflen;
@@ -444,12 +471,56 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
   *buflen = ctbuflen;
 
-  /* FIXME: map the errorcodes to GNUPG ones, so that they can be
-     shared between CTAPI and PCSC. */
-  return rc;
+  return rc? SW_HOST_CARD_IO_ERROR: 0;
 }
 
 
+
+/* Open a reader and return an internal handle for it.  PORT is a
+   non-negative value with the port number of the reader. USB readers
+   do have port numbers starting at 32769. */
+static int
+open_ct_reader (int port)
+{
+  int rc, reader;
+
+  if (port < 0 || port > 0xffff)
+    {
+      log_error ("open_ct_reader: invalid port %d requested\n", port);
+      return -1;
+    }
+  reader = new_reader_slot ();
+  if (reader == -1)
+    return reader;
+  reader_table[reader].port = port;
+
+  rc = CT_init (reader, (unsigned short)port);
+  if (rc)
+    {
+      log_error ("apdu_open_ct_reader failed on port %d: %s\n",
+                 port, ct_error_string (rc));
+      reader_table[reader].used = 0;
+      return -1;
+    }
+
+  /* Only try to activate the card. */
+  rc = ct_activate_card (reader);
+  if (rc)
+    {
+      reader_table[reader].atrlen = 0;
+      rc = 0;
+    }
+
+  reader_table[reader].close_reader = close_ct_reader;
+  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].dump_status_reader = ct_dump_reader_status;
+
+  dump_reader_status (reader);
+  return reader;
+}
+
 \f
 #ifdef NEED_PCSC_WRAPPER
 static int
@@ -568,76 +639,322 @@ pcsc_error_string (long err)
        PC/SC Interface
  */
 
+static void
+dump_pcsc_reader_status (int slot)
+{
+  log_info ("reader slot %d: active protocol:", slot);
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+    log_printf (" T0");
+  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+    log_printf (" T1");
+  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+    log_printf (" raw");
+  log_printf ("\n");
+}
+
+
+
 static int
-open_pcsc_reader (const char *portstr)
+pcsc_get_status (int slot, unsigned int *status)
+{
+  *status = 1|2|4;  /* FIXME!!!! */
+  return 0;
+}
+
+static int
+reset_pcsc_reader (int slot)
+{
+  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: CT API error code. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
 {
 #ifdef NEED_PCSC_WRAPPER
-/* Open the PC/SC reader using the pcsc_wrapper program.  This is
-   needed to cope with different thread models and other peculiarities
-   of libpcsclite. */
-  int slot;
+  long err;
   reader_table_t slotp;
-  int fd, rp[2], wp[2];
-  int n, i;
-  pid_t pid;
-  size_t len;
+  size_t len, full_len;
+  int i, n;
   unsigned char msgbuf[9];
-  int err;
 
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
+  if (!reader_table[slot].atrlen
+      && (err = reset_pcsc_reader (slot)))
+    return err;
+
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
   slotp = reader_table + slot;
 
-  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
-     the common directy but implement it direclty so that this file
-     may still be source copied. */
-  
-  if (pipe (rp) == -1)
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      slotp->used = 0;
-      return -1;
+      log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
+      return SW_HOST_CARD_IO_ERROR;
     }
-  if (pipe (wp) == -1)
+
+  msgbuf[0] = 0x03; /* TRANSMIT command. */
+  len = apdulen;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+       || writen (slotp->pcsc.req_fd, apdu, len))
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      slotp->used = 0;
-      return -1;
+      log_error ("error sending PC/SC TRANSMIT request: %s\n",
+                 strerror (errno));
+      goto command_failed;
     }
-      
-  pid = fork ();
-  if (pid == -1)
+
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
     {
-      log_error ("error forking process: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      close (wp[0]);
-      close (wp[1]);
-      slotp->used = 0;
-      return -1;
+      log_error ("error receiving PC/SC TRANSMIT 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_transmit failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
     }
-  slotp->pcsc.pid = pid;
-
-  if (!pid)
-    { /*
-         === Child ===
-       */
 
-      /* Double fork. */
-      pid = fork ();
-      if (pid == -1)
-        _exit (31); 
-      if (pid)
-        _exit (0); /* Immediate exit this parent, so that the child
-                      gets cleaned up by the init process. */
+   full_len = len;
+   
+   n = *buflen < len ? *buflen : len;
+   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+     {
+       log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+                  i? strerror (errno) : "premature EOF");
+       goto command_failed;
+     }
+   *buflen = n;
 
-      /* Connect our pipes. */
-      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
-        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
-      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
+   full_len -= len;
+   if (full_len)
+     {
+       log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+       err = SW_HOST_INV_VALUE;
+     }
+   /* We need to read any rest of the response, to keep the
+      protocol runnng. */
+   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;
+     }
+
+   return err;
+
+ 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 -1;
+
+#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;
+
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+      send_pci.protocol = PCSC_PROTOCOL_T1;
+  else
+      send_pci.protocol = PCSC_PROTOCOL_T0;
+  send_pci.pci_len = sizeof send_pci;
+  recv_len = *buflen;
+  err = pcsc_transmit (reader_table[slot].pcsc.card,
+                       &send_pci, apdu, apdulen,
+                       NULL, buffer, &recv_len);
+  *buflen = recv_len;
+  if (err)
+    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+  
+  return err? SW_HOST_CARD_IO_ERROR:0; 
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i;
+  unsigned char msgbuf[9];
+
+  slotp = reader_table + slot;
+
+  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");
+      return 0;
+    }
+
+  msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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_close failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+  
+  /* We will the wrapper in any case - errors are merely
+     informational. */
+  
+ 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 0;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+  pcsc_release_context (reader_table[slot].pcsc.context);
+  reader_table[slot].used = 0;
+  return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+/* Open the PC/SC reader using the pcsc_wrapper program.  This is
+   needed to cope with different thread models and other peculiarities
+   of libpcsclite. */
+  int slot;
+  reader_table_t slotp;
+  int fd, rp[2], wp[2];
+  int n, i;
+  pid_t pid;
+  size_t len;
+  unsigned char msgbuf[9];
+  int err;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
+     the common directy but implement it direclty so that this file
+     may still be source copied. */
+  
+  if (pipe (rp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      slotp->used = 0;
+      return -1;
+    }
+  if (pipe (wp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+      
+  pid = fork ();
+  if (pid == -1)
+    {
+      log_error ("error forking process: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+  slotp->pcsc.pid = pid;
+
+  if (!pid)
+    { /*
+         === Child ===
+       */
+
+      /* Double fork. */
+      pid = fork ();
+      if (pid == -1)
+        _exit (31); 
+      if (pid)
+        _exit (0); /* Immediate exit this parent, so that the child
+                      gets cleaned up by the init process. */
+
+      /* Connect our pipes. */
+      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
+        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. */
@@ -724,6 +1041,12 @@ open_pcsc_reader (const char *portstr)
     }
   slotp->atrlen = len;
 
+  reader_table[slot].close_reader = close_pcsc_reader;
+  reader_table[slot].reset_reader = reset_pcsc_reader;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
   dump_reader_status (slot); 
   return slot;
 
@@ -819,300 +1142,49 @@ open_pcsc_reader (const char *portstr)
   /* (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)
-    {
-      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;
-    }
-  if (atrlen >= DIM (reader_table[0].atr))
-    log_bug ("ATR returned by pcsc_status is too large\n");
-  reader_table[slot].atrlen = atrlen;
-/*   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); 
-  return slot;
-#endif /*!NEED_PCSC_WRAPPER */
-}
-
-
-static int
-pcsc_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: CT API error code. */
-static int
-pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
-{
-#ifdef NEED_PCSC_WRAPPER
-  long err;
-  reader_table_t slotp;
-  size_t len, full_len;
-  int i, n;
-  unsigned char msgbuf[9];
-
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
-
-  slotp = reader_table + slot;
-
-  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 -1;
-    }
-
-  msgbuf[0] = 0x03; /* TRANSMIT command. */
-  len = apdulen;
-  msgbuf[1] = (len >> 24);
-  msgbuf[2] = (len >> 16);
-  msgbuf[3] = (len >>  8);
-  msgbuf[4] = (len      );
-  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
-       || writen (slotp->pcsc.req_fd, apdu, len))
-    {
-      log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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_transmit failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      return -1;
-    }
-
-   full_len = len;
-   
-   n = *buflen < len ? *buflen : len;
-   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
-     {
-       log_error ("error receiving PC/SC TRANSMIT response: %s\n",
-                  i? strerror (errno) : "premature EOF");
-       goto command_failed;
-     }
-   *buflen = n;
-
-   full_len -= len;
-   if (full_len)
-     {
-       log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
-       err = -1; 
-     }
-   /* We need to read any rest of the response, to keep the
-      protocol runnng. */
-   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;
-     }
-
-   return err;
-
- 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 -1;
-
-#else /*!NEED_PCSC_WRAPPER*/
-
-  long err;
-  struct pcsc_io_request_s send_pci;
-  unsigned long recv_len;
-  
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
-
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-      send_pci.protocol = PCSC_PROTOCOL_T1;
-  else
-      send_pci.protocol = PCSC_PROTOCOL_T0;
-  send_pci.pci_len = sizeof send_pci;
-  recv_len = *buflen;
-  err = pcsc_transmit (reader_table[slot].pcsc.card,
-                       &send_pci, apdu, apdulen,
-                       NULL, buffer, &recv_len);
-  *buflen = recv_len;
-  if (err)
-    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
-  
-  return err? -1:0; /* FIXME: Return appropriate error code. */
-#endif /*!NEED_PCSC_WRAPPER*/
-}
-
-
-static int
-close_pcsc_reader (int slot)
-{
-#ifdef NEED_PCSC_WRAPPER
-  long err;
-  reader_table_t slotp;
-  size_t len;
-  int i;
-  unsigned char msgbuf[9];
-
-  slotp = reader_table + slot;
-
-  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");
-      return 0;
-    }
-
-  msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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_close failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
-  
-  /* We will the wrapper in any case - errors are merely
-     informational. */
-  
- 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 0;
-
-#else /*!NEED_PCSC_WRAPPER*/
-
-  pcsc_release_context (reader_table[slot].pcsc.context);
-  reader_table[slot].used = 0;
-  return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
-}
-
-static int
-reset_pcsc_reader (int slot)
-{
-  return SW_HOST_NOT_SUPPORTED;
-}
-
-
-
-
-\f
-#ifdef HAVE_LIBUSB
-/* 
-     Internal CCID driver interface.
- */
-
-static const char *
-get_ccid_error_string (long err)
-{
-  if (!err)
-    return "okay";
-  else
-    return "unknown CCID error";
-}
-
-static int
-open_ccid_reader (void)
-{
-  int err;
-  int slot;
-  reader_table_t slotp;
-
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
-  slotp = reader_table + slot;
-
-  err = ccid_open_reader (&slotp->ccid.handle, 0);
-  if (err)
-    {
-      slotp->used = 0;
-      return -1;
-    }
-
-  err = ccid_get_atr (slotp->ccid.handle,
-                      slotp->atr, sizeof slotp->atr, &slotp->atrlen);
+                     list, &listlen,
+                     &card_state, &card_protocol,
+                     reader_table[slot].atr, &atrlen);
+  xfree (list);
   if (err)
     {
-      slotp->used = 0;
+      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;
     }
+  if (atrlen >= DIM (reader_table[0].atr))
+    log_bug ("ATR returned by pcsc_status is too large\n");
+  reader_table[slot].atrlen = atrlen;
 
-  slotp->is_ccid = 1;
+  reader_table[slot].close_reader = close_pcsc_reader;
+  reader_table[slot].reset_reader = reset_pcsc_reader;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+/*   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); 
   return slot;
+#endif /*!NEED_PCSC_WRAPPER */
+}
+
+
+
+\f
+#ifdef HAVE_LIBUSB
+/* 
+     Internal CCID driver interface.
+ */
+
+
+static void
+dump_ccid_reader_status (int slot)
+{
+  log_info ("reader slot %d: using ccid driver\n", slot);
 }
 
 static int
@@ -1134,7 +1206,7 @@ reset_ccid_reader (int slot)
 
   err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
   if (err)
-    return -1;
+    return err;
   /* If the reset was successful, update the ATR. */
   assert (sizeof slotp->atr >= sizeof atr);
   slotp->atrlen = atrlen;
@@ -1175,6 +1247,11 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
   long err;
   size_t maxbuflen;
 
+  /* If we don't have an ATR, we need to reset the reader first. */
+  if (!reader_table[slot].atrlen
+      && (err = reset_ccid_reader (slot)))
+    return err;
+
   if (DBG_CARD_IO)
     log_printhex ("  APDU_data:", apdu, apdulen);
 
@@ -1186,9 +1263,49 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
     log_error ("ccid_transceive failed: (0x%lx)\n",
                err);
   
-  return err? -1:0; /* FIXME: Return appropriate error code. */
+  return err; 
+}
+
+/* Open the reader and try to read an ATR.  */
+static int
+open_ccid_reader (void)
+{
+  int err;
+  int slot;
+  reader_table_t slotp;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  err = ccid_open_reader (&slotp->ccid.handle, 0);
+  if (err)
+    {
+      slotp->used = 0;
+      return -1;
+    }
+
+  err = ccid_get_atr (slotp->ccid.handle,
+                      slotp->atr, sizeof slotp->atr, &slotp->atrlen);
+  if (err)
+    {
+      slotp->atrlen = 0;
+      err = 0;
+    }
+
+  reader_table[slot].close_reader = close_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].dump_status_reader = dump_ccid_reader_status;
+
+  dump_reader_status (slot); 
+  return slot;
 }
 
+
+
 #endif /* HAVE_LIBUSB */
 
 
@@ -1202,6 +1319,117 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
      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)
 {
@@ -1286,30 +1514,119 @@ open_osc_reader (int portno)
   slotp->atrlen = slotp->osc.scard->atr_len;
   memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen);
 
-  slotp->is_osc = 1;
+  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.
+
+     The port number is actually an index into the list of ports as
+     returned via the protocol.
+ */
+
+
+static int
+rapdu_status_to_sw (int status)
+{
+  int rc;
+
+  switch (status)
+    {
+    case RAPDU_STATUS_SUCCESS:  rc = 0; break;
+
+    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;
+
+    case RAPDU_STATUS_TIMEOUT:  rc = SW_HOST_CARD_IO_ERROR; break;
+    case RAPDU_STATUS_CARDIO:   rc = SW_HOST_CARD_IO_ERROR; break;
+    case RAPDU_STATUS_NOCARD:   rc = SW_HOST_NO_CARD; break;
+    case RAPDU_STATUS_CARDCHG:  rc = SW_HOST_NO_CARD; break;
+    case RAPDU_STATUS_BUSY:     rc = SW_HOST_BUSY; break;
+    case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
+
+    default: rc = SW_HOST_GENERAL_ERROR; break;
+    }
+
+  return rc;
+}
+
+
 
 static int
-close_osc_reader (int slot)
+close_rapdu_reader (int slot)
 {
-  /* FIXME: Implement. */
+  rapdu_release (reader_table[slot].rapdu.handle);
   reader_table[slot].used = 0;
   return 0;
 }
 
+
 static int
-reset_osc_reader (int slot)
+reset_rapdu_reader (int slot)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  int err;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
+
+  slotp = reader_table + slot;
+
+  err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+  if (err)
+    {
+      log_error ("sending rapdu command RESET failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
+    {
+      log_error ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      int sw = rapdu_status_to_sw (msg->cmd);
+      log_error ("rapdu command RESET failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      rapdu_msg_release (msg);
+      return sw;
+    }
+  if (msg->datalen >= DIM (slotp->atr))
+    {
+      log_error ("ATR returned by the RAPDU layer is too large\n");
+      rapdu_msg_release (msg);
+      return SW_HOST_INV_VALUE; 
+    }
+  slotp->atrlen = msg->datalen;
+  memcpy (slotp->atr, msg->data, msg->datalen);
+
+  rapdu_msg_release (msg);
+  return 0;
 }
 
 
 static int
-osc_get_status (int slot, unsigned int *status)
+my_rapdu_get_status (int slot, unsigned int *status)
 {
   return SW_HOST_NOT_SUPPORTED;
 }
@@ -1319,91 +1636,151 @@ osc_get_status (int slot, unsigned int *status)
    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)
+my_rapdu_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];
+  int err;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
+  size_t maxlen = *buflen;
+
+  slotp = reader_table + slot;
 
+  *buflen = 0;
   if (DBG_CARD_IO)
     log_printhex ("  APDU_data:", apdu, apdulen);
 
   if (apdulen < 4)
     {
-      log_error ("osc_send_apdu: APDU is too short\n");
-      return SC_ERROR_CMD_TOO_SHORT;
+      log_error ("rapdu_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) 
+  err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
+  if (err)
     {
-      a.le = *apdu? *apdu : 256;
-      apdu++; apdulen--;
-      a.cse = SC_APDU_CASE_2_SHORT;
+      log_error ("sending rapdu command APDU failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
     }
-  else
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
     {
-      a.lc = *apdu++; apdulen--;
-      if (apdulen < a.lc)
-        {
-          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
-          return SC_ERROR_CMD_TOO_SHORT;
+      log_error ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      int sw = rapdu_status_to_sw (msg->cmd);
+      log_error ("rapdu command APDU failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      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; 
+    }
+
+  *buflen = msg->datalen;
+  memcpy (buffer, msg->data, msg->datalen);
+
+  rapdu_msg_release (msg);
+  return 0;
+}
+
+static int
+open_rapdu_reader (int portno,
+                   const unsigned char *cookie, size_t length,
+                   int (*readfnc) (void *opaque,
+                                   void *buffer, size_t size),
+                   void *readfnc_value,
+                   int (*writefnc) (void *opaque,
+                                    const void *buffer, size_t size),
+                   void *writefnc_value,
+                   void (*closefnc) (void *opaque),
+                   void *closefnc_value)
+{
+  int err;
+  int slot;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
 
-        }
-      memcpy(data, apdu, a.lc);
-      apdu += a.lc; apdulen -= a.lc;
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
 
-      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 SC_ERROR_CMD_TOO_LONG;
-            }
-          a.cse = SC_APDU_CASE_4_SHORT;
-        }
+  slotp->rapdu.handle = rapdu_new ();
+  if (!slotp->rapdu.handle)
+    {
+      slotp->used = 0;
+      return -1;
     }
 
-  a.resp = result;
-  a.resplen = DIM(result);
 
-  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
+  rapdu_set_iofunc (slotp->rapdu.handle,
+                    readfnc, readfnc_value,
+                    writefnc, writefnc_value,
+                    closefnc, closefnc_value);
+  rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
+
+  /* First try to get the current ATR, but if the card is inactive
+     issue a reset instead.  */
+  err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
+  if (err == RAPDU_STATUS_NEEDRESET)
+    err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
   if (err)
     {
-      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
-      return err;
+      log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      goto failure;
     }
-
-  if (*buflen < 2 || a.resplen > *buflen - 2)
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
     {
-      log_error ("osc_send_apdu: provided buffer too short to store result\n");
-      return SC_ERROR_BUFFER_TOO_SMALL;
+      log_info ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      goto failure;
     }
-  memcpy (buffer, a.resp, a.resplen);
-  buffer[a.resplen] = a.sw1;
-  buffer[a.resplen+1] = a.sw2;
-  *buflen = a.resplen + 2;
-  return 0;
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      log_info ("rapdu command GET ATR failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      goto failure;
+    }
+  if (msg->datalen >= DIM (slotp->atr))
+    {
+      log_error ("ATR returned by the RAPDU layer is too large\n");
+      goto failure;
+    }
+  slotp->atrlen = msg->datalen;
+  memcpy (slotp->atr, msg->data, msg->datalen);
+
+  reader_table[slot].close_reader = close_rapdu_reader;
+  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].dump_status_reader = NULL;
+
+  dump_reader_status (slot); 
+  rapdu_msg_release (msg);
+  return slot;
+
+ failure:      
+  rapdu_msg_release (msg);
+  rapdu_release (slotp->rapdu.handle);
+  slotp->used = 0;
+  return -1;
 }
 
-#endif /* HAVE_OPENSC */
+#endif /*USE_G10CODE_RAPDU*/
 
 
 \f
@@ -1583,23 +1960,46 @@ apdu_open_reader (const char *portstr)
 }
 
 
+/* Open an remote reader and return an internal slot number or -1 on
+   error. This function is an alternative to apdu_open_reader and used
+   with remote readers only.  Note that the supplied CLOSEFNC will
+   only be called once and the slot will not be valid afther this.
+
+   If PORTSTR is NULL we default to the first availabe port.
+*/  
+int
+apdu_open_remote_reader (const char *portstr,
+                         const unsigned char *cookie, size_t length,
+                         int (*readfnc) (void *opaque,
+                                         void *buffer, size_t size),
+                         void *readfnc_value,
+                         int (*writefnc) (void *opaque,
+                                          const void *buffer, size_t size),
+                         void *writefnc_value,
+                         void (*closefnc) (void *opaque),
+                         void *closefnc_value)
+{
+#ifdef USE_G10CODE_RAPDU
+  return open_rapdu_reader (portstr? atoi (portstr) : 0,
+                            cookie, length,
+                            readfnc, readfnc_value,
+                            writefnc, writefnc_value,
+                            closefnc, closefnc_value);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
 int
 apdu_close_reader (int slot)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  if (reader_table[slot].is_ctapi)
-    return close_ct_reader (slot);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return close_ccid_reader (slot);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return close_osc_reader (slot);
-#endif
-  else
-    return close_pcsc_reader (slot);
+  if (reader_table[slot].close_reader)
+    return reader_table[slot].close_reader (slot);
+  return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Enumerate all readers and return information on whether this reader
@@ -1626,23 +2026,52 @@ apdu_reset (int slot)
   if ((sw = lock_slot (slot)))
     return sw;
 
-  if (reader_table[slot].is_ctapi)
-    sw = reset_ct_reader (slot);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    sw = reset_ccid_reader (slot);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    sw = reset_osc_reader (slot);
-#endif
-  else
-    sw = reset_pcsc_reader (slot);
+  if (reader_table[slot].reset_reader)
+    sw = reader_table[slot].reset_reader (slot);
+
+  unlock_slot (slot);
+  return sw;
+}
+
 
+/* Activate a card if it has not yet been done.  This is a kind of
+   reset-if-required.  It is useful to test for presence of a card
+   before issuing a bunch of apdu commands.  It does not wait on a
+   locked card. */
+int
+apdu_activate (int slot)
+{
+  int sw;
+  unsigned int s;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+  
+  if ((sw = trylock_slot (slot)))
+    return sw;
+
+  if (reader_table[slot].get_status_reader)
+    sw = reader_table[slot].get_status_reader (slot, &s);
+
+  if (!sw)
+    {
+      if (!(s & 2))  /* Card not present.  */
+        sw = SW_HOST_NO_CARD;
+      else if ( ((s & 2) && !(s & 4))
+                || !reader_table[slot].atrlen )
+        {
+          /* We don't have an ATR or a card is present though inactive:
+             do a reset now. */
+          if (reader_table[slot].reset_reader)
+            sw = reader_table[slot].reset_reader (slot);
+        }
+    }
+  
   unlock_slot (slot);
   return sw;
 }
 
+  
 
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
@@ -1660,29 +2089,9 @@ apdu_get_atr (int slot, size_t *atrlen)
   return buf;
 }
 
-  
-    
-static const char *
-error_string (int slot, long rc)
-{
-  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return "[invalid slot]";
-  if (reader_table[slot].is_ctapi)
-    return ct_error_string (rc);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return get_ccid_error_string (rc);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return sc_strerror (rc);
-#endif
-  else
-    return pcsc_error_string (rc);
-}
-
 
-/* Retrieve the status for SLOT. The function does obnly wait fot the
+    
+/* 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
 
@@ -1691,7 +2100,7 @@ error_string (int slot, long rc)
      bit 2 = card active
      bit 3 = card access locked [not yet implemented]
 
-   For must application, tetsing bit 0 is sufficient.
+   For must application, testing bit 0 is sufficient.
 
    CHANGED will receive the value of the counter tracking the number
    of card insertions.  This value may be used to detect a card
@@ -1710,18 +2119,8 @@ apdu_get_status (int slot, int hang,
   if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
     return sw;
 
-  if (reader_table[slot].is_ctapi)
-    sw = ct_get_status (slot, &s);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    sw = get_status_ccid (slot, &s);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    sw = osc_get_status (slot, &s);
-#endif
-  else
-    sw = pcsc_get_status (slot, &s);
+  if (reader_table[slot].get_status_reader)
+    sw = reader_table[slot].get_status_reader (slot, &s);
 
   unlock_slot (slot);
 
@@ -1744,18 +2143,13 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  if (reader_table[slot].is_ctapi)
-    return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return send_apdu_ccid (slot, apdu, apdulen, buffer, buflen);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return osc_send_apdu (slot, apdu, apdulen, buffer, buflen);
-#endif
+
+  if (reader_table[slot].send_apdu_reader)
+    return reader_table[slot].send_apdu_reader (slot,
+                                                apdu, apdulen,
+                                                buffer, buflen);
   else
-    return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen);
+    return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -1819,9 +2213,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_simple(%d) failed: %s\n",
-                 slot, error_string (slot, rc));
+                 slot, apdu_strerror (rc));
       unlock_slot (slot);
-      return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+      return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
     }
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
   /* store away the returned data but strip the statusword. */
@@ -1886,9 +2280,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
-                         slot, error_string (slot, rc));
+                         slot, apdu_strerror (rc));
               unlock_slot (slot);
-              return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+              return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
           resultlen -= 2;
@@ -1977,5 +2371,180 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1,
 }
 
 
+/* This is a more generic version of the apdu sending routine.  It
+   takes an already formatted APDU in APDUDATA or length APDUDATALEN
+   and returns the with the APDU including the status word.  With
+   HANDLE_MORE set to true this function will handle the MORE DATA
+   status and return all APDUs concatenated with one status word at
+   the end.  The function does not return a regular status word but 0
+   on success.  If the slot is locked, the fucntion returns
+   immediately.*/
+int 
+apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+                  int handle_more,
+                  unsigned char **retbuf, size_t *retbuflen)
+{
+#define RESULTLEN 256
+  unsigned char apdu[5+256+1];
+  size_t apdulen;
+  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
+                                         the driver. */
+  size_t resultlen;
+  int sw;
+  long rc; /* we need a long here due to PC/SC. */
+  int class;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if ((sw = trylock_slot (slot)))
+    return sw;
+
+  /* We simply trucntate 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);
+  if (rc || resultlen < 2)
+    {
+      log_error ("apdu_send_direct(%d) failed: %s\n",
+                 slot, apdu_strerror (rc));
+      unlock_slot (slot);
+      return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  /* Store away the returned data but strip the statusword. */
+  resultlen -= 2;
+  if (DBG_CARD_IO)
+    {
+      log_debug (" response: sw=%04X  datalen=%d\n", sw, resultlen);
+      if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+        log_printhex ("     dump: ", result, resultlen);
+    }
+
+  if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
+    {
+      unsigned char *p = NULL, *tmp;
+      size_t bufsize = 4096;
+
+      /* It is likely that we need to return much more data, so we
+         start off with a large buffer. */
+      if (retbuf)
+        {
+          *retbuf = p = xtrymalloc (bufsize + 2);
+          if (!*retbuf)
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
+          assert (resultlen < bufsize);
+          memcpy (p, result, resultlen);
+          p += resultlen;
+        }
+
+      do
+        {
+          int len = (sw & 0x00ff);
+          
+          if (DBG_CARD_IO)
+            log_debug ("apdu_send_direct(%d): %d more bytes available\n",
+                       slot, len);
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = 0xC0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = len; 
+          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+          resultlen = RESULTLEN;
+          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          if (rc || resultlen < 2)
+            {
+              log_error ("apdu_send_direct(%d) for get response failed: %s\n",
+                         slot, apdu_strerror (rc));
+              unlock_slot (slot);
+              return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+            }
+          sw = (result[resultlen-2] << 8) | result[resultlen-1];
+          resultlen -= 2;
+          if (DBG_CARD_IO)
+            {
+              log_debug ("     more: sw=%04X  datalen=%d\n", sw, resultlen);
+              if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+                log_printhex ("     dump: ", result, resultlen);
+            }
+
+          if ((sw & 0xff00) == SW_MORE_DATA
+              || sw == SW_SUCCESS
+              || sw == SW_EOF_REACHED )
+            {
+              if (retbuf && resultlen)
+                {
+                  if (p - *retbuf + resultlen > bufsize)
+                    {
+                      bufsize += resultlen > 4096? resultlen: 4096;
+                      tmp = xtryrealloc (*retbuf, bufsize + 2);
+                      if (!tmp)
+                        {
+                          unlock_slot (slot);
+                          return SW_HOST_OUT_OF_CORE;
+                        }
+                      p = tmp + (p - *retbuf);
+                      *retbuf = tmp;
+                    }
+                  memcpy (p, result, resultlen);
+                  p += resultlen;
+                }
+            }
+          else
+            log_info ("apdu_send_sdirect(%d) "
+                      "got unexpected status %04X from get response\n",
+                      slot, sw);
+        }
+      while ((sw & 0xff00) == SW_MORE_DATA);
+      
+      if (retbuf)
+        {
+          *retbuflen = p - *retbuf;
+          tmp = xtryrealloc (*retbuf, *retbuflen + 2);
+          if (tmp)
+            *retbuf = tmp;
+        }
+    }
+  else
+    {
+      if (retbuf)
+        {
+          *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
+          if (!*retbuf)
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
+          *retbuflen = resultlen;
+          memcpy (*retbuf, result, resultlen);
+        }
+    }
+
+  unlock_slot (slot);
+
+  /* Append the status word - we reseved the two extra bytes while
+     allocating the buffer. */
+  if (retbuf)
+    {
+      (*retbuf)[(*retbuflen)++] = (sw >> 8);
+      (*retbuf)[(*retbuflen)++] = sw;
+    }
+
+  if (DBG_CARD_IO && retbuf)
+    log_printhex ("      dump: ", *retbuf, *retbuflen);
+  return 0;
+#undef RESULTLEN
+}
 
 
index f74bab7..a2b7812 100644 (file)
@@ -53,19 +53,38 @@ enum {
   SW_HOST_NO_DRIVER     = 0x10004,
   SW_HOST_NOT_SUPPORTED = 0x10005,
   SW_HOST_LOCKING_FAILED= 0x10006,
-  SW_HOST_BUSY          = 0x10007
+  SW_HOST_BUSY          = 0x10007,
+  SW_HOST_NO_CARD       = 0x10008,
+  SW_HOST_CARD_INACTIVE = 0x10009,
+  SW_HOST_CARD_IO_ERROR = 0x1000a,
+  SW_HOST_GENERAL_ERROR = 0x1000b,
+  SW_HOST_NO_READER     = 0x1000c
 };
 
 
 
 /* Note , that apdu_open_reader returns no status word but -1 on error. */
 int apdu_open_reader (const char *portstr);
+int apdu_open_remote_reader (const char *portstr,
+                             const unsigned char *cookie, size_t length,
+                             int (*readfnc) (void *opaque,
+                                             void *buffer, size_t size),
+                             void *readfnc_value,
+                             int (*writefnc) (void *opaque,
+                                              const void *buffer, size_t size),
+                             void *writefnc_value,
+                             void (*closefnc) (void *opaque),
+                             void *closefnc_value);
 int apdu_close_reader (int slot);
 int apdu_enum_reader (int slot, int *used);
 unsigned char *apdu_get_atr (int slot, size_t *atrlen);
 
+const char *apdu_strerror (int rc);
 
-/* The apdu send functions do return status words. */
+
+/* These apdu functions do return status words. */
+
+int apdu_activate (int slot);
 int apdu_reset (int slot);
 int apdu_get_status (int slot, int hang,
                      unsigned int *status, unsigned int *changed);
@@ -77,6 +96,10 @@ int apdu_send (int slot, int class, int ins, int p0, int p1,
 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);
+int apdu_send_direct (int slot,
+                      const unsigned char *apdudata, size_t apdudatalen,
+                      int handle_more,
+                      unsigned char **retbuf, size_t *retbuflen);
 
 
 #endif /*APDU_H*/
index cd0bee6..4751b8c 100644 (file)
@@ -442,11 +442,11 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev)
             }
         }
     }
-  return -1; /* No suitable device found. */
+  return CCID_DRIVER_ERR_NO_READER; /* No suitable device found. */
 }
 
 
-/* Open the reader with the internal number READERNO and return a a
+/* Open the reader with the internal number READERNO and return a 
    pointer to be used as handle in HANDLE.  Returns 0 on success. */
 int 
 ccid_open_reader (ccid_driver_t *handle, int readerno)
@@ -469,7 +469,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
   if (rc)
     {
       DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
-      return -1;
+      return CCID_DRIVER_ERR_NO_READER;
     }
 
   while (usb_find_device(match, dev, &dev) >= 0) 
@@ -482,7 +482,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
           if (!*handle)
             {
               DEBUGOUT ("out of memory\n");
-              rc = -1;
+              rc = CCID_DRIVER_ERR_OUT_OF_CORE;
               free (*handle);
               *handle = NULL;
               goto leave;
@@ -503,6 +503,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
               DEBUGOUT_1 ("usb_open failed: %d\n", rc);
               free (*handle);
               *handle = NULL;
+              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
               goto leave;
             }
 
@@ -515,6 +516,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
               DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
               free (*handle);
               *handle = NULL;
+              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
               goto leave;
             }
 
@@ -605,7 +607,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
     DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
   else
     DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
-  return -1;
+  return CCID_DRIVER_ERR_CARD_IO_ERROR;
 }
 
 
@@ -631,7 +633,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   if (rc < 0)
     {
       DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
-      return -1;
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
     }
 
   *nread = msglen = rc;
@@ -639,23 +641,23 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   if (msglen < 10)
     {
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[0] != expected_type)
     {
       DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[5] != 0)    
     {
       DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[6] != seqno)    
     {
       DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
                   seqno, buffer[6]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
 
   if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
@@ -672,6 +674,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
     DEBUGOUT_CONT_1 (" %02X", buffer[i]);
   DEBUGOUT_LF ();
 
+  switch ((buffer[7] & 0x03))
+    {
+    case 0: /* no error */ break;
+    case 1: return CCID_DRIVER_ERR_CARD_INACTIVE;
+    case 2: return CCID_DRIVER_ERR_NO_CARD;
+    case 3: /* RFU */ break;
+    }
   return 0;
 }
 
@@ -695,7 +704,7 @@ ccid_poll (ccid_driver_t handle)
   if (rc < 0)
     {
       DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
-      return -1;
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
     }
 
   msglen = rc;
@@ -704,7 +713,7 @@ ccid_poll (ccid_driver_t handle)
   if (msglen < 1)
     {
       DEBUGOUT ("intr-in msg too short\n");
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
 
   if (msg[0] == RDR_to_PC_NotifySlotChange)
@@ -731,7 +740,8 @@ ccid_poll (ccid_driver_t handle)
 }
 
 
-
+/* Note that this fucntion won't return the error codes NO_CARD or
+   CARD_INACTIVE */
 int 
 ccid_slot_status (ccid_driver_t handle, int *statusbits)
 {
@@ -752,7 +762,8 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
-  if (rc)
+  if (rc && rc != CCID_DRIVER_ERR_NO_CARD
+      && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
     return rc;
   *statusbits = (msg[7] & 3);
 
@@ -1018,7 +1029,7 @@ ccid_transceive (ccid_driver_t handle,
 
           /* Construct an I-Block. */
           if (apdulen > 254)
-            return -1; /* Invalid length. */
+            return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
 
           tpdu = msg+10;
           /* NAD: DAD=1, SAD=0 */
@@ -1082,7 +1093,7 @@ ccid_transceive (ccid_driver_t handle,
       if (tpdulen < 4) 
         {
           DEBUGOUT ("cannot yet handle short blocks!\n");
-          return -1
+          return CCID_DRIVER_ERR_NOT_SUPPORTED
         }
 
 #ifdef DEBUG_T1
@@ -1132,7 +1143,7 @@ ccid_transceive (ccid_driver_t handle,
                   DEBUGOUT_2 ("provided buffer too short for received data "
                               "(%u/%u)\n",
                               (unsigned int)n, (unsigned int)maxresplen);
-                  return -1;
+                  return CCID_DRIVER_ERR_INV_VALUE;
                 }
               
               memcpy (resp, p, n); 
@@ -1163,7 +1174,7 @@ ccid_transceive (ccid_driver_t handle,
               if (++retries > 3)
                 {
                   DEBUGOUT ("3 failed retries\n");
-                  return -1;
+                  return CCID_DRIVER_ERR_CARD_IO_ERROR;
                 }
               msg = send_buffer;
               tpdulen = last_tpdulen;
@@ -1171,7 +1182,7 @@ ccid_transceive (ccid_driver_t handle,
           else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
             { /* Reponse does not match our sequence number. */
               DEBUGOUT ("R-block with wrong seqno received on more bit\n");
-              return -1;
+              return CCID_DRIVER_ERR_CARD_IO_ERROR;
             }
           else if (sending)
             { /* Send next chunk. */
@@ -1183,7 +1194,7 @@ ccid_transceive (ccid_driver_t handle,
           else
             {
               DEBUGOUT ("unexpected ACK R-block received\n");
-              return -1;
+              return CCID_DRIVER_ERR_CARD_IO_ERROR;
             }
         }
       else 
@@ -1210,7 +1221,7 @@ ccid_transceive (ccid_driver_t handle,
               DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
             }
           else
-            return -1;
+            return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
     } /* end T=1 protocol loop. */
 
index 8b86eb1..0b108f1 100644 (file)
 #ifndef CCID_DRIVER_H
 #define CCID_DRIVER_H
 
+/* The CID driver returns the same error codes as the statsu words
+   used by GnuPG's apdu.h.  For ease of maintenance they should always
+   match.  */
+#define CCID_DRIVER_ERR_OUT_OF_CORE    0x10001 
+#define CCID_DRIVER_ERR_INV_VALUE      0x10002
+#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003
+#define CCID_DRIVER_ERR_NO_DRIVER      0x10004
+#define CCID_DRIVER_ERR_NOT_SUPPORTED  0x10005
+#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006
+#define CCID_DRIVER_ERR_BUSY           0x10007
+#define CCID_DRIVER_ERR_NO_CARD        0x10008
+#define CCID_DRIVER_ERR_CARD_INACTIVE  0x10009
+#define CCID_DRIVER_ERR_CARD_IO_ERROR  0x1000a
+#define CCID_DRIVER_ERR_GENERAL_ERROR  0x1000b
+#define CCID_DRIVER_ERR_NO_READER      0x1000c
+
 
 struct ccid_driver_s;
 typedef struct ccid_driver_s *ccid_driver_t;
index fd3f048..d5db607 100644 (file)
@@ -85,6 +85,12 @@ map_sw (int sw)
     case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
     case SW_HOST_BUSY:           ec = GPG_ERR_EBUSY; break;
+    case SW_HOST_NO_CARD:        ec = GPG_ERR_CARD_NOT_PRESENT; break;
+    case SW_HOST_CARD_INACTIVE:  ec = GPG_ERR_CARD_RESET; break;
+    case SW_HOST_CARD_IO_ERROR:  ec = GPG_ERR_EIO; break;
+    case SW_HOST_GENERAL_ERROR:  ec = GPG_ERR_GENERAL; break;
+    case SW_HOST_NO_READER:      ec = GPG_ERR_ENODEV; break;
+
     default:
       if ((sw & 0x010000))
         ec = GPG_ERR_GENERAL; /* Should not happen. */