#include <assert.h>
#include <time.h>
#include <ctype.h>
-#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
* 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>
#include "darwin_usb.h"
-static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static int init_count = 0;
/* async event thread */
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);
(*(cached_dev->device))->Release(cached_dev->device);
cached_dev->device = NULL;
}
+ IOObjectRelease (cached_dev->service);
free (cached_dev);
}
}
}
/* add this device to each active context's device list */
- list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ for_each_context(ctx) {
process_new_device (ctx, cached_device, old_session_id);
}
struct darwin_cached_device *old_device;
io_service_t device;
- UInt64 session;
+ UInt64 session, locationID;
int ret;
usbi_mutex_lock(&active_contexts_lock);
/* 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;
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 detatched due to re-enumeration");
+ usbi_dbg ("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) {
continue;
}
- list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ for_each_context(ctx) {
usbi_dbg ("notifying context %p of device disconnect", ctx);
dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
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;
--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);
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) {
(*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
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);
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;
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);
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
}
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++)
}
if (!usbInterface) {
- usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+ usbi_info (HANDLE_CTX (dev_handle), "interface not found");
return LIBUSB_ERROR_NOT_FOUND;
}
/* 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 (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
IOReturn kresult;
enum libusb_error ret;
+ int i;
+ uint8_t old_alt_setting;
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
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;
+ }
+ else
+ usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult));
+
+ if (kresult != kIOUSBPipeStalled)
+ 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.
+ Mimick 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. */
+
+ /* For some reason we need to reclaim the interface after the pipe error */
+ ret = darwin_claim_interface (dev_handle, iface);
- /* 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");
- return ret;
+ usbi_err (HANDLE_CTX (dev_handle), "could not reclaim interface");
}
- return darwin_to_libusb (kresult);
+ /* Return error if a change to another value was attempted */
+ kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &old_alt_setting);
+ if (kresult == kIOReturnSuccess && altsetting != old_alt_setting)
+ return LIBUSB_ERROR_PIPE;
+
+ for (i = 0 ; i < cInterface->num_endpoints ; i++)
+ darwin_clear_halt(dev_handle, cInterface->endpoint_addrs[i]);
+
+ return LIBUSB_SUCCESS;
}
static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
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;
+ UInt32 time;
if (dpriv->in_reenumerate) {
/* ack, two (or more) threads are trying to reset the device! abort! */
memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
}
+ /* if we need to release capture */
+ if (HAS_CAPTURE_DEVICE()) {
+ if (capture) {
+ options |= kUSBReEnumerateCaptureDeviceMask;
+ }
+ } 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));
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 ("darwin/reenumerate_device: restoring state...");
+ dpriv->in_reenumerate = false;
+ return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
+ }
+
+ usbi_dbg ("darwin/reenumerate_device: waiting for re-enumeration to complete...");
+ time = 0;
while (dpriv->in_reenumerate) {
struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
nanosleep (&delay, NULL);
+ if (time++ >= DARWIN_REENUMERATE_TIMEOUT_US) {
+ usbi_err (HANDLE_CTX (dev_handle), "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 ("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 ("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 ("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 ("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;
- 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));
-
+ if (dpriv->capture_count > 0) {
+ /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */
+ kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
return darwin_to_libusb (kresult);
+ } else {
+ return darwin_reenumerate_device (dev_handle, false);
}
+}
- driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
- IOObjectRelease (usbInterface);
-
- if (driver) {
- CFRelease (driver);
-
- return 1;
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+ enum libusb_error ret = darwin_claim_interface (dev_handle, interface);
+ if (ret == LIBUSB_SUCCESS) {
+ darwin_release_interface (dev_handle, interface);
}
-
- /* no driver */
- return 0;
+ return (ret == LIBUSB_ERROR_ACCESS);
}
static void darwin_destroy_device(struct libusb_device *dev) {
struct darwin_interface *cInterface;
#if InterfaceVersion >= 550
- IOUSBEndpointProperties pipeProperties;
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
#else
/* None of the values below are used in libusb for bulk transfers */
uint8_t direction, number, interval;
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);
- bool isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
- bool isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
- bool isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
- bool isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
- int i;
+ const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
+ const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL};
+ bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
- if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+ if (transfer->type > max_transfer_type) {
usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
- usbi_dbg ("handling %s completion with kernel status %d",
- isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result);
+ if (NULL == tpriv) {
+ usbi_err (TRANSFER_CTX(transfer), "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);
- if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
- if (isIsoc && tpriv->isoc_framelist) {
+ if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result || kIOUSBTransactionTimeout == tpriv->result) {
+ if (is_isoc && tpriv->isoc_framelist) {
/* copy isochronous results back */
- for (i = 0; i < transfer->num_iso_packets ; i++) {
+ for (int i = 0; i < transfer->num_iso_packets ; i++) {
struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
lib_desc->status = darwin_transfer_status (itransfer, tpriv->isoc_framelist[i].frStatus);
lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
}
- } else if (!isIsoc)
+ } else if (!is_isoc) {
itransfer->transferred += tpriv->size;
+ }
}
/* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
}
#if !defined(HAVE_CLOCK_GETTIME)
-int usbi_clock_gettime(int clk_id, struct timespec *tp) {
+void usbi_get_monotonic_time(struct timespec *tp) {
mach_timespec_t sys_time;
- clock_serv_t clock_ref;
-
- switch (clk_id) {
- case USBI_CLOCK_REALTIME:
- /* CLOCK_REALTIME represents time since the epoch */
- clock_ref = clock_realtime;
- break;
- case USBI_CLOCK_MONOTONIC:
- /* use system boot time as reference for the monotonic clock */
- clock_ref = clock_monotonic;
- break;
- default:
- errno = EINVAL;
- return -1;
- }
- clock_get_time (clock_ref, &sys_time);
+ /* use system boot time as reference for the monotonic clock */
+ clock_get_time (clock_monotonic, &sys_time);
tp->tv_sec = sys_time.tv_sec;
tp->tv_nsec = sys_time.tv_nsec;
+}
- return 0;
+void usbi_get_real_time(struct timespec *tp) {
+ mach_timespec_t sys_time;
+
+ /* CLOCK_REALTIME represents time since the epoch */
+ clock_get_time (clock_realtime, &sys_time);
+
+ tp->tv_sec = sys_time.tv_sec;
+ tp->tv_nsec = sys_time.tv_nsec;
}
#endif
uint8_t pipeRef;
int rc, i;
- /* find the mimimum number of supported streams on the endpoint list */
+ /* find the minimum number of supported streams on the endpoint list */
for (i = 0 ; i < num_endpoints ; ++i) {
if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) {
return rc;
}
#endif
+#if InterfaceVersion >= 700
+
+/* macOS APIs for getting entitlement values */
+
+#if TARGET_OS_OSX
+#include <Security/Security.h>
+#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 (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;
+
+ if (HAS_CAPTURE_DEVICE()) {
+ } else {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ if (dpriv->capture_count == 0) {
+ usbi_dbg ("attempting to detach kernel driver from device");
+
+ if (!darwin_has_capture_entitlements ()) {
+ usbi_info (HANDLE_CTX (dev_handle), "no capture entitlements. can not detach the kernel driver for this device");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ /* request authorization */
+ kresult = IOServiceAuthorize (dpriv->service, kIOServiceInteractionAllowed);
+ if (kresult != kIOReturnSuccess) {
+ usbi_warn (HANDLE_CTX (dev_handle), "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;
+ }
+
+ /* 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 ("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) {
+ 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,
.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,
.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,