diff options
author | dianshi <dos21h@gmail.com> | 2022-03-11 15:58:03 +0000 |
---|---|---|
committer | dianshi <dos21h@gmail.com> | 2022-03-11 15:58:03 +0000 |
commit | 5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa (patch) | |
tree | e31a4629eb1ced145622571cc67d06ee23818f1f /src | |
parent | c6d603981adec7c0099fb48fc3369517b458ee85 (diff) | |
download | librusb-5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa.tar.gz librusb-5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa.zip |
Fix linux compilation
Diffstat (limited to 'src')
-rw-r--r-- | src/config.h | 157 | ||||
-rw-r--r-- | src/make.mk | 36 | ||||
-rw-r--r-- | src/os/linux/linux_udev.c (renamed from src/os/linux_udev.c) | 0 | ||||
-rw-r--r-- | src/os/linux/linux_usbfs.c (renamed from src/os/linux_usbfs.c) | 0 | ||||
-rw-r--r-- | src/os/linux/linux_usbfs.h (renamed from src/os/linux_usbfs.h) | 0 | ||||
-rw-r--r-- | src/os/macos/darwin_usb.c | 2611 | ||||
-rw-r--r-- | src/os/macos/darwin_usb.h | 227 |
7 files changed, 3023 insertions, 8 deletions
diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..97a82c2 --- /dev/null +++ b/src/config.h @@ -0,0 +1,157 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to the attribute for default visibility. */ +#define DEFAULT_VISIBILITY __attribute__ ((visibility ("default"))) + +/* Define to 1 to start with debug message logging enabled. */ +/* #undef ENABLE_DEBUG_LOGGING */ + +/* Define to 1 to enable message logging. */ +#define ENABLE_LOGGING 1 + +/* Define to 1 if you have the <asm/types.h> header file. */ +/* #undef HAVE_ASM_TYPES_H */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the declaration of `EFD_CLOEXEC', and to 0 if you + don't. */ +#define HAVE_DECL_EFD_CLOEXEC 1 + +/* Define to 1 if you have the declaration of `EFD_NONBLOCK', and to 0 if you + don't. */ +#define HAVE_DECL_EFD_NONBLOCK 1 + +/* Define to 1 if you have the declaration of `TFD_CLOEXEC', and to 0 if you + don't. */ +#define HAVE_DECL_TFD_CLOEXEC 1 + +/* Define to 1 if you have the declaration of `TFD_NONBLOCK', and to 0 if you + don't. */ +#define HAVE_DECL_TFD_NONBLOCK 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if the system has eventfd functionality. */ +#define HAVE_EVENTFD 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <IOKit/usb/IOUSBHostFamilyDefinitions.h> header + file. */ +/* #undef HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H */ + +/* Define to 1 if you have the `udev' library (-ludev). */ +#define HAVE_LIBUDEV 1 + +/* Define to 1 if the system has the type `nfds_t'. */ +#define HAVE_NFDS_T 1 + +/* Define to 1 if you have the `pipe2' function. */ +#define HAVE_PIPE2 1 + +/* Define to 1 if you have the `pthread_condattr_setclock' function. */ +#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1 + +/* Define to 1 if you have the `pthread_setname_np' function. */ +#define HAVE_PTHREAD_SETNAME_NP 1 + +/* Define to 1 if you have the `pthread_threadid_np' function. */ +/* #undef HAVE_PTHREAD_THREADID_NP */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if the system has the type `struct timespec'. */ +/* #undef HAVE_STRUCT_TIMESPEC */ + +/* Define to 1 if you have the `syslog' function. */ +/* #undef HAVE_SYSLOG */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if the system has timerfd functionality. */ +#define HAVE_TIMERFD 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libusb-1.0" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libusb-devel@lists.sourceforge.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libusb-1.0" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libusb-1.0 1.0.25" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libusb-1.0" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "http://libusb.info" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.25" + +/* Define to 1 if compiling for a POSIX platform. */ +#define PLATFORM_POSIX 1 + +/* Define to 1 if compiling for a Windows platform. */ +/* #undef PLATFORM_WINDOWS */ + +/* Define to the attribute for enabling parameter checks on printf-like + functions. */ +#define PRINTF_FORMAT(a, b) __attribute__ ((__format__ (__printf__, a, b))) + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define to 1 to output logging messages to the systemwide log. */ +/* #undef USE_SYSTEM_LOGGING_FACILITY */ + +/* Version number of package */ +#define VERSION "1.0.25" + +/* Enable GNU extensions. */ +#define _GNU_SOURCE 1 + +/* Define to the oldest supported Windows version. */ +/* #undef _WIN32_WINNT */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/src/make.mk b/src/make.mk index acf8f12..85d90e8 100644 --- a/src/make.mk +++ b/src/make.mk @@ -1,20 +1,40 @@ DIR=src -SRC_UTILS_PRE += $(wildcard $(DIR)/os/*.c) -SRC_UTILS += $(wildcard $(DIR)/*.c) -OBJ_UTILS += $(SRC_UTILS:.c=.o) -OBJ_UTILS_PRE += $(SRC_UTILS_PRE:.c=.o) -LDFLAGS_UTILS = -INCLUDE = -I./include -I./src -CFLAGS = -fPIC +SRC_UTILS_PRE += $(wildcard $(DIR)/os/*.c) +SRC_UTILS += $(wildcard $(DIR)/*.c) +SRC_UTILS_PRE_L += $(wildcard $(DIR)/os/linux/*.c) +SRC_UTILS_PRE_M = $(wildcard $(DIR)/os/macos/*.c) +OBJ_UTILS += $(SRC_UTILS:.c=.o) +OBJ_UTILS_PRE += $(SRC_UTILS_PRE:.c=.o) +OBJ_UTILS_PRE_M += $(SRC_UTILS_PRE_M:.c=.o) +OBJ_UTILS_PRE_L += $(SRC_UTILS_PRE_L:.c=.o) +LDFLAGS_UTILS = +INCLUDE = -I./include -I./src +CFLAGS = -fPIC build-src: src-pre + echo "Hello" echo $(SRC_UTILS_PRE) echo $(SRC_UTILS) -$(DIR)-pre: $(OBJ_UTILS_PRE) $(OBJ_UTILS) + + +build-src-macos: print src-pre-macos + echo "Build macos" + +print: + echo "$(OBJ_UTILS_PRE)" + echo "$(OBJ_UTILS_PRE_M)" + echo "$(OBJ_UTILS)" + +$(DIR)-pre-macos: $(OBJ_UTILS_PRE) $(OBJ_UTILS_PRE_M) $(OBJ_UTILS) + echo "here 2" + +$(DIR)-pre: $(OBJ_UTILS_PRE) $(OBJ_UTILS_PRE_L) $(OBJ_UTILS) + echo "here 1" $(DIR)/os/%.o: $(DIR)/os/%.c + echo "A" $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $(BUILD_DIR)$@ $(DIR)/%.o: $(DIR)/%.c diff --git a/src/os/linux_udev.c b/src/os/linux/linux_udev.c index 9ec9eb1..9ec9eb1 100644 --- a/src/os/linux_udev.c +++ b/src/os/linux/linux_udev.c diff --git a/src/os/linux_usbfs.c b/src/os/linux/linux_usbfs.c index c300675..c300675 100644 --- a/src/os/linux_usbfs.c +++ b/src/os/linux/linux_usbfs.c diff --git a/src/os/linux_usbfs.h b/src/os/linux/linux_usbfs.h index 1238ffa..1238ffa 100644 --- a/src/os/linux_usbfs.h +++ b/src/os/linux/linux_usbfs.h diff --git a/src/os/macos/darwin_usb.c b/src/os/macos/darwin_usb.c new file mode 100644 index 0000000..903422c --- /dev/null +++ b/src/os/macos/darwin_usb.c @@ -0,0 +1,2611 @@ +/* -*- Mode: C; indent-tabs-mode:nil -*- */ +/* + * darwin backend for libusb 1.0 + * Copyright © 2008-2021 Nathan Hjelm <hjelmn@cs.unm.edu> + * 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 + * 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 + */ + +#include <config.h> +#include <assert.h> +#include <time.h> +#include <ctype.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/sysctl.h> + +#include <mach/clock.h> +#include <mach/clock_types.h> +#include <mach/mach_host.h> +#include <mach/mach_port.h> + +/* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector + * 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 10000000 + +#include <AvailabilityMacros.h> +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + #include <objc/objc-auto.h> +#endif + +#include "darwin_usb.h" + +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; + +#if !defined(HAVE_CLOCK_GETTIME) +static clock_serv_t clock_realtime; +static clock_serv_t clock_monotonic; +#endif + +#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1) + +static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ +static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */ + +static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER; +static struct list_head darwin_cached_devices; +static const char *darwin_device_class = "IOUSBDevice"; + +#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev) + +/* async event thread */ +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 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(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out, + UInt64 *old_session_id); + +#if defined(ENABLE_LOGGING) +static const char *darwin_error_str (IOReturn result) { + static char string_buffer[50]; + switch (result) { + case kIOReturnSuccess: + return "no error"; + case kIOReturnNotOpen: + return "device not opened for exclusive access"; + case kIOReturnNoDevice: + return "no connection to an IOService"; + case kIOUSBNoAsyncPortErr: + return "no async port has been opened for interface"; + 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"; + case kIOUSBTransactionTimeout: + return "transaction timed out"; + case kIOReturnBadArgument: + return "invalid argument"; + case kIOReturnAborted: + return "transaction aborted"; + case kIOReturnNotResponding: + return "device not responding"; + case kIOReturnOverrun: + return "data overrun"; + case kIOReturnCannotWire: + return "physical memory can not be wired down"; + case kIOReturnNoResources: + return "out of resources"; + case kIOUSBHighSpeedSplitError: + return "high speed split error"; + case kIOUSBUnknownPipeErr: + return "pipe ref not recognized"; + default: + snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result); + return string_buffer; + } +} +#endif + +static enum libusb_error darwin_to_libusb (IOReturn result) { + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_SUCCESS; + case kIOReturnNotOpen: + case kIOReturnNoDevice: + return LIBUSB_ERROR_NO_DEVICE; + 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: + default: + return LIBUSB_ERROR_OTHER; + } +} + +/* this function must be called with the darwin_cached_devices_lock held */ +static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount--; + /* free the device and remove it from the cache */ + if (0 == cached_dev->refcount) { + list_del(&cached_dev->list); + + if (cached_dev->device) { + (*(cached_dev->device))->Release(cached_dev->device); + cached_dev->device = NULL; + } + IOObjectRelease (cached_dev->service); + free (cached_dev); + } +} + +static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount++; +} + +static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + + /* current interface */ + struct darwin_interface *cInterface; + + uint8_t i, iface; + + 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]; + + if (dev_handle->claimed_interfaces & (1U << iface)) { + for (i = 0 ; i < cInterface->num_endpoints ; i++) { + if (cInterface->endpoint_addrs[i] == ep) { + *pipep = i + 1; + + if (ifcp) + *ifcp = iface; + + if (interface_out) + *interface_out = cInterface; + + usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface); + return LIBUSB_SUCCESS; + } + } + } + } + + /* No pipe found with the correct endpoint address */ + usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep); + + return LIBUSB_ERROR_NOT_FOUND; +} + +static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { + CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class); + + if (!matchingDict) + return kIOReturnError; + + if (location) { + CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + /* there are no unsigned CFNumber types so treat the value as signed. the OS seems to do this + internally (CFNumberType of locationID is kCFNumberSInt32Type) */ + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); + + if (propertyMatchDict && locationCF) { + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); + CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + } + /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */ + + /* release our references as per the Create Rule */ + if (propertyMatchDict) + CFRelease (propertyMatchDict); + if (locationCF) + CFRelease (locationCF); + } + + return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator); +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { + CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + Boolean success = 0; + + if (cfNumber) { + if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { + success = CFNumberGetValue(cfNumber, type, p); + } + + CFRelease (cfNumber); + } + + return (success != 0); +} + +/* Returns 1 on success, 0 on failure. */ +static bool get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) { + CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + bool success = false; + + if (cfData) { + if (CFGetTypeID (cfData) == CFDataGetTypeID ()) { + CFIndex length = CFDataGetLength (cfData); + if (length < size) { + size = length; + } + + CFDataGetBytes (cfData, CFRangeMake(0, size), p); + success = true; + } + + CFRelease (cfData); + } + + return success; +} + +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; + IOReturn kresult; + SInt32 score; + const int max_retries = 5; + + /* The IOCreatePlugInInterfaceForService function might consistently return + an "out of resources" error with certain USB devices the first time we run + it. The reason is still unclear, but retrying fixes the problem */ + for (int count = 0; count < max_retries; count++) { + kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); + if (kIOReturnSuccess == kresult && plugInInterface) { + break; + } + + 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 (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult)); + return NULL; + } + + (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), + (LPVOID)&device); + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); + + return device; +} + +static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { + UNUSED(ptr); + struct darwin_cached_device *cached_device; + UInt64 old_session_id; + struct libusb_context *ctx; + io_service_t service; + int ret; + + usbi_mutex_lock(&active_contexts_lock); + + while ((service = IOIteratorNext(add_devices))) { + ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id); + if (ret < 0 || !cached_device->can_enumerate) { + continue; + } + + /* add this device to each active context's device list */ + for_each_context(ctx) { + process_new_device (ctx, cached_device, old_session_id); + } + + if (cached_device->in_reenumerate) { + usbi_dbg (NULL, "cached device in reset state. reset complete..."); + cached_device->in_reenumerate = false; + } + + IOObjectRelease(service); + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { + UNUSED(ptr); + struct libusb_device *dev = NULL; + struct libusb_context *ctx; + struct darwin_cached_device *old_device; + + io_service_t device; + UInt64 session, locationID; + int ret; + + usbi_mutex_lock(&active_contexts_lock); + + while ((device = IOIteratorNext (rem_devices)) != 0) { + bool is_reenumerating = false; + + /* 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; + + /* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function + otherwise no cached device will ever get freed */ + usbi_mutex_lock(&darwin_cached_devices_lock); + list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) { + if (old_device->session == session) { + 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 (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) { + (*(old_device->device))->Release(old_device->device); + old_device->device = NULL; + } + + is_reenumerating = true; + } else { + darwin_deref_cached_device (old_device); + } + + break; + } + } + + usbi_mutex_unlock(&darwin_cached_devices_lock); + if (is_reenumerating) { + continue; + } + + for_each_context(ctx) { + usbi_dbg (ctx, "notifying context %p of device disconnect", ctx); + + dev = usbi_get_device_by_session_id(ctx, (unsigned long) session); + if (dev) { + /* signal the core that this device has been disconnected. the core will tear down this device + when the reference count reaches 0 */ + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } + } + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_hotplug_poll (void) +{ + /* not sure if 1 ms will be too long/short but it should work ok */ + mach_timespec_t timeout = {.tv_sec = 0, .tv_nsec = 1000000ul}; + + /* 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 (darwin_default_master_port, &timeout); +} + +static void darwin_clear_iterator (io_iterator_t iter) { + io_service_t device; + + while ((device = IOIteratorNext (iter)) != 0) + IOObjectRelease (device); +} + +static void darwin_fail_startup(void) { + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfl = LIBUSB_DARWIN_STARTUP_FAILURE; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + pthread_exit (NULL); +} + +static void *darwin_event_thread_main (void *arg0) { + IOReturn kresult; + struct libusb_context *ctx = (struct libusb_context *)arg0; + CFRunLoopRef runloop; + CFRunLoopSourceRef libusb_shutdown_cfsource; + CFRunLoopSourceContext libusb_shutdown_cfsourcectx; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + /* Set this thread's name, so it can be seen in the debugger + and crash reports. */ + pthread_setname_np ("org.libusb.device-hotplug"); +#endif + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + /* Tell the Objective-C garbage collector about this thread. + This is required because, unlike NSThreads, pthreads are + not automatically registered. Although we don't use + Objective-C, we use CoreFoundation, which does. + Garbage collection support was entirely removed in 10.12, + so don't bother there. */ + objc_registerThreadWithCollector(); +#endif + + /* hotplug (device arrival/removal) sources */ + CFRunLoopSourceRef libusb_notification_cfsource; + io_notification_port_t libusb_notification_port; + io_iterator_t libusb_rem_device_iterator; + io_iterator_t libusb_add_device_iterator; + + usbi_dbg (ctx, "creating hotplug event source"); + + runloop = CFRunLoopGetCurrent (); + CFRetain (runloop); + + /* add the shutdown cfsource to the run loop */ + memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx)); + libusb_shutdown_cfsourcectx.info = runloop; + libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop; + libusb_shutdown_cfsource = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx); + CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); + + /* add the notification port to the run loop */ + libusb_notification_port = IONotificationPortCreate (darwin_default_master_port); + libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port); + CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* create notifications for removed devices */ + kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_detached, + ctx, &libusb_rem_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); + } + + /* create notifications for attached devices */ + kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_attached, + ctx, &libusb_add_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); + } + + /* arm notifiers */ + darwin_clear_iterator (libusb_rem_device_iterator); + darwin_clear_iterator (libusb_add_device_iterator); + + 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); + libusb_darwin_acfl = runloop; + libusb_darwin_acfls = libusb_shutdown_cfsource; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + + /* run the runloop */ + CFRunLoopRun(); + + usbi_dbg (ctx, "darwin event thread exiting"); + + /* signal the main thread that the hotplug runloop has finished. */ + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfls = NULL; + libusb_darwin_acfl = NULL; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + + /* remove the notification cfsource */ + CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* remove the shutdown cfsource */ + CFRunLoopRemoveSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); + + /* delete notification port */ + IONotificationPortDestroy (libusb_notification_port); + + /* delete iterators */ + IOObjectRelease (libusb_rem_device_iterator); + IOObjectRelease (libusb_add_device_iterator); + + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + + pthread_exit (NULL); +} + +/* cleanup function to destroy cached devices */ +static void darwin_cleanup_devices(void) { + struct darwin_cached_device *dev, *next; + + list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { + darwin_deref_cached_device(dev); + } +} + +static int darwin_init(struct libusb_context *ctx) { + bool first_init; + int rc; + + first_init = (1 == ++init_count); + + do { + if (first_init) { + 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; + + host_self = mach_host_self(); + host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); + host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); + mach_port_deallocate(mach_task_self(), host_self); +#endif + } + + rc = darwin_scan_devices (ctx); + if (LIBUSB_SUCCESS != rc) + break; + + if (first_init) { + rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); + if (0 != rc) { + usbi_err (ctx, "could not create event thread, error %d", rc); + rc = LIBUSB_ERROR_OTHER; + break; + } + + pthread_mutex_lock (&libusb_darwin_at_mutex); + while (!libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) { + libusb_darwin_acfl = NULL; + rc = LIBUSB_ERROR_OTHER; + } + pthread_mutex_unlock (&libusb_darwin_at_mutex); + + if (0 != rc) + pthread_join (libusb_darwin_at, NULL); + } + } while (0); + + if (LIBUSB_SUCCESS != rc) { + if (first_init) { + darwin_cleanup_devices (); +#if !defined(HAVE_CLOCK_GETTIME) + mach_port_deallocate(mach_task_self(), clock_realtime); + mach_port_deallocate(mach_task_self(), clock_monotonic); +#endif + } + --init_count; + } + + return rc; +} + +static void darwin_exit (struct libusb_context *ctx) { + UNUSED(ctx); + + if (0 == --init_count) { + /* stop the event runloop and wait for the thread to terminate. */ + pthread_mutex_lock (&libusb_darwin_at_mutex); + CFRunLoopSourceSignal (libusb_darwin_acfls); + CFRunLoopWakeUp (libusb_darwin_acfl); + while (libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + pthread_join (libusb_darwin_at, NULL); + + darwin_cleanup_devices (); + +#if !defined(HAVE_CLOCK_GETTIME) + mach_port_deallocate(mach_task_self(), clock_realtime); + mach_port_deallocate(mach_task_self(), clock_monotonic); +#endif + } +} + +static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + UInt8 i, numConfig; + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + + /* is there a simpler way to determine the index? */ + kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + for (i = 0 ; i < numConfig ; i++) { + (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc); + + if (desc->bConfigurationValue == config_value) + return i; + } + + /* configuration not found */ + return LIBUSB_ERROR_NOT_FOUND; +} + +static int darwin_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + int config_index; + + if (0 == priv->active_config) + return LIBUSB_ERROR_NOT_FOUND; + + config_index = get_configuration_index (dev, priv->active_config); + if (config_index < 0) + return config_index; + + assert(config_index >= 0 && config_index <= UINT8_MAX); + return darwin_get_config_descriptor (dev, (UInt8)config_index, buffer, len); +} + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + int ret; + + if (!priv || !priv->device) + return LIBUSB_ERROR_OTHER; + + kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc); + if (kresult == kIOReturnSuccess) { + /* copy descriptor */ + if (libusb_le16_to_cpu(desc->wTotalLength) < len) + len = libusb_le16_to_cpu(desc->wTotalLength); + + memmove (buffer, desc, len); + } + + ret = darwin_to_libusb (kresult); + if (ret != LIBUSB_SUCCESS) + return ret; + + return (int) len; +} + +/* check whether the os has configured the device */ +static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { + usb_device_t **darwin_device = dev->dev |