Skip to content

Commit

Permalink
Fix polling transfer restart after InterruptStop
Browse files Browse the repository at this point in the history
Fix the regression introduced in the "Fix InterruptStop if called
before InterruptRead" commit: it should be possible to start a new
polling transfer after the previous one has been stopped.

This problem prevented the polling transfer from working after an
SCardDisconnect call, and led to using the sleep-based polling every
400 ms.

Implementation-wise, the fix is to change the "terminate" flag to be
non-sticky, i.e., unset it after reading from it.
  • Loading branch information
emaxx-google authored and LudovicRousseau committed Oct 29, 2023
1 parent 440ab83 commit f5a731e
Showing 1 changed file with 10 additions and 11 deletions.
21 changes: 10 additions & 11 deletions src/ccid_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,11 @@ typedef struct
*/
_ccid_descriptor ccid;

/* whether the polling should be terminated */
_Atomic bool terminated;

/* libusb transfer for the polling (or NULL) */
pthread_mutex_t polling_transfer_mutex;
struct libusb_transfer *polling_transfer;
/* whether the polling should be terminated */
bool terminate_requested;

/* pointer to the multislot extension (if any) */
struct usbDevice_MultiSlot_Extension *multislot_extension;
Expand Down Expand Up @@ -745,9 +744,9 @@ status_t OpenUSBByName(unsigned int reader_index, /*@null@*/ char *device)
usbDevice[reader_index].interface = interface;
usbDevice[reader_index].real_nb_opened_slots = 1;
usbDevice[reader_index].nb_opened_slots = &usbDevice[reader_index].real_nb_opened_slots;
atomic_init(&usbDevice[reader_index].terminated, false);
pthread_mutex_init(&usbDevice[reader_index].polling_transfer_mutex, NULL);
usbDevice[reader_index].polling_transfer = NULL;
usbDevice[reader_index].terminate_requested = false;
usbDevice[reader_index].disconnected = false;

/* CCID common information */
Expand Down Expand Up @@ -1534,13 +1533,14 @@ int InterruptRead(int reader_index, int timeout /* in ms */)

pthread_mutex_lock(&usbDevice[reader_index].polling_transfer_mutex);
usbDevice[reader_index].polling_transfer = transfer;
bool terminate_requested = usbDevice[reader_index].terminate_requested;
usbDevice[reader_index].terminate_requested = false;
pthread_mutex_unlock(&usbDevice[reader_index].polling_transfer_mutex);

// The termination might've been requested by the other thread before the
// polling_transfer field was written. In that case, we have to cancel the
// transfer here as opposed to InterruptStop().
bool terminated = atomic_load(&usbDevice[reader_index].terminated);
if (terminated) {
if (terminate_requested) {
libusb_cancel_transfer(transfer);
}

Expand Down Expand Up @@ -1608,11 +1608,6 @@ void InterruptStop(int reader_index)
return;
}

// Set the termination flag to handle the case in which the polling_transfer
// value read below is null. The order of operations is important, and it has
// to be opposite to the one in InterruptRead() to avoid race conditions.
atomic_store(&usbDevice[reader_index].terminated, true);

pthread_mutex_lock(&usbDevice[reader_index].polling_transfer_mutex);
if (usbDevice[reader_index].polling_transfer)
{
Expand All @@ -1622,6 +1617,10 @@ void InterruptStop(int reader_index)
if (ret < 0)
DEBUG_CRITICAL2("libusb_cancel_transfer failed: %s",
libusb_error_name(ret));
} else {
// Indicate that the next attempt to start an interrupt transfer shouldn't
// be proceeded.
usbDevice[reader_index].terminate_requested = true;
}
pthread_mutex_unlock(&usbDevice[reader_index].polling_transfer_mutex);
} /* InterruptStop */
Expand Down

0 comments on commit f5a731e

Please sign in to comment.