summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2009-07-16 17:54:59 +0200
committerWerner Koch <wk@gnupg.org>2009-07-16 17:54:59 +0200
commite49d808e1f02d8f01c60753022bc20664b215062 (patch)
treedd65f9e2f0eb20d949e56852c22ee3cb0aba31a9 /scd
parent[g10] (diff)
downloadgnupg2-e49d808e1f02d8f01c60753022bc20664b215062.tar.xz
gnupg2-e49d808e1f02d8f01c60753022bc20664b215062.zip
Add code to better handle unplugging of a reader.
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog14
-rw-r--r--scd/apdu.c29
-rw-r--r--scd/apdu.h1
-rw-r--r--scd/app.c4
-rw-r--r--scd/ccid-driver.c45
-rw-r--r--scd/command.c14
-rw-r--r--scd/scdaemon.c2
7 files changed, 94 insertions, 15 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index fcb13e386..46b15f0c7 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -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,
diff --git a/scd/apdu.c b/scd/apdu.c
index 57e82c49c..36bae89c0 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -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. */
diff --git a/scd/apdu.h b/scd/apdu.h
index 03c774cc2..c47dea882 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -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);
diff --git a/scd/app.c b/scd/app.c
index efaf9ec0b..a23c4a546 100644
--- 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);
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index f9b0bb599..373f55f8a 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -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);
}
diff --git a/scd/command.c b/scd/command.c
index ae1f4981b..1fdcf7d51 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -54,12 +54,14 @@
#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;
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 49f09cf62..24cb346e6 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -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();