diff --git a/.gitignore b/.gitignore index 5fc74a5..b270a47 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/libusbx-1.0.15.tar.bz2 +SOURCES/libusb-1.0.20.tar.bz2 diff --git a/.libusbx.metadata b/.libusbx.metadata index 9daa697..86cb0b6 100644 --- a/.libusbx.metadata +++ b/.libusbx.metadata @@ -1 +1 @@ -1ca868f775093b0109d9240cb3ccd36367764dc6 SOURCES/libusbx-1.0.15.tar.bz2 +9537243f165927bde74ad742e6b3effb0bd50cd2 SOURCES/libusb-1.0.20.tar.bz2 diff --git a/SOURCES/0001-core-Store-different-event-types-as-a-bitmask-within.patch b/SOURCES/0001-core-Store-different-event-types-as-a-bitmask-within.patch new file mode 100644 index 0000000..f60357c --- /dev/null +++ b/SOURCES/0001-core-Store-different-event-types-as-a-bitmask-within.patch @@ -0,0 +1,113 @@ +From 9e2e370574ddedc1ad2e47a20bb0a8a85bd2249d Mon Sep 17 00:00:00 2001 +From: Chris Dickens +Date: Wed, 9 Dec 2015 23:45:21 -0800 +Subject: [PATCH 1/3] core: Store different event types as a bitmask within the + context + +This change introduces a new event_flags member to the libusb_context +that holds a bitmask of different events that have occurred. This will +allow multiple "one-shot" events (those that don't require counting) to +be stored in a single variable. The only existing event of this type, +pollfds_modified, has been converted to use this bitmask instead. + +Signed-off-by: Chris Dickens +--- + libusb/io.c | 8 ++++---- + libusb/libusbi.h | 16 +++++++++++----- + libusb/version_nano.h | 2 +- + 3 files changed, 16 insertions(+), 10 deletions(-) + +diff --git a/libusb/io.c b/libusb/io.c +index 279288c..ce0abb9 100644 +--- a/libusb/io.c ++++ b/libusb/io.c +@@ -2055,7 +2055,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) + /* only reallocate the poll fds when the list of poll fds has been modified + * since the last poll, otherwise reuse them to save the additional overhead */ + usbi_mutex_lock(&ctx->event_data_lock); +- if (ctx->pollfds_modified) { ++ if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) { + usbi_dbg("poll fds modified, reallocating"); + + if (ctx->pollfds) { +@@ -2081,7 +2081,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) + } + + /* reset the flag now that we have the updated list */ +- ctx->pollfds_modified = 0; ++ ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED; + + /* if no further pending events, clear the event pipe so that we do + * not immediately return from poll */ +@@ -2125,7 +2125,7 @@ redo_poll: + usbi_mutex_lock(&ctx->event_data_lock); + + /* check if someone added a new poll fd */ +- if (ctx->pollfds_modified) ++ if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) + usbi_dbg("someone updated the poll fds"); + + /* check if someone is closing a device */ +@@ -2583,7 +2583,7 @@ static void usbi_fd_notification(struct libusb_context *ctx) + /* Record that there is a new poll fd. + * Only signal an event if there are no prior pending events. */ + pending_events = usbi_pending_events(ctx); +- ctx->pollfds_modified = 1; ++ ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED; + if (!pending_events) + usbi_signal_event(ctx); + } +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 822612e..40ae608 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -287,18 +287,19 @@ struct libusb_context { + /* A lock to protect internal context event data. */ + usbi_mutex_t event_data_lock; + ++ /* A bitmask of flags that are set to indicate specific events that need to ++ * be handled. Protected by event_data_lock. */ ++ unsigned int event_flags; ++ + /* A counter that is set when we want to interrupt and prevent event handling, + * in order to safely close a device. Protected by event_data_lock. */ + unsigned int device_close; + + /* list and count of poll fds and an array of poll fd structures that is +- * (re)allocated as necessary prior to polling, and a flag to indicate +- * when the list of poll fds has changed since the last poll. +- * Protected by event_data_lock. */ ++ * (re)allocated as necessary prior to polling. Protected by event_data_lock. */ + struct list_head ipollfds; + struct pollfd *pollfds; + POLL_NFDS_TYPE pollfds_cnt; +- unsigned int pollfds_modified; + + /* A list of pending hotplug messages. Protected by event_data_lock. */ + struct list_head hotplug_msgs; +@@ -315,9 +316,14 @@ struct libusb_context { + struct list_head list; + }; + ++enum usbi_event_flags { ++ /* The list of pollfds has been modified */ ++ USBI_EVENT_POLLFDS_MODIFIED = 1 << 0, ++}; ++ + /* Update the following macro if new event sources are added */ + #define usbi_pending_events(ctx) \ +- ((ctx)->device_close || (ctx)->pollfds_modified \ ++ ((ctx)->event_flags || (ctx)->device_close \ + || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers)) + + #ifdef USBI_TIMERFD_AVAILABLE +diff --git a/libusb/version_nano.h b/libusb/version_nano.h +index 4afcd97..7abae0e 100644 +--- a/libusb/version_nano.h ++++ b/libusb/version_nano.h +@@ -1 +1 @@ +-#define LIBUSB_NANO 11004 ++#define LIBUSB_NANO 11005 +-- +2.7.4 + diff --git a/SOURCES/0001-linux_usbfs-Work-around-a-driver-binding-race-in-res.patch b/SOURCES/0001-linux_usbfs-Work-around-a-driver-binding-race-in-res.patch deleted file mode 100644 index 3a39c1c..0000000 --- a/SOURCES/0001-linux_usbfs-Work-around-a-driver-binding-race-in-res.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 78a150bfbbd84eb524e878bf05101c1ad2eac0b8 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Fri, 6 Jul 2012 14:35:53 +0200 -Subject: [PATCH 3/3] linux_usbfs: Work around a driver binding race in reset - handling - -I've been seeing these intermittent failures to reclaim an interface after -a device reset. After much debugging and inserting sleeps in strategic -places to make the race window larger I've found the following race: -1) A user is running some software using libusb which will automatically - detect, and "bind" to, any newly plugged in USB-devices. For example - a virtual machine viewer with automatic USB-redirection -2) The user plugs in a new usb-storage device -3) The usb-storage driver is not yet loaded, udev spawns - "modprobe usb-storage", this blocks on disk-io -4) The libusb app opens the device, claims all interfaces, does a device-reset -5) While the IOCTL_USBFS_RESET is running the modprobe completes -6) The driver registration blocks on an USB lock held by the reset code path -7) When the reset finishes the driver registration completes and the driver - binds itself to the device, before IOCTL_USBFS_RESET returns to userspace -8) libusb tries to re-claim all interfaces it had claimed before the reset -9) libusb fails as usb-storage is now bound to it - -This patch works around this issue by simply unbinding the driver for all -interfaces which were claimed before the reset. Normally this is a no-op as -no driver (other then usbfs) can be bound for claimed interfaces before the -reset. - -But as the above example shows, the exception is a driver completing -registration, and as part of this binding to any elegible devices, between -IOCTL_USBFS_RESET and our re-claiming of the interface. The largest part -of the race window here is the time IOCTL_USBFS_RESET takes, as that does a -fair amount of IO with the device. This part of the race window is -worked around by this patch. - -This still leaves a theoretical race window where the driver registration -finishes between our driver-unbind and interface-reclaim, I'm afraid there -is nothing we can against this. - -This patch also improves the error logging, and makes libusb_device_reset -properly return an error when re-claiming fails. - -Signed-off-by: Hans de Goede ---- - libusb/os/linux_usbfs.c | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index 3894554..10d138a 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -108,6 +108,9 @@ static int sysfs_can_relate_devices = 0; - /* do we have a descriptors file? */ - static int sysfs_has_descriptors = 0; - -+static int op_detach_kernel_driver(struct libusb_device_handle *handle, -+ int interface); -+ - struct linux_device_priv { - char *sysfs_dir; - unsigned char *dev_descriptor; -@@ -1497,11 +1500,20 @@ static int op_reset_device(struct libusb_device_handle *handle) - /* And re-claim any interfaces which were claimed before the reset */ - for (i = 0; i < USB_MAXINTERFACES; i++) { - if (handle->claimed_interfaces & (1L << i)) { -+ /* -+ * A driver may have completed modprobing during -+ * IOCTL_USBFS_RESET, and bound itself as soon as -+ * IOCTL_USBFS_RESET released the device lock -+ */ -+ op_detach_kernel_driver(handle, i); -+ - r = op_claim_interface(handle, i); - if (r) { - usbi_warn(HANDLE_CTX(handle), -- "failed to re-claim interface %d after reset", i); -+ "failed to re-claim interface %d after reset: %s", -+ i, libusb_error_name(r)); - handle->claimed_interfaces &= ~(1L << i); -+ ret = LIBUSB_ERROR_NOT_FOUND; - } - } - } --- -1.7.11.2 - diff --git a/SOURCES/0002-API-Add-libusb_interrupt_event_handler-function.patch b/SOURCES/0002-API-Add-libusb_interrupt_event_handler-function.patch new file mode 100644 index 0000000..776ad21 --- /dev/null +++ b/SOURCES/0002-API-Add-libusb_interrupt_event_handler-function.patch @@ -0,0 +1,123 @@ +From 3daf9625c7f9212a762927239b2f0f14d68f4361 Mon Sep 17 00:00:00 2001 +From: Chris Dickens +Date: Thu, 10 Dec 2015 00:20:18 -0800 +Subject: [PATCH 2/3] API: Add libusb_interrupt_event_handler() function + +This new function will allow the user to purposely interrupt an +event handling thread, causing it to return from the event handling +function. This is mainly useful for cleanly exiting from a dedicated +event handling thread during application shutdown. + +Signed-off-by: Chris Dickens +--- + libusb/io.c | 28 ++++++++++++++++++++++++++++ + libusb/libusb-1.0.def | 2 ++ + libusb/libusb.h | 3 ++- + libusb/libusbi.h | 3 +++ + libusb/version_nano.h | 2 +- + 5 files changed, 36 insertions(+), 2 deletions(-) + +diff --git a/libusb/io.c b/libusb/io.c +index ce0abb9..bc85438 100644 +--- a/libusb/io.c ++++ b/libusb/io.c +@@ -1848,6 +1848,29 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) + } + + /** \ingroup poll ++ * Interrupt any active thread that is handling events. This is mainly useful ++ * for interrupting a dedicated event handling thread when an application ++ * wishes to call libusb_exit(). ++ * ++ * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 ++ * ++ * \param ctx the context to operate on, or NULL for the default context ++ * \ref mtasync ++ */ ++void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) ++{ ++ USBI_GET_CONTEXT(ctx); ++ ++ usbi_dbg(""); ++ usbi_mutex_lock(&ctx->event_data_lock); ++ if (!usbi_pending_events(ctx)) { ++ ctx->event_flags |= USBI_EVENT_USER_INTERRUPT; ++ usbi_signal_event(ctx); ++ } ++ usbi_mutex_unlock(&ctx->event_data_lock); ++} ++ ++/** \ingroup poll + * Acquire the event waiters lock. This lock is designed to be obtained under + * the situation where you want to be aware when events are completed, but + * some other thread is event handling so calling libusb_handle_events() is not +@@ -2128,6 +2151,11 @@ redo_poll: + if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) + usbi_dbg("someone updated the poll fds"); + ++ if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { ++ usbi_dbg("someone purposely interrupted"); ++ ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT; ++ } ++ + /* check if someone is closing a device */ + if (ctx->device_close) + usbi_dbg("someone is closing a device"); +diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def +index cbcf3e9..050b4b9 100644 +--- a/libusb/libusb-1.0.def ++++ b/libusb/libusb-1.0.def +@@ -116,6 +116,8 @@ EXPORTS + libusb_hotplug_register_callback@36 = libusb_hotplug_register_callback + libusb_init + libusb_init@4 = libusb_init ++ libusb_interrupt_event_handler ++ libusb_interrupt_event_handler@4 = libusb_interrupt_event_handler + libusb_interrupt_transfer + libusb_interrupt_transfer@24 = libusb_interrupt_transfer + libusb_kernel_driver_active +diff --git a/libusb/libusb.h b/libusb/libusb.h +index 513945f..ba82c36 100644 +--- a/libusb/libusb.h ++++ b/libusb/libusb.h +@@ -141,7 +141,7 @@ typedef unsigned __int32 uint32_t; + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + */ +-#define LIBUSB_API_VERSION 0x01000104 ++#define LIBUSB_API_VERSION 0x01000105 + + /* The following is kept for compatibility, but will be deprecated in the future */ + #define LIBUSBX_API_VERSION LIBUSB_API_VERSION +@@ -1801,6 +1801,7 @@ void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); + void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); + int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); + int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); ++void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); + void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); + void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); + int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 40ae608..a39cbe1 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -319,6 +319,9 @@ struct libusb_context { + enum usbi_event_flags { + /* The list of pollfds has been modified */ + USBI_EVENT_POLLFDS_MODIFIED = 1 << 0, ++ ++ /* The user has interrupted the event handler */ ++ USBI_EVENT_USER_INTERRUPT = 1 << 1, + }; + + /* Update the following macro if new event sources are added */ +diff --git a/libusb/version_nano.h b/libusb/version_nano.h +index 7abae0e..ea79210 100644 +--- a/libusb/version_nano.h ++++ b/libusb/version_nano.h +@@ -1 +1 @@ +-#define LIBUSB_NANO 11005 ++#define LIBUSB_NANO 11006 +-- +2.7.4 + diff --git a/SOURCES/0003-core-Refactor-code-related-to-transfer-flags-and-tim.patch b/SOURCES/0003-core-Refactor-code-related-to-transfer-flags-and-tim.patch new file mode 100644 index 0000000..4421640 --- /dev/null +++ b/SOURCES/0003-core-Refactor-code-related-to-transfer-flags-and-tim.patch @@ -0,0 +1,494 @@ +From 65115ea3b437612c5c75408d9a4028cdb070e406 Mon Sep 17 00:00:00 2001 +From: Chris Dickens +Date: Mon, 26 Oct 2015 14:18:33 +0100 +Subject: [PATCH 3/3] core: Refactor code related to transfer flags and timeout + handling + +Commit a886bb02 sped up the library a bit by removing the serialization +of transfer submission with respect to the flying_transfers list, but +it introduced two separate issues. + +1) A deadlock scenario is possible given the following sequence: + + - Thread A submits transfer with very short timeout (say 1ms) + -> takes transfer->lock + -> adds transfer to flying_transfers list and arms timerfd + -> actually calls backend to submit transfer, but it fails + + - Thread B is doing event handling and sees the timerfd trigger + -> takes ctx->flying_transfers_lock + -> finds the transfer above on the list + -> calls libusb_cancel_transfer() for this transfer + --> takes transfer->lock + + - Thread A sees the transfer failed to submit + -> removes transfer from flying_transfers list + --> takes ctx->flying_transfers_lock (still holding transfer->lock) + ** DEADLOCK ** + +2) The transfer state flags (e.g. submitting, in-flight) were protected + by transfer->flags_lock, but the timeout-related flags were OR'ed in + during timeout handling operations outside of the lock. This leads to + the possibility that transfer state might get overwritten. + +This change corrects these issues and simplifies the transfer submission +code a bit by separating the state and timeout flags into their own flag +variables. The state flags are protected by the transfer lock. The timeout +flags are protected by the flying_transfers_lock. + +The transfer submission code sheds some weight because it no longer needs +to worry about the timing of events that modify the transfer state flags. +These flags are always viewed and modified under the protection of the +transfer lock. Since libusb_submit_transfer() holds the transfer lock for +the entire duration of the operation, the other code paths that would +possibly touch the transfer (e.g. usbi_handle_disconnect() and +usbi_handle_transfer_completion()) have to wait for transfer submission +to fully complete. This eliminates any possible race conditions. + +Signed-off-by: Chris Dickens +[hdegoede@redhat.com: Reworked libusb_submit_transfer changes so that in + case both flying_transfer_lock and itransfer->lock are taken + flying_transfers_lock is always taken first] +[hdegoede@redhat.com: Removed some unrelated changes (will be submitted + as separate patches)] +Signed-off-by: Hans de Goede +--- + libusb/core.c | 4 +- + libusb/io.c | 144 ++++++++++++++++++++++++++----------------------- + libusb/libusbi.h | 43 +++++++-------- + libusb/os/darwin_usb.c | 10 ++-- + libusb/version_nano.h | 2 +- + 5 files changed, 104 insertions(+), 99 deletions(-) + +diff --git a/libusb/core.c b/libusb/core.c +index 5884dcf..837e8f2 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -1331,10 +1331,10 @@ static void do_close(struct libusb_context *ctx, + if (transfer->dev_handle != dev_handle) + continue; + +- if (!(itransfer->flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { ++ if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); + +- if (itransfer->flags & USBI_TRANSFER_CANCELLING) ++ if (itransfer->state_flags & USBI_TRANSFER_CANCELLING) + usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); + else + usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); +diff --git a/libusb/io.c b/libusb/io.c +index bc85438..c216814 100644 +--- a/libusb/io.c ++++ b/libusb/io.c +@@ -1260,7 +1260,6 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( + + itransfer->num_iso_packets = iso_packets; + usbi_mutex_init(&itransfer->lock, NULL); +- usbi_mutex_init(&itransfer->flags_lock, NULL); + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg("transfer %p", transfer); + return transfer; +@@ -1295,7 +1294,6 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) + + itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + usbi_mutex_destroy(&itransfer->lock); +- usbi_mutex_destroy(&itransfer->flags_lock); + free(itransfer); + } + +@@ -1330,8 +1328,8 @@ static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) + if (!timerisset(cur_tv)) + goto disarm; + +- /* act on first transfer that is not already cancelled */ +- if (!(transfer->flags & USBI_TRANSFER_TIMEOUT_HANDLED)) { ++ /* act on first transfer that has not already been handled */ ++ if (!(transfer->timeout_flags & USBI_TRANSFER_TIMEOUT_HANDLED)) { + int r; + const struct itimerspec it = { {0, 0}, + { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } }; +@@ -1365,8 +1363,6 @@ static int add_to_flying_list(struct usbi_transfer *transfer) + int r = 0; + int first = 1; + +- usbi_mutex_lock(&ctx->flying_transfers_lock); +- + /* if we have no other flying transfers, start the list with this one */ + if (list_empty(&ctx->flying_transfers)) { + list_add(&transfer->list, &ctx->flying_transfers); +@@ -1419,7 +1415,6 @@ out: + if (r) + list_del(&transfer->list); + +- usbi_mutex_unlock(&ctx->flying_transfers_lock); + return r; + } + +@@ -1460,62 +1455,79 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) + { + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); +- int remove = 0; ++ struct libusb_context *ctx = TRANSFER_CTX(transfer); + int r; + + usbi_dbg("transfer %p", transfer); ++ ++ /* ++ * Important note on locking, this function takes / releases locks ++ * in the following order: ++ * take flying_transfers_lock ++ * take itransfer->lock ++ * clear transfer ++ * add to flying_transfers list ++ * release flying_transfers_lock ++ * submit transfer ++ * release itransfer->lock ++ * if submit failed: ++ * take flying_transfers_lock ++ * remove from flying_transfers list ++ * release flying_transfers_lock ++ * ++ * Note that it takes locks in the order a-b and then releases them ++ * in the same order a-b. This is somewhat unusual but not wrong, ++ * release order is not important as long as *all* locks are released ++ * before re-acquiring any locks. ++ * ++ * This means that the ordering of first releasing itransfer->lock ++ * and then re-acquiring the flying_transfers_list on error is ++ * important and must not be changed! ++ * ++ * This is done this way because when we take both locks we must always ++ * take flying_transfers_lock first to avoid ab-ba style deadlocks with ++ * the timeout handling and usbi_handle_disconnect paths. ++ * ++ * And we cannot release itransfer->lock before the submission is ++ * complete otherwise timeout handling for transfers with short ++ * timeouts may run before submission. ++ */ ++ usbi_mutex_lock(&ctx->flying_transfers_lock); + usbi_mutex_lock(&itransfer->lock); +- usbi_mutex_lock(&itransfer->flags_lock); +- if (itransfer->flags & USBI_TRANSFER_IN_FLIGHT) { +- r = LIBUSB_ERROR_BUSY; +- goto out; ++ if (itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) { ++ usbi_mutex_unlock(&ctx->flying_transfers_lock); ++ usbi_mutex_unlock(&itransfer->lock); ++ return LIBUSB_ERROR_BUSY; + } + itransfer->transferred = 0; +- itransfer->flags = 0; ++ itransfer->state_flags = 0; ++ itransfer->timeout_flags = 0; + r = calculate_timeout(itransfer); + if (r < 0) { +- r = LIBUSB_ERROR_OTHER; +- goto out; ++ usbi_mutex_unlock(&ctx->flying_transfers_lock); ++ usbi_mutex_unlock(&itransfer->lock); ++ return LIBUSB_ERROR_OTHER; + } +- itransfer->flags |= USBI_TRANSFER_SUBMITTING; +- usbi_mutex_unlock(&itransfer->flags_lock); + + r = add_to_flying_list(itransfer); + if (r) { +- usbi_mutex_lock(&itransfer->flags_lock); +- itransfer->flags = 0; +- goto out; ++ usbi_mutex_unlock(&ctx->flying_transfers_lock); ++ usbi_mutex_unlock(&itransfer->lock); ++ return r; + } ++ usbi_mutex_unlock(&ctx->flying_transfers_lock); + +- /* keep a reference to this device */ +- libusb_ref_device(transfer->dev_handle->dev); + r = usbi_backend->submit_transfer(itransfer); +- +- usbi_mutex_lock(&itransfer->flags_lock); +- itransfer->flags &= ~USBI_TRANSFER_SUBMITTING; + if (r == LIBUSB_SUCCESS) { +- /* check for two possible special conditions: +- * 1) device disconnect occurred immediately after submission +- * 2) transfer completed before we got here to update the flags +- */ +- if (itransfer->flags & USBI_TRANSFER_DEVICE_DISAPPEARED) { +- usbi_backend->clear_transfer_priv(itransfer); +- remove = 1; +- r = LIBUSB_ERROR_NO_DEVICE; +- } +- else if (!(itransfer->flags & USBI_TRANSFER_COMPLETED)) { +- itransfer->flags |= USBI_TRANSFER_IN_FLIGHT; +- } +- } else { +- remove = 1; +- } +-out: +- usbi_mutex_unlock(&itransfer->flags_lock); +- if (remove) { +- libusb_unref_device(transfer->dev_handle->dev); +- remove_from_flying_list(itransfer); ++ itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; ++ /* keep a reference to this device */ ++ libusb_ref_device(transfer->dev_handle->dev); + } + usbi_mutex_unlock(&itransfer->lock); ++ ++ if (r != LIBUSB_SUCCESS) ++ remove_from_flying_list(itransfer); ++ + return r; + } + +@@ -1541,9 +1553,8 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) + + usbi_dbg("transfer %p", transfer ); + usbi_mutex_lock(&itransfer->lock); +- usbi_mutex_lock(&itransfer->flags_lock); +- if (!(itransfer->flags & USBI_TRANSFER_IN_FLIGHT) +- || (itransfer->flags & USBI_TRANSFER_CANCELLING)) { ++ if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) ++ || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } +@@ -1557,13 +1568,12 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) + usbi_dbg("cancel transfer failed error %d", r); + + if (r == LIBUSB_ERROR_NO_DEVICE) +- itransfer->flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; ++ itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; + } + +- itransfer->flags |= USBI_TRANSFER_CANCELLING; ++ itransfer->state_flags |= USBI_TRANSFER_CANCELLING; + + out: +- usbi_mutex_unlock(&itransfer->flags_lock); + usbi_mutex_unlock(&itransfer->lock); + return r; + } +@@ -1626,10 +1636,9 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + if (r < 0) + usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno); + +- usbi_mutex_lock(&itransfer->flags_lock); +- itransfer->flags &= ~USBI_TRANSFER_IN_FLIGHT; +- itransfer->flags |= USBI_TRANSFER_COMPLETED; +- usbi_mutex_unlock(&itransfer->flags_lock); ++ usbi_mutex_lock(&itransfer->lock); ++ itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT; ++ usbi_mutex_unlock(&itransfer->lock); + + if (status == LIBUSB_TRANSFER_COMPLETED + && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { +@@ -1665,7 +1674,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) + { + /* if the URB was cancelled due to timeout, report timeout to the user */ +- if (transfer->flags & USBI_TRANSFER_TIMED_OUT) { ++ if (transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) { + usbi_dbg("detected timeout cancellation"); + return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT); + } +@@ -1966,10 +1975,10 @@ static void handle_timeout(struct usbi_transfer *itransfer) + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int r; + +- itransfer->flags |= USBI_TRANSFER_TIMEOUT_HANDLED; ++ itransfer->timeout_flags |= USBI_TRANSFER_TIMEOUT_HANDLED; + r = libusb_cancel_transfer(transfer); + if (r == 0) +- itransfer->flags |= USBI_TRANSFER_TIMED_OUT; ++ itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + else + usbi_warn(TRANSFER_CTX(transfer), + "async cancel failed %d errno=%d", r, errno); +@@ -2002,7 +2011,7 @@ static int handle_timeouts_locked(struct libusb_context *ctx) + return 0; + + /* ignore timeouts we've already handled */ +- if (transfer->flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) ++ if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if transfer has non-expired timeout, nothing more to do */ +@@ -2534,7 +2543,7 @@ 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_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) ++ if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if we've reached transfers of infinte timeout, we're done looking */ +@@ -2751,9 +2760,10 @@ void usbi_handle_disconnect(struct libusb_device_handle *handle) + * possible scenarios: + * 1. the transfer is currently in-flight, in which case we terminate the + * transfer here +- * 2. the transfer is not in-flight (or is but hasn't been marked as such), +- * in which case we record that the device disappeared and this will be +- * handled by libusb_submit_transfer() ++ * 2. the transfer has been added to the flying transfer list by ++ * libusb_submit_transfer, has failed to submit and ++ * libusb_submit_transfer is waiting for us to release the ++ * flying_transfers_lock to remove it, so we ignore it + */ + + while (1) { +@@ -2761,12 +2771,10 @@ void usbi_handle_disconnect(struct libusb_device_handle *handle) + usbi_mutex_lock(&HANDLE_CTX(handle)->flying_transfers_lock); + list_for_each_entry(cur, &HANDLE_CTX(handle)->flying_transfers, list, struct usbi_transfer) + if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == handle) { +- usbi_mutex_lock(&cur->flags_lock); +- if (cur->flags & USBI_TRANSFER_IN_FLIGHT) ++ usbi_mutex_lock(&cur->lock); ++ if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) + to_cancel = cur; +- else +- cur->flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; +- usbi_mutex_unlock(&cur->flags_lock); ++ usbi_mutex_unlock(&cur->lock); + + if (to_cancel) + break; +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index a39cbe1..95badd3 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -266,6 +266,8 @@ struct libusb_context { + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; ++ /* Note paths taking both this and usbi_transfer->lock must always ++ * take this lock first */ + usbi_mutex_t flying_transfers_lock; + + /* user callbacks for pollfd changes */ +@@ -407,7 +409,8 @@ struct usbi_transfer { + struct timeval timeout; + int transferred; + uint32_t stream_id; +- uint8_t flags; ++ uint8_t state_flags; /* Protected by usbi_transfer->lock */ ++ uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */ + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate +@@ -415,38 +418,32 @@ struct usbi_transfer { + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend +- * if this were possible). */ ++ * if this were possible). ++ * Note paths taking both this and the flying_transfers_lock must ++ * always take the flying_transfers_lock first */ + usbi_mutex_t lock; +- +- /* this lock should be held whenever viewing or modifying flags +- * relating to the transfer state */ +- usbi_mutex_t flags_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, ++enum usbi_transfer_state_flags { ++ /* Transfer successfully submitted by backend */ ++ USBI_TRANSFER_IN_FLIGHT = 1 << 0, + + /* Cancellation was requested via libusb_cancel_transfer() */ +- USBI_TRANSFER_CANCELLING = 1 << 2, ++ USBI_TRANSFER_CANCELLING = 1 << 1, + + /* Operation on the transfer failed because the device disappeared */ +- USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 3, +- +- /* Transfer is currently being submitted */ +- USBI_TRANSFER_SUBMITTING = 1 << 4, +- +- /* Transfer successfully submitted by backend */ +- USBI_TRANSFER_IN_FLIGHT = 1 << 5, ++ USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2, ++}; + +- /* Completion handler has run */ +- USBI_TRANSFER_COMPLETED = 1 << 6, ++enum usbi_transfer_timeout_flags { ++ /* Set by backend submit_transfer() if the OS handles timeout */ ++ USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0, + + /* The transfer timeout has been handled */ +- USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 7, ++ USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1, ++ ++ /* The transfer timeout was successfully processed */ ++ USBI_TRANSFER_TIMED_OUT = 1 << 2, + }; + + #define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ +diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c +index 6794509..736bcc1 100644 +--- a/libusb/os/darwin_usb.c ++++ b/libusb/os/darwin_usb.c +@@ -1471,7 +1471,7 @@ 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; ++ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, +@@ -1503,7 +1503,7 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) { + return LIBUSB_ERROR_NOT_FOUND; + } + +- itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; ++ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, +@@ -1631,7 +1631,7 @@ 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; ++ itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + /* all transfers in libusb-1.0 are async */ + +@@ -1780,7 +1780,7 @@ 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) ++ if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) + result = kIOUSBTransactionTimeout; + + switch (result) { +@@ -1797,7 +1797,7 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_ + return LIBUSB_TRANSFER_OVERFLOW; + case kIOUSBTransactionTimeout: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out"); +- itransfer->flags |= USBI_TRANSFER_TIMED_OUT; ++ itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + return LIBUSB_TRANSFER_TIMED_OUT; + default: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); +diff --git a/libusb/version_nano.h b/libusb/version_nano.h +index ea79210..3adf53e 100644 +--- a/libusb/version_nano.h ++++ b/libusb/version_nano.h +@@ -1 +1 @@ +-#define LIBUSB_NANO 11006 ++#define LIBUSB_NANO 11007 +-- +2.7.4 + diff --git a/SPECS/libusbx.spec b/SPECS/libusbx.spec index a4732bd..660a5e9 100644 --- a/SPECS/libusbx.spec +++ b/SPECS/libusbx.spec @@ -1,13 +1,16 @@ Summary: Library for accessing USB devices Name: libusbx -Version: 1.0.15 -Release: 4%{?dist} -Source0: http://downloads.sourceforge.net/libusbx/libusbx-%{version}.tar.bz2 -Patch1: 0001-linux_usbfs-Work-around-a-driver-binding-race-in-res.patch +Version: 1.0.20 +Release: 1%{?dist} +Source0: http://downloads.sourceforge.net/libusb/libusb-%{version}.tar.bz2 +# A couple of fixes from upstream +Patch1: 0001-core-Store-different-event-types-as-a-bitmask-within.patch +Patch2: 0002-API-Add-libusb_interrupt_event_handler-function.patch +Patch3: 0003-core-Refactor-code-related-to-transfer-flags-and-tim.patch License: LGPLv2+ Group: System Environment/Libraries -URL: http://sourceforge.net/apps/mediawiki/libusbx/ -BuildRequires: doxygen +URL: http://libusb.info/ +BuildRequires: systemd-devel doxygen Provides: libusb1 = %{version}-%{release} Obsoletes: libusb1 <= 1.0.9 @@ -47,13 +50,16 @@ This package contains API documentation for %{name}. %prep -%setup -q +%setup -q -n libusb-%{version} %patch1 -p1 +%patch2 -p1 +%patch3 -p1 %build %configure --disable-static --enable-examples-build -make %{?_smp_mflags} +# Parallel builds seem to be broken +make pushd doc make docs popd @@ -82,6 +88,11 @@ rm $RPM_BUILD_ROOT%{_libdir}/*.la %changelog +* Wed Jun 8 2016 Hans de Goede - 1.0.20-1 +- Upgrade to 1.0.20 +- Resolves: rhbz#1033092 +- Resolves: rhbz#1115797 + * Fri Jan 24 2014 Daniel Mach - 1.0.15-4 - Mass rebuild 2014-01-24