diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-05-11 20:31:58 +0100 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-05-11 20:47:27 +0100 |
commit | fec7c84163e25b8f811632828334d75da82bcb16 (patch) | |
tree | 9ccdab1835c2396bfe79ecc484328932ff186427 /libusb | |
parent | aeb905fa5d78cdbba80a680aa7a2bb7338f27f65 (diff) | |
download | libusb-fec7c84163e25b8f811632828334d75da82bcb16.tar.gz |
Handle hot-unplugging
This involved moving from select() to poll() because there is no way to
distinguish usbfs's POLLERR condition with select().
Diffstat (limited to 'libusb')
-rw-r--r-- | libusb/core.c | 24 | ||||
-rw-r--r-- | libusb/io.c | 112 | ||||
-rw-r--r-- | libusb/libusb.h | 23 | ||||
-rw-r--r-- | libusb/libusbi.h | 6 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 127 | ||||
-rw-r--r-- | libusb/sync.c | 9 |
6 files changed, 222 insertions, 79 deletions
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( |