diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index 407237feb71a..09f4795afaa1 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -114,6 +114,19 @@ libusb_set_nonblocking(int f) fcntl(f, F_SETFL, flags); } +static void +libusb10_wakeup_event_loop(libusb_context *ctx) +{ + uint8_t dummy = 0; + int err; + + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (err < (int)sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "Waking up event loop failed!"); + } +} + int libusb_init(libusb_context **context) { @@ -484,7 +497,6 @@ libusb_open(libusb_device *dev, libusb_device_handle **devh) { libusb_context *ctx = dev->ctx; struct libusb20_device *pdev = dev->os_priv; - uint8_t dummy; int err; if (devh == NULL) @@ -506,12 +518,8 @@ libusb_open(libusb_device *dev, libusb_device_handle **devh) POLLOUT | POLLRDNORM | POLLWRNORM); /* make sure our event loop detects the new device */ - dummy = 0; - err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); - if (err < (int)sizeof(dummy)) { - /* ignore error, if any */ - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!"); - } + libusb10_wakeup_event_loop(ctx); + *devh = pdev; return (0); @@ -564,8 +572,6 @@ libusb_close(struct libusb20_device *pdev) { libusb_context *ctx; struct libusb_device *dev; - uint8_t dummy; - int err; if (pdev == NULL) return; /* be NULL safe */ @@ -581,12 +587,7 @@ libusb_close(struct libusb20_device *pdev) libusb_unref_device(dev); /* make sure our event loop detects the closed device */ - dummy = 0; - err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); - if (err < (int)sizeof(dummy)) { - /* ignore error, if any */ - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!"); - } + libusb10_wakeup_event_loop(ctx); } libusb_device * @@ -1314,7 +1315,6 @@ libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint) int buffsize; int maxframe; int temp; - uint8_t dummy; dev = libusb_get_device(pdev); @@ -1415,10 +1415,8 @@ found: failure: libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR); - /* make sure our event loop spins the done handler */ - dummy = 0; - err = write(dev->ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + libusb10_wakeup_event_loop(dev->ctx); } /* The following function must be called unlocked */ @@ -1459,6 +1457,8 @@ libusb_submit_transfer(struct libusb_transfer *uxfer) (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) || (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) { err = LIBUSB_ERROR_BUSY; + } else if (dev->device_is_gone != 0) { + err = LIBUSB_ERROR_NO_DEVICE; } else { /* set pending state */ @@ -1490,6 +1490,7 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer) struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; + struct libusb_device_handle *devh; uint8_t endpoint; int retval; @@ -1497,12 +1498,12 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer) return (LIBUSB_ERROR_INVALID_PARAM); /* check if not initialised */ - if (uxfer->dev_handle == NULL) + if ((devh = uxfer->dev_handle) == NULL) return (LIBUSB_ERROR_NOT_FOUND); endpoint = uxfer->endpoint; - dev = libusb_get_device(uxfer->dev_handle); + dev = libusb_get_device(devh); DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); @@ -1513,8 +1514,8 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer) CTX_LOCK(dev->ctx); - pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0); - pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1); + pxfer0 = libusb10_get_transfer(devh, endpoint, 0); + pxfer1 = libusb10_get_transfer(devh, endpoint, 1); if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) { /* only update the transfer status */ @@ -1526,23 +1527,38 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer) sxfer->entry.tqe_prev = NULL; libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_CANCELLED); + /* make sure our event loop spins the done handler */ + libusb10_wakeup_event_loop(dev->ctx); } else if (pxfer0 == NULL || pxfer1 == NULL) { /* not started */ retval = LIBUSB_ERROR_NOT_FOUND; } else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) { libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_CANCELLED); - libusb20_tr_stop(pxfer0); - /* make sure the queue doesn't stall */ - libusb10_submit_transfer_sub( - uxfer->dev_handle, endpoint); + if (dev->device_is_gone != 0) { + /* clear transfer pointer */ + libusb20_tr_set_priv_sc1(pxfer0, NULL); + /* make sure our event loop spins the done handler */ + libusb10_wakeup_event_loop(dev->ctx); + } else { + libusb20_tr_stop(pxfer0); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub(devh, endpoint); + } } else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) { libusb10_complete_transfer(pxfer1, sxfer, LIBUSB_TRANSFER_CANCELLED); - libusb20_tr_stop(pxfer1); - /* make sure the queue doesn't stall */ - libusb10_submit_transfer_sub( - uxfer->dev_handle, endpoint); + /* check if handle is still active */ + if (dev->device_is_gone != 0) { + /* clear transfer pointer */ + libusb20_tr_set_priv_sc1(pxfer1, NULL); + /* make sure our event loop spins the done handler */ + libusb10_wakeup_event_loop(dev->ctx); + } else { + libusb20_tr_stop(pxfer1); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub(devh, endpoint); + } } else { /* not started */ retval = LIBUSB_ERROR_NOT_FOUND; @@ -1571,6 +1587,35 @@ libusb10_cancel_all_transfer(libusb_device *dev) } } +UNEXPORTED void +libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev) +{ + struct libusb_super_transfer *sxfer; + unsigned x; + + for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) { + struct libusb20_transfer *xfer; + + xfer = libusb20_tr_get_pointer(pdev, x); + if (xfer == NULL) + continue; + if (libusb20_tr_pending(xfer) == 0) + continue; + sxfer = libusb20_tr_get_priv_sc1(xfer); + if (sxfer == NULL) + continue; + /* complete pending transfer */ + libusb10_complete_transfer(xfer, sxfer, LIBUSB_TRANSFER_ERROR); + } + + while ((sxfer = TAILQ_FIRST(&dev->tr_head))) { + TAILQ_REMOVE(&dev->tr_head, sxfer, entry); + + /* complete pending transfer */ + libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_ERROR); + } +} + uint16_t libusb_cpu_to_le16(uint16_t x) { diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h index e516de17b1e9..7c34f895b0aa 100644 --- a/lib/libusb/libusb10.h +++ b/lib/libusb/libusb10.h @@ -116,6 +116,8 @@ struct libusb_context { struct libusb_device { int refcnt; + int device_is_gone; + uint32_t claimed_interfaces; struct libusb_super_pollfd dev_poll; @@ -134,5 +136,6 @@ extern struct libusb_context *usbi_default_context; void libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, struct libusb20_device *pdev, int fd, short events); void libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd); void libusb10_cancel_all_transfer(libusb_device *dev); +void libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev); #endif /* __LIBUSB10_H__ */ diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c index e25b6a37d77d..42fbd0e2e30b 100644 --- a/lib/libusb/libusb10_io.c +++ b/lib/libusb/libusb10_io.c @@ -161,17 +161,19 @@ libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) if (ppdev[i] != NULL) { dev = libusb_get_device(ppdev[i]); - if (fds[i].revents == 0) - err = 0; /* nothing to do */ - else + if (fds[i].revents != 0) { err = libusb20_dev_process(ppdev[i]); - if (err) { - /* cancel all transfers - device is gone */ - libusb10_cancel_all_transfer(dev); + if (err) { + /* set device is gone */ + dev->device_is_gone = 1; - /* remove USB device from polling loop */ - libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + /* remove USB device from polling loop */ + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + /* cancel all pending transfers */ + libusb10_cancel_all_transfer_locked(ppdev[i], dev); + } } CTX_UNLOCK(ctx); libusb_unref_device(dev); @@ -180,10 +182,8 @@ libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) } else { uint8_t dummy; - while (1) { - if (read(fds[i].fd, &dummy, 1) != 1) - break; - } + while (read(fds[i].fd, &dummy, 1) == 1) + ; } }