summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authordianshi <dos21h@gmail.com>2022-03-11 15:58:03 +0000
committerdianshi <dos21h@gmail.com>2022-03-11 15:58:03 +0000
commit5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa (patch)
treee31a4629eb1ced145622571cc67d06ee23818f1f /src
parentc6d603981adec7c0099fb48fc3369517b458ee85 (diff)
downloadlibrusb-5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa.tar.gz
librusb-5ee81fbbaf5c6a3bbc583d37ce324cf8546dc9aa.zip
Fix linux compilation
Diffstat (limited to 'src')
-rw-r--r--src/config.h157
-rw-r--r--src/make.mk36
-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.c2611
-rw-r--r--src/os/macos/darwin_usb.h227
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