summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--examples/dpfp.c22
-rw-r--r--libusb/core.c24
-rw-r--r--libusb/io.c112
-rw-r--r--libusb/libusb.h23
-rw-r--r--libusb/libusbi.h6
-rw-r--r--libusb/os/linux_usbfs.c127
-rw-r--r--libusb/sync.c9
8 files changed, 236 insertions, 89 deletions
diff --git a/TODO b/TODO
index 887976f..ba66de7 100644
--- a/TODO
+++ b/TODO
@@ -3,7 +3,6 @@ for 1.0
review functionality missing over 0.1
serialization of handle_events
internal docs for OS porters
-hot-unplugging
1.0 API style/naming points to reconsider
=========================================
@@ -13,6 +12,5 @@ for 1.1 or future
==================
optional timerfd support (runtime detection)
notifications of hotplugged/unplugged devices
-use poll() rather than select()?
offer API to create/destroy handle_events thread
isochronous sync I/O?
diff --git a/examples/dpfp.c b/examples/dpfp.c
index a64abdc..7808184 100644
--- a/examples/dpfp.c
+++ b/examples/dpfp.c
@@ -283,6 +283,7 @@ static void cb_irq(struct libusb_transfer *transfer)
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fprintf(stderr, "irq transfer status %d?\n", transfer->status);
do_exit = 2;
+ libusb_free_transfer(transfer);
irq_transfer = NULL;
return;
}
@@ -319,6 +320,7 @@ static void cb_img(struct libusb_transfer *transfer)
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fprintf(stderr, "img transfer status %d?\n", transfer->status);
do_exit = 2;
+ libusb_free_transfer(transfer);
img_transfer = NULL;
return;
}
@@ -431,7 +433,7 @@ int main(void)
r = libusb_claim_interface(devh, 0);
if (r < 0) {
- fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r));
+ fprintf(stderr, "usb_claim_interface error %d\n", r);
goto out;
}
printf("claimed interface\n");
@@ -468,14 +470,18 @@ int main(void)
}
printf("shutting down...\n");
+
+ if (irq_transfer) {
+ r = libusb_cancel_transfer(irq_transfer);
+ if (r < 0)
+ goto out_deinit;
+ }
- r = libusb_cancel_transfer(irq_transfer);
- if (r < 0)
- goto out_deinit;
-
- r = libusb_cancel_transfer(img_transfer);
- if (r < 0)
- goto out_deinit;
+ if (img_transfer) {
+ r = libusb_cancel_transfer(img_transfer);
+ if (r < 0)
+ goto out_deinit;
+ }
while (irq_transfer || img_transfer)
if (libusb_handle_events() < 0)
diff --git a/libusb/core.c b/libusb/core.c
index e36989f..83f0cdb 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -140,6 +140,13 @@ pthread_mutex_t usbi_open_devs_lock = PTHREAD_MUTEX_INITIALIZER;
* libusb-1.0 lacks functionality for providing notifications of when devices
* are added or removed. This functionality is planned to be implemented
* for libusb-1.1.
+ *
+ * That said, there is basic disconnection handling for open device handles:
+ * - If there are ongoing transfers, libusb's handle_events loop will detect
+ * disconnections and complete ongoing transfers with the
+ * LIBUSB_TRANSFER_NO_DEVICE status code.
+ * - Many functions such as libusb_set_configuration() return the special
+ * LIBUSB_ERROR_NO_DEVICE error code when the device has been disconnected.
*/
/**
@@ -734,6 +741,7 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
* \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_set_configuration(libusb_device_handle *dev,
@@ -764,6 +772,7 @@ API_EXPORTED int libusb_set_configuration(libusb_device_handle *dev,
* \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
* \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the
* interface
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns a LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_claim_interface(libusb_device_handle *dev,
@@ -798,8 +807,10 @@ out:
* \param dev a device handle
* \param interface_number the <tt>bInterfaceNumber</tt> of the
* previously-claimed interface
- * \returns 0 on success, or a LIBUSB_ERROR code on failure.
- * LIBUSB_ERROR_NOT_FOUND indicates that the interface was not claimed.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_release_interface(libusb_device_handle *dev,
int interface_number)
@@ -843,6 +854,7 @@ out:
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
* requested alternate setting does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_set_interface_alt_setting(libusb_device_handle *dev,
@@ -876,6 +888,7 @@ API_EXPORTED int libusb_set_interface_alt_setting(libusb_device_handle *dev,
* \param endpoint the endpoint to clear halt status
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_clear_halt(libusb_device_handle *dev,
@@ -900,7 +913,8 @@ API_EXPORTED int libusb_clear_halt(libusb_device_handle *dev,
*
* \param dev a handle of the device to reset
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required
+ * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
+ * device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_reset_device(libusb_device_handle *dev)
@@ -918,7 +932,8 @@ API_EXPORTED int libusb_reset_device(libusb_device_handle *dev)
* \param interface the interface to check
* \returns 0 if no kernel driver is active
* \returns 1 if a kernel driver is active
- * \returns LIBUSB_ERROR code on failure
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
* \see libusb_detach_kernel_driver()
*/
API_EXPORTED int libusb_kernel_driver_active(libusb_device_handle *dev,
@@ -940,6 +955,7 @@ API_EXPORTED int libusb_kernel_driver_active(libusb_device_handle *dev,
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
* \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_kernel_driver_active()
*/
diff --git a/libusb/io.c b/libusb/io.c
index 133f849..f91b14c 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -26,7 +26,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
@@ -715,7 +714,9 @@ API_EXPORTED void libusb_free_transfer(struct libusb_transfer *transfer)
* submitted but has not yet completed.
*
* \param transfer the transfer to submit
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
*/
API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer)
{
@@ -751,7 +752,7 @@ API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer)
*
* \param transfer the transfer to cancel
* \returns 0 on success
- * \returns non-zero on error
+ * \returns a LIBUSB_ERROR code on failure
*/
API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer)
{
@@ -873,15 +874,13 @@ out:
static int handle_events(struct timeval *tv)
{
int r;
- int maxfd = 0;
- fd_set readfds, writefds;
- fd_set *_readfds = NULL;
- fd_set *_writefds = NULL;
struct usbi_pollfd *ipollfd;
- int have_readfds = 0;
- int have_writefds = 0;
- struct timeval select_timeout;
struct timeval timeout;
+ struct timeval poll_timeout;
+ nfds_t nfds = 0;
+ struct pollfd *fds;
+ int i = -1;
+ int timeout_ms;
r = libusb_get_next_timeout(&timeout);
if (r) {
@@ -891,53 +890,46 @@ static int handle_events(struct timeval *tv)
/* choose the smallest of next URB timeout or user specified timeout */
if (timercmp(&timeout, tv, <))
- select_timeout = timeout;
+ poll_timeout = timeout;
else
- select_timeout = *tv;
+ poll_timeout = *tv;
} else {
- select_timeout = *tv;
+ poll_timeout = *tv;
}
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
pthread_mutex_lock(&pollfds_lock);
+ list_for_each_entry(ipollfd, &pollfds, list)
+ nfds++;
+
+ /* TODO: malloc when number of fd's changes, not on every poll */
+ fds = malloc(sizeof(*fds) * nfds);
+ if (!fds)
+ return LIBUSB_ERROR_NO_MEM;
+
list_for_each_entry(ipollfd, &pollfds, list) {
struct libusb_pollfd *pollfd = &ipollfd->pollfd;
int fd = pollfd->fd;
- if (pollfd->events & POLLIN) {
- have_readfds = 1;
- FD_SET(fd, &readfds);
- }
- if (pollfd->events & POLLOUT) {
- have_writefds = 1;
- FD_SET(fd, &writefds);
- }
- if (fd > maxfd)
- maxfd = fd;
+ i++;
+ fds[i].fd = fd;
+ fds[i].events = pollfd->events;
+ fds[i].revents = 0;
}
pthread_mutex_unlock(&pollfds_lock);
- if (have_readfds)
- _readfds = &readfds;
- if (have_writefds)
- _writefds = &writefds;
-
- usbi_dbg("select() with timeout in %d.%06ds", select_timeout.tv_sec,
- select_timeout.tv_usec);
- r = select(maxfd + 1, _readfds, _writefds, NULL, &select_timeout);
- usbi_dbg("select() returned %d with %d.%06ds remaining",
- r, select_timeout.tv_sec, select_timeout.tv_usec);
+ timeout_ms = (poll_timeout.tv_sec * 1000) + (poll_timeout.tv_usec / 1000);
+ usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
+ r = poll(fds, nfds, timeout_ms);
+ usbi_dbg("poll() returned %d", r);
if (r == 0) {
- *tv = select_timeout;
return handle_timeouts();
} else if (r == -1 && errno == EINTR) {
- return 0;
+ return LIBUSB_ERROR_INTERRUPTED;
} else if (r < 0) {
- usbi_err("select failed %d err=%d\n", r, errno);
+ usbi_err("poll failed %d err=%d\n", r, errno);
return LIBUSB_ERROR_IO;
}
- r = usbi_backend->handle_events(_readfds, _writefds);
+ r = usbi_backend->handle_events(fds, nfds, r);
if (r)
usbi_err("backend handle_events failed with error %d", r);
@@ -1113,7 +1105,7 @@ void usbi_remove_pollfd(int fd)
}
if (!found) {
- usbi_err("couldn't find fd %d to remove", fd);
+ usbi_dbg("couldn't find fd %d to remove", fd);
pthread_mutex_unlock(&pollfds_lock);
return;
}
@@ -1159,3 +1151,43 @@ out:
return (const struct libusb_pollfd **) ret;
}
+void usbi_handle_disconnect(struct libusb_device_handle *handle)
+{
+ struct usbi_transfer *cur;
+ struct usbi_transfer *to_cancel;
+
+ usbi_dbg("device %d.%d",
+ handle->dev->bus_number, handle->dev->device_address);
+
+ /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
+ * status code.
+ *
+ * this is a bit tricky because:
+ * 1. we can't do transfer completion while holding flying_transfers_lock
+ * 2. the transfers list can change underneath us - if we were to build a
+ * list of transfers to complete (while holding look), the situation
+ * might be different by the time we come to free them
+ *
+ * so we resort to a loop-based approach as below
+ * FIXME: is this still potentially racy?
+ */
+
+ while (1) {
+ pthread_mutex_lock(&flying_transfers_lock);
+ to_cancel = NULL;
+ list_for_each_entry(cur, &flying_transfers, list)
+ if (__USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == handle) {
+ to_cancel = cur;
+ break;
+ }
+ pthread_mutex_unlock(&flying_transfers_lock);
+
+ if (!to_cancel)
+ break;
+
+ usbi_backend->clear_transfer_priv(to_cancel);
+ usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
+ }
+
+}
+
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 19f7d81..2cd6633 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -581,26 +581,32 @@ enum libusb_error {
/** Access denied (insufficient permissions) */
LIBUSB_ERROR_ACCESS = -3,
+ /** No such device (it may have been disconnected) */
+ LIBUSB_ERROR_NO_DEVICE = -4,
+
/** Entity not found */
- LIBUSB_ERROR_NOT_FOUND = -4,
+ LIBUSB_ERROR_NOT_FOUND = -5,
/** Resource busy */
- LIBUSB_ERROR_BUSY = -5,
+ LIBUSB_ERROR_BUSY = -6,
/** Operation timed out */
- LIBUSB_ERROR_TIMEOUT = -6,
+ LIBUSB_ERROR_TIMEOUT = -7,
/** Pipe error */
- LIBUSB_ERROR_PIPE = -7,
+ LIBUSB_ERROR_PIPE = -8,
+
+ /** System call interrupted (perhaps due to signal) */
+ LIBUSB_ERROR_INTERRUPTED = -9,
/** Insufficient memory */
- LIBUSB_ERROR_NO_MEM = -8,
+ LIBUSB_ERROR_NO_MEM = -10,
/** Operation not supported or unimplemented on this platform */
- LIBUSB_ERROR_NOT_SUPPORTED = -9,
+ LIBUSB_ERROR_NOT_SUPPORTED = -11,
/** Other error */
- LIBUSB_ERROR_OTHER = -10,
+ LIBUSB_ERROR_OTHER = -12,
};
/** \ingroup asyncio
@@ -622,6 +628,9 @@ enum libusb_transfer_status {
/** For bulk/interrupt endpoints: halt condition detected (endpoint
* stalled). For control endpoints: control request not supported. */
LIBUSB_TRANSFER_STALL,
+
+ /** Device was disconnected */
+ LIBUSB_TRANSFER_NO_DEVICE,
};
/** \ingroup asyncio
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 414da25..87b2953 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -23,9 +23,9 @@
#include <config.h>
+#include <poll.h>
#include <pthread.h>
#include <stddef.h>
-#include <sys/select.h>
#include <time.h>
#include <libusb.h>
@@ -211,6 +211,7 @@ void usbi_io_init(void);
struct libusb_device *usbi_alloc_device(unsigned long session_id);
struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id);
int usbi_sanitize_device(struct libusb_device *dev);
+void usbi_handle_disconnect(struct libusb_device_handle *handle);
void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status);
@@ -288,8 +289,9 @@ struct usbi_os_backend {
int (*submit_transfer)(struct usbi_transfer *itransfer);
int (*cancel_transfer)(struct usbi_transfer *itransfer);
+ void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
- int (*handle_events)(fd_set *readfds, fd_set *writefds);
+ int (*handle_events)(struct pollfd *fds, nfds_t nfds, int num_ready);
/* number of bytes to reserve for libusb_device.os_priv */
size_t device_priv_size;
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index e69578c..e3aa7a9 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -29,7 +29,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -482,6 +481,9 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd)
r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl);
if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
usbi_err("get_configuration failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
@@ -869,6 +871,8 @@ static int op_open(struct libusb_device_handle *handle)
"libusb requires write access to USB device nodes.\n",
filename);
return LIBUSB_ERROR_ACCESS;
+ } else if (errno == ENOENT) {
+ return LIBUSB_ERROR_NO_DEVICE;
} else {
usbi_err("open failed, code %d errno %d", hpriv->fd, errno);
return LIBUSB_ERROR_IO;
@@ -895,6 +899,8 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
return LIBUSB_ERROR_NOT_FOUND;
else if (errno == EBUSY)
return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -927,6 +933,8 @@ static int op_claim_interface(struct libusb_device_handle *handle, int iface)
return LIBUSB_ERROR_NOT_FOUND;
else if (errno == EBUSY)
return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("claim interface failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -939,6 +947,9 @@ static int op_release_interface(struct libusb_device_handle *handle, int iface)
int fd = __device_handle_priv(handle)->fd;
int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface);
if (r) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
usbi_err("release interface failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
@@ -958,6 +969,8 @@ static int op_set_interface(struct libusb_device_handle *handle, int iface,
if (r) {
if (errno == EINVAL)
return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("setintf failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -975,6 +988,8 @@ static int op_clear_halt(struct libusb_device_handle *handle,
if (r) {
if (errno == ENOENT)
return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("clear_halt failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -1010,6 +1025,8 @@ static int op_kernel_driver_active(struct libusb_device_handle *handle,
if (r) {
if (errno == ENODATA)
return 0;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("get driver failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -1035,6 +1052,8 @@ static int op_detach_kernel_driver(struct libusb_device_handle *handle,
return LIBUSB_ERROR_NOT_FOUND;
else if (errno == EINVAL)
return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
usbi_err("detach failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
@@ -1118,14 +1137,20 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer,
r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
if (r < 0) {
int j;
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_err("submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
/* if the first URB submission fails, we can simply free up and
* return failure immediately. */
if (i == 0) {
usbi_dbg("first URB failed, easy peasy");
free(urbs);
- return LIBUSB_ERROR_IO;
+ return r;
}
/* if it's not the first URB that failed, the situation is a bit
@@ -1266,14 +1291,20 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
int r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]);
if (r < 0) {
int j;
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_err("submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
/* if the first URB submission fails, we can simply free up and
* return failure immediately. */
if (i == 0) {
usbi_dbg("first URB failed, easy peasy");
free_iso_urbs(tpriv);
- return LIBUSB_ERROR_IO;
+ return r;
}
/* if it's not the first URB that failed, the situation is a bit
@@ -1338,8 +1369,11 @@ static int submit_control_transfer(struct usbi_transfer *itransfer)
r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
if (r < 0) {
- usbi_err("submiturb failed error %d errno=%d", r, errno);
free(urb);
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err("submiturb failed error %d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
return 0;
@@ -1450,12 +1484,33 @@ static int op_cancel_transfer(struct usbi_transfer *itransfer)
}
}
+static void op_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ free(tpriv->urbs);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ free_iso_urbs(tpriv);
+ break;
+ default:
+ usbi_err("unknown endpoint type %d", transfer->type);
+ }
+}
+
static int handle_bulk_completion(struct usbi_transfer *itransfer,
struct usbfs_urb *urb)
{
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
int num_urbs = tpriv->num_urbs;
int urb_idx = urb - tpriv->urbs;
+ enum libusb_transfer_status status;
usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status,
urb_idx + 1, num_urbs);
@@ -1483,19 +1538,20 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
if (tpriv->awaiting_reap == 0 && tpriv->awaiting_discard == 0) {
usbi_dbg("CANCEL: last URB handled, reporting");
free(tpriv->urbs);
- if (tpriv->reap_action == CANCELLED)
+ if (tpriv->reap_action == CANCELLED) {
usbi_handle_transfer_cancellation(itransfer);
- else
- usbi_handle_transfer_completion(itransfer,
- LIBUSB_TRANSFER_ERROR);
+ } else {
+ status = LIBUSB_TRANSFER_ERROR;
+ goto out;
+ }
}
return 0;
}
if (urb->status == -EPIPE) {
usbi_dbg("detected endpoint stall");
- usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_STALL);
- return 0;
+ status = LIBUSB_TRANSFER_STALL;
+ goto out;
} else if (urb->status != 0) {
usbi_warn("unrecognised urb status %d", urb->status);
}
@@ -1510,6 +1566,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
else
return 0;
+out:
free(tpriv->urbs);
usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED);
return 0;
@@ -1641,10 +1698,13 @@ static int reap_for_handle(struct libusb_device_handle *handle)
r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb);
if (r == -1 && errno == EAGAIN)
- return r;
+ return 1;
if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
usbi_err("reap failed error %d errno=%d", r, errno);
- return r;
+ return LIBUSB_ERROR_IO;
}
itransfer = urb->usercontext;
@@ -1667,30 +1727,44 @@ static int reap_for_handle(struct libusb_device_handle *handle)
}
}
-static int op_handle_events(fd_set *readfds, fd_set *writefds)
+static int op_handle_events(struct pollfd *fds, nfds_t nfds, int num_ready)
{
- struct libusb_device_handle *handle;
- int ret = 0;
+ int r;
+ int i = 0;
pthread_mutex_lock(&usbi_open_devs_lock);
- list_for_each_entry(handle, &usbi_open_devs, list) {
- struct linux_device_handle_priv *hpriv = __device_handle_priv(handle);
- int r;
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ struct pollfd *pollfd = &fds[i];
+ struct libusb_device_handle *handle;
+ struct linux_device_handle_priv *hpriv = NULL;
- if (!FD_ISSET(hpriv->fd, writefds))
+ if (!pollfd->revents)
continue;
+
+ num_ready--;
+ list_for_each_entry(handle, &usbi_open_devs, list) {
+ hpriv = __device_handle_priv(handle);
+ if (hpriv->fd == pollfd->fd)
+ break;
+ }
+
+ if (pollfd->revents & POLLERR) {
+ usbi_remove_pollfd(hpriv->fd);
+ usbi_handle_disconnect(handle);
+ continue;
+ }
+
r = reap_for_handle(handle);
- if (r == -1 && errno == EAGAIN)
+ if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE)
continue;
- if (r < 0) {
- ret = LIBUSB_ERROR_IO;
+ else if (r < 0)
goto out;
- }
}
+ r = 0;
out:
pthread_mutex_unlock(&usbi_open_devs_lock);
- return ret;
+ return r;
}
const struct usbi_os_backend linux_usbfs_backend = {
@@ -1719,6 +1793,7 @@ const struct usbi_os_backend linux_usbfs_backend = {
.submit_transfer = op_submit_transfer,
.cancel_transfer = op_cancel_transfer,
+ .clear_transfer_priv = op_clear_transfer_priv,
.handle_events = op_handle_events,
diff --git a/libusb/sync.c b/libusb/sync.c
index 1d0f7c4..d158922 100644
--- a/libusb/sync.c
+++ b/libusb/sync.c
@@ -66,6 +66,7 @@ static void ctrl_transfer_cb(struct libusb_transfer *transfer)
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
* \returns LIBUSB_ERROR_PIPE if the control request was not supported by the
* device
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failures
*/
API_EXPORTED int libusb_control_transfer(libusb_device_handle *dev_handle,
@@ -125,6 +126,9 @@ API_EXPORTED int libusb_control_transfer(libusb_device_handle *dev_handle,
case LIBUSB_TRANSFER_STALL:
r = LIBUSB_ERROR_PIPE;
break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
default:
usbi_warn("unrecognised status code %d", transfer->status);
r = LIBUSB_ERROR_OTHER;
@@ -186,6 +190,9 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
case LIBUSB_TRANSFER_STALL:
r = LIBUSB_ERROR_PIPE;
break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
default:
usbi_warn("unrecognised status code %d", transfer->status);
r = LIBUSB_ERROR_OTHER;
@@ -230,6 +237,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates
* <tt>transferred</tt>)
* \returns LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failures
*/
API_EXPORTED int libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
@@ -276,6 +284,7 @@ API_EXPORTED int libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
* \returns 0 on success (and populates <tt>transferred</tt>)
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
* \returns LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other error
*/
API_EXPORTED int libusb_interrupt_transfer(