scd: Improve watching USB device removal.

* scd/apdu.c(struct reader_table_s): Add require_get_status.
(apdu_connect): Change return value meaning.  Call apdu_reset here.
* scd/app.c (app_new_register): Add require_get_status.
(select_application): Use the return value of apdu_connect.
(scd_update_reader_status_file): Call update_fdset_for_usb with
checking all_have_intr_endp.
(app_list_start, app_list_finish): Remove.
* scd/ccid-driver.c (struct ccid_driver_s): Add transfer.
(intr_cb): Don't call libusb_transfer in this callback.
(ccid_require_get_status): New.
(do_close_reader): Call libusb_transfer here.
* scd/scdaemon.c (update_fdset_for_usb): Remove the first argument.

--

With Gnuk Token, it works fine as expected.  With Gemalto reader,
intr_cb is not called when card is removed.  So, the macro
LIBUSB_WORKS_EXPECTED_FOR_INTERRUPT_ENDP is not defined yet.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2017-01-27 18:01:52 +09:00
parent 031e3fa7b9
commit 25cc8575da
8 changed files with 130 additions and 109 deletions

View File

@ -141,11 +141,12 @@ struct reader_table_s {
} rapdu;
#endif /*USE_G10CODE_RAPDU*/
char *rdrname; /* Name of the connected reader or NULL if unknown. */
int is_t0; /* True if we know that we are running T=0. */
int is_spr532; /* True if we know that the reader is a SPR532. */
int pinpad_varlen_supported; /* True if we know that the reader
supports variable length pinpad
input. */
unsigned int is_t0:1; /* True if we know that we are running T=0. */
unsigned int is_spr532:1; /* True if we know that the reader is a SPR532. */
unsigned int pinpad_varlen_supported:1; /* True if we know that the reader
supports variable length pinpad
input. */
unsigned int require_get_status:1;
unsigned char atr[33];
size_t atrlen; /* A zero length indicates that the ATR has
not yet been read; i.e. the card is not
@ -470,6 +471,7 @@ new_reader_slot (void)
reader_table[reader].is_t0 = 1;
reader_table[reader].is_spr532 = 0;
reader_table[reader].pinpad_varlen_supported = 0;
reader_table[reader].require_get_status = 1;
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
@ -2572,6 +2574,7 @@ open_ccid_reader (struct dev_list *dl)
{
int err;
int slot;
int require_get_status;
reader_table_t slotp;
slot = new_reader_slot ();
@ -2596,6 +2599,8 @@ open_ccid_reader (struct dev_list *dl)
err = 0;
}
require_get_status = ccid_require_get_status (slotp->ccid.handle);
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;
@ -2608,6 +2613,7 @@ open_ccid_reader (struct dev_list *dl)
/* Our CCID reader code does not support T=0 at all, thus reset the
flag. */
reader_table[slot].is_t0 = 0;
reader_table[slot].require_get_status = require_get_status;
dump_reader_status (slot);
unlock_slot (slot);
@ -2970,22 +2976,15 @@ apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
return 0;
}
int
void
apdu_dev_list_finish (struct dev_list *dl)
{
int all_have_intr_endp;
#ifdef HAVE_LIBUSB
if (dl->ccid_table)
all_have_intr_endp = ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
else
all_have_intr_endp = 0;
#else
all_have_intr_endp = 0;
ccid_dev_scan_finish (dl->ccid_table, dl->idx_max);
#endif
xfree (dl);
npth_mutex_unlock (&reader_table_lock);
return all_have_intr_endp;
}
@ -3347,8 +3346,11 @@ apdu_enum_reader (int slot, int *used)
/* Connect a card. This is used to power up the card and make sure
that an ATR is available. Depending on the reader backend it may
return an error for an inactive card or if no card is
available. */
return an error for an inactive card or if no card is available.
Return -1 on error. Return 1 if reader requires get_status to
watch card removal. Return 0 if it's a token (always with a card),
or it supports INTERRUPT endpoint to watch card removal.
*/
int
apdu_connect (int slot)
{
@ -3362,7 +3364,7 @@ apdu_connect (int slot)
{
if (DBG_READER)
log_debug ("leave: apdu_connect => SW_HOST_NO_DRIVER\n");
return SW_HOST_NO_DRIVER;
return -1;
}
/* Only if the access method provides a connect function we use it.
@ -3393,10 +3395,19 @@ apdu_connect (int slot)
else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
sw = SW_HOST_CARD_INACTIVE;
if (sw == SW_HOST_CARD_INACTIVE)
{
/* Try power it up again. */
sw = apdu_reset (slot);
}
if (DBG_READER)
log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
return sw;
if (sw)
return -1;
return reader_table[slot].require_get_status;
}

View File

@ -88,7 +88,7 @@ struct dev_list;
gpg_error_t apdu_init (void);
gpg_error_t apdu_dev_list_start (const char *portstr, struct dev_list **l_p);
int apdu_dev_list_finish (struct dev_list *l);
void apdu_dev_list_finish (struct dev_list *l);
/* Note, that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (struct dev_list *l);

View File

@ -121,8 +121,6 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/
app_t app_list_start (void);
void app_list_finish (void);
void app_send_card_list (ctrl_t ctrl);
char *app_get_serialno (app_t app);

126
scd/app.c
View File

@ -174,7 +174,8 @@ app_reset (app_t app, ctrl_t ctrl, int send_reset)
}
static gpg_error_t
app_new_register (int slot, ctrl_t ctrl, const char *name)
app_new_register (int slot, ctrl_t ctrl, const char *name,
int require_get_status)
{
gpg_error_t err = 0;
app_t app = NULL;
@ -303,7 +304,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
return err;
}
app->require_get_status = 1; /* For token, this can be 0. */
app->require_get_status = require_get_status;
npth_mutex_lock (&app_list_lock);
app->next = app_top;
@ -330,7 +331,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
if (scan || !app_top)
{
struct dev_list *l;
int all_have_intr_endp;
int all_have_intr_endp = 1;
err = apdu_dev_list_start (opt.reader_port, &l);
if (err)
@ -339,39 +340,31 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
while (1)
{
int slot;
int sw;
int require_get_status;
slot = apdu_open_reader (l);
if (slot < 0)
break;
err = 0;
sw = apdu_connect (slot);
if (sw == SW_HOST_CARD_INACTIVE)
{
/* Try again. */
sw = apdu_reset (slot);
}
if (!sw || sw == SW_HOST_ALREADY_CONNECTED)
err = 0;
else if (sw == SW_HOST_NO_CARD)
err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
else
err = gpg_error (GPG_ERR_ENODEV);
if (!err)
err = app_new_register (slot, ctrl, name);
else
require_get_status = apdu_connect (slot);
if (require_get_status < 0)
{
/* We close a reader with no card. */
apdu_close_reader (slot);
err = gpg_error (GPG_ERR_ENODEV);
}
else
{
err = app_new_register (slot, ctrl, name, require_get_status);
if (require_get_status)
all_have_intr_endp = 0;
}
if (err)
apdu_close_reader (slot);
}
all_have_intr_endp = apdu_dev_list_finish (l);
update_fdset_for_usb (1, all_have_intr_endp);
apdu_dev_list_finish (l);
update_fdset_for_usb (all_have_intr_endp);
}
npth_mutex_lock (&app_list_lock);
@ -1021,46 +1014,60 @@ void
scd_update_reader_status_file (void)
{
app_t a, app_next;
int all_have_intr_endp = 1;
int removal_detected = 0;
npth_mutex_lock (&app_list_lock);
for (a = app_top; a; a = app_next)
{
int sw;
unsigned int status;
sw = apdu_get_status (a->slot, 0, &status);
app_next = a->next;
if (a->require_get_status)
if (sw == SW_HOST_NO_READER)
{
int sw;
unsigned int status;
sw = apdu_get_status (a->slot, 0, &status);
/* Most likely the _reader_ has been unplugged. */
status = 0;
}
else if (sw)
{
/* Get status failed. Ignore that. */
if (a->require_get_status)
all_have_intr_endp = 0;
continue;
}
if (sw == SW_HOST_NO_READER)
{
/* Most likely the _reader_ has been unplugged. */
status = 0;
}
else if (sw)
{
/* Get status failed. Ignore that. */
continue;
}
if (a->card_status != status)
{
report_change (a->slot, a->card_status, status);
send_client_notifications (a, status == 0);
if (a->card_status != status)
if (status == 0)
{
report_change (a->slot, a->card_status, status);
send_client_notifications (a, status == 0);
if (status == 0)
{
log_debug ("Removal of a card: %d\n", a->slot);
apdu_close_reader (a->slot);
deallocate_app (a);
update_fdset_for_usb (0, 0);
}
else
a->card_status = status;
log_debug ("Removal of a card: %d\n", a->slot);
apdu_close_reader (a->slot);
deallocate_app (a);
removal_detected = 1;
}
else
{
a->card_status = status;
if (a->require_get_status)
all_have_intr_endp = 0;
}
}
else
{
if (a->require_get_status)
all_have_intr_endp = 0;
}
}
npth_mutex_unlock (&app_list_lock);
if (removal_detected)
update_fdset_for_usb (all_have_intr_endp);
}
/* This function must be called once to initialize this module. This
@ -1082,19 +1089,6 @@ initialize_module_command (void)
return apdu_init ();
}
app_t
app_list_start (void)
{
npth_mutex_lock (&app_list_lock);
return app_top;
}
void
app_list_finish (void)
{
npth_mutex_unlock (&app_list_lock);
}
void
app_send_card_list (ctrl_t ctrl)
{

View File

@ -274,6 +274,7 @@ struct ccid_driver_s
void *progress_cb_arg;
unsigned char intr_buf[64];
struct libusb_transfer *transfer;
};
@ -1699,17 +1700,13 @@ ccid_dev_scan (int *idx_max_p, struct ccid_dev_table **t_p)
return err;
}
int
void
ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
{
int all_have_intr_endp = 1;
int i;
for (i = 0; i < max; i++)
{
if (tbl[i].ep_intr == -1)
all_have_intr_endp = 0;
free (tbl[i].ifcdesc_extra);
tbl[i].transport = 0;
tbl[i].n = 0;
@ -1723,8 +1720,6 @@ ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
}
libusb_free_device_list (ccid_usb_dev_list, 1);
ccid_usb_dev_list = NULL;
return all_have_intr_endp;
}
unsigned int
@ -1767,14 +1762,14 @@ intr_cb (struct libusb_transfer *transfer)
{
ccid_driver_t handle = transfer->user_data;
DEBUGOUT ("CCID: interrupt callback\n");
DEBUGOUT_1 ("CCID: interrupt callback %d\n", transfer->status);
if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE
|| transfer->status == LIBUSB_TRANSFER_ERROR)
{
device_removed:
DEBUGOUT ("CCID: device removed\n");
handle->powered_off = 1;
libusb_free_transfer (transfer);
}
else if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
@ -1784,7 +1779,6 @@ intr_cb (struct libusb_transfer *transfer)
{
DEBUGOUT ("CCID: card removed\n");
handle->powered_off = 1;
libusb_free_transfer (transfer);
}
else
{
@ -1795,12 +1789,12 @@ intr_cb (struct libusb_transfer *transfer)
if (err == LIBUSB_ERROR_NO_DEVICE)
goto device_removed;
DEBUGOUT_1 ("CCID submit transfer again %d", err);
DEBUGOUT_1 ("CCID submit transfer again %d\n", err);
}
}
else
{
;
DEBUGOUT_1 ("CCID intr_cb: %d\n", transfer->status);
}
}
@ -1811,6 +1805,7 @@ ccid_setup_intr (ccid_driver_t handle)
int err;
transfer = libusb_alloc_transfer (0);
handle->transfer = transfer;
libusb_fill_interrupt_transfer (transfer, handle->idev, handle->ep_intr,
handle->intr_buf, sizeof (handle->intr_buf),
intr_cb, handle, 0);
@ -1913,7 +1908,7 @@ ccid_open_usb_reader (const char *spec_reader_name,
}
}
if ((*handle)->ep_intr)
if ((*handle)->ep_intr >= 0)
ccid_setup_intr (*handle);
rc = ccid_vendor_specific_init (*handle);
@ -2010,6 +2005,26 @@ ccid_open_reader (const char *spec_reader_name, int idx,
}
int
ccid_require_get_status (ccid_driver_t handle)
{
#ifdef LIBUSB_WORKS_EXPECTED_FOR_INTERRUPT_ENDP
if (handle->ep_intr >= 0)
return 0;
#endif
/* Here comes products check for tokens which
always have card inserted. */
switch (handle->id_vendor)
{
case VENDOR_FSIJ:
return 0;
}
return 1;
}
static void
do_close_reader (ccid_driver_t handle)
{
@ -2037,6 +2052,7 @@ do_close_reader (ccid_driver_t handle)
}
if (handle->idev)
{
libusb_free_transfer (handle->transfer);
libusb_release_interface (handle->idev, handle->ifc_no);
libusb_close (handle->idev);
handle->idev = NULL;

View File

@ -115,7 +115,7 @@ int ccid_set_debug_level (int level);
char *ccid_get_reader_list (void);
gpg_error_t ccid_dev_scan (int *idx_max, struct ccid_dev_table **t_p);
int ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max);
void ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max);
unsigned int ccid_get_BAI (int, struct ccid_dev_table *tbl);
int ccid_compare_BAI (ccid_driver_t handle, unsigned int);
int ccid_open_reader (const char *spec_reader_name,
@ -140,7 +140,7 @@ int ccid_transceive_escape (ccid_driver_t handle,
const unsigned char *data, size_t datalen,
unsigned char *resp, size_t maxresplen,
size_t *nresp);
int ccid_require_get_status (ccid_driver_t handle);
#endif /*CCID_DRIVER_H*/

View File

@ -1202,15 +1202,14 @@ start_connection_thread (void *arg)
void
update_fdset_for_usb (int scanned, int all_have_intr_endp)
update_fdset_for_usb (int all_have_intr_endp)
{
#ifdef HAVE_LIBUSB
const struct libusb_pollfd **pfd_array = libusb_get_pollfds (NULL);
const struct libusb_pollfd **p;
#endif
if (scanned)
usb_all_have_intr_endp = all_have_intr_endp;
usb_all_have_intr_endp = all_have_intr_endp;
FD_ZERO (&fdset);
nfd = 0;
@ -1230,6 +1229,7 @@ update_fdset_for_usb (int scanned, int all_have_intr_endp)
if (nfd < fd)
nfd = fd;
p++;
log_debug ("USB: add %d to fdset\n", fd);
}
libusb_free_pollfds (pfd_array);
@ -1238,8 +1238,7 @@ update_fdset_for_usb (int scanned, int all_have_intr_endp)
/* Kick the select loop. */
write (notify_fd, "", 1);
log_debug ("update_fdset_for_usb (%d, %d): %d %lx\n",
scanned, all_have_intr_endp, nfd, fdset.fds_bits[0]);
log_debug ("update_fdset_for_usb (%d): %d\n", all_have_intr_endp, nfd);
}
static int
@ -1395,6 +1394,7 @@ handle_connections (void)
char buf[256];
read (pipe_fd[0], buf, sizeof buf);
ret--;
}
if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
@ -1439,6 +1439,8 @@ handle_connections (void)
if (ret)
{
struct timeval tv = {0, 0};
log_debug ("scd main: USB handle events\n");
libusb_handle_events_timeout_completed (NULL, &tv, NULL);
}
#endif

View File

@ -125,7 +125,7 @@ void send_status_info (ctrl_t ctrl, const char *keyword, ...)
void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
void scd_update_reader_status_file (void);
void send_client_notifications (app_t app, int removal);
void update_fdset_for_usb (int scanned, int all_have_intr_endp);
void update_fdset_for_usb (int all_have_intr_endp);
#endif /*SCDAEMON_H*/