Treat any unexpected timeouts waiting for async transfers as though there may be a usb halt condition and attempt to clear the halt before cancelling the tranfer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
diff --git a/usbutils.c b/usbutils.c
index 14a2bf4..dc983b0 100644
--- a/usbutils.c
+++ b/usbutils.c
@@ -2228,6 +2228,7 @@ static void LIBUSB_CALL bulk_callback(struct libusb_transfer *transfer)
* use our own timer to cancel the request if we go beyond the timeout. */
static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int timeout)
{
+ struct libusb_transfer *transfer= ut->transfer;
struct timespec ts_now, ts_end;
struct timeval tv_now;
int ret;
@@ -2239,15 +2240,22 @@ static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int
timeraddspec(&ts_end, &ts_now);
ret = pthread_cond_timedwait(&ut->cond, &ut->mutex, &ts_end);
if (ret) {
- libusb_cancel_transfer(ut->transfer);
+ /* Assume that if we timed out on the conditional then the
+ * transfer has stalled for some reason and attempt to clear
+ * a halt as a solution. Then cancel the transaction, treating
+ * it the same as a timeout. */
+ libusb_clear_halt(transfer->dev_handle, transfer->endpoint);
+ libusb_cancel_transfer(transfer);
+
+ /* Now wait for the callback function to be invoked. */
pthread_cond_wait(&ut->cond, &ut->mutex);
/* Fake the timed out message since it's effectively that */
ret = LIBUSB_TRANSFER_TIMED_OUT;
} else
- ret = ut->transfer->status;
+ ret = transfer->status;
/* No need to sort out mutexes here since they won't be reused */
- *transferred = ut->transfer->actual_length;
- libusb_free_transfer(ut->transfer);
+ *transferred = transfer->actual_length;
+ libusb_free_transfer(transfer);
return ret;
}