Add code to better handle unplugging of a reader.

This commit is contained in:
Werner Koch 2009-07-16 15:54:59 +00:00
parent e535e3fccc
commit e49d808e1f
7 changed files with 94 additions and 15 deletions

View 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,

View 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. */

View 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);

View File

@ -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);

View 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);
}

View File

@ -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;

View 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();