From: osy <50960678+osy@users.noreply.github.com> Date: Wed, 12 May 2021 03:56:27 +0000 (-0700) Subject: darwin: use detach kernel APIs for capture X-Git-Tag: upstream/1.0.25~80 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=abd788c203624930241f45f32b501c16936bd806;p=platform%2Fupstream%2Flibusb.git darwin: use detach kernel APIs for capture When libusb_detach_kernel_driver() is called on the first interface, we use the capture re-enumerate APIs to force kernel drivers to detach. On subsequent calls, a counter is incremented to keep track of the number of detach calls. When libusb_attach_kernel_driver() is called for the same number of times, then we re-enumerate (reset) and let kernel drivers re-attach. darwin_kernel_driver_active() is changed to attempt to claim an interface and return 1 only if a kIOReturnExclusiveAccess status is returned. The old implementation which looks for a CFBundleID in the IORegistryEntry does not seem to work in all cases. darwin_reset_device() is aware of the capture status and will re-set twice if capture mode is enabled: once to do a USB reset and again to re-capture. Signed-off-by: Nathan Hjelm --- diff --git a/libusb/core.c b/libusb/core.c index e04ec67..7a2f6c3 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -1994,7 +1994,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 @@ -2030,10 +2030,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 diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index 0a3b79f..988ca9e 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -1674,10 +1674,11 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t return LIBUSB_SUCCESS; } -static int darwin_reenumerate_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; @@ -1701,14 +1702,30 @@ static int darwin_reenumerate_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) { + 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); } + /* 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; @@ -1744,30 +1761,25 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle) { 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) { @@ -2280,9 +2292,98 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne } #endif +#if InterfaceVersion >= 700 + +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) { + /* 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; + } else { + dpriv->capture_count = 0; + } + /* 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) { + return 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; + + ret = darwin_release_interface (dev_handle, iface); + if (ret != LIBUSB_SUCCESS) { + return ret; + } + if (dev_handle->auto_detach_kernel_driver) { + ret = darwin_attach_kernel_driver (dev_handle, iface); + } + return ret; +} + +#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, @@ -2293,12 +2394,10 @@ 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, - .reset_device = darwin_reenumerate_device, + .reset_device = darwin_reset_device, #if InterfaceVersion >= 550 .alloc_streams = darwin_alloc_streams, @@ -2307,6 +2406,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/libusb/os/darwin_usb.h b/libusb/os/darwin_usb.h index b799bfd..179341c 100644 --- a/libusb/os/darwin_usb.h +++ b/libusb/os/darwin_usb.h @@ -148,6 +148,16 @@ #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; @@ -166,6 +176,7 @@ struct darwin_cached_device { int can_enumerate; int refcount; bool in_reenumerate; + int capture_count; }; struct darwin_device_priv { diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 8ed8ddc..fcbf3cd 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11611 +#define LIBUSB_NANO 11612