Add code to better handle unplugging of a reader.
authorWerner Koch <wk@gnupg.org>
Thu, 16 Jul 2009 15:54:59 +0000 (15:54 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 16 Jul 2009 15:54:59 +0000 (15:54 +0000)
scd/ChangeLog
scd/apdu.c
scd/apdu.h
scd/app.c
scd/ccid-driver.c
scd/command.c
scd/scdaemon.c

index fcb13e3..46b15f0 100644 (file)
@@ -1,3 +1,17 @@
+2009-07-16  Werner Koch  <wk@g10code.com>
+
+       * command.c (update_reader_status_file): Test for unplugged reader.
+       (TEST_CARD_REMOVAL): Ditto.
+       * app.c (select_application): Ditto.
+       * ccid-driver.c (bulk_out): Return CCID_DRIVER_ERR_NO_READER if a
+       reader was unplugged.
+       (struct ccid_driver_s): Turn nonnull_nad into an unsigned char.
+       Turn apdu_level, auto_ifsd, powered_off, has_pinpad into
+       bitfields. Add enodev_seen.
+       * apdu.c (apdu_prepare_exit): New.
+       (get_status_ccid): Return the status word and nut just -1.
+       * scdaemon.c (scd_exit): Call it.
+
 2009-07-13  Werner Koch  <wk@g10code.com>
 
        * ccid-driver.c (struct ccid_driver_s): Add fields last_progress,
index 57e82c4..36bae89 100644 (file)
@@ -1853,7 +1853,7 @@ get_status_ccid (int slot, unsigned int *status)
 
   rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
   if (rc)
-    return -1;
+    return rc;
 
   if (bits == 0)
     *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
@@ -2523,6 +2523,33 @@ apdu_close_reader (int slot)
   return SW_HOST_NOT_SUPPORTED;
 }
 
+
+/* Function suitable for a cleanup function to close all reader.  It
+   should not be used if the reader will be opened again.  The reason
+   for implementing this to properly close USB devices so that they
+   will startup the next time without error. */
+void
+apdu_prepare_exit (void)
+{
+  static int sentinel;
+  int slot;
+
+  if (!sentinel)
+    {
+      sentinel = 1;
+      for (slot = 0; slot < MAX_READER; slot++)
+        if (reader_table[slot].used)
+          {
+            apdu_disconnect (slot);
+            if (reader_table[slot].close_reader)
+              reader_table[slot].close_reader (slot);
+            reader_table[slot].used = 0;
+          }
+      sentinel = 0;
+    }
+}
+
+
 /* Shutdown a reader; that is basically the same as a close but keeps
    the handle ready for later use. A apdu_reset_reader or apdu_connect
    should be used to get it active again. */
index 03c774c..c47dea8 100644 (file)
@@ -94,6 +94,7 @@ int apdu_open_remote_reader (const char *portstr,
                              void *closefnc_value);
 int apdu_shutdown_reader (int slot);
 int apdu_close_reader (int slot);
+void apdu_prepare_exit (void);
 int apdu_enum_reader (int slot, int *used);
 unsigned char *apdu_get_atr (int slot, size_t *atrlen);
 
index efaf9ec..a23c4a5 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -367,10 +367,10 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
     }
 
   /* For certain error codes, there is no need to try more.  */
-  if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
+  if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
+      || gpg_err_code (err) == GPG_ERR_ENODEV)
     goto leave;
   
-
   /* Figure out the application to use.  */
   err = gpg_error (GPG_ERR_NOT_FOUND);
 
index f9b0bb5..373f55f 100644 (file)
@@ -84,6 +84,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <time.h>
 
 #include <usb.h>
 
@@ -244,16 +245,18 @@ 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 ifsc;
-  int powered_off;
-  int has_pinpad;
-  int apdu_level;     /* Reader supports short APDU level exchange.
-                         With a value of 2 short and extended level is
-                         supported.*/
+  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_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
@@ -1423,7 +1426,7 @@ 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, 3 additionally enables debuggng for GetSlotStatus, other
+   protocol, 3 additionally enables debugging for GetSlotStatus, other
    values are not yet defined.
 
    Note that libusb may provide its own debugging feature which is
@@ -1763,6 +1766,11 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
 {
   int rc;
 
+  /* No need to continue and clutter the log withy USB error if we
+     ever got an ENODEV.  */
+  if (handle->enodev_seen)
+    return CCID_DRIVER_ERR_NO_READER;
+
   if (debug_level && (!no_debug || debug_level >= 3))
     {
       switch (msglen? msg[0]:0)
@@ -1823,8 +1831,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);
     }
index ae1f498..1fdcf7d 100644 (file)
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
 
-/* Macro to flag a removed card.  */
+/* Macro to flag a removed card.  ENODEV is also tested to catch teh
+   case of a removed reader.  */
 #define TEST_CARD_REMOVAL(c,r)                              \
        do {                                                 \
           int _r = (r);                                     \
           if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
-              || gpg_err_code (_r) == GPG_ERR_CARD_REMOVED) \
+              || gpg_err_code (_r) == GPG_ERR_CARD_REMOVED  \
+              || gpg_err_code (_r) == GPG_ERR_ENODEV )      \
             update_card_removed ((c)->reader_slot, 1);      \
        } while (0)
 
@@ -2159,7 +2161,13 @@ update_reader_status_file (int set_card_removed_flag)
         continue; /* Not valid or reader not yet open. */
       
       sw_apdu = apdu_get_status (ss->slot, 0, &status, &changed);
-      if (sw_apdu)
+      if (sw_apdu == SW_HOST_NO_READER)
+        {
+          /* Most likely the _reader_ has been unplugged.  */
+          status = 0;
+          changed = ss->changed;
+        }
+      else if (sw_apdu)
         {
           /* Get status failed.  Ignore that.  */
           continue; 
index 49f09cf..24cb346 100644 (file)
@@ -48,6 +48,7 @@
 #include "i18n.h"
 #include "sysutils.h"
 #include "app-common.h"
+#include "apdu.h"
 #include "ccid-driver.h"
 #include "mkdtemp.h"
 #include "gc-opt-flags.h"
@@ -858,6 +859,7 @@ main (int argc, char **argv )
 void
 scd_exit (int rc)
 {
+  apdu_prepare_exit ();
 #if 0
 #warning no update_random_seed_file
   update_random_seed_file();