From a6785e56069a2c3f8c14237db2703cf7a6e9e292 Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Mon, 23 May 2022 16:19:19 -0600 Subject: [PATCH] update libusb to 1.0.26. (#877) The following warnings are resolved in 1.0.25, but the lateset release is 1.0.26. warning: 'kIOMasterPortDefault' is deprecated: first deprecated in macOS 12.0 [-Wdeprecated-declarations] --- libusb.cmake | 3 +- libusb.pri | 3 +- mac/libusb/README | 2 +- mac/libusb/core.c | 454 ++++++++++++------------ mac/libusb/descriptor.c | 26 +- mac/libusb/hotplug.c | 256 +++++++++----- mac/libusb/hotplug.h | 105 ------ mac/libusb/io.c | 146 ++++---- mac/libusb/libusb.h | 55 ++- mac/libusb/libusb.pro | 3 +- mac/libusb/libusbi.h | 153 +++++++- mac/libusb/os/darwin_usb.c | 668 +++++++++++++++++++++++++++-------- mac/libusb/os/darwin_usb.h | 52 ++- mac/libusb/os/events_posix.c | 6 +- mac/libusb/sync.c | 2 +- mac/libusb/version.h | 2 +- mac/libusb/version_nano.h | 2 +- 17 files changed, 1241 insertions(+), 697 deletions(-) delete mode 100644 mac/libusb/hotplug.h diff --git a/libusb.cmake b/libusb.cmake index 171193fe2..1b973f1fa 100644 --- a/libusb.cmake +++ b/libusb.cmake @@ -41,7 +41,6 @@ if(UNIX) mac/libusb/os/darwin_usb.c mac/libusb/os/events_posix.c mac/libusb/os/threads_posix.c - mac/libusb/hotplug.h mac/libusb/libusb.h mac/libusb/libusbi.h mac/libusb/version.h @@ -52,7 +51,7 @@ if(UNIX) mac/libusb/XCode/config.h ) target_include_directories(usb-1.0 PRIVATE mac/libusb/XCode PUBLIC mac/libusb) - target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation") + target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation" "-framework Security") list(APPEND LIBS usb-1.0) elseif(GPSBABEL_WITH_LIBUSB STREQUAL "custom") message(STATUS "libusb-1.0 is enabled but but must be manually configured.") diff --git a/libusb.pri b/libusb.pri index 9d589001f..4b8cc2ee7 100644 --- a/libusb.pri +++ b/libusb.pri @@ -32,7 +32,7 @@ unix { # TODO: It would be better to create an archive and link to it # to separate library build requirements from gpsbabel requirements. DEFINES += LIBUSB_H_INCLUDE=$$shell_quote(\"mac/libusb/libusb.h\") - LIBS += -lobjc -framework IOKit -framework CoreFoundation + LIBS += -lobjc -framework IOKit -framework CoreFoundation -framework Security INCLUDEPATH += mac/libusb \ mac/libusb/Xcode SOURCES += \ @@ -46,7 +46,6 @@ unix { mac/libusb/os/events_posix.c \ mac/libusb/os/threads_posix.c HEADERS += \ - mac/libusb/hotplug.h \ mac/libusb/libusb.h \ mac/libusb/libusbi.h \ mac/libusb/version.h \ diff --git a/mac/libusb/README b/mac/libusb/README index 77ef9b8a0..47aaf8106 100644 --- a/mac/libusb/README +++ b/mac/libusb/README @@ -1,4 +1,4 @@ -This is libusb-1.0.24 from https://libusb.info/. +This is libusb-1.0.26 from https://libusb.info/. Since we have such problems with people getting libusb successfully built - between the Universal Build issues and the fact that we have to work hard to go find where it's installed diff --git a/mac/libusb/core.c b/mac/libusb/core.c index 07d459c12..ec429b7cf 100644 --- a/mac/libusb/core.c +++ b/mac/libusb/core.c @@ -21,7 +21,6 @@ */ #include "libusbi.h" -#include "hotplug.h" #include "version.h" #ifdef __ANDROID__ @@ -33,17 +32,21 @@ #include #endif -struct libusb_context *usbi_default_context; static const struct libusb_version libusb_version_internal = { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, LIBUSB_RC, "http://libusb.info" }; -static int default_context_refcnt; -static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; static struct timespec timestamp_origin; #if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY) static libusb_log_cb log_handler; #endif +struct libusb_context *usbi_default_context; +struct libusb_context *usbi_fallback_context; +static int default_context_refcnt; +static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; +static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; + + usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER; struct list_head active_contexts_list; @@ -310,7 +313,8 @@ if (cfg != desired) * - libusb is able to send a packet of zero length to an endpoint simply by * submitting a transfer of zero length. * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET - * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux. + * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux, + * Darwin and Windows (WinUSB). */ /** @@ -577,7 +581,9 @@ libusb_free_device_list(list, 1); * * The libusb_get_device_list() function can be used to obtain a list of * devices currently connected to the system. This is known as device - * discovery. + * discovery. Devices can also be discovered with the hotplug mechanism, + * whereby a callback function registered with libusb_hotplug_register_callback() + * will be called when a device of interest is connected or disconnected. * * Just because you have a reference to a device does not mean it is * necessarily usable. The device may have been unplugged, you may not have @@ -608,7 +614,7 @@ libusb_free_device_list(list, 1); * * With the above information in mind, the process of opening a device can * be viewed as follows: - * -# Discover devices using libusb_get_device_list(). + * -# Discover devices using libusb_get_device_list() or libusb_hotplug_register_callback(). * -# Choose the device that you want to operate, and call libusb_open(). * -# Unref all devices in the discovered device list. * -# Free the discovered device list. @@ -633,7 +639,7 @@ libusb_free_device_list(list, 1); * which grows when required. it can be freed once discovery has completed, * eliminating the need for a list node in the libusb_device structure * itself. */ -#define DISCOVERED_DEVICES_SIZE_STEP 8 +#define DISCOVERED_DEVICES_SIZE_STEP 16 static struct discovered_devs *discovered_devs_alloc(void) { @@ -674,7 +680,7 @@ struct discovered_devs *discovered_devs_append( } /* exceeded capacity, need to grow */ - usbi_dbg("need to increase capacity"); + usbi_dbg(DEVICE_CTX(dev), "need to increase capacity"); capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP; /* can't use usbi_reallocf here because in failure cases it would * free the existing discdevs without unreferencing its devices. */ @@ -704,16 +710,14 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, if (!dev) return NULL; - usbi_mutex_init(&dev->lock); + usbi_atomic_store(&dev->refcnt, 1); dev->ctx = ctx; - dev->refcnt = 1; dev->session_data = session_id; dev->speed = LIBUSB_SPEED_UNKNOWN; - if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { - usbi_connect_device (dev); - } + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + usbi_connect_device(dev); return dev; } @@ -722,39 +726,26 @@ void usbi_connect_device(struct libusb_device *dev) { struct libusb_context *ctx = DEVICE_CTX(dev); - dev->attached = 1; + usbi_atomic_store(&dev->attached, 1); usbi_mutex_lock(&dev->ctx->usb_devs_lock); list_add(&dev->list, &dev->ctx->usb_devs); usbi_mutex_unlock(&dev->ctx->usb_devs_lock); - /* Signal that an event has occurred for this device if we support hotplug AND - * the hotplug message list is ready. This prevents an event from getting raised - * during initial enumeration. */ - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { - usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); - } + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); } void usbi_disconnect_device(struct libusb_device *dev) { struct libusb_context *ctx = DEVICE_CTX(dev); - usbi_mutex_lock(&dev->lock); - dev->attached = 0; - usbi_mutex_unlock(&dev->lock); + usbi_atomic_store(&dev->attached, 0); usbi_mutex_lock(&ctx->usb_devs_lock); list_del(&dev->list); usbi_mutex_unlock(&ctx->usb_devs_lock); - /* Signal that an event has occurred for this device if we support hotplug AND - * the hotplug message list is ready. This prevents an event from getting raised - * during initial enumeration. libusb_handle_events will take care of dereferencing - * the device. */ - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { - usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); - } + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); } /* Perform some final sanity checks on a newly discovered device. If this @@ -775,7 +766,7 @@ int usbi_sanitize_device(struct libusb_device *dev) usbi_err(DEVICE_CTX(dev), "too many configurations"); return LIBUSB_ERROR_IO; } else if (0 == num_configurations) { - usbi_dbg("zero configurations, maybe an unauthorized device"); + usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device"); } return 0; @@ -830,7 +821,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, int r = 0; ssize_t i, len; - usbi_dbg(" "); + usbi_dbg(ctx, " "); if (!discdevs) return LIBUSB_ERROR_NO_MEM; @@ -1173,9 +1164,11 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, DEFAULT_VISIBILITY libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) { - usbi_mutex_lock(&dev->lock); - dev->refcnt++; - usbi_mutex_unlock(&dev->lock); + long refcnt; + + refcnt = usbi_atomic_inc(&dev->refcnt); + assert(refcnt >= 2); + return dev; } @@ -1186,17 +1179,16 @@ libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) */ void API_EXPORTED libusb_unref_device(libusb_device *dev) { - int refcnt; + long refcnt; if (!dev) return; - usbi_mutex_lock(&dev->lock); - refcnt = --dev->refcnt; - usbi_mutex_unlock(&dev->lock); + refcnt = usbi_atomic_dec(&dev->refcnt); + assert(refcnt >= 0); if (refcnt == 0) { - usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address); + usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address); libusb_unref_device(dev->parent_dev); @@ -1208,7 +1200,6 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) usbi_disconnect_device(dev); } - usbi_mutex_destroy(&dev->lock); free(dev); } } @@ -1218,8 +1209,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * handle for the underlying device. The handle allows you to use libusb to * perform I/O on the device in question. * - * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY) - * before libusb_init if don't have authority to access the usb device directly. + * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before + * libusb_init() if you want to skip enumeration of USB devices. In particular, + * this might be needed on Android if you don't have authority to access USB + * devices in general. * * On Linux, the system device handle must be a valid file descriptor opened * on the device node. @@ -1233,6 +1226,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * * This is a non-blocking function; no requests are sent over the bus. * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \param ctx the context to operate on, or NULL for the default context * \param sys_dev the platform-specific system device handle * \param dev_handle output location for the returned device handle pointer. Only @@ -1251,7 +1246,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, size_t priv_size = usbi_backend.device_handle_priv_size; int r; - usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); + usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); ctx = usbi_get_context(ctx); @@ -1266,7 +1261,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev); if (r < 0) { - usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r); + usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r); usbi_mutex_destroy(&_dev_handle->lock); free(_dev_handle); return r; @@ -1306,11 +1301,11 @@ int API_EXPORTED libusb_open(libusb_device *dev, struct libusb_device_handle *_dev_handle; size_t priv_size = usbi_backend.device_handle_priv_size; int r; - usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); - if (!dev->attached) { + usbi_dbg(DEVICE_CTX(dev), "open %d.%d", dev->bus_number, dev->device_address); + + if (!usbi_atomic_load(&dev->attached)) return LIBUSB_ERROR_NO_DEVICE; - } _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size); if (!_dev_handle) @@ -1322,7 +1317,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, r = usbi_backend.open(_dev_handle); if (r < 0) { - usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r); + usbi_dbg(DEVICE_CTX(dev), "open %d.%d returns %d", dev->bus_number, dev->device_address, r); libusb_unref_device(dev); usbi_mutex_destroy(&_dev_handle->lock); free(_dev_handle); @@ -1428,7 +1423,7 @@ static void do_close(struct libusb_context *ctx, * just making sure that we don't attempt to process the transfer after * the device handle is invalid */ - usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed", + usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed", transfer, dev_handle); } usbi_mutex_unlock(&ctx->flying_transfers_lock); @@ -1462,9 +1457,9 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) if (!dev_handle) return; - usbi_dbg(" "); - ctx = HANDLE_CTX(dev_handle); + usbi_dbg(ctx, " "); + handling_events = usbi_handling_events(ctx); /* Similarly to libusb_open(), we want to interrupt all event handlers @@ -1546,27 +1541,28 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, { int r = LIBUSB_ERROR_NOT_SUPPORTED; uint8_t tmp = 0; + struct libusb_context *ctx = HANDLE_CTX(dev_handle); - usbi_dbg(" "); + usbi_dbg(ctx, " "); if (usbi_backend.get_configuration) r = usbi_backend.get_configuration(dev_handle, &tmp); if (r == LIBUSB_ERROR_NOT_SUPPORTED) { - usbi_dbg("falling back to control message"); + usbi_dbg(ctx, "falling back to control message"); r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000); if (r == 1) { r = 0; } else if (r == 0) { - usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?"); + usbi_err(ctx, "zero bytes returned in ctrl transfer?"); r = LIBUSB_ERROR_IO; } else { - usbi_dbg("control failed, error %d", r); + usbi_dbg(ctx, "control failed, error %d", r); } } if (r == 0) { - usbi_dbg("active config %u", tmp); + usbi_dbg(ctx, "active config %u", tmp); *config = (int)tmp; } @@ -1630,7 +1626,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, int configuration) { - usbi_dbg("configuration %d", configuration); + usbi_dbg(HANDLE_CTX(dev_handle), "configuration %d", configuration); if (configuration < -1 || configuration > (int)UINT8_MAX) return LIBUSB_ERROR_INVALID_PARAM; return usbi_backend.set_configuration(dev_handle, configuration); @@ -1669,11 +1665,11 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, { int r = 0; - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; usbi_mutex_lock(&dev_handle->lock); @@ -1713,7 +1709,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, { int r; - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; @@ -1756,19 +1752,19 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, int interface_number, int alternate_setting) { - usbi_dbg("interface %d altsetting %d", + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d altsetting %d", interface_number, alternate_setting); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX) return LIBUSB_ERROR_INVALID_PARAM; - usbi_mutex_lock(&dev_handle->lock); - if (!dev_handle->dev->attached) { + if (!usbi_atomic_load(&dev_handle->dev->attached)) { usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NO_DEVICE; } + usbi_mutex_lock(&dev_handle->lock); if (!(dev_handle->claimed_interfaces & (1U << interface_number))) { usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NOT_FOUND; @@ -1798,8 +1794,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, unsigned char endpoint) { - usbi_dbg("endpoint %x", endpoint); - if (!dev_handle->dev->attached) + usbi_dbg(HANDLE_CTX(dev_handle), "endpoint 0x%x", endpoint); + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; return usbi_backend.clear_halt(dev_handle, endpoint); @@ -1826,8 +1822,8 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, */ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) { - usbi_dbg(" "); - if (!dev_handle->dev->attached) + usbi_dbg(HANDLE_CTX(dev_handle), " "); + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.reset_device) @@ -1860,12 +1856,12 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, int num_endpoints) { - usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints); + usbi_dbg(HANDLE_CTX(dev_handle), "streams %u eps %d", (unsigned)num_streams, num_endpoints); if (!num_streams || !endpoints || num_endpoints <= 0) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.alloc_streams) @@ -1890,12 +1886,12 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) { - usbi_dbg("eps %d", num_endpoints); + usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints); if (!endpoints || num_endpoints <= 0) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.free_streams) @@ -1933,7 +1929,7 @@ DEFAULT_VISIBILITY unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, size_t length) { - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return NULL; if (usbi_backend.dev_mem_alloc) @@ -1979,12 +1975,12 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.kernel_driver_active) @@ -1997,7 +1993,7 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, * Detach a kernel driver from an interface. If successful, you will then be * able to claim the interface and perform I/O. * - * This functionality is not available on Darwin or Windows. + * This functionality is not available on Windows. * * Note that libusb itself also talks to the device through a special kernel * driver, if this driver is already attached to the device, this call will @@ -2017,12 +2013,12 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.detach_kernel_driver) @@ -2033,10 +2029,9 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, /** \ingroup libusb_dev * Re-attach an interface's kernel driver, which was previously detached - * using libusb_detach_kernel_driver(). This call is only effective on - * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms. + * using libusb_detach_kernel_driver(). * - * This functionality is not available on Darwin or Windows. + * This functionality is not available on Windows. * * \param dev_handle a device handle * \param interface_number the interface to attach the driver from @@ -2054,12 +2049,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.attach_kernel_driver) @@ -2130,6 +2125,8 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) * If ENABLE_DEBUG_LOGGING is defined then per context callback function will * never be called. * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \param ctx context on which to assign log handler, or NULL for the default * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested. * \param cb pointer to the callback function, or NULL to stop log @@ -2170,6 +2167,9 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, * Some options require one or more arguments to be provided. Consult each * option's documentation for specific requirements. * + * If the context ctx is NULL, the option will be added to a list of default + * options that will be applied to all subsequently created contexts. + * * Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106 * * \param ctx context on which to operate @@ -2185,40 +2185,63 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int API_EXPORTED libusb_set_option(libusb_context *ctx, enum libusb_option option, ...) { - int arg, r = LIBUSB_SUCCESS; + int arg = 0, r = LIBUSB_SUCCESS; va_list ap; - ctx = usbi_get_context(ctx); - va_start(ap, option); - switch (option) { - case LIBUSB_OPTION_LOG_LEVEL: + if (LIBUSB_OPTION_LOG_LEVEL == option) { arg = va_arg(ap, int); if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) { r = LIBUSB_ERROR_INVALID_PARAM; - break; } + } + va_end(ap); + + if (LIBUSB_SUCCESS != r) { + return r; + } + + if (option >= LIBUSB_OPTION_MAX) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (NULL == ctx) { + usbi_mutex_static_lock(&default_context_lock); + default_context_options[option].is_set = 1; + if (LIBUSB_OPTION_LOG_LEVEL == option) { + default_context_options[option].arg.ival = arg; + } + usbi_mutex_static_unlock(&default_context_lock); + } + + ctx = usbi_get_context(ctx); + if (NULL == ctx) { + return LIBUSB_SUCCESS; + } + + switch (option) { + case LIBUSB_OPTION_LOG_LEVEL: #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) if (!ctx->debug_fixed) ctx->debug = (enum libusb_log_level)arg; #endif break; - /* Handle all backend-specific options here */ + /* Handle all backend-specific options here */ case LIBUSB_OPTION_USE_USBDK: - case LIBUSB_OPTION_WEAK_AUTHORITY: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: if (usbi_backend.set_option) - r = usbi_backend.set_option(ctx, option, ap); - else - r = LIBUSB_ERROR_NOT_SUPPORTED; + return usbi_backend.set_option(ctx, option, ap); + + return LIBUSB_ERROR_NOT_SUPPORTED; break; + case LIBUSB_OPTION_MAX: default: - r = LIBUSB_ERROR_INVALID_PARAM; + return LIBUSB_ERROR_INVALID_PARAM; } - va_end(ap); - return r; + return LIBUSB_SUCCESS;; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) @@ -2249,113 +2272,125 @@ static enum libusb_log_level get_env_debug_level(void) * context will be created. If there was already a default context, it will * be reused (and nothing will be initialized/reinitialized). * - * \param context Optional output location for context pointer. + * \param ctx Optional output location for context pointer. * Only valid on return code 0. * \returns 0 on success, or a LIBUSB_ERROR code on failure * \see libusb_contexts */ -int API_EXPORTED libusb_init(libusb_context **context) +int API_EXPORTED libusb_init(libusb_context **ctx) { - struct libusb_device *dev, *next; size_t priv_size = usbi_backend.context_priv_size; - struct libusb_context *ctx; - static int first_init = 1; - int r = 0; + struct libusb_context *_ctx; + int r; usbi_mutex_static_lock(&default_context_lock); - if (!timestamp_origin.tv_sec) - usbi_get_monotonic_time(×tamp_origin); - - if (!context && usbi_default_context) { - usbi_dbg("reusing default context"); + if (!ctx && default_context_refcnt > 0) { + usbi_dbg(usbi_default_context, "reusing default context"); default_context_refcnt++; usbi_mutex_static_unlock(&default_context_lock); return 0; } - ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size); - if (!ctx) { - r = LIBUSB_ERROR_NO_MEM; - goto err_unlock; + /* check for first init */ + if (!active_contexts_list.next) { + list_init(&active_contexts_list); + usbi_get_monotonic_time(×tamp_origin); + } + + _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size); + if (!_ctx) { + usbi_mutex_static_unlock(&default_context_lock); + return LIBUSB_ERROR_NO_MEM; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - ctx->debug = get_env_debug_level(); - if (ctx->debug != LIBUSB_LOG_LEVEL_NONE) - ctx->debug_fixed = 1; + if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { + _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; + } else { + _ctx->debug = get_env_debug_level(); + } + if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE) + _ctx->debug_fixed = 1; #endif - /* default context should be initialized before calling usbi_dbg */ - if (!usbi_default_context) { - usbi_default_context = ctx; - default_context_refcnt++; - usbi_dbg("created default context"); + usbi_mutex_init(&_ctx->usb_devs_lock); + usbi_mutex_init(&_ctx->open_devs_lock); + list_init(&_ctx->usb_devs); + list_init(&_ctx->open_devs); + + /* apply default options to all new contexts */ + for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) { + if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) { + continue; + } + r = libusb_set_option(_ctx, option); + if (LIBUSB_SUCCESS != r) + goto err_free_ctx; + } + + /* default context must be initialized before calling usbi_dbg */ + if (!ctx) { + usbi_default_context = _ctx; + default_context_refcnt = 1; + usbi_dbg(usbi_default_context, "created default context"); } - usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor, + usbi_dbg(_ctx, "libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor, libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc); - usbi_mutex_init(&ctx->usb_devs_lock); - usbi_mutex_init(&ctx->open_devs_lock); - usbi_mutex_init(&ctx->hotplug_cbs_lock); - list_init(&ctx->usb_devs); - list_init(&ctx->open_devs); - list_init(&ctx->hotplug_cbs); - ctx->next_hotplug_cb_handle = 1; + r = usbi_io_init(_ctx); + if (r < 0) + goto err_free_ctx; usbi_mutex_static_lock(&active_contexts_lock); - if (first_init) { - first_init = 0; - list_init(&active_contexts_list); - } - list_add (&ctx->list, &active_contexts_list); + list_add(&_ctx->list, &active_contexts_list); usbi_mutex_static_unlock(&active_contexts_lock); if (usbi_backend.init) { - r = usbi_backend.init(ctx); + r = usbi_backend.init(_ctx); if (r) - goto err_free_ctx; + goto err_io_exit; } - r = usbi_io_init(ctx); - if (r < 0) - goto err_backend_exit; + /* Initialize hotplug after the initial enumeration is done. */ + usbi_hotplug_init(_ctx); - usbi_mutex_static_unlock(&default_context_lock); + if (ctx) { + *ctx = _ctx; - if (context) - *context = ctx; + if (!usbi_fallback_context) { + usbi_fallback_context = _ctx; + usbi_warn(usbi_fallback_context, "installing new context as implicit default"); + } + } - return 0; + usbi_mutex_static_unlock(&default_context_lock); -err_backend_exit: - if (usbi_backend.exit) - usbi_backend.exit(ctx); -err_free_ctx: - if (ctx == usbi_default_context) { - usbi_default_context = NULL; - default_context_refcnt--; - } + return 0; +err_io_exit: usbi_mutex_static_lock(&active_contexts_lock); - list_del(&ctx->list); + list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); - usbi_mutex_lock(&ctx->usb_devs_lock); - for_each_device_safe(ctx, dev, next) { - list_del(&dev->list); - libusb_unref_device(dev); + usbi_hotplug_exit(_ctx); + usbi_io_exit(_ctx); + +err_free_ctx: + if (!ctx) { + /* clear default context that was not fully initialized */ + usbi_default_context = NULL; + default_context_refcnt = 0; } - usbi_mutex_unlock(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->open_devs_lock); - usbi_mutex_destroy(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->hotplug_cbs_lock); + usbi_mutex_destroy(&_ctx->open_devs_lock); + usbi_mutex_destroy(&_ctx->usb_devs_lock); + + free(_ctx); - free(ctx); -err_unlock: usbi_mutex_static_unlock(&default_context_lock); + return r; } @@ -2366,90 +2401,66 @@ int API_EXPORTED libusb_init(libusb_context **context) */ void API_EXPORTED libusb_exit(libusb_context *ctx) { - struct libusb_device *dev, *next; - struct timeval tv = { 0, 0 }; - int destroying_default_context = 0; - - usbi_dbg(" "); + struct libusb_context *_ctx; + struct libusb_device *dev; - ctx = usbi_get_context(ctx); + usbi_mutex_static_lock(&default_context_lock); /* if working with default context, only actually do the deinitialization * if we're the last user */ - usbi_mutex_static_lock(&default_context_lock); - if (ctx == usbi_default_context) { + if (!ctx) { if (!usbi_default_context) { - usbi_dbg("no default context, not initialized?"); + usbi_dbg(ctx, "no default context, not initialized?"); usbi_mutex_static_unlock(&default_context_lock); return; } if (--default_context_refcnt > 0) { - usbi_dbg("not destroying default context"); + usbi_dbg(ctx, "not destroying default context"); usbi_mutex_static_unlock(&default_context_lock); return; } - usbi_dbg("destroying default context"); - /* - * Setting this flag without unlocking the default context, as - * we are actually destroying the default context. - * usbi_default_context is not set to NULL yet, as all activities - * would only stop after usbi_backend->exit() returns. - */ - destroying_default_context = 1; + usbi_dbg(ctx, "destroying default context"); + _ctx = usbi_default_context; } else { - /* Unlock default context, as we're not modifying it. */ - usbi_mutex_static_unlock(&default_context_lock); + usbi_dbg(ctx, " "); + _ctx = ctx; } usbi_mutex_static_lock(&active_contexts_lock); - list_del(&ctx->list); + list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { - usbi_hotplug_deregister(ctx, 1); - - /* - * Ensure any pending unplug events are read from the hotplug - * pipe. The usb_device-s hold in the events are no longer part - * of usb_devs, but the events still hold a reference! - * - * Note we don't do this if the application has left devices - * open (which implies a buggy app) to avoid packet completion - * handlers running when the app does not expect them to run. - */ - if (list_empty(&ctx->open_devs)) - libusb_handle_events_timeout(ctx, &tv); + if (usbi_backend.exit) + usbi_backend.exit(_ctx); - usbi_mutex_lock(&ctx->usb_devs_lock); - for_each_device_safe(ctx, dev, next) { - list_del(&dev->list); - libusb_unref_device(dev); - } - usbi_mutex_unlock(&ctx->usb_devs_lock); - } + if (!ctx) + usbi_default_context = NULL; + if (ctx == usbi_fallback_context) + usbi_fallback_context = NULL; - /* a few sanity checks. don't bother with locking because unless - * there is an application bug, nobody will be accessing these. */ - if (!list_empty(&ctx->usb_devs)) - usbi_warn(ctx, "some libusb_devices were leaked"); - if (!list_empty(&ctx->open_devs)) - usbi_warn(ctx, "application left some devices open"); + usbi_mutex_static_unlock(&default_context_lock); - usbi_io_exit(ctx); - if (usbi_backend.exit) - usbi_backend.exit(ctx); + /* Don't bother with locking after this point because unless there is + * an application bug, nobody will be accessing the context. */ - usbi_mutex_destroy(&ctx->open_devs_lock); - usbi_mutex_destroy(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->hotplug_cbs_lock); - free(ctx); + usbi_hotplug_exit(_ctx); + usbi_io_exit(_ctx); - if (destroying_default_context) { - usbi_default_context = NULL; - usbi_mutex_static_unlock(&default_context_lock); + for_each_device(_ctx, dev) { + usbi_warn(_ctx, "device %d.%d still referenced", + dev->bus_number, dev->device_address); + DEVICE_CTX(dev) = NULL; } + + if (!list_empty(&_ctx->open_devs)) + usbi_warn(_ctx, "application left some devices open"); + + usbi_mutex_destroy(&_ctx->open_devs_lock); + usbi_mutex_destroy(&_ctx->usb_devs_lock); + + free(_ctx); } /** \ingroup libusb_misc @@ -2574,7 +2585,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, #else enum libusb_log_level ctx_level; - ctx = usbi_get_context(ctx); + ctx = ctx ? ctx : usbi_default_context; + ctx = ctx ? ctx : usbi_fallback_context; if (ctx) ctx_level = ctx->debug; else diff --git a/mac/libusb/descriptor.c b/mac/libusb/descriptor.c index ecd944180..253ef1c31 100644 --- a/mac/libusb/descriptor.c +++ b/mac/libusb/descriptor.c @@ -140,7 +140,7 @@ static int parse_endpoint(struct libusb_context *ctx, header->bDescriptorType == LIBUSB_DT_DEVICE) break; - usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType); + usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); buffer += header->bLength; size -= header->bLength; parsed += header->bLength; @@ -414,7 +414,7 @@ static int parse_configuration(struct libusb_context *ctx, header->bDescriptorType == LIBUSB_DT_DEVICE) break; - usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType); + usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); buffer += header->bLength; size -= header->bLength; } @@ -531,7 +531,7 @@ static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { - usbi_dbg(" "); + usbi_dbg(DEVICE_CTX(dev), " "); static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE, "struct libusb_device_descriptor is not expected size"); *desc = dev->device_descriptor; @@ -601,7 +601,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, uint8_t *buf; int r; - usbi_dbg("index %u", config_index); + usbi_dbg(DEVICE_CTX(dev), "index %u", config_index); if (config_index >= dev->device_descriptor.bNumConfigurations) return LIBUSB_ERROR_NOT_FOUND; @@ -656,7 +656,7 @@ int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); } - usbi_dbg("value %u", bConfigurationValue); + usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue); for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { union usbi_config_desc_buf _config; @@ -850,23 +850,24 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, uint16_t bos_len; uint8_t *bos_data; int r; + struct libusb_context *ctx = HANDLE_CTX(dev_handle); /* Read the BOS. This generates 2 requests on the bus, * one for the header, and one for the full BOS */ r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf)); if (r < 0) { if (r != LIBUSB_ERROR_PIPE) - usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); + usbi_err(ctx, "failed to read BOS (%d)", r); return r; } if (r < LIBUSB_DT_BOS_SIZE) { - usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d", + usbi_err(ctx, "short BOS read %d/%d", r, LIBUSB_DT_BOS_SIZE); return LIBUSB_ERROR_IO; } bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength); - usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities", + usbi_dbg(ctx, "found BOS descriptor: size %u bytes, %u capabilities", bos_len, _bos.desc.bNumDeviceCaps); bos_data = calloc(1, bos_len); if (!bos_data) @@ -875,11 +876,10 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len); if (r >= 0) { if (r != (int)bos_len) - usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u", - r, bos_len); + usbi_warn(ctx, "short BOS read %d/%u", r, bos_len); r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r); } else { - usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); + usbi_err(ctx, "failed to read BOS (%d)", r); } free(bos_data); @@ -1109,7 +1109,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) return LIBUSB_ERROR_IO; else if (str.desc.bLength & 1) - usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength); + usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength); langid = libusb_le16_to_cpu(str.desc.wData[0]); r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf)); @@ -1120,7 +1120,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) return LIBUSB_ERROR_IO; else if ((str.desc.bLength & 1) || str.desc.bLength != r) - usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength); + usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r); di = 0; for (si = 2; si < str.desc.bLength; si += 2) { diff --git a/mac/libusb/hotplug.c b/mac/libusb/hotplug.c index e3e5e76e6..6b743c704 100644 --- a/mac/libusb/hotplug.c +++ b/mac/libusb/hotplug.c @@ -1,7 +1,7 @@ /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* * Hotplug functions for libusb - * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2012-2021 Nathan Hjelm * Copyright © 2012-2013 Peter Stuge * * This library is free software; you can redistribute it and/or @@ -20,7 +20,6 @@ */ #include "libusbi.h" -#include "hotplug.h" /** * @defgroup libusb_hotplug Device hotplug event notification @@ -63,7 +62,7 @@ * A hotplug event can listen for either or both of these events. * * Note: If you receive notification that a device has left and you have any - * a libusb_device_handles for the device it is up to you to call libusb_close() + * libusb_device_handles for the device it is up to you to call libusb_close() * on each device handle to free up any remaining resources associated with the device. * Once a device has left any libusb_device_handle associated with the device * are invalid and will remain so even if the device comes back. @@ -144,15 +143,79 @@ int main (void) { */ #define VALID_HOTPLUG_EVENTS \ - (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \ - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) #define VALID_HOTPLUG_FLAGS \ - (LIBUSB_HOTPLUG_ENUMERATE) + (LIBUSB_HOTPLUG_ENUMERATE) -static int usbi_hotplug_match_cb(struct libusb_context *ctx, - struct libusb_device *dev, libusb_hotplug_event event, - struct libusb_hotplug_callback *hotplug_cb) +void usbi_hotplug_init(struct libusb_context *ctx) +{ + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + return; + + usbi_mutex_init(&ctx->hotplug_cbs_lock); + list_init(&ctx->hotplug_cbs); + ctx->next_hotplug_cb_handle = 1; + usbi_atomic_store(&ctx->hotplug_ready, 1); +} + +void usbi_hotplug_exit(struct libusb_context *ctx) +{ + struct usbi_hotplug_callback *hotplug_cb, *next_cb; + struct usbi_hotplug_message *msg; + struct libusb_device *dev, *next_dev; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + return; + + if (!usbi_atomic_load(&ctx->hotplug_ready)) + return; + + /* free all registered hotplug callbacks */ + for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + + /* free all pending hotplug messages */ + while (!list_empty(&ctx->hotplug_msgs)) { + msg = list_first_entry(&ctx->hotplug_msgs, struct usbi_hotplug_message, list); + + /* if the device left, the message holds a reference + * and we must drop it */ + if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + libusb_unref_device(msg->device); + + list_del(&msg->list); + free(msg); + } + + /* free all discovered devices. due to parent references loop until no devices are freed. */ + for_each_device_safe(ctx, dev, next_dev) { + /* remove the device from the usb_devs list only if there are no + * references held, otherwise leave it on the list so that a + * warning message will be shown */ + if (usbi_atomic_load(&dev->refcnt) == 1) { + list_del(&dev->list); + } + if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) { + /* the parent was before this device in the list and will be released. + remove it from the list. this is safe as parent_dev can not be + equal to next_dev. */ + assert (dev->parent_dev != next_dev); + list_del(&dev->parent_dev->list); + } + libusb_unref_device(dev); + } + + usbi_mutex_destroy(&ctx->hotplug_cbs_lock); +} + +static int usbi_hotplug_match_cb(struct libusb_device *dev, + libusb_hotplug_event event, struct usbi_hotplug_callback *hotplug_cb) { if (!(hotplug_cb->flags & event)) { return 0; @@ -173,71 +236,100 @@ static int usbi_hotplug_match_cb(struct libusb_context *ctx, return 0; } - return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data); -} - -void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event) -{ - struct libusb_hotplug_callback *hotplug_cb, *next; - int ret; - - usbi_mutex_lock(&ctx->hotplug_cbs_lock); - - for_each_hotplug_cb_safe(ctx, hotplug_cb, next) { - if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) { - /* process deregistration in usbi_hotplug_deregister() */ - continue; - } - - usbi_mutex_unlock(&ctx->hotplug_cbs_lock); - ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb); - usbi_mutex_lock(&ctx->hotplug_cbs_lock); - - if (ret) { - list_del(&hotplug_cb->list); - free(hotplug_cb); - } - } - - usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + return hotplug_cb->cb(DEVICE_CTX(dev), dev, event, hotplug_cb->user_data); } void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event) { - struct libusb_hotplug_message *message = calloc(1, sizeof(*message)); + struct usbi_hotplug_message *msg; unsigned int event_flags; - if (!message) { + /* Only generate a notification if hotplug is ready. This prevents hotplug + * notifications from being generated during initial enumeration or if the + * backend does not support hotplug. */ + if (!usbi_atomic_load(&ctx->hotplug_ready)) + return; + + msg = calloc(1, sizeof(*msg)); + if (!msg) { usbi_err(ctx, "error allocating hotplug message"); return; } - message->event = event; - message->device = dev; + msg->event = event; + msg->device = dev; /* Take the event data lock and add this message to the list. * Only signal an event if there are no prior pending events. */ usbi_mutex_lock(&ctx->event_data_lock); event_flags = ctx->event_flags; ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING; - list_add_tail(&message->list, &ctx->hotplug_msgs); + list_add_tail(&msg->list, &ctx->hotplug_msgs); if (!event_flags) usbi_signal_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); } +void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs) +{ + struct usbi_hotplug_callback *hotplug_cb, *next_cb; + struct usbi_hotplug_message *msg; + int r; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + /* dispatch all pending hotplug messages */ + while (!list_empty(hotplug_msgs)) { + msg = list_first_entry(hotplug_msgs, struct usbi_hotplug_message, list); + + for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) { + /* skip callbacks that have unregistered */ + if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) + continue; + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + r = usbi_hotplug_match_cb(msg->device, msg->event, hotplug_cb); + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + if (r) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + } + + /* if the device left, the message holds a reference + * and we must drop it */ + if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + libusb_unref_device(msg->device); + + list_del(&msg->list); + free(msg); + } + + /* free any callbacks that have unregistered */ + for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) { + if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) { + usbi_dbg(ctx, "freeing hotplug cb %p with handle %d", + hotplug_cb, hotplug_cb->handle); + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); +} + int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, int events, int flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *callback_handle) { - struct libusb_hotplug_callback *new_callback; + struct usbi_hotplug_callback *hotplug_cb; /* check for sane values */ - if ((!events || (~VALID_HOTPLUG_EVENTS & events)) || + if (!events || (~VALID_HOTPLUG_EVENTS & events) || (~VALID_HOTPLUG_FLAGS & flags) || (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) || (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) || @@ -247,47 +339,45 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, } /* check for hotplug support */ - if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) return LIBUSB_ERROR_NOT_SUPPORTED; - } ctx = usbi_get_context(ctx); - new_callback = calloc(1, sizeof(*new_callback)); - if (!new_callback) { + hotplug_cb = calloc(1, sizeof(*hotplug_cb)); + if (!hotplug_cb) return LIBUSB_ERROR_NO_MEM; - } - new_callback->flags = (uint8_t)events; + hotplug_cb->flags = (uint8_t)events; if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) { - new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID; - new_callback->vendor_id = (uint16_t)vendor_id; + hotplug_cb->flags |= USBI_HOTPLUG_VENDOR_ID_VALID; + hotplug_cb->vendor_id = (uint16_t)vendor_id; } if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) { - new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID; - new_callback->product_id = (uint16_t)product_id; + hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID; + hotplug_cb->product_id = (uint16_t)product_id; } if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) { - new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID; - new_callback->dev_class = (uint8_t)dev_class; + hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID; + hotplug_cb->dev_class = (uint8_t)dev_class; } - new_callback->cb = cb_fn; - new_callback->user_data = user_data; + hotplug_cb->cb = cb_fn; + hotplug_cb->user_data = user_data; usbi_mutex_lock(&ctx->hotplug_cbs_lock); /* protect the handle by the context hotplug lock */ - new_callback->handle = ctx->next_hotplug_cb_handle++; + hotplug_cb->handle = ctx->next_hotplug_cb_handle++; /* handle the unlikely case of overflow */ if (ctx->next_hotplug_cb_handle < 0) ctx->next_hotplug_cb_handle = 1; - list_add(&new_callback->list, &ctx->hotplug_cbs); + list_add(&hotplug_cb->list, &ctx->hotplug_cbs); usbi_mutex_unlock(&ctx->hotplug_cbs_lock); - usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle); + usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle); if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) { ssize_t i, len; @@ -295,23 +385,21 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, len = libusb_get_device_list(ctx, &devs); if (len < 0) { - libusb_hotplug_deregister_callback(ctx, - new_callback->handle); + libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle); return (int)len; } for (i = 0; i < len; i++) { - usbi_hotplug_match_cb(ctx, devs[i], + usbi_hotplug_match_cb(devs[i], LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, - new_callback); + hotplug_cb); } libusb_free_device_list(devs, 1); } - if (callback_handle) - *callback_handle = new_callback->handle; + *callback_handle = hotplug_cb->handle; return LIBUSB_SUCCESS; } @@ -319,24 +407,24 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle) { - struct libusb_hotplug_callback *hotplug_cb; + struct usbi_hotplug_callback *hotplug_cb; int deregistered = 0; /* check for hotplug support */ - if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) return; - } - usbi_dbg("deregister hotplug cb %d", callback_handle); + usbi_dbg(ctx, "deregister hotplug cb %d", callback_handle); ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->hotplug_cbs_lock); for_each_hotplug_cb(ctx, hotplug_cb) { if (callback_handle == hotplug_cb->handle) { - /* Mark this callback for deregistration */ + /* mark this callback for deregistration */ hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE; deregistered = 1; + break; } } usbi_mutex_unlock(&ctx->hotplug_cbs_lock); @@ -357,15 +445,14 @@ DEFAULT_VISIBILITY void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle) { - struct libusb_hotplug_callback *hotplug_cb; + struct usbi_hotplug_callback *hotplug_cb; void *user_data = NULL; /* check for hotplug support */ - if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) return NULL; - } - usbi_dbg("get hotplug user data %d", callback_handle); + usbi_dbg(ctx, "get hotplug cb %d user data", callback_handle); ctx = usbi_get_context(ctx); @@ -373,25 +460,10 @@ void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, for_each_hotplug_cb(ctx, hotplug_cb) { if (callback_handle == hotplug_cb->handle) { user_data = hotplug_cb->user_data; + break; } } usbi_mutex_unlock(&ctx->hotplug_cbs_lock); return user_data; } - -void usbi_hotplug_deregister(struct libusb_context *ctx, int forced) -{ - struct libusb_hotplug_callback *hotplug_cb, *next; - - usbi_mutex_lock(&ctx->hotplug_cbs_lock); - for_each_hotplug_cb_safe(ctx, hotplug_cb, next) { - if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) { - usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb, - hotplug_cb->handle); - list_del(&hotplug_cb->list); - free(hotplug_cb); - } - } - usbi_mutex_unlock(&ctx->hotplug_cbs_lock); -} diff --git a/mac/libusb/hotplug.h b/mac/libusb/hotplug.h deleted file mode 100644 index 161f7e5fc..000000000 --- a/mac/libusb/hotplug.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ -/* - * Hotplug support for libusb - * Copyright © 2012-2013 Nathan Hjelm - * Copyright © 2012-2013 Peter Stuge - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef USBI_HOTPLUG_H -#define USBI_HOTPLUG_H - -#include "libusbi.h" - -enum usbi_hotplug_flags { - /* This callback is interested in device arrivals */ - USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, - - /* This callback is interested in device removals */ - USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, - - /* IMPORTANT: The values for the below entries must start *after* - * the highest value of the above entries!!! - */ - - /* The vendor_id field is valid for matching */ - USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3), - - /* The product_id field is valid for matching */ - USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4), - - /* The dev_class field is valid for matching */ - USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5), - - /* This callback has been unregistered and needs to be freed */ - USBI_HOTPLUG_NEEDS_FREE = (1U << 6), -}; - -/** \ingroup hotplug - * The hotplug callback structure. The user populates this structure with - * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback() - * to receive notification of hotplug events. - */ -struct libusb_hotplug_callback { - /** Flags that control how this callback behaves */ - uint8_t flags; - - /** Vendor ID to match (if flags says this is valid) */ - uint16_t vendor_id; - - /** Product ID to match (if flags says this is valid) */ - uint16_t product_id; - - /** Device class to match (if flags says this is valid) */ - uint8_t dev_class; - - /** Callback function to invoke for matching event/device */ - libusb_hotplug_callback_fn cb; - - /** Handle for this callback (used to match on deregister) */ - libusb_hotplug_callback_handle handle; - - /** User data that will be passed to the callback function */ - void *user_data; - - /** List this callback is registered in (ctx->hotplug_cbs) */ - struct list_head list; -}; - -struct libusb_hotplug_message { - /** The hotplug event that occurred */ - libusb_hotplug_event event; - - /** The device for which this hotplug event occurred */ - struct libusb_device *device; - - /** List this message is contained in (ctx->hotplug_msgs) */ - struct list_head list; -}; - -#define for_each_hotplug_cb(ctx, c) \ - for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) - -#define for_each_hotplug_cb_safe(ctx, c, n) \ - for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) - -void usbi_hotplug_deregister(struct libusb_context *ctx, int forced); -void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); -void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); - -#endif diff --git a/mac/libusb/io.c b/mac/libusb/io.c index 0e960ddfd..9e3146cf9 100644 --- a/mac/libusb/io.c +++ b/mac/libusb/io.c @@ -22,7 +22,6 @@ */ #include "libusbi.h" -#include "hotplug.h" /** * \page libusb_io Synchronous and asynchronous device I/O @@ -73,7 +72,7 @@ * a single function call. When the function call returns, the transfer has * completed and you can parse the results. * - * If you have used the libusb-0.1 before, this I/O style will seem familiar to + * If you have used libusb-0.1 before, this I/O style will seem familiar to * you. libusb-0.1 only offered a synchronous interface. * * In our input device example, to read button presses you might write code @@ -1181,12 +1180,12 @@ int usbi_io_init(struct libusb_context *ctx) #ifdef HAVE_OS_TIMER r = usbi_create_timer(&ctx->timer); if (r == 0) { - usbi_dbg("using timer for timeouts"); + usbi_dbg(ctx, "using timer for timeouts"); r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS); if (r < 0) goto err_destroy_timer; } else { - usbi_dbg("timer not available for timeouts"); + usbi_dbg(ctx, "timer not available for timeouts"); } #endif @@ -1310,7 +1309,6 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( itransfer->priv = ptr; usbi_mutex_init(&itransfer->lock); transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - usbi_dbg("transfer %p", transfer); return transfer; } @@ -1340,12 +1338,14 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) if (!transfer) return; - usbi_dbg("transfer %p", transfer); + usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer); if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(transfer->buffer); itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); usbi_mutex_destroy(&itransfer->lock); + if (itransfer->dev) + libusb_unref_device(itransfer->dev); priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); ptr = (unsigned char *)itransfer - priv_size; @@ -1376,12 +1376,12 @@ static int arm_timer_for_next_timeout(struct libusb_context *ctx) /* act on first transfer that has not already been handled */ if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { - usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); return usbi_arm_timer(&ctx->timer, cur_ts); } } - usbi_dbg("no timeouts, disarming timer"); + usbi_dbg(ctx, "no timeouts, disarming timer"); return usbi_disarm_timer(&ctx->timer); } #else @@ -1438,7 +1438,7 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) { /* if this transfer has the lowest timeout of all active transfers, * rearm the timer with this transfer's timeout */ - usbi_dbg("arm timer for timeout in %ums (first in line)", + usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); r = usbi_arm_timer(&ctx->timer, timeout); } @@ -1491,10 +1491,16 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) { struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); - struct libusb_context *ctx = TRANSFER_CTX(transfer); + struct libusb_context *ctx; int r; - usbi_dbg("transfer %p", transfer); + assert(transfer->dev_handle); + if (itransfer->dev) + libusb_unref_device(itransfer->dev); + itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); + + ctx = HANDLE_CTX(transfer->dev_handle); + usbi_dbg(ctx, "transfer %p", transfer); /* * Important note on locking, this function takes / releases locks @@ -1546,15 +1552,13 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) } /* * We must release the flying transfers lock here, because with - * some backends the submit_transfer method is synchroneous. + * some backends the submit_transfer method is synchronous. */ usbi_mutex_unlock(&ctx->flying_transfers_lock); r = usbi_backend.submit_transfer(itransfer); if (r == LIBUSB_SUCCESS) { 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); @@ -1572,6 +1576,26 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED * "LIBUSB_TRANSFER_CANCELLED." * + * This function behaves differently on Darwin-based systems (macOS and iOS): + * + * - Calling this function for one transfer will cause all transfers on the + * same endpoint to be cancelled. Your callback function will be invoked with + * a transfer status of + * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED + * "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled. + + * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request + * for the transfer's endpoint. If the device does not handle this request + * correctly, the data toggle bits for the endpoint can be left out of sync + * between host and device, which can have unpredictable results when the + * next data is sent on the endpoint, including data being silently lost. + * A call to \ref libusb_clear_halt will not resolve this situation, since + * that function uses the same request. Therefore, if your program runs on + * Darwin and uses a device that does not correctly implement + * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel + * transfers when followed by a device reset using + * \ref libusb_reset_device. + * * \param transfer the transfer to cancel * \returns 0 on success * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, @@ -1582,9 +1606,10 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) { struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); int r; - usbi_dbg("transfer %p", transfer ); + usbi_dbg(ctx, "transfer %p", transfer ); usbi_mutex_lock(&itransfer->lock); if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { @@ -1595,10 +1620,9 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) if (r < 0) { if (r != LIBUSB_ERROR_NOT_FOUND && r != LIBUSB_ERROR_NO_DEVICE) - usbi_err(TRANSFER_CTX(transfer), - "cancel transfer failed error %d", r); + usbi_err(ctx, "cancel transfer failed error %d", r); else - usbi_dbg("cancel transfer failed error %d", r); + usbi_dbg(ctx, "cancel transfer failed error %d", r); if (r == LIBUSB_ERROR_NO_DEVICE) itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; @@ -1661,13 +1685,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct libusb_device_handle *dev_handle = transfer->dev_handle; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); uint8_t flags; int r; r = remove_from_flying_list(itransfer); if (r < 0) - usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout"); + usbi_err(ctx, "failed to set timer for next timeout"); usbi_mutex_lock(&itransfer->lock); itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT; @@ -1679,7 +1703,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) rqlen -= LIBUSB_CONTROL_SETUP_SIZE; if (rqlen != itransfer->transferred) { - usbi_dbg("interpreting short transfer as error"); + usbi_dbg(ctx, "interpreting short transfer as error"); status = LIBUSB_TRANSFER_ERROR; } } @@ -1687,14 +1711,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, flags = transfer->flags; transfer->status = status; transfer->actual_length = itransfer->transferred; - usbi_dbg("transfer %p has callback %p", transfer, transfer->callback); + usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback); if (transfer->callback) transfer->callback(transfer); /* transfer might have been freed by the above call, do not use from * this point. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) libusb_free_transfer(transfer); - libusb_unref_device(dev_handle->dev); return r; } @@ -1715,7 +1738,7 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) /* if the URB was cancelled due to timeout, report timeout to the user */ if (timed_out) { - usbi_dbg("detected timeout cancellation"); + usbi_dbg(ctx, "detected timeout cancellation"); return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT); } @@ -1728,10 +1751,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) * function will be called the next time an event handler runs. */ void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) { - libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; + struct libusb_device *dev = itransfer->dev; - if (dev_handle) { - struct libusb_context *ctx = HANDLE_CTX(dev_handle); + if (dev) { + struct libusb_context *ctx = DEVICE_CTX(dev); unsigned int event_flags; usbi_mutex_lock(&ctx->event_data_lock); @@ -1776,7 +1799,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) ru = ctx->device_close; usbi_mutex_unlock(&ctx->event_data_lock); if (ru) { - usbi_dbg("someone else is closing a device"); + usbi_dbg(ctx, "someone else is closing a device"); return 1; } @@ -1868,7 +1891,7 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) r = ctx->device_close; usbi_mutex_unlock(&ctx->event_data_lock); if (r) { - usbi_dbg("someone else is closing a device"); + usbi_dbg(ctx, "someone else is closing a device"); return 0; } @@ -1897,7 +1920,7 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) r = ctx->device_close; usbi_mutex_unlock(&ctx->event_data_lock); if (r) { - usbi_dbg("someone else is closing a device"); + usbi_dbg(ctx, "someone else is closing a device"); return 1; } @@ -1918,7 +1941,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) { unsigned int event_flags; - usbi_dbg(" "); + usbi_dbg(ctx, " "); ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->event_data_lock); @@ -2073,9 +2096,10 @@ static void handle_timeouts(struct libusb_context *ctx) static int handle_event_trigger(struct libusb_context *ctx) { struct list_head hotplug_msgs; + int hotplug_event = 0; int r = 0; - usbi_dbg("event triggered"); + usbi_dbg(ctx, "event triggered"); list_init(&hotplug_msgs); @@ -2084,21 +2108,28 @@ static int handle_event_trigger(struct libusb_context *ctx) /* check if someone modified the event sources */ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) - usbi_dbg("someone updated the event sources"); + usbi_dbg(ctx, "someone updated the event sources"); if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { - usbi_dbg("someone purposefully interrupted"); + usbi_dbg(ctx, "someone purposefully interrupted"); ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT; } + if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) { + usbi_dbg(ctx, "someone unregistered a hotplug cb"); + ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED; + hotplug_event = 1; + } + /* check if someone is closing a device */ if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE) - usbi_dbg("someone is closing a device"); + usbi_dbg(ctx, "someone is closing a device"); /* check for any pending hotplug messages */ if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) { - usbi_dbg("hotplug message received"); + usbi_dbg(ctx, "hotplug message received"); ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING; + hotplug_event = 1; assert(!list_empty(&ctx->hotplug_msgs)); list_cut(&hotplug_msgs, &ctx->hotplug_msgs); } @@ -2136,20 +2167,9 @@ static int handle_event_trigger(struct libusb_context *ctx) usbi_mutex_unlock(&ctx->event_data_lock); - /* process the hotplug messages, if any */ - while (!list_empty(&hotplug_msgs)) { - struct libusb_hotplug_message *message = - list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list); - - usbi_hotplug_match(ctx, message->device, message->event); - - /* the device left, dereference the device */ - if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) - libusb_unref_device(message->device); - - list_del(&message->list); - free(message); - } + /* process the hotplug events, if any */ + if (hotplug_event) + usbi_hotplug_process(ctx, &hotplug_msgs); return r; } @@ -2190,7 +2210,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) * save the additional overhead */ usbi_mutex_lock(&ctx->event_data_lock); if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) { - usbi_dbg("event sources modified, reallocating event data"); + usbi_dbg(ctx, "event sources modified, reallocating event data"); /* free anything removed since we last ran */ cleanup_removed_event_sources(ctx); @@ -2337,7 +2357,7 @@ int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, if (libusb_try_lock_events(ctx) == 0) { if (completed == NULL || !*completed) { /* we obtained the event lock: do our own event handling */ - usbi_dbg("doing our own event handling"); + usbi_dbg(ctx, "doing our own event handling"); r = handle_events(ctx, &poll_timeout); } libusb_unlock_events(ctx); @@ -2355,11 +2375,11 @@ int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, /* we hit a race: whoever was event handling earlier finished in the * time it took us to reach this point. try the cycle again. */ libusb_unlock_event_waiters(ctx); - usbi_dbg("event handler was active but went away, retrying"); + usbi_dbg(ctx, "event handler was active but went away, retrying"); goto retry; } - usbi_dbg("another thread is doing event handling"); + usbi_dbg(ctx, "another thread is doing event handling"); r = libusb_wait_for_event(ctx, &poll_timeout); already_done: @@ -2554,7 +2574,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, usbi_mutex_lock(&ctx->flying_transfers_lock); if (list_empty(&ctx->flying_transfers)) { usbi_mutex_unlock(&ctx->flying_transfers_lock); - usbi_dbg("no URBs, no timeout!"); + usbi_dbg(ctx, "no URBs, no timeout!"); return 0; } @@ -2573,19 +2593,19 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, usbi_mutex_unlock(&ctx->flying_transfers_lock); if (!TIMESPEC_IS_SET(&next_timeout)) { - usbi_dbg("no URB with timeout or all handled by OS; no timeout!"); + usbi_dbg(ctx, "no URB with timeout or all handled by OS; no timeout!"); return 0; } usbi_get_monotonic_time(&systime); if (!TIMESPEC_CMP(&systime, &next_timeout, <)) { - usbi_dbg("first timeout already expired"); + usbi_dbg(ctx, "first timeout already expired"); timerclear(tv); } else { TIMESPEC_SUB(&next_timeout, &systime, &next_timeout); TIMESPEC_TO_TIMEVAL(tv, &next_timeout); - usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec); + usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec); } return 1; @@ -2656,7 +2676,7 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle if (!ievent_source) return LIBUSB_ERROR_NO_MEM; - usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events); + usbi_dbg(ctx, "add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events); ievent_source->data.os_handle = os_handle; ievent_source->data.poll_events = poll_events; usbi_mutex_lock(&ctx->event_data_lock); @@ -2678,7 +2698,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha struct usbi_event_source *ievent_source; int found = 0; - usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle); + usbi_dbg(ctx, "remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle); usbi_mutex_lock(&ctx->event_data_lock); for_each_event_source(ctx, ievent_source) { if (ievent_source->data.os_handle == os_handle) { @@ -2688,7 +2708,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha } if (!found) { - usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle); + usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle); usbi_mutex_unlock(&ctx->event_data_lock); return; } @@ -2787,7 +2807,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) struct usbi_transfer *cur; struct usbi_transfer *to_cancel; - usbi_dbg("device %d.%d", + usbi_dbg(ctx, "device %d.%d", dev_handle->dev->bus_number, dev_handle->dev->device_address); /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE @@ -2822,7 +2842,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) if (!to_cancel) break; - usbi_dbg("cancelling transfer %p from disconnect", + usbi_dbg(ctx, "cancelling transfer %p from disconnect", USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel)); usbi_mutex_lock(&to_cancel->lock); diff --git a/mac/libusb/libusb.h b/mac/libusb/libusb.h index 1308571cd..2592ea779 100644 --- a/mac/libusb/libusb.h +++ b/mac/libusb/libusb.h @@ -26,13 +26,19 @@ #define LIBUSB_H #if defined(_MSC_VER) +#pragma warning(push) +/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */ +#pragma warning(disable:4200) /* on MS environments, the inline keyword is available in C++ only */ #if !defined(__cplusplus) #define inline __inline #endif /* ssize_t is also not available */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED #include typedef SSIZE_T ssize_t; +#endif /* _SSIZE_T_DEFINED */ #endif /* _MSC_VER */ #include @@ -136,7 +142,7 @@ typedef SSIZE_T ssize_t; * Internally, LIBUSB_API_VERSION is defined as follows: * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) */ -#define LIBUSB_API_VERSION 0x01000108 +#define LIBUSB_API_VERSION 0x01000109 /* The following is kept for compatibility, but will be deprecated in the future */ #define LIBUSBX_API_VERSION LIBUSB_API_VERSION @@ -904,7 +910,7 @@ struct libusb_container_id_descriptor { /** \ingroup libusb_asyncio * Setup packet for control transfers. */ -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__WATCOMC__) #pragma pack(push, 1) #endif struct libusb_control_setup { @@ -932,7 +938,7 @@ struct libusb_control_setup { /** Number of bytes to transfer */ uint16_t wLength; } LIBUSB_PACKED; -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__WATCOMC__) #pragma pack(pop) #endif @@ -979,8 +985,9 @@ struct libusb_version { * Sessions are created by libusb_init() and destroyed through libusb_exit(). * If your application is guaranteed to only ever include a single libusb * user (i.e. you), you do not have to worry about contexts: pass NULL in - * every function call where a context is required. The default context - * will be used. + * every function call where a context is required, and the default context + * will be used. Note that libusb_set_option(NULL, ...) is special, and adds + * an option to a list of default options for new contexts. * * For more information, see \ref libusb_contexts. */ @@ -989,7 +996,7 @@ typedef struct libusb_context libusb_context; /** \ingroup libusb_dev * Structure representing a USB device detected on the system. This is an * opaque type for which you are only ever provided with a pointer, usually - * originating from libusb_get_device_list(). + * originating from libusb_get_device_list() or libusb_hotplug_register_callback(). * * Certain operations can be performed on a device, but in order to do any * I/O you will have to first obtain a device handle using libusb_open(). @@ -1325,6 +1332,9 @@ enum libusb_log_level { /** \ingroup libusb_lib * Log callback mode. + * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \see libusb_set_log_cb() */ enum libusb_log_cb_mode { @@ -1341,6 +1351,9 @@ enum libusb_log_cb_mode { * is a global log message * \param level the log level, see \ref libusb_log_level for a description * \param str the log message + * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \see libusb_set_log_cb() */ typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, @@ -2092,20 +2105,36 @@ enum libusb_option { */ LIBUSB_OPTION_USE_USBDK = 1, - /** Set libusb has weak authority. With this option, libusb will skip - * scan devices in libusb_init. + /** Do not scan for devices + * + * With this option set, libusb will skip scanning devices in + * libusb_init(). Must be set before calling libusb_init(). * - * This option should be set before calling libusb_init(), otherwise - * libusb_init will failed. Normally libusb_wrap_sys_device need set - * this option. + * Hotplug functionality will also be deactivated. * - * Only valid on Linux-based operating system, such as Android. + * The option is useful in combination with libusb_wrap_sys_device(), + * which can access a device directly without prior device scanning. + * + * This is typically needed on Android, where access to USB devices + * is limited. + * + * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY + * + * Only valid on Linux. */ - LIBUSB_OPTION_WEAK_AUTHORITY = 2 + LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, + +#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY + + LIBUSB_OPTION_MAX = 3 }; int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #if defined(__cplusplus) } #endif diff --git a/mac/libusb/libusb.pro b/mac/libusb/libusb.pro index 07c7d23da..192d06fe3 100644 --- a/mac/libusb/libusb.pro +++ b/mac/libusb/libusb.pro @@ -18,8 +18,7 @@ SOURCES = core.c \ os/events_posix.c \ os/threads_posix.c -HEADERS = hotplug.h \ - libusb.h \ +HEADERS = libusb.h \ libusbi.h \ version.h \ version_nano.h \ diff --git a/mac/libusb/libusbi.h b/mac/libusb/libusbi.h index 491114be9..b1fc88c99 100644 --- a/mac/libusb/libusbi.h +++ b/mac/libusb/libusbi.h @@ -83,6 +83,34 @@ #define PTR_ALIGN(v) \ (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) +/* Atomic operations + * + * Useful for reference counting or when accessing a value without a lock + * + * The following atomic operations are defined: + * usbi_atomic_load() - Atomically read a variable's value + * usbi_atomic_store() - Atomically write a new value value to a variable + * usbi_atomic_inc() - Atomically increment a variable's value and return the new value + * usbi_atomic_dec() - Atomically decrement a variable's value and return the new value + * + * All of these operations are ordered with each other, thus the effects of + * any one operation is guaranteed to be seen by any other operation. + */ +#ifdef _MSC_VER +typedef volatile LONG usbi_atomic_t; +#define usbi_atomic_load(a) (*(a)) +#define usbi_atomic_store(a, v) (*(a)) = (v) +#define usbi_atomic_inc(a) InterlockedIncrement((a)) +#define usbi_atomic_dec(a) InterlockedDecrement((a)) +#else +#include +typedef atomic_long usbi_atomic_t; +#define usbi_atomic_load(a) atomic_load((a)) +#define usbi_atomic_store(a, v) atomic_store((a), (v)) +#define usbi_atomic_inc(a) (atomic_fetch_add((a), 1) + 1) +#define usbi_atomic_dec(a) (atomic_fetch_add((a), -1) - 1) +#endif + /* Internal abstractions for event handling and thread synchronization */ #if defined(PLATFORM_POSIX) #include "os/events_posix.h" @@ -289,22 +317,23 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, #define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__) #define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__) #define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__) -#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) +#define usbi_dbg(ctx ,...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) #else /* ENABLE_LOGGING */ #define usbi_err(ctx, ...) UNUSED(ctx) #define usbi_warn(ctx, ...) UNUSED(ctx) #define usbi_info(ctx, ...) UNUSED(ctx) -#define usbi_dbg(...) do {} while (0) +#define usbi_dbg(ctx, ...) do {} while (0) #endif /* ENABLE_LOGGING */ #define DEVICE_CTX(dev) ((dev)->ctx) -#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) -#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) #define ITRANSFER_CTX(itransfer) \ - (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) + ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL) +#define TRANSFER_CTX(transfer) \ + (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer))) #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) #define IS_EPOUT(ep) (!IS_EPIN(ep)) @@ -340,6 +369,9 @@ struct libusb_context { libusb_hotplug_callback_handle next_hotplug_cb_handle; usbi_mutex_t hotplug_cbs_lock; + /* A flag to indicate that the context is ready for hotplug notifications */ + usbi_atomic_t hotplug_ready; + /* this is a list of in-flight transfer handles, sorted by timeout * expiration. URBs to timeout the soonest are placed at the beginning of * the list, URBs that will time out later are placed after, and urbs with @@ -404,13 +436,26 @@ struct libusb_context { }; extern struct libusb_context *usbi_default_context; +extern struct libusb_context *usbi_fallback_context; extern struct list_head active_contexts_list; extern usbi_mutex_static_t active_contexts_lock; static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) { - return ctx ? ctx : usbi_default_context; + static int warned = 0; + + if (!ctx) { + ctx = usbi_default_context; + } + if (!ctx) { + ctx = usbi_fallback_context; + if (ctx && warned == 0) { + usbi_err(ctx, "API misuse! Using non-default context as implicit default."); + warned = 1; + } + } + return ctx; } enum usbi_event_flags { @@ -450,10 +495,7 @@ static inline void usbi_end_event_handling(struct libusb_context *ctx) } struct libusb_device { - /* lock protects refcnt, everything else is finalized at initialization - * time */ - usbi_mutex_t lock; - int refcnt; + usbi_atomic_t refcnt; struct libusb_context *ctx; struct libusb_device *parent_dev; @@ -467,7 +509,7 @@ struct libusb_device { unsigned long session_data; struct libusb_device_descriptor device_descriptor; - int attached; + usbi_atomic_t attached; }; struct libusb_device_handle { @@ -534,6 +576,10 @@ struct usbi_transfer { uint32_t state_flags; /* Protected by usbi_transfer->lock */ uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ + /* The device reference is held until destruction for logging + * even after dev_handle is set to NULL. */ + struct libusb_device *dev; + /* this lock is held during libusb_submit_transfer() and * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate * cancellation, submission-during-cancellation, etc). the OS backend @@ -664,8 +710,75 @@ union usbi_bos_desc_buf { uint16_t align; /* Force 2-byte alignment */ }; +enum usbi_hotplug_flags { + /* This callback is interested in device arrivals */ + USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, + + /* This callback is interested in device removals */ + USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, + + /* IMPORTANT: The values for the below entries must start *after* + * the highest value of the above entries!!! + */ + + /* The vendor_id field is valid for matching */ + USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3), + + /* The product_id field is valid for matching */ + USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4), + + /* The dev_class field is valid for matching */ + USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5), + + /* This callback has been unregistered and needs to be freed */ + USBI_HOTPLUG_NEEDS_FREE = (1U << 6), +}; + +struct usbi_hotplug_callback { + /* Flags that control how this callback behaves */ + uint8_t flags; + + /* Vendor ID to match (if flags says this is valid) */ + uint16_t vendor_id; + + /* Product ID to match (if flags says this is valid) */ + uint16_t product_id; + + /* Device class to match (if flags says this is valid) */ + uint8_t dev_class; + + /* Callback function to invoke for matching event/device */ + libusb_hotplug_callback_fn cb; + + /* Handle for this callback (used to match on deregister) */ + libusb_hotplug_callback_handle handle; + + /* User data that will be passed to the callback function */ + void *user_data; + + /* List this callback is registered in (ctx->hotplug_cbs) */ + struct list_head list; +}; + +struct usbi_hotplug_message { + /* The hotplug event that occurred */ + libusb_hotplug_event event; + + /* The device for which this hotplug event occurred */ + struct libusb_device *device; + + /* List this message is contained in (ctx->hotplug_msgs) */ + struct list_head list; +}; + /* shared data and functions */ +void usbi_hotplug_init(struct libusb_context *ctx); +void usbi_hotplug_exit(struct libusb_context *ctx); +void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event); +void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs); + int usbi_io_init(struct libusb_context *ctx); void usbi_io_exit(struct libusb_context *ctx); @@ -696,6 +809,13 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle short poll_events); void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle); +struct usbi_option { + int is_set; + union { + int ival; + } arg; +}; + /* OS event abstraction */ int usbi_create_event(usbi_event_t *event); @@ -793,7 +913,8 @@ struct usbi_os_backend { * data structures for later, etc. * * This function is called when a libusb user initializes the library - * prior to use. + * prior to use. Mutual exclusion with other init and exit calls is + * guaranteed when this function is called. * * Return 0 on success, or a LIBUSB_ERROR code on failure. */ @@ -803,6 +924,8 @@ struct usbi_os_backend { * that was set up by init. * * This function is called when the user deinitializes the library. + * Mutual exclusion with other init and exit calls is guaranteed when + * this function is called. */ void (*exit)(struct libusb_context *ctx); @@ -1365,6 +1488,12 @@ extern const struct usbi_os_backend usbi_backend; #define for_each_removed_event_source_safe(ctx, e, n) \ for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source) +#define for_each_hotplug_cb(ctx, c) \ + for_each_helper(c, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback) + +#define for_each_hotplug_cb_safe(ctx, c, n) \ + for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback) + #ifdef __cplusplus } #endif diff --git a/mac/libusb/os/darwin_usb.c b/mac/libusb/os/darwin_usb.c index e415589d6..388dbca60 100644 --- a/mac/libusb/os/darwin_usb.c +++ b/mac/libusb/os/darwin_usb.c @@ -1,8 +1,8 @@ /* -*- Mode: C; indent-tabs-mode:nil -*- */ /* * darwin backend for libusb 1.0 - * Copyright © 2008-2020 Nathan Hjelm - * Copyright © 2019-2020 Google LLC. All rights reserved. + * Copyright © 2008-2021 Nathan Hjelm + * Copyright © 2019-2021 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -41,6 +41,10 @@ * function. Its use is also conditionalized to only older deployment targets. */ #define OBJC_SILENCE_GC_DEPRECATIONS 1 +/* Default timeout to 10s for reenumerate. This is needed because USBDeviceReEnumerate + * does not return error status on macOS. */ +#define DARWIN_REENUMERATE_TIMEOUT_US (10 * USEC_PER_SEC) + #include #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 #include @@ -48,9 +52,11 @@ #include "darwin_usb.h" -static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER; static int init_count = 0; +/* Both kIOMasterPortDefault or kIOMainPortDefault are synonyms for 0. */ +static const mach_port_t darwin_default_master_port = 0; + /* async event thread */ static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER; @@ -77,14 +83,17 @@ static pthread_t libusb_darwin_at; static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len); static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface); static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface); +static int darwin_reenumerate_device(struct libusb_device_handle *dev_handle, bool capture); +static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); static int darwin_reset_device(struct libusb_device_handle *dev_handle); +static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface); static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0); static enum libusb_error darwin_scan_devices(struct libusb_context *ctx); static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device, UInt64 old_session_id); -static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out, +static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out, UInt64 *old_session_id); #if defined(ENABLE_LOGGING) @@ -102,6 +111,9 @@ static const char *darwin_error_str (IOReturn result) { case kIOReturnExclusiveAccess: return "another process has device opened for exclusive access"; case kIOUSBPipeStalled: +#if defined(kUSBHostReturnPipeStalled) + case kUSBHostReturnPipeStalled: +#endif return "pipe is stalled"; case kIOReturnError: return "could not establish a connection to the Darwin kernel"; @@ -141,16 +153,20 @@ static enum libusb_error darwin_to_libusb (IOReturn result) { case kIOReturnExclusiveAccess: return LIBUSB_ERROR_ACCESS; case kIOUSBPipeStalled: +#if defined(kUSBHostReturnPipeStalled) + case kUSBHostReturnPipeStalled: +#endif return LIBUSB_ERROR_PIPE; case kIOReturnBadArgument: return LIBUSB_ERROR_INVALID_PARAM; case kIOUSBTransactionTimeout: return LIBUSB_ERROR_TIMEOUT; + case kIOUSBUnknownPipeErr: + return LIBUSB_ERROR_NOT_FOUND; case kIOReturnNotResponding: case kIOReturnAborted: case kIOReturnError: case kIOUSBNoAsyncPortErr: - case kIOUSBUnknownPipeErr: default: return LIBUSB_ERROR_OTHER; } @@ -167,6 +183,7 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) (*(cached_dev->device))->Release(cached_dev->device); cached_dev->device = NULL; } + IOObjectRelease (cached_dev->service); free (cached_dev); } } @@ -183,7 +200,9 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui uint8_t i, iface; - usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep); + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + + usbi_dbg (ctx, "converting ep address 0x%02x to pipeRef and interface", ep); for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { cInterface = &priv->interfaces[iface]; @@ -199,7 +218,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui if (interface_out) *interface_out = cInterface; - usbi_dbg ("pipe %d on interface %d matches", *pipep, iface); + usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface); return LIBUSB_SUCCESS; } } @@ -240,7 +259,7 @@ static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 CFRelease (locationCF); } - return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator); + return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator); } /* Returns 1 on success, 0 on failure. */ @@ -281,7 +300,7 @@ static bool get_ioregistry_value_data (io_service_t service, CFStringRef propert return success; } -static usb_device_t **darwin_device_from_service (io_service_t service) +static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service) { io_cf_plugin_ref_t *plugInInterface = NULL; usb_device_t **device; @@ -300,14 +319,14 @@ static usb_device_t **darwin_device_from_service (io_service_t service) break; } - usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult)); + usbi_dbg (ctx, "set up plugin for service retry: %s", darwin_error_str (kresult)); /* sleep for a little while before trying again */ nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL); } if (kIOReturnSuccess != kresult || !plugInInterface) { - usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult)); + usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult)); return NULL; } @@ -330,7 +349,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { usbi_mutex_lock(&active_contexts_lock); while ((service = IOIteratorNext(add_devices))) { - ret = darwin_get_cached_device (service, &cached_device, &old_session_id); + ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id); if (ret < 0 || !cached_device->can_enumerate) { continue; } @@ -341,7 +360,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { } if (cached_device->in_reenumerate) { - usbi_dbg ("cached device in reset state. reset complete..."); + usbi_dbg (NULL, "cached device in reset state. reset complete..."); cached_device->in_reenumerate = false; } @@ -358,7 +377,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { struct darwin_cached_device *old_device; io_service_t device; - UInt64 session; + UInt64 session, locationID; int ret; usbi_mutex_lock(&active_contexts_lock); @@ -368,6 +387,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { /* get the location from the i/o registry */ ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); + (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); IOObjectRelease (device); if (!ret) continue; @@ -380,7 +400,8 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { if (old_device->in_reenumerate) { /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device() * will deref if needed. */ - usbi_dbg ("detected device detached due to re-enumeration"); + usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64, + session, locationID); /* the device object is no longer usable so go ahead and release it */ if (old_device->device) { @@ -403,7 +424,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { } for_each_context(ctx) { - usbi_dbg ("notifying context %p of device disconnect", ctx); + usbi_dbg (ctx, "notifying context %p of device disconnect", ctx); dev = usbi_get_device_by_session_id(ctx, (unsigned long) session); if (dev) { @@ -426,7 +447,7 @@ static void darwin_hotplug_poll (void) /* since a kernel thread may notify the IOIterators used for * hotplug notification we can't just clear the iterators. * instead just wait until all IOService providers are quiet */ - (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout); + (void) IOKitWaitQuiet (darwin_default_master_port, &timeout); } static void darwin_clear_iterator (io_iterator_t iter) { @@ -473,7 +494,8 @@ static void *darwin_event_thread_main (void *arg0) { io_iterator_t libusb_rem_device_iterator; io_iterator_t libusb_add_device_iterator; - usbi_dbg ("creating hotplug event source"); + /* ctx must only be used for logging during thread startup */ + usbi_dbg (ctx, "creating hotplug event source"); runloop = CFRunLoopGetCurrent (); CFRetain (runloop); @@ -486,7 +508,7 @@ static void *darwin_event_thread_main (void *arg0) { CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); /* add the notification port to the run loop */ - libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault); + libusb_notification_port = IONotificationPortCreate (darwin_default_master_port); libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port); CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); @@ -494,7 +516,7 @@ static void *darwin_event_thread_main (void *arg0) { kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, IOServiceMatching(darwin_device_class), darwin_devices_detached, - ctx, &libusb_rem_device_iterator); + NULL, &libusb_rem_device_iterator); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); @@ -507,7 +529,7 @@ static void *darwin_event_thread_main (void *arg0) { kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, IOServiceMatching(darwin_device_class), darwin_devices_attached, - ctx, &libusb_add_device_iterator); + NULL, &libusb_add_device_iterator); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); @@ -520,7 +542,7 @@ static void *darwin_event_thread_main (void *arg0) { darwin_clear_iterator (libusb_rem_device_iterator); darwin_clear_iterator (libusb_add_device_iterator); - usbi_dbg ("darwin event thread ready to receive events"); + usbi_dbg (ctx, "darwin event thread ready to receive events"); /* signal the main thread that the hotplug runloop has been created. */ pthread_mutex_lock (&libusb_darwin_at_mutex); @@ -532,7 +554,7 @@ static void *darwin_event_thread_main (void *arg0) { /* run the runloop */ CFRunLoopRun(); - usbi_dbg ("darwin event thread exiting"); + usbi_dbg (NULL, "darwin event thread exiting"); /* signal the main thread that the hotplug runloop has finished. */ pthread_mutex_lock (&libusb_darwin_at_mutex); @@ -567,23 +589,20 @@ static void darwin_cleanup_devices(void) { list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { darwin_deref_cached_device(dev); } - - darwin_cached_devices.prev = darwin_cached_devices.next = NULL; } static int darwin_init(struct libusb_context *ctx) { bool first_init; int rc; - pthread_mutex_lock (&libusb_darwin_init_mutex); - first_init = (1 == ++init_count); do { if (first_init) { - assert (NULL == darwin_cached_devices.next); - list_init (&darwin_cached_devices); - + if (NULL == darwin_cached_devices.next) { + list_init (&darwin_cached_devices); + } + assert(list_empty(&darwin_cached_devices)); #if !defined(HAVE_CLOCK_GETTIME) /* create the clocks that will be used if clock_gettime() is not available */ host_name_port_t host_self; @@ -632,16 +651,12 @@ static int darwin_init(struct libusb_context *ctx) { --init_count; } - pthread_mutex_unlock (&libusb_darwin_init_mutex); - return rc; } static void darwin_exit (struct libusb_context *ctx) { UNUSED(ctx); - pthread_mutex_lock (&libusb_darwin_init_mutex); - if (0 == --init_count) { /* stop the event runloop and wait for the thread to terminate. */ pthread_mutex_lock (&libusb_darwin_at_mutex); @@ -659,8 +674,6 @@ static void darwin_exit (struct libusb_context *ctx) { mach_port_deallocate(mach_task_self(), clock_monotonic); #endif } - - pthread_mutex_unlock (&libusb_darwin_init_mutex); } static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) { @@ -744,7 +757,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, not usable anyway */ if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) && 0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) { - usbi_dbg ("ignoring configuration on root hub simulation"); + usbi_dbg (ctx, "ignoring configuration on root hub simulation"); dev->active_config = 0; return LIBUSB_SUCCESS; } @@ -787,7 +800,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, /* not configured */ dev->active_config = 0; - usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config); + usbi_dbg (ctx, "active config: %u, first config: %u", dev->active_config, dev->first_config); return LIBUSB_SUCCESS; } @@ -812,7 +825,7 @@ static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UI return (*device)->DeviceRequestTO (device, &req); } -static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) { +static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) { usb_device_t **device = dev->device; int retries = 1; long delay = 30000; // microseconds @@ -850,7 +863,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de 0 == dev->dev_descriptor.bcdUSB)) { /* work around for incorrectly configured devices */ if (try_reconfigure && is_open) { - usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again..."); + usbi_dbg(ctx, "descriptor appears to be invalid. resetting configuration before trying again..."); /* set the first configuration */ (*device)->SetConfiguration(device, 1); @@ -881,7 +894,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de if (kIOReturnSuccess != ret2) { /* prevent log spew from poorly behaving devices. this indicates the os actually had trouble communicating with the device */ - usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); + usbi_dbg(ctx, "could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); } else unsuspended = 1; @@ -890,7 +903,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de } if (kIOReturnSuccess != ret) { - usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000); + usbi_dbg(ctx, "kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000); /* sleep for a little while before trying again */ nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL); } @@ -906,10 +919,10 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de if (ret != kIOReturnSuccess) { /* a debug message was already printed out for this error */ if (LIBUSB_CLASS_HUB == bDeviceClass) - usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + usbi_dbg (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", idVendor, idProduct, darwin_error_str (ret), ret); else - usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", idVendor, idProduct, darwin_error_str (ret), ret); return darwin_to_libusb (ret); } @@ -922,20 +935,20 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de return LIBUSB_ERROR_NO_DEVICE; } - usbi_dbg ("cached device descriptor:"); - usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType); - usbi_dbg (" bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB)); - usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass); - usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass); - usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol); - usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0); - usbi_dbg (" idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor)); - usbi_dbg (" idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); - usbi_dbg (" bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice)); - usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer); - usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct); - usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber); - usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations); + usbi_dbg (ctx, "cached device descriptor:"); + usbi_dbg (ctx, " bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType); + usbi_dbg (ctx, " bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB)); + usbi_dbg (ctx, " bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass); + usbi_dbg (ctx, " bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass); + usbi_dbg (ctx, " bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol); + usbi_dbg (ctx, " bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0); + usbi_dbg (ctx, " idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor)); + usbi_dbg (ctx, " idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); + usbi_dbg (ctx, " bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice)); + usbi_dbg (ctx, " iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer); + usbi_dbg (ctx, " iProduct: 0x%02x", dev->dev_descriptor.iProduct); + usbi_dbg (ctx, " iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber); + usbi_dbg (ctx, " bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations); dev->can_enumerate = 1; @@ -979,7 +992,7 @@ static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_ses return false; } -static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out, +static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out, UInt64 *old_session_id) { struct darwin_cached_device *new_device; UInt64 sessionID = 0, parent_sessionID = 0; @@ -996,28 +1009,28 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID); (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); if (!get_device_port (service, &port)) { - usbi_dbg("could not get connected port number"); + usbi_dbg(ctx, "could not get connected port number"); } - usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID); + usbi_dbg(ctx, "finding cached device for sessionID 0x%" PRIx64, sessionID); if (get_device_parent_sessionID(service, &parent_sessionID)) { - usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID); + usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID); } usbi_mutex_lock(&darwin_cached_devices_lock); do { list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { - usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x", + usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x", sessionID, locationID, new_device->session, new_device->location); if (new_device->location == locationID && new_device->in_reenumerate) { - usbi_dbg ("found cached device with matching location that is being re-enumerated"); + usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated"); *old_session_id = new_device->session; break; } if (new_device->session == sessionID) { - usbi_dbg("using cached device for device"); + usbi_dbg(ctx, "using cached device for device"); *cached_out = new_device; break; } @@ -1026,9 +1039,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d if (*cached_out) break; - usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID); + usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID); - device = darwin_device_from_service (service); + device = darwin_device_from_service (ctx, service); if (!device) { ret = LIBUSB_ERROR_NO_DEVICE; break; @@ -1052,6 +1065,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d (*device)->GetLocationID (device, &new_device->location); new_device->port = port; new_device->parent_session = parent_sessionID; + } else { + /* release the ref to old device's service */ + IOObjectRelease (new_device->service); } /* keep track of devices regardless of if we successfully enumerate them to @@ -1060,9 +1076,13 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d new_device->session = sessionID; new_device->device = device; + new_device->service = service; + + /* retain the service */ + IOObjectRetain (service); /* cache the device descriptor */ - ret = darwin_cache_device_descriptor(new_device); + ret = darwin_cache_device_descriptor(ctx, new_device); if (ret) break; @@ -1094,14 +1114,14 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct break; if (0 != old_session_id) { - usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64, + usbi_dbg (ctx, "re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64, ctx, old_session_id, cached_device->session); /* save the libusb device before the session id is updated */ dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id); } if (!dev) { - usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64, + usbi_dbg (ctx, "allocating new device in context %p for with session 0x%" PRIx64, ctx, cached_device->session); dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session); @@ -1114,6 +1134,8 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct priv->dev = cached_device; darwin_ref_cached_device (priv->dev); dev->port_number = cached_device->port; + /* the location ID encodes the path to the device. the top byte of the location ID contains the bus number + (numbered from 0). the remaining bytes can be used to construct the device tree for that bus. */ dev->bus_number = cached_device->location >> 24; assert(cached_device->address <= UINT8_MAX); dev->device_address = (uint8_t)cached_device->address; @@ -1127,10 +1149,13 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct usbi_localize_device_descriptor(&dev->device_descriptor); dev->session_data = cached_device->session; + if (NULL != dev->parent_dev) { + libusb_unref_device(dev->parent_dev); + dev->parent_dev = NULL; + } + if (cached_device->parent_session > 0) { dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session); - } else { - dev->parent_dev = NULL; } (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); @@ -1139,7 +1164,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break; case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 @@ -1153,7 +1178,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct if (ret < 0) break; - usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address, + usbi_dbg (ctx, "found device with address %d port = %d parent = %p at %p", dev->device_address, dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path); } while (0); @@ -1180,7 +1205,7 @@ static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) { return darwin_to_libusb (kresult); while ((service = IOIteratorNext (deviceIterator))) { - ret = darwin_get_cached_device (service, &cached_device, &old_session_id); + ret = darwin_get_cached_device (ctx, service, &cached_device, &old_session_id); if (ret < 0 || !cached_device->can_enumerate) { continue; } @@ -1232,14 +1257,14 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { CFRetain (libusb_darwin_acfl); - /* add the cfSource to the aync run loop */ + /* add the cfSource to the async run loop */ CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); } /* device opened successfully */ dpriv->open_count++; - usbi_dbg ("device open for access"); + usbi_dbg (HANDLE_CTX(dev_handle), "device open for access"); return 0; } @@ -1257,6 +1282,10 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { } dpriv->open_count--; + if (NULL == dpriv->device) { + usbi_warn (HANDLE_CTX (dev_handle), "darwin_close device missing IOService"); + return; + } /* make sure all interfaces are released */ for (i = 0 ; i < USB_MAXINTERFACES ; i++) @@ -1362,28 +1391,39 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; +#if InterfaceVersion >= 550 + IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; +#else + UInt8 dont_care1, dont_care3; + UInt16 dont_care2; +#endif IOReturn kresult; UInt8 numep, direction, number; - UInt8 dont_care1, dont_care3; - UInt16 dont_care2; int rc; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + - usbi_dbg ("building table of endpoints."); + usbi_dbg (ctx, "building table of endpoints."); /* retrieve the total number of endpoints on this interface */ kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); + usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } /* iterate through pipe references */ for (UInt8 i = 1 ; i <= numep ; i++) { +#if InterfaceVersion >= 550 + kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties); + number = pipeProperties.bEndpointNumber; + direction = pipeProperties.bDirection; +#else kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, &dont_care2, &dont_care3); - +#endif if (kresult != kIOReturnSuccess) { /* probably a buggy device. try to get the endpoint address from the descriptors */ struct libusb_config_descriptor *config; @@ -1401,6 +1441,10 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, return rc; } + if (iface >= config->bNumInterfaces) { + usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface); + return LIBUSB_ERROR_NOT_FOUND; + } endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1; cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress; @@ -1408,7 +1452,7 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); } - usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift, + usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift, cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK); } @@ -1429,30 +1473,32 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); /* make sure we have an interface */ if (!usbInterface && dpriv->first_config != 0) { - usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config); + usbi_info (ctx, "no interface found; setting configuration: %d", dpriv->first_config); /* set the configuration */ ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config); if (ret != LIBUSB_SUCCESS) { - usbi_err (HANDLE_CTX (dev_handle), "could not set configuration"); + usbi_err (ctx, "could not set configuration"); return ret; } kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); + usbi_err (ctx, "darwin_get_interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } } if (!usbInterface) { - usbi_err (HANDLE_CTX (dev_handle), "interface not found"); + usbi_info (ctx, "interface not found"); return LIBUSB_ERROR_NOT_FOUND; } @@ -1464,12 +1510,12 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 (void)IOObjectRelease (usbInterface); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); + usbi_err (ctx, "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } if (!plugInInterface) { - usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found"); + usbi_err (ctx, "plugin interface not found"); return LIBUSB_ERROR_NOT_FOUND; } @@ -1481,14 +1527,14 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ (*plugInInterface)->Release (plugInInterface); if (kresult != kIOReturnSuccess || !cInterface->interface) { - usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult)); + usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } /* claim the interface */ kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult)); + usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } @@ -1497,7 +1543,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 if (ret) { /* this should not happen */ darwin_release_interface (dev_handle, iface); - usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + usbi_err (ctx, "could not build endpoint table"); return ret; } @@ -1506,7 +1552,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 /* create async event source */ kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "could not create async event source"); + usbi_err (ctx, "could not create async event source"); /* can't continue without an async event source */ (void)darwin_release_interface (dev_handle, iface); @@ -1517,7 +1563,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 /* add the cfSource to the async thread's run loop */ CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); - usbi_dbg ("interface opened"); + usbi_dbg (ctx, "interface opened"); return LIBUSB_SUCCESS; } @@ -1540,6 +1586,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin if (cInterface->cfSource) { CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); CFRelease (cInterface->cfSource); + cInterface->cfSource = NULL; } kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); @@ -1555,6 +1602,30 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin return darwin_to_libusb (kresult); } +static int check_alt_setting_and_clear_halt(struct libusb_device_handle *dev_handle, uint8_t altsetting, struct darwin_interface *cInterface) { + enum libusb_error ret; + IOReturn kresult; + uint8_t current_alt_setting; + + kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, ¤t_alt_setting); + if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) { + return LIBUSB_ERROR_PIPE; + } + + for (int i = 0 ; i < cInterface->num_endpoints ; i++) { + ret = darwin_clear_halt(dev_handle, cInterface->endpoint_addrs[i]); + if (LIBUSB_SUCCESS != ret) { + usbi_warn(HANDLE_CTX (dev_handle), "error clearing pipe halt for endpoint %d", i); + if (LIBUSB_ERROR_NOT_FOUND == ret) { + /* may need to re-open the interface */ + return ret; + } + } + } + + return LIBUSB_SUCCESS; +} + static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) { struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); IOReturn kresult; @@ -1567,19 +1638,41 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand return LIBUSB_ERROR_NO_DEVICE; kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting); - if (kresult != kIOReturnSuccess) - darwin_reset_device (dev_handle); + if (kresult == kIOReturnSuccess) { + /* update the list of endpoints */ + ret = get_endpoints (dev_handle, iface); + if (ret) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + } + return ret; + } - /* update list of endpoints */ - ret = get_endpoints (dev_handle, iface); - if (ret) { - /* this should not happen */ - darwin_release_interface (dev_handle, iface); - usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult)); + + ret = darwin_to_libusb(kresult); + if (ret != LIBUSB_ERROR_PIPE) { return ret; } - return darwin_to_libusb (kresult); + /* If a device only supports a default setting for the specified interface, then a STALL + (kIOUSBPipeStalled) may be returned. Ref: USB 2.0 specs 9.4.10. + Mimic the behaviour in e.g. the Linux kernel: in such case, reset all endpoints + of the interface (as would have been done per 9.1.1.5) and return success. */ + + ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface); + if (LIBUSB_ERROR_NOT_FOUND == ret) { + /* For some reason we need to reclaim the interface after the pipe error with some versions of macOS */ + ret = darwin_claim_interface (dev_handle, iface); + if (LIBUSB_SUCCESS != ret) { + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not reclaim interface: %s", darwin_error_str(kresult)); + } + ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface); + } + + return ret; } static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) { @@ -1610,6 +1703,8 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t int open_count = dpriv->open_count; int ret; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + /* clear claimed interfaces temporarily */ dev_handle->claimed_interfaces = 0; @@ -1629,16 +1724,16 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t } if (dpriv->active_config != active_config) { - usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config); + usbi_dbg (ctx, "darwin/restore_state: restoring configuration %d...", active_config); ret = darwin_set_configuration (dev_handle, active_config); if (LIBUSB_SUCCESS != ret) { - usbi_dbg ("darwin/restore_state: could not restore configuration"); + usbi_dbg (ctx, "darwin/restore_state: could not restore configuration"); return LIBUSB_ERROR_NOT_FOUND; } } - usbi_dbg ("darwin/restore_state: reclaiming interfaces"); + usbi_dbg (ctx, "darwin/restore_state: reclaiming interfaces"); if (claimed_interfaces) { for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) { @@ -1646,11 +1741,11 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t continue; } - usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface); + usbi_dbg (ctx, "darwin/restore_state: re-claiming interface %u", iface); ret = darwin_claim_interface (dev_handle, iface); if (LIBUSB_SUCCESS != ret) { - usbi_dbg ("darwin/restore_state: could not claim interface %u", iface); + usbi_dbg (ctx, "darwin/restore_state: could not claim interface %u", iface); return LIBUSB_ERROR_NOT_FOUND; } @@ -1658,21 +1753,24 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t } } - usbi_dbg ("darwin/restore_state: device state restored"); + usbi_dbg (ctx, "darwin/restore_state: device state restored"); return LIBUSB_SUCCESS; } -static int darwin_reset_device(struct libusb_device_handle *dev_handle) { +static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); unsigned long claimed_interfaces = dev_handle->claimed_interfaces; int8_t active_config = dpriv->active_config; + UInt32 options = 0; IOUSBDeviceDescriptor descriptor; IOUSBConfigurationDescriptorPtr cached_configuration; IOUSBConfigurationDescriptor *cached_configurations; IOReturn kresult; UInt8 i; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + if (dpriv->in_reenumerate) { /* ack, two (or more) threads are trying to reset the device! abort! */ return LIBUSB_ERROR_NOT_FOUND; @@ -1689,62 +1787,152 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) { memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i])); } + /* if we need to release capture */ + if (HAS_CAPTURE_DEVICE()) { + if (capture) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 + options |= kUSBReEnumerateCaptureDeviceMask; +#endif + } + } else { + capture = false; + } + /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */ - kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0); + kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options); if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult)); + usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult)); dpriv->in_reenumerate = false; return darwin_to_libusb (kresult); } - usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete..."); + /* capture mode does not re-enumerate but it does require re-open */ + if (capture) { + usbi_dbg (ctx, "darwin/reenumerate_device: restoring state..."); + dpriv->in_reenumerate = false; + return darwin_restore_state (dev_handle, active_config, claimed_interfaces); + } + + usbi_dbg (ctx, "darwin/reenumerate_device: waiting for re-enumeration to complete..."); + + struct timespec start; + usbi_get_monotonic_time(&start); while (dpriv->in_reenumerate) { struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000}; nanosleep (&delay, NULL); + + struct timespec now; + usbi_get_monotonic_time(&now); + unsigned long elapsed_us = (now.tv_sec - start.tv_sec) * USEC_PER_SEC + + (now.tv_nsec - start.tv_nsec) / 1000; + + if (elapsed_us >= DARWIN_REENUMERATE_TIMEOUT_US) { + usbi_err (ctx, "darwin/reenumerate_device: timeout waiting for reenumerate"); + dpriv->in_reenumerate = false; + return LIBUSB_ERROR_TIMEOUT; + } } /* compare descriptors */ - usbi_dbg ("darwin/reset_device: checking whether descriptors changed"); + usbi_dbg (ctx, "darwin/reenumerate_device: checking whether descriptors changed"); if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) { /* device descriptor changed. need to return not found. */ - usbi_dbg ("darwin/reset_device: device descriptor changed"); + usbi_dbg (ctx, "darwin/reenumerate_device: device descriptor changed"); return LIBUSB_ERROR_NOT_FOUND; } for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) { - usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i); + usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i); return LIBUSB_ERROR_NOT_FOUND; } } - usbi_dbg ("darwin/reset_device: device reset complete. restoring state..."); + usbi_dbg (ctx, "darwin/reenumerate_device: device reset complete. restoring state..."); return darwin_restore_state (dev_handle, active_config, claimed_interfaces); } -static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) { +static int darwin_reset_device (struct libusb_device_handle *dev_handle) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); - io_service_t usbInterface; - CFTypeRef driver; IOReturn kresult; + enum libusb_error ret; - kresult = darwin_get_interface (dpriv->device, interface, &usbInterface); - if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); - - return darwin_to_libusb (kresult); +#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 + if (dpriv->capture_count > 0) { + /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */ + kresult = (*(dpriv->device))->ResetDevice (dpriv->device); + ret = darwin_to_libusb (kresult); + } else { + ret = darwin_reenumerate_device (dev_handle, false); + } +#else + /* ResetDevice() is missing on non-macOS platforms */ + ret = darwin_reenumerate_device (dev_handle, false); + if ((ret == LIBUSB_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND) && dpriv->capture_count > 0) { + int capture_count; + int8_t active_config = dpriv->active_config; + unsigned long claimed_interfaces = dev_handle->claimed_interfaces; + + /* save old capture_count */ + capture_count = dpriv->capture_count; + /* reset capture count */ + dpriv->capture_count = 0; + /* attempt to detach kernel driver again as it is now re-attached */ + ret = darwin_detach_kernel_driver (dev_handle, 0); + if (ret != LIBUSB_SUCCESS) { + return ret; + } + /* restore capture_count */ + dpriv->capture_count = capture_count; + /* restore configuration */ + ret = darwin_restore_state (dev_handle, active_config, claimed_interfaces); } +#endif + return ret; +} + +static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) { + CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name); + CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); + CFTypeRef interfaceCF = CFNumberCreate (NULL, kCFNumberSInt8Type, &interface_number); - driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0); - IOObjectRelease (usbInterface); + CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBHostMatchingPropertyInterfaceNumber), interfaceCF); - if (driver) { - CFRelease (driver); + CFRelease (interfaceCF); + CFRelease (locationCF); + CFRelease (propertyMatchDict); + + return IOServiceGetMatchingService (darwin_default_master_port, matchingDict); +} + +static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + io_service_t usb_interface, child = IO_OBJECT_NULL; + + /* locate the IO registry entry for this interface */ + usb_interface = usb_find_interface_matching_location (kIOUSBHostInterfaceClassName, interface, dpriv->location); + if (0 == usb_interface) { + /* check for the legacy class entry */ + usb_interface = usb_find_interface_matching_location (kIOUSBInterfaceClassName, interface, dpriv->location); + if (0 == usb_interface) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + /* if the IO object has a child entry in the IO Registry it has a kernel driver attached */ + (void) IORegistryEntryGetChildEntry (usb_interface, kIOServicePlane, &child); + IOObjectRelease (usb_interface); + if (IO_OBJECT_NULL != child) { + IOObjectRelease (child); return 1; } @@ -1873,11 +2061,17 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); IOReturn kresult; - uint8_t direction, number, interval, pipeRef, transferType; - uint16_t maxPacketSize; + uint8_t pipeRef, interval; UInt64 frame; AbsoluteTime atTime; int i; +#if InterfaceVersion >= 550 + IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; +#else + /* None of the values below are used in libusb for iso transfers */ + uint8_t direction, number, transferType; + uint16_t maxPacketSize; +#endif struct darwin_interface *cInterface; @@ -1909,8 +2103,20 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { } /* determine the properties of this endpoint and the speed of the device */ - (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, +#if InterfaceVersion >= 550 + kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties); + interval = pipeProperties.bInterval; +#else + kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval); +#endif + if (kresult != kIOReturnSuccess) { + usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult); + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + + return darwin_to_libusb (kresult); + } /* Last but not least we need the bus frame number */ kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); @@ -1922,9 +2128,6 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { return darwin_to_libusb (kresult); } - (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); - /* schedule for a frame a little in the future */ frame += 4; @@ -2051,8 +2254,10 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { uint8_t pipeRef, iface; IOReturn kresult; + struct libusb_context *ctx = ITRANSFER_CTX (itransfer); + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) { - usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + usbi_err (ctx, "endpoint not found on any open interface"); return LIBUSB_ERROR_NOT_FOUND; } @@ -2060,7 +2265,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { if (!dpriv->device) return LIBUSB_ERROR_NO_DEVICE; - usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef); + usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef); /* abort transactions */ #if InterfaceVersion >= 550 @@ -2070,7 +2275,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { #endif (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef); - usbi_dbg ("calling clear pipe stall to clear the data toggle bit"); + usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit"); /* newer versions of darwin support clearing additional bits on the device's endpoint */ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); @@ -2099,7 +2304,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); - usbi_dbg ("an async io operation has completed"); + usbi_dbg (TRANSFER_CTX(transfer), "an async io operation has completed"); /* if requested write a zero packet */ if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { @@ -2122,6 +2327,8 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) result = kIOUSBTransactionTimeout; + struct libusb_context *ctx = ITRANSFER_CTX (itransfer); + switch (result) { case kIOReturnUnderrun: case kIOReturnSuccess: @@ -2129,17 +2336,17 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer case kIOReturnAborted: return LIBUSB_TRANSFER_CANCELLED; case kIOUSBPipeStalled: - usbi_dbg ("transfer error: pipe is stalled"); + usbi_dbg (ctx, "transfer error: pipe is stalled"); return LIBUSB_TRANSFER_STALL; case kIOReturnOverrun: - usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun"); + usbi_warn (ctx, "transfer error: data overrun"); return LIBUSB_TRANSFER_OVERFLOW; case kIOUSBTransactionTimeout: - usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out"); + usbi_warn (ctx, "transfer error: 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); + usbi_warn (ctx, "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); return LIBUSB_TRANSFER_ERROR; } } @@ -2148,22 +2355,23 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; - const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"}; + const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL}; bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; + struct libusb_context *ctx = ITRANSFER_CTX (itransfer); if (transfer->type > max_transfer_type) { - usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + usbi_err (ctx, "unknown endpoint type %d", transfer->type); return LIBUSB_ERROR_INVALID_PARAM; } if (NULL == tpriv) { - usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv"); + usbi_err (ctx, "malformed request is missing transfer priv"); return LIBUSB_ERROR_INVALID_PARAM; } - usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result); + usbi_dbg (ctx, "handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result); - if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) { + if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result || kIOUSBTransactionTimeout == tpriv->result) { if (is_isoc && tpriv->isoc_framelist) { /* copy isochronous results back */ @@ -2262,9 +2470,159 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne } #endif +#if InterfaceVersion >= 700 + +/* macOS APIs for getting entitlement values */ + +#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 +#include +#else +typedef struct __SecTask *SecTaskRef; +extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator); +extern CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error); +#endif + +static bool darwin_has_capture_entitlements (void) { + SecTaskRef task; + CFTypeRef value; + bool entitled; + + task = SecTaskCreateFromSelf (kCFAllocatorDefault); + if (task == NULL) { + return false; + } + value = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.vm.device-access"), NULL); + CFRelease (task); + entitled = value && (CFGetTypeID (value) == CFBooleanGetTypeID ()) && CFBooleanGetValue (value); + if (value) { + CFRelease (value); + } + return entitled; +} + +static int darwin_reload_device (struct libusb_device_handle *dev_handle) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + enum libusb_error err; + + usbi_mutex_lock(&darwin_cached_devices_lock); + (*(dpriv->device))->Release(dpriv->device); + dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service); + if (!dpriv->device) { + err = LIBUSB_ERROR_NO_DEVICE; + } else { + err = LIBUSB_SUCCESS; + } + usbi_mutex_unlock(&darwin_cached_devices_lock); + + return err; +} + +/* On macOS, we capture an entire device at once, not individual interfaces. */ + +static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) { + UNUSED(interface); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + enum libusb_error err; + struct libusb_context *ctx = HANDLE_CTX (dev_handle); + + if (HAS_CAPTURE_DEVICE()) { + } else { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + if (dpriv->capture_count == 0) { + usbi_dbg (ctx, "attempting to detach kernel driver from device"); + + if (darwin_has_capture_entitlements ()) { + /* request authorization */ + kresult = IOServiceAuthorize (dpriv->service, kIOServiceInteractionAllowed); + if (kresult != kIOReturnSuccess) { + usbi_warn (ctx, "IOServiceAuthorize: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* we need start() to be called again for authorization status to refresh */ + err = darwin_reload_device (dev_handle); + if (err != LIBUSB_SUCCESS) { + return err; + } + } else { + usbi_info (ctx, "no capture entitlements. may not be able to detach the kernel driver for this device"); + if (0 != geteuid()) { + usbi_warn (ctx, "USB device capture requires either an entitlement (com.apple.vm.device-access) or root privilege"); + return LIBUSB_ERROR_ACCESS; + } + } + + /* reset device to release existing drivers */ + err = darwin_reenumerate_device (dev_handle, true); + if (err != LIBUSB_SUCCESS) { + return err; + } + } + dpriv->capture_count++; + return LIBUSB_SUCCESS; +} + + +static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) { + UNUSED(interface); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + + if (HAS_CAPTURE_DEVICE()) { + } else { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + dpriv->capture_count--; + if (dpriv->capture_count > 0) { + return LIBUSB_SUCCESS; + } + + usbi_dbg (HANDLE_CTX (dev_handle), "reenumerating device for kernel driver attach"); + + /* reset device to attach kernel drivers */ + return darwin_reenumerate_device (dev_handle, false); +} + +static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { + enum libusb_error ret; + if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) { + ret = darwin_detach_kernel_driver (dev_handle, iface); + if (ret != LIBUSB_SUCCESS) { + usbi_info (HANDLE_CTX (dev_handle), "failed to auto-detach the kernel driver for this device, ret=%d", ret); + } + } + + return darwin_claim_interface (dev_handle, iface); +} + +static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { + enum libusb_error ret; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + + ret = darwin_release_interface (dev_handle, iface); + if (ret != LIBUSB_SUCCESS) { + return ret; + } + + if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) { + ret = darwin_attach_kernel_driver (dev_handle, iface); + if (LIBUSB_SUCCESS != ret) { + usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret); + } + /* ignore the error as the interface was successfully released */ + } + + return LIBUSB_SUCCESS; +} + +#endif + const struct usbi_os_backend usbi_backend = { .name = "Darwin", - .caps = 0, + .caps = USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, .init = darwin_init, .exit = darwin_exit, .get_active_config_descriptor = darwin_get_active_config_descriptor, @@ -2275,8 +2633,6 @@ const struct usbi_os_backend usbi_backend = { .close = darwin_close, .get_configuration = darwin_get_configuration, .set_configuration = darwin_set_configuration, - .claim_interface = darwin_claim_interface, - .release_interface = darwin_release_interface, .set_interface_altsetting = darwin_set_interface_altsetting, .clear_halt = darwin_clear_halt, @@ -2289,6 +2645,16 @@ const struct usbi_os_backend usbi_backend = { .kernel_driver_active = darwin_kernel_driver_active, +#if InterfaceVersion >= 700 + .detach_kernel_driver = darwin_detach_kernel_driver, + .attach_kernel_driver = darwin_attach_kernel_driver, + .claim_interface = darwin_capture_claim_interface, + .release_interface = darwin_capture_release_interface, +#else + .claim_interface = darwin_claim_interface, + .release_interface = darwin_release_interface, +#endif + .destroy_device = darwin_destroy_device, .submit_transfer = darwin_submit_transfer, diff --git a/mac/libusb/os/darwin_usb.h b/mac/libusb/os/darwin_usb.h index b799bfd44..7b72fffb8 100644 --- a/mac/libusb/os/darwin_usb.h +++ b/mac/libusb/os/darwin_usb.h @@ -30,59 +30,63 @@ #include #include +#if defined(HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H) +#include +#endif + /* IOUSBInterfaceInferface */ /* New in OS 10.12.0. */ -#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) +#if defined (kIOUSBInterfaceInterfaceID800) #define usb_interface_t IOUSBInterfaceInterface800 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800 #define InterfaceVersion 800 /* New in OS 10.10.0. */ -#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) +#elif defined (kIOUSBInterfaceInterfaceID700) #define usb_interface_t IOUSBInterfaceInterface700 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700 #define InterfaceVersion 700 /* New in OS 10.9.0. */ -#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) +#elif defined (kIOUSBInterfaceInterfaceID650) #define usb_interface_t IOUSBInterfaceInterface650 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650 #define InterfaceVersion 650 /* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) +#elif defined (kIOUSBInterfaceInterfaceID550) #define usb_interface_t IOUSBInterfaceInterface550 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 #define InterfaceVersion 550 /* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) +#elif defined (kIOUSBInterfaceInterfaceID500) #define usb_interface_t IOUSBInterfaceInterface500 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 #define InterfaceVersion 500 /* New in OS 10.5.0. */ -#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +#elif defined (kIOUSBInterfaceInterfaceID300) #define usb_interface_t IOUSBInterfaceInterface300 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 #define InterfaceVersion 300 /* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +#elif defined (kIOUSBInterfaceInterfaceID245) #define usb_interface_t IOUSBInterfaceInterface245 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 #define InterfaceVersion 245 /* New in OS 10.4.0. */ -#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040) +#elif defined (kIOUSBInterfaceInterfaceID220) #define usb_interface_t IOUSBInterfaceInterface220 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 @@ -97,42 +101,42 @@ /* IOUSBDeviceInterface */ /* New in OS 10.9.0. */ -#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) +#if defined (kIOUSBDeviceInterfaceID650) #define usb_device_t IOUSBDeviceInterface650 #define DeviceInterfaceID kIOUSBDeviceInterfaceID650 #define DeviceVersion 650 /* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) +#elif defined (kIOUSBDeviceInterfaceID500) #define usb_device_t IOUSBDeviceInterface500 #define DeviceInterfaceID kIOUSBDeviceInterfaceID500 #define DeviceVersion 500 /* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) +#elif defined (kIOUSBDeviceInterfaceID320) #define usb_device_t IOUSBDeviceInterface320 #define DeviceInterfaceID kIOUSBDeviceInterfaceID320 #define DeviceVersion 320 /* New in OS 10.5.0. */ -#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +#elif defined (kIOUSBDeviceInterfaceID300) #define usb_device_t IOUSBDeviceInterface300 #define DeviceInterfaceID kIOUSBDeviceInterfaceID300 #define DeviceVersion 300 /* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) +#elif defined (kIOUSBDeviceInterfaceID245) #define usb_device_t IOUSBDeviceInterface245 #define DeviceInterfaceID kIOUSBDeviceInterfaceID245 #define DeviceVersion 245 /* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030) +#elif defined (kIOUSBDeviceInterfaceID197) #define usb_device_t IOUSBDeviceInterface197 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197 @@ -144,10 +148,28 @@ #endif +#if !defined(kIOUSBHostInterfaceClassName) +#define kIOUSBHostInterfaceClassName "IOUSBHostInterface" +#endif + +#if !defined(kUSBHostMatchingPropertyInterfaceNumber) +#define kUSBHostMatchingPropertyInterfaceNumber "bInterfaceNumber" +#endif + #if !defined(IO_OBJECT_NULL) #define IO_OBJECT_NULL ((io_object_t) 0) #endif +/* Testing availability */ +#ifndef __has_builtin + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif +#if __has_builtin(__builtin_available) + #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *) +#else + #define HAS_CAPTURE_DEVICE() 0 +#endif + typedef IOCFPlugInInterface *io_cf_plugin_ref_t; typedef IONotificationPortRef io_notification_port_t; @@ -161,11 +183,13 @@ struct darwin_cached_device { USBDeviceAddress address; char sys_path[21]; usb_device_t **device; + io_service_t service; int open_count; UInt8 first_config, active_config, port; int can_enumerate; int refcount; bool in_reenumerate; + int capture_count; }; struct darwin_device_priv { diff --git a/mac/libusb/os/events_posix.c b/mac/libusb/os/events_posix.c index b74189b94..715a2d551 100644 --- a/mac/libusb/os/events_posix.c +++ b/mac/libusb/os/events_posix.c @@ -222,9 +222,9 @@ int usbi_wait_for_events(struct libusb_context *ctx, usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt; int internal_fds, num_ready; - usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); + usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); num_ready = poll(fds, nfds, timeout_ms); - usbi_dbg("poll() returned %d", num_ready); + usbi_dbg(ctx, "poll() returned %d", num_ready); if (num_ready == 0) { if (usbi_using_timer(ctx)) goto done; @@ -279,7 +279,7 @@ int usbi_wait_for_events(struct libusb_context *ctx, continue; /* pollfd was removed between the creation of the fds array and * here. remove triggered revent as it is no longer relevant. */ - usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd); + usbi_dbg(ctx, "fd %d was removed, ignoring raised events", fds[n].fd); fds[n].revents = 0; num_ready--; break; diff --git a/mac/libusb/sync.c b/mac/libusb/sync.c index adc95b402..1fa1f0be5 100644 --- a/mac/libusb/sync.c +++ b/mac/libusb/sync.c @@ -36,7 +36,7 @@ static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) { int *completed = transfer->user_data; *completed = 1; - usbi_dbg("actual_length=%d", transfer->actual_length); + usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); /* caller interprets result and frees transfer */ } diff --git a/mac/libusb/version.h b/mac/libusb/version.h index d8ebde4e3..fe95d84b6 100644 --- a/mac/libusb/version.h +++ b/mac/libusb/version.h @@ -7,7 +7,7 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 24 +#define LIBUSB_MICRO 26 #endif #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 diff --git a/mac/libusb/version_nano.h b/mac/libusb/version_nano.h index 0f100a824..dbd5d5f5b 100644 --- a/mac/libusb/version_nano.h +++ b/mac/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11584 +#define LIBUSB_NANO 11724