summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Batard <pbatard@gmail.com>2011-01-30 01:24:59 +0000
committerPete Batard <pbatard@gmail.com>2011-01-30 01:24:59 +0000
commitbeeb853a0db49c30a7cb6e85d4af1305922945db (patch)
treea4a4cb5eeefc8c1ac9d9f7338028c5de877a4d74
parent43aecbb8f05995517479fa91db5d1db25be50d03 (diff)
downloadlibusb-beeb853a0db49c30a7cb6e85d4af1305922945db.tar.gz
merged latest official changes
* up to 295c9d12e25bc2dbdd8b42bd67a1f7120f0631a1
-rw-r--r--README6
-rw-r--r--configure.ac1
-rw-r--r--libusb-1.0.pc.in2
-rw-r--r--libusb/descriptor.c21
-rw-r--r--libusb/io.c30
-rw-r--r--libusb/libusbi.h18
-rw-r--r--libusb/os/darwin_usb.c8
-rw-r--r--libusb/os/linux_usbfs.c116
8 files changed, 118 insertions, 84 deletions
diff --git a/README b/README
index eefe5ee..c5dabd7 100644
--- a/README
+++ b/README
@@ -1,7 +1,8 @@
libusb
======
-libusb is a library for USB device access from Linux userspace.
+libusb is a library for USB device access from Linux, Mac OS X and
+Windows userspace.
It is written in C and licensed under the LGPL-2.1 (see COPYING).
libusb is abstracted internally in such a way that it can hopefully
@@ -9,7 +10,7 @@ be ported to other operating systems. See the PORTING file for some
information, if you fancy a challenge :)
libusb homepage:
-http://libusb.sourceforge.net
+http://libusb.org/
Developers will wish to consult the API documentation:
http://libusb.sourceforge.net/api-1.0/
@@ -18,4 +19,5 @@ Use the mailing list for questions, comments, etc:
https://sourceforge.net/mailarchive/forum.php?forum_name=libusb-devel
- Daniel Drake <dsd@gentoo.org>
+- Peter Stuge <peter@stuge.se>
(use the mailing list rather than mailing developers directly)
diff --git a/configure.ac b/configure.ac
index 0083d1b..3afc7d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,7 +57,6 @@ case $host in
AC_DEFINE(OS_DARWIN, [], [Darwin backend])
AC_SUBST(OS_DARWIN)
AC_DEFINE([THREADS_POSIX], [], [Use Posix Threads])
- AC_DEFINE(USBI_OS_HANDLES_TIMEOUT, [], [Backend handles timeout])
AC_MSG_RESULT([Darwin/MacOS X])
backend="darwin"
threads="posix"
diff --git a/libusb-1.0.pc.in b/libusb-1.0.pc.in
index 0abb16b..81b56ec 100644
--- a/libusb-1.0.pc.in
+++ b/libusb-1.0.pc.in
@@ -4,7 +4,7 @@ libdir=@libdir@
includedir=@includedir@
Name: libusb-1.0
-Description: C API for USB device access from Linux userspace
+Description: C API for USB device access from Linux, Mac OS X and Windows userspace
Version: @VERSION@
Libs: -L${libdir} -lusb-1.0
Libs.private: @PC_LIBS_PRIVATE@
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 54a47b4..11480e8 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -39,12 +39,12 @@
/* set host_endian if the w values are already in host endian format,
* as opposed to bus endian. */
-int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest,
- int host_endian)
+int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+ void *dest, int host_endian)
{
unsigned char *sp = source, *dp = dest;
uint16_t w;
- char *cp;
+ const char *cp;
for (cp = descriptor; *cp; cp++) {
switch (*cp) {
@@ -681,10 +681,17 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev,
uint16_t langid;
/* Asking for the zero'th index is special - it returns a string
- * descriptor that contains all the language IDs supported by the device.
- * Typically there aren't many - often only one. The language IDs are 16
- * bit numbers, and they start at the third byte in the descriptor. See
- * USB 2.0 specification section 9.6.7 for more information. */
+ * descriptor that contains all the language IDs supported by the
+ * device. Typically there aren't many - often only one. Language
+ * IDs are 16 bit numbers, and they start at the third byte in the
+ * descriptor. There's also no point in trying to read descriptor 0
+ * with this function. See USB 2.0 specification section 9.6.7 for
+ * more information.
+ */
+
+ if (desc_index == 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
r = libusb_get_string_descriptor(dev, 0, 0, tbuf, sizeof(tbuf));
if (r < 0)
return r;
diff --git a/libusb/io.c b/libusb/io.c
index d4f6f3d..b23c76b 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -1710,16 +1710,6 @@ static void handle_timeout(struct usbi_transfer *itransfer)
"async cancel failed %d errno=%d", r, errno);
}
-#ifdef USBI_OS_HANDLES_TIMEOUT
-static int handle_timeouts_locked(struct libusb_context *ctx)
-{
- return 0;
-}
-static int handle_timeouts(struct libusb_context *ctx)
-{
- return 0;
-}
-#else
static int handle_timeouts_locked(struct libusb_context *ctx)
{
int r;
@@ -1747,7 +1737,7 @@ static int handle_timeouts_locked(struct libusb_context *ctx)
return 0;
/* ignore timeouts we've already handled */
- if (transfer->flags & USBI_TRANSFER_TIMED_OUT)
+ if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
continue;
/* if transfer has non-expired timeout, nothing more to do */
@@ -1771,7 +1761,6 @@ static int handle_timeouts(struct libusb_context *ctx)
usbi_mutex_unlock(&ctx->flying_transfers_lock);
return r;
}
-#endif
#ifdef USBI_TIMERFD_AVAILABLE
static int handle_timerfd_trigger(struct libusb_context *ctx)
@@ -2094,9 +2083,7 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
*/
int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
{
-#if defined(USBI_OS_HANDLES_TIMEOUT)
- return 1;
-#elif defined(USBI_TIMERFD_AVAILABLE)
+#if defined(USBI_TIMERFD_AVAILABLE)
USBI_GET_CONTEXT(ctx);
return usbi_using_timerfd(ctx);
#else
@@ -2135,7 +2122,6 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
struct timeval *tv)
{
-#ifndef USBI_OS_HANDLES_TIMEOUT
struct usbi_transfer *transfer;
struct timespec cur_ts;
struct timeval cur_tv;
@@ -2156,10 +2142,11 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
/* find next transfer which hasn't already been processed as timed out */
list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) {
- found = 1;
- break;
- }
+ if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ continue;
+
+ found = 1;
+ break;
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
@@ -2192,9 +2179,6 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
}
return 1;
-#else
- return 0;
-#endif
}
/** \ingroup poll
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 28c5b0e..caceb65 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -130,13 +130,13 @@ void usbi_log(struct libusb_context *ctx, enum usbi_log_level level,
#ifdef ENABLE_LOGGING
#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__)
#else
-#define _usbi_log(ctx, level, ...)
+#define _usbi_log(ctx, level, ...) do {} while(0)
#endif
#if defined(ENABLE_DEBUG_LOGGING) || defined(INCLUDE_DEBUG_LOGGING)
#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__)
#else
-#define usbi_dbg(...)
+#define usbi_dbg(...) do {} while(0)
#endif
#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__)
@@ -289,8 +289,6 @@ struct libusb_device_handle {
unsigned char os_priv[0];
};
-#define USBI_TRANSFER_TIMED_OUT (1<<0)
-
enum {
USBI_CLOCK_MONOTONIC,
USBI_CLOCK_REALTIME
@@ -326,6 +324,14 @@ struct usbi_transfer {
usbi_mutex_t lock;
};
+enum usbi_transfer_flags {
+ /* The transfer has timed out */
+ USBI_TRANSFER_TIMED_OUT = 1 << 0,
+
+ /* Set by backend submit_transfer() if the OS handles timeout */
+ USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1
+};
+
#define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \
((struct libusb_transfer *)(((unsigned char *)(transfer)) \
+ sizeof(struct usbi_transfer)))
@@ -365,8 +371,8 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status);
int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
-int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest,
- int host_endian);
+int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+ void *dest, int host_endian);
int usbi_get_config_index_by_value(struct libusb_device *dev,
uint8_t bConfigurationValue, int *idx);
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c
index 4c19592..163439d 100644
--- a/libusb/os/darwin_usb.c
+++ b/libusb/os/darwin_usb.c
@@ -1141,6 +1141,8 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
transfer->length, darwin_async_io_callback, itransfer);
} else {
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
if (is_read)
ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
transfer->length, transfer->timeout, transfer->timeout,
@@ -1248,6 +1250,8 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) {
tpriv->req.completionTimeout = transfer->timeout;
tpriv->req.noDataTimeout = transfer->timeout;
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
/* all transfers in libusb-1.0 are async */
kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
@@ -1362,6 +1366,9 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0)
}
static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+ if (itransfer->flags & USBI_TRANSFER_TIMED_OUT)
+ result = kIOUSBTransactionTimeout;
+
switch (result) {
case kIOReturnUnderrun:
case kIOReturnSuccess:
@@ -1376,6 +1383,7 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_
return LIBUSB_TRANSFER_OVERFLOW;
case kIOUSBTransactionTimeout:
usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
return LIBUSB_TRANSFER_TIMED_OUT;
default:
usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index b7d9066..9f9b140 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -74,18 +74,18 @@ static const char *usbfs_path = NULL;
/* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically
* allows us to mark URBs as being part of a specific logical transfer when
- * we submit them to the kernel. then, on any error error except a
- * cancellation, all URBs within that transfer will be cancelled with the
- * endpoint is disabled, meaning that no more data can creep in during the
- * time it takes to cancel the remaining URBs.
+ * we submit them to the kernel. then, on any error except a cancellation, all
+ * URBs within that transfer will be cancelled and no more URBs will be
+ * accepted for the transfer, meaning that no more data can creep in.
*
* The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer
* (in either direction) except the first.
- * For IN transfers, we must also set SHORT_NOT_OK on all the URBs.
- * For OUT transfers, SHORT_NOT_OK must not be set. The effective behaviour
- * (where an OUT transfer does not complete, the rest of the URBs in the
- * transfer get cancelled) is already in effect, and setting this flag is
- * disallowed (a kernel with USB debugging enabled will reject such URBs).
+ * For IN transfers, we must also set SHORT_NOT_OK on all URBs except the
+ * last; it means that the kernel should treat a short reply as an error.
+ * For OUT transfers, SHORT_NOT_OK must not be set. it isn't needed (OUT
+ * transfers can't be short unless there's already some sort of error), and
+ * setting this flag is disallowed (a kernel with USB debugging enabled will
+ * reject such URBs).
*/
static int supports_flag_bulk_continuation = -1;
@@ -221,17 +221,25 @@ static clockid_t find_monotonic_clock(void)
static int check_flag_bulk_continuation(void)
{
struct utsname uts;
- int sublevel;
+ int major, minor, sublevel;
if (uname(&uts) < 0)
return -1;
if (strlen(uts.release) < 4)
return 0;
- if (strncmp(uts.release, "2.6.", 4) != 0)
+ if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &sublevel) != 3)
return 0;
-
- sublevel = atoi(uts.release + 4);
- return sublevel >= 32;
+ if (major < 2)
+ return 0;
+ if (major == 2) {
+ if (minor < 6)
+ return 0;
+ if (minor == 6) {
+ if (sublevel < 32)
+ return 0;
+ }
+ }
+ return 1;
}
static int op_init(struct libusb_context *ctx)
@@ -1452,7 +1460,9 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer,
if (COMPLETED_EARLY == tpriv->reap_action)
return 0;
- for (j = 0; j < i; j++) {
+ /* The URBs are discarded in reverse order of
+ * submission, to avoid races. */
+ for (j = i - 1; j >= 0; j--) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &urbs[j]);
if (tmp && errno != EINVAL)
usbi_warn(TRANSFER_CTX(transfer),
@@ -1609,7 +1619,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
/* The URBs we haven't submitted yet we count as already
* retired. */
tpriv->num_retired = num_urbs - i;
- for (j = 0; j < i; j++) {
+ for (j = i - 1; j >= 0; j--) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urbs[j]);
if (tmp && errno != EINVAL)
usbi_warn(TRANSFER_CTX(transfer),
@@ -1732,7 +1742,7 @@ static int cancel_bulk_transfer(struct usbi_transfer *itransfer)
if (tpriv->reap_action != ERROR)
tpriv->reap_action = CANCELLED;
- for (i = 0; i < tpriv->num_urbs; i++) {
+ for (i = tpriv->num_urbs - 1; i >= 0; i--) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]);
if (tmp && errno != EINVAL)
usbi_warn(TRANSFER_CTX(transfer),
@@ -1754,7 +1764,7 @@ static int cancel_iso_transfer(struct usbi_transfer *itransfer)
return LIBUSB_ERROR_NOT_FOUND;
tpriv->reap_action = CANCELLED;
- for (i = 0; i < tpriv->num_urbs; i++) {
+ for (i = tpriv->num_urbs - 1; i >= 0; i--) {
int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->iso_urbs[i]);
if (tmp && errno != EINVAL)
usbi_warn(TRANSFER_CTX(transfer),
@@ -1832,6 +1842,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
* 2. we receive a short URB which marks the early completion condition,
* so we start cancelling the remaining URBs. however, we're too
* slow and another URB completes (or at least completes partially).
+ * (this can't happen since we always use BULK_CONTINUATION.)
*
* When this happens, our objectives are not to lose any "surplus" data,
* and also to stick it at the end of the previously-received data
@@ -1860,16 +1871,23 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
goto out_unlock;
}
- if (urb->status == 0 || urb->status == -EREMOTEIO ||
- (urb->status == -EOVERFLOW && urb->actual_length > 0))
- itransfer->transferred += urb->actual_length;
-
+ itransfer->transferred += urb->actual_length;
+ /* Many of these errors can occur on *any* urb of a multi-urb
+ * transfer. When they do, we tear down the rest of the transfer.
+ */
switch (urb->status) {
case 0:
break;
case -EREMOTEIO: /* short transfer */
break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE;
+ goto cancel_remaining;
case -EPIPE:
usbi_dbg("detected endpoint stall");
if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
@@ -1884,18 +1902,13 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
case -ETIME:
case -EPROTO:
case -EILSEQ:
- /* These can happen on *any* urb of a multi-urb transfer, so
- * save a status and tear down rest of the transfer */
usbi_dbg("low level error %d", urb->status);
tpriv->reap_action = ERROR;
- if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
- tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
goto cancel_remaining;
default:
usbi_warn(ITRANSFER_CTX(itransfer),
"unrecognised urb status %d", urb->status);
- if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
- tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+ tpriv->reap_action = ERROR;
goto cancel_remaining;
}
@@ -1913,17 +1926,20 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
goto out_unlock;
cancel_remaining:
+ if (ERROR == tpriv->reap_action && LIBUSB_TRANSFER_COMPLETED == tpriv->reap_status)
+ tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+
if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */
goto completed;
/* cancel remaining urbs and wait for their completion before
* reporting results */
- while (++urb_idx < tpriv->num_urbs) {
+ for (int i = tpriv->num_urbs - 1; i > urb_idx; i--) {
/* remaining URBs with continuation flag are
* automatically cancelled by the kernel */
- if (tpriv->urbs[urb_idx].flags & USBFS_URB_BULK_CONTINUATION)
+ if (tpriv->urbs[i].flags & USBFS_URB_BULK_CONTINUATION)
continue;
- int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[urb_idx]);
+ int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]);
if (tmp && errno != EINVAL)
usbi_warn(TRANSFER_CTX(transfer),
"unrecognised discard errno %d", errno);
@@ -1951,6 +1967,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
int num_urbs = tpriv->num_urbs;
int urb_idx = 0;
int i;
+ enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED;
usbi_mutex_lock(&itransfer->lock);
for (i = 0; i < num_urbs; i++) {
@@ -1968,16 +1985,14 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status,
urb_idx, num_urbs);
- if (urb->status == 0) {
- /* copy isochronous results back in */
+ /* copy isochronous results back in */
- for (i = 0; i < urb->number_of_packets; i++) {
- struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i];
- struct libusb_iso_packet_descriptor *lib_desc =
- &transfer->iso_packet_desc[tpriv->iso_packet_offset++];
- lib_desc->status = urb_desc->status;
- lib_desc->actual_length = urb_desc->actual_length;
- }
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i];
+ struct libusb_iso_packet_descriptor *lib_desc =
+ &transfer->iso_packet_desc[tpriv->iso_packet_offset++];
+ lib_desc->status = urb_desc->status;
+ lib_desc->actual_length = urb_desc->actual_length;
}
tpriv->num_retired++;
@@ -1985,6 +2000,9 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */
usbi_dbg("CANCEL: urb status %d", urb->status);
+ if (status == LIBUSB_TRANSFER_COMPLETED)
+ status = LIBUSB_TRANSFER_ERROR;
+
if (tpriv->num_retired == num_urbs) {
usbi_dbg("CANCEL: last URB handled, reporting");
free_iso_urbs(tpriv);
@@ -2003,6 +2021,12 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
switch (urb->status) {
case 0:
break;
+ case -ENOENT: /* cancelled */
+ break;
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
case -ETIME:
case -EPROTO:
case -EILSEQ:
@@ -2020,7 +2044,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
usbi_dbg("last URB in transfer --> complete!");
free_iso_urbs(tpriv);
usbi_mutex_unlock(&itransfer->lock);
- return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
+ return usbi_handle_transfer_completion(itransfer, status);
}
out:
@@ -2037,8 +2061,7 @@ static int handle_control_completion(struct usbi_transfer *itransfer,
usbi_mutex_lock(&itransfer->lock);
usbi_dbg("handling completion status %d", urb->status);
- if (urb->status == 0)
- itransfer->transferred += urb->actual_length;
+ itransfer->transferred += urb->actual_length;
if (tpriv->reap_action == CANCELLED) {
if (urb->status != 0 && urb->status != -ENOENT)
@@ -2052,9 +2075,14 @@ static int handle_control_completion(struct usbi_transfer *itransfer,
switch (urb->status) {
case 0:
- itransfer->transferred = urb->actual_length;
status = LIBUSB_TRANSFER_COMPLETED;
break;
+ case -ENOENT: /* cancelled */
+ break;
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
case -EPIPE:
usbi_dbg("unsupported control request");
status = LIBUSB_TRANSFER_STALL;