+/* -*- Mode: C; indent-tabs-mode:nil -*- */
/*
* darwin backend for libusb 1.0
- * Copyright (C) 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2014 Nathan Hjelm <hjelmn@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <config.h>
+#include "config.h"
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <fcntl.h>
#include <libkern/OSAtomic.h>
#include <mach/clock.h>
#include <objc/objc-auto.h>
#endif
-#include <IOKit/IOCFBundle.h>
-#include <IOKit/usb/IOUSBLib.h>
-#include <IOKit/IOCFPlugIn.h>
-
#include "darwin_usb.h"
/* async event thread */
-static pthread_mutex_t libusb_darwin_at_mutex;
-static pthread_cond_t libusb_darwin_at_cond;
+static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
static clock_serv_t clock_realtime;
static clock_serv_t clock_monotonic;
-static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */
+static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
static volatile int32_t initCount = 0;
+static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
+
+#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
+
/* async event thread */
static pthread_t libusb_darwin_at;
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
+static int darwin_scan_devices(struct libusb_context *ctx);
+static int process_new_device (struct libusb_context *ctx, io_service_t service);
+
+#if defined(ENABLE_LOGGING)
static const char *darwin_error_str (int result) {
+ static char string_buffer[50];
switch (result) {
case kIOReturnSuccess:
return "no error";
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";
default:
- return "unknown error";
+ snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result);
+ return string_buffer;
}
}
+#endif
static int darwin_to_libusb (int result) {
switch (result) {
}
}
+/* 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);
+
+ (*(cached_dev->device))->Release(cached_dev->device);
+ free (cached_dev);
+ }
+}
-static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp) {
+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 = (struct darwin_device_handle_priv *)dev_handle->os_priv;
/* current interface */
int8_t i, iface;
- usbi_info (HANDLE_CTX(dev_handle), "converting ep address 0x%02x to pipeRef and interface", ep);
+ usbi_dbg ("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 & (1 << iface)) {
for (i = 0 ; i < cInterface->num_endpoints ; i++) {
- if (cInterface->endpoint_addrs[i] == ep) {
- *pipep = i + 1;
- *ifcp = iface;
- usbi_info (HANDLE_CTX(dev_handle), "pipe %d on interface %d matches", *pipep, *ifcp);
- return 0;
- }
+ if (cInterface->endpoint_addrs[i] == ep) {
+ *pipep = i + 1;
+
+ if (ifcp)
+ *ifcp = iface;
+
+ if (interface_out)
+ *interface_out = cInterface;
+
+ usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+ return 0;
+ }
}
}
}
/* No pipe found with the correct endpoint address */
usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep);
- return -1;
+ return LIBUSB_ERROR_NOT_FOUND;
}
-static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long location) {
+static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (!matchingDict)
&kCFTypeDictionaryValueCallBacks);
if (propertyMatchDict) {
- CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberLongType, &location);
+ /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this
+ internally (CFNumberType of locationID is 3) */
+ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
/* release our reference to the CFNumber (CFDictionarySetValue retains it) */
return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
}
-static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) {
- io_cf_plugin_ref_t *plugInInterface = NULL;
- usb_device_t **device;
- io_service_t usbDevice;
- long result;
- SInt32 score;
+/* Returns 1 on success, 0 on failure. */
+static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
+ CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
+ int ret = 0;
- if (!IOIteratorIsValid (deviceIterator))
- return NULL;
+ if (cfNumber) {
+ if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) {
+ ret = CFNumberGetValue(cfNumber, type, p);
+ }
+ CFRelease (cfNumber);
+ }
- while ((usbDevice = IOIteratorNext(deviceIterator))) {
- result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
- kIOCFPlugInInterfaceID, &plugInInterface,
- &score);
+ return ret;
+}
- /* we are done with the usb_device_t */
- (void)IOObjectRelease(usbDevice);
- if (kIOReturnSuccess == result && plugInInterface)
- break;
+static usb_device_t **darwin_device_from_service (io_service_t service)
+{
+ io_cf_plugin_ref_t *plugInInterface = NULL;
+ usb_device_t **device;
+ kern_return_t result;
+ SInt32 score;
- usbi_dbg ("libusb/darwin.c usb_get_next_device: could not set up plugin for service: %s\n", darwin_error_str (result));
- }
+ result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface,
+ &score);
- if (!usbDevice)
+ if (kIOReturnSuccess != result || !plugInInterface) {
+ usbi_dbg ("could not set up plugin for service: %s\n", darwin_error_str (result));
return NULL;
+ }
(void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
- (LPVOID)&device);
-
- (*plugInInterface)->Stop(plugInInterface);
- IODestroyPlugInInterface (plugInInterface);
-
- /* get the location from the device */
- if (locationp)
- (*(device))->GetLocationID(device, locationp);
+ (LPVOID)&device);
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
return device;
}
-static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***darwin_device) {
- kern_return_t kresult;
- UInt32 location;
- io_iterator_t deviceIterator;
+static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
+ struct libusb_context *ctx;
+ io_service_t service;
- kresult = usb_setup_device_iterator (&deviceIterator, dev_location);
- if (kresult)
- return kresult;
+ usbi_mutex_lock(&active_contexts_lock);
- /* This port of libusb uses locations to keep track of devices. */
- while ((*darwin_device = usb_get_next_device (deviceIterator, &location)) != NULL) {
- if (location == dev_location)
- break;
+ while ((service = IOIteratorNext(add_devices))) {
+ /* add this device to each active context's device list */
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ process_new_device (ctx, service);;
+ }
- (**darwin_device)->Release(*darwin_device);
+ IOObjectRelease(service);
}
- IOObjectRelease (deviceIterator);
-
- if (!(*darwin_device))
- return kIOReturnNoDevice;
-
- return kIOReturnSuccess;
+ usbi_mutex_unlock(&active_contexts_lock);
}
-
-
static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
- struct libusb_context *ctx = (struct libusb_context *)ptr;
- struct libusb_device_handle *handle;
- struct darwin_device_priv *dpriv;
- struct darwin_device_handle_priv *priv;
+ struct libusb_device *dev = NULL;
+ struct libusb_context *ctx;
io_service_t device;
- long location;
- bool locationValid;
- CFTypeRef locationCF;
- UInt32 message;
+ UInt64 session;
+ int ret;
- usbi_info (ctx, "a device has been detached");
+ usbi_mutex_lock(&active_contexts_lock);
while ((device = IOIteratorNext (rem_devices)) != 0) {
/* get the location from the i/o registry */
- locationCF = IORegistryEntryCreateCFProperty (device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
-
+ ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
IOObjectRelease (device);
-
- if (!locationCF)
+ if (!ret)
continue;
- locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() &&
- CFNumberGetValue(locationCF, kCFNumberLongType, &location);
-
- CFRelease (locationCF);
-
- if (!locationValid)
- continue;
-
- usbi_mutex_lock(&ctx->open_devs_lock);
- list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
- dpriv = (struct darwin_device_priv *)handle->dev->os_priv;
-
- /* the device may have been opened several times. write to each handle's event descriptor */
- if (dpriv->location == location && handle->os_priv) {
- priv = (struct darwin_device_handle_priv *)handle->os_priv;
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ usbi_dbg ("notifying context %p of device disconnect", ctx);
- message = MESSAGE_DEVICE_GONE;
- write (priv->fds[1], &message, sizeof (message));
+ 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(&ctx->open_devs_lock);
}
+
+ usbi_mutex_unlock(&active_contexts_lock);
+}
+
+static void darwin_hotplug_poll (void)
+{
+ /* not sure if 5 seconds will be too long/short but it should work ok */
+ mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0};
+
+ /* since a kernel thread may nodify the IOInterators used for
+ * hotplug notidication we can't just clear the iterators.
+ * instead just wait until all IOService providers are quiet */
+ (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
}
static void darwin_clear_iterator (io_iterator_t iter) {
IOObjectRelease (device);
}
-static void *event_thread_main (void *arg0) {
+static void *darwin_event_thread_main (void *arg0) {
IOReturn kresult;
struct libusb_context *ctx = (struct libusb_context *)arg0;
CFRunLoopRef runloop;
/* Set this thread's name, so it can be seen in the debugger
and crash reports. */
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
- pthread_setname_np ("org.libusb.device-detach");
-#endif
+ pthread_setname_np ("org.libusb.device-hotplug");
/* 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. */
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
objc_registerThreadWithCollector();
#endif
- /* hotplug (device removal) source */
+ /* 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_info (ctx, "creating hotplug event source");
+ usbi_dbg ("creating hotplug event source");
runloop = CFRunLoopGetCurrent ();
CFRetain (runloop);
/* create notifications for removed devices */
kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
- IOServiceMatching(kIOUSBDeviceClassName),
- (IOServiceMatchingCallback)darwin_devices_detached,
- (void *)ctx, &libusb_rem_device_iterator);
+ IOServiceMatching(kIOUSBDeviceClassName),
+ 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));
+
+ pthread_exit (NULL);
+ }
+
+ /* create notifications for attached devices */
+ kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ 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));
/* arm notifiers */
darwin_clear_iterator (libusb_rem_device_iterator);
+ darwin_clear_iterator (libusb_add_device_iterator);
- usbi_info (ctx, "thread ready to receive events");
+ usbi_dbg ("darwin event thread ready to receive events");
- /* signal the main thread that the async runloop has been created. */
+ /* signal the main thread that the hotplug runloop has been created. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
libusb_darwin_acfl = runloop;
pthread_cond_signal (&libusb_darwin_at_cond);
/* run the runloop */
CFRunLoopRun();
- usbi_info (ctx, "thread exiting");
+ usbi_dbg ("darwin event thread exiting");
+
+ /* remove the notification cfsource */
+ CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
/* delete notification port */
IONotificationPortDestroy (libusb_notification_port);
+
+ /* delete iterators */
IOObjectRelease (libusb_rem_device_iterator);
+ IOObjectRelease (libusb_add_device_iterator);
CFRelease (runloop);
pthread_exit (NULL);
}
+/* cleanup function to destroy cached devices */
+static void __attribute__((destructor)) _darwin_finalize(void) {
+ struct darwin_cached_device *dev, *next;
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
+ darwin_deref_cached_device(dev);
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+}
+
static int darwin_init(struct libusb_context *ctx) {
host_name_port_t host_self;
+ int rc;
+
+ rc = darwin_scan_devices (ctx);
+ if (LIBUSB_SUCCESS != rc) {
+ return rc;
+ }
if (OSAtomicIncrement32Barrier(&initCount) == 1) {
/* create the clocks that will be used */
host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
mach_port_deallocate(mach_task_self(), host_self);
- pthread_mutex_init (&libusb_darwin_at_mutex, NULL);
- pthread_cond_init (&libusb_darwin_at_cond, NULL);
-
- pthread_create (&libusb_darwin_at, NULL, event_thread_main, (void *)ctx);
+ pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
pthread_mutex_lock (&libusb_darwin_at_mutex);
while (!libusb_darwin_acfl)
pthread_mutex_unlock (&libusb_darwin_at_mutex);
}
- return 0;
+ return rc;
}
static void darwin_exit (void) {
mach_port_deallocate(mach_task_self(), clock_realtime);
mach_port_deallocate(mach_task_self(), clock_monotonic);
- /* stop the async runloop and wait for the thread to terminate. */
+ /* stop the event runloop and wait for the thread to terminate. */
CFRunLoopStop (libusb_darwin_acfl);
pthread_join (libusb_darwin_at, NULL);
}
}
static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
/* return cached copy */
memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
}
static int get_configuration_index (struct libusb_device *dev, int config_value) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
UInt8 i, numConfig;
IOUSBConfigurationDescriptorPtr desc;
IOReturn kresult;
}
/* configuration not found */
- return LIBUSB_ERROR_OTHER;
+ return LIBUSB_ERROR_NOT_FOUND;
}
static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
int config_index;
if (0 == priv->active_config)
- return LIBUSB_ERROR_INVALID_PARAM;
+ return LIBUSB_ERROR_NOT_FOUND;
config_index = get_configuration_index (dev, priv->active_config);
if (config_index < 0)
}
static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
IOUSBConfigurationDescriptorPtr desc;
IOReturn kresult;
- usb_device_t **device = NULL;
+ int ret;
- if (!priv)
+ if (!priv || !priv->device)
return LIBUSB_ERROR_OTHER;
- if (!priv->device) {
- kresult = darwin_get_device (priv->location, &device);
- if (kresult || !device) {
- usbi_err (DEVICE_CTX (dev), "could not find device: %s", darwin_error_str (kresult));
-
- return darwin_to_libusb (kresult);
- }
-
- /* don't have to open the device to get a config descriptor */
- } else
- device = priv->device;
-
- kresult = (*device)->GetConfigurationDescriptorPtr (device, config_index, &desc);
+ kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc);
if (kresult == kIOReturnSuccess) {
/* copy descriptor */
if (libusb_le16_to_cpu(desc->wTotalLength) < len)
*host_endian = 0;
}
- if (!priv->device)
- (*device)->Release (device);
+ ret = darwin_to_libusb (kresult);
+ if (ret != LIBUSB_SUCCESS)
+ return ret;
- return darwin_to_libusb (kresult);
+ return (int) len;
}
/* check whether the os has configured the device */
-static int darwin_check_configuration (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **darwin_device) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
+static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **darwin_device = dev->device;
IOUSBConfigurationDescriptorPtr configDesc;
IOUSBFindInterfaceRequest request;
io_iterator_t interface_iterator;
io_service_t firstInterface;
- if (priv->dev_descriptor.bNumConfigurations < 1) {
+ if (dev->dev_descriptor.bNumConfigurations < 1) {
usbi_err (ctx, "device has no configurations");
return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */
}
/* find the first configuration */
kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc);
- priv->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1;
+ dev->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1;
/* check if the device is already configured. there is probably a better way than iterating over the
to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices
IOObjectRelease (firstInterface);
/* device is configured */
- if (priv->dev_descriptor.bNumConfigurations == 1)
+ if (dev->dev_descriptor.bNumConfigurations == 1)
/* to avoid problems with some devices get the configurations value from the configuration descriptor */
- priv->active_config = priv->first_config;
+ dev->active_config = dev->first_config;
else
/* devices with more than one configuration should work with GetConfiguration */
- (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config);
+ (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config);
} else
/* not configured */
- priv->active_config = 0;
+ dev->active_config = 0;
- usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config);
+ usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
return 0;
}
-static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **device) {
- struct darwin_device_priv *priv;
- int retries = 5, delay = 30000;
+static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+ IOUSBDevRequestTO req;
+
+ memset (buffer, 0, buffer_size);
+
+ /* Set up request for descriptor/ */
+ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = desc << 8;
+ req.wIndex = desc_index;
+ req.wLength = buffer_size;
+ req.pData = buffer;
+ req.noDataTimeout = 20;
+ req.completionTimeout = 100;
+
+ return (*device)->DeviceRequestTO (device, &req);
+}
+
+static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **device = dev->device;
+ int retries = 1, delay = 30000;
int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
int is_open = 0;
int ret = 0, ret2;
- IOUSBDevRequest req;
UInt8 bDeviceClass;
UInt16 idProduct, idVendor;
+ dev->can_enumerate = 0;
+
(*device)->GetDeviceClass (device, &bDeviceClass);
(*device)->GetDeviceProduct (device, &idProduct);
(*device)->GetDeviceVendor (device, &idVendor);
- priv = (struct darwin_device_priv *)dev->os_priv;
-
- /* try to open the device (we can usually continue even if this fails) */
+ /* According to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some
+ * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still,
+ * to follow the spec as closely as possible, try opening the device */
is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess);
- /**** retrieve device descriptor ****/
do {
- /* Set up request for device descriptor */
- memset (&(priv->dev_descriptor), 0, sizeof(IOUSBDeviceDescriptor));
- req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
- req.bRequest = kUSBRqGetDescriptor;
- req.wValue = kUSBDeviceDesc << 8;
- req.wIndex = 0;
- req.wLength = sizeof(priv->dev_descriptor);
- req.pData = &(priv->dev_descriptor);
-
- /* according to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some
- * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still,
- * to follow the spec as closely as possible, try opening the device */
-
- ret = (*(device))->DeviceRequest (device, &req);
-
- if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType)
+ /**** retrieve device descriptor ****/
+ ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor));
+
+ if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType)
/* received an overrun error but we still received a device descriptor */
ret = kIOReturnSuccess;
- if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.bNumConfigurations ||
- 0 == priv->dev_descriptor.bcdUSB)) {
+ if (kIOUSBVendorIDAppleComputer == idVendor) {
+ /* NTH: don't bother retrying or unsuspending Apple devices */
+ break;
+ }
+
+ if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations ||
+ 0 == dev->dev_descriptor.bcdUSB)) {
/* work around for incorrectly configured devices */
if (try_reconfigure && is_open) {
- usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
+ usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
- /* set the first configuration */
- (*device)->SetConfiguration(device, 1);
+ /* set the first configuration */
+ (*device)->SetConfiguration(device, 1);
- /* don't try to reconfigure again */
- try_reconfigure = 0;
+ /* don't try to reconfigure again */
+ try_reconfigure = 0;
}
ret = kIOUSBPipeStalled;
if (kIOReturnSuccess != ret && is_open && try_unsuspend) {
/* device may be suspended. unsuspend it and try again */
#if DeviceVersion >= 320
- UInt32 info;
+ UInt32 info = 0;
/* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
(void)(*device)->GetUSBDeviceInformation (device, &info);
- try_unsuspend = info & (1 << kUSBInformationDeviceIsSuspendedBit);
+ /* note that the device was suspended */
+ if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+ try_unsuspend = 1;
#endif
if (try_unsuspend) {
- /* resume the device */
- ret2 = (*device)->USBDeviceSuspend (device, 0);
- if (kIOReturnSuccess != ret2) {
- /* prevent log spew from poorly behaving devices. this indicates the
- os actually had trouble communicating with the device */
- usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
- } else
- unsuspended = 1;
-
- try_unsuspend = 0;
+ /* try to unsuspend the device */
+ ret2 = (*device)->USBDeviceSuspend (device, 0);
+ if (kIOReturnSuccess != ret2) {
+ /* prevent log spew from poorly behaving devices. this indicates the
+ os actually had trouble communicating with the device */
+ usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
+ } else
+ unsuspended = 1;
+
+ try_unsuspend = 0;
}
}
if (ret != kIOReturnSuccess) {
/* a debug message was already printed out for this error */
if (LIBUSB_CLASS_HUB == bDeviceClass)
- usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s. skipping device", idVendor, idProduct, darwin_error_str (ret));
+ usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
else
- usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s. skipping device", idVendor, idProduct, darwin_error_str (ret));
-
- return -1;
- }
-
- usbi_dbg ("device descriptor:");
- usbi_dbg (" bDescriptorType: 0x%02x", priv->dev_descriptor.bDescriptorType);
- usbi_dbg (" bcdUSB: 0x%04x", priv->dev_descriptor.bcdUSB);
- usbi_dbg (" bDeviceClass: 0x%02x", priv->dev_descriptor.bDeviceClass);
- usbi_dbg (" bDeviceSubClass: 0x%02x", priv->dev_descriptor.bDeviceSubClass);
- usbi_dbg (" bDeviceProtocol: 0x%02x", priv->dev_descriptor.bDeviceProtocol);
- usbi_dbg (" bMaxPacketSize0: 0x%02x", priv->dev_descriptor.bMaxPacketSize0);
- usbi_dbg (" idVendor: 0x%04x", priv->dev_descriptor.idVendor);
- usbi_dbg (" idProduct: 0x%04x", priv->dev_descriptor.idProduct);
- usbi_dbg (" bcdDevice: 0x%04x", priv->dev_descriptor.bcdDevice);
- usbi_dbg (" iManufacturer: 0x%02x", priv->dev_descriptor.iManufacturer);
- usbi_dbg (" iProduct: 0x%02x", priv->dev_descriptor.iProduct);
- usbi_dbg (" iSerialNumber: 0x%02x", priv->dev_descriptor.iSerialNumber);
- usbi_dbg (" bNumConfigurations: 0x%02x", priv->dev_descriptor.bNumConfigurations);
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
+ return darwin_to_libusb (ret);
+ }
/* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */
- if (libusb_le16_to_cpu (priv->dev_descriptor.idProduct) != idProduct) {
+ if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) {
/* not a valid device */
usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
- idProduct, libusb_le16_to_cpu (priv->dev_descriptor.idProduct));
- return -1;
+ idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ return LIBUSB_ERROR_NO_DEVICE;
}
- return 0;
+ usbi_dbg ("cached device descriptor:");
+ usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
+ usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB);
+ usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
+ usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
+ usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
+ usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+ usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor);
+ usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct);
+ usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice);
+ usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
+ usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct);
+ usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
+ usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
+
+ dev->can_enumerate = 1;
+
+ return LIBUSB_SUCCESS;
}
-static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) {
- struct darwin_device_priv *priv;
- struct libusb_device *dev;
- struct discovered_devs *discdevs;
- UInt16 address;
- UInt8 devSpeed;
- int ret = 0, need_unref = 0;
+static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
+ struct darwin_cached_device **cached_out) {
+ struct darwin_cached_device *new_device;
+ UInt64 sessionID = 0, parent_sessionID = 0;
+ int ret = LIBUSB_SUCCESS;
+ usb_device_t **device;
+ io_service_t parent;
+ kern_return_t result;
+ UInt8 port = 0;
+
+ /* get some info from the io registry */
+ (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
+ (void) get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, &port);
+
+ usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
+ result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent);
+
+ if (kIOReturnSuccess == result) {
+ (void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID);
+ IOObjectRelease(parent);
+ }
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
do {
- dev = usbi_get_device_by_session_id(ctx, locationID);
- if (!dev) {
- usbi_info (ctx, "allocating new device for location 0x%08x", locationID);
- dev = usbi_alloc_device(ctx, locationID);
- need_unref = 1;
- } else
- usbi_info (ctx, "using existing device for location 0x%08x", locationID);
+ *cached_out = NULL;
+
+ list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
+ usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session);
+ if (new_device->session == sessionID) {
+ usbi_dbg("using cached device for device");
+ *cached_out = new_device;
+ break;
+ }
+ }
- if (!dev) {
+ if (*cached_out)
+ break;
+
+ usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+
+ device = darwin_device_from_service (service);
+ if (!device) {
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ }
+
+ new_device = calloc (1, sizeof (*new_device));
+ if (!new_device) {
ret = LIBUSB_ERROR_NO_MEM;
break;
}
- priv = (struct darwin_device_priv *)dev->os_priv;
+ /* add this device to the cached device list */
+ list_add(&new_device->list, &darwin_cached_devices);
- (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address);
+ (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
- ret = darwin_cache_device_descriptor (ctx, dev, device);
- if (ret < 0)
+ /* keep a reference to this device */
+ darwin_ref_cached_device(new_device);
+
+ new_device->device = device;
+ new_device->session = sessionID;
+ (*device)->GetLocationID (device, &new_device->location);
+ new_device->port = port;
+ new_device->parent_session = parent_sessionID;
+
+ /* cache the device descriptor */
+ ret = darwin_cache_device_descriptor(ctx, new_device);
+ if (ret)
break;
- /* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */
- ret = darwin_check_configuration (ctx, dev, device);
- if (ret < 0)
+ if (new_device->can_enumerate) {
+ snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address,
+ new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct,
+ new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass);
+ }
+ } while (0);
+
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ /* keep track of devices regardless of if we successfully enumerate them to
+ prevent them from being enumerated multiple times */
+
+ *cached_out = new_device;
+
+ return ret;
+}
+
+static int process_new_device (struct libusb_context *ctx, io_service_t service) {
+ struct darwin_device_priv *priv;
+ struct libusb_device *dev = NULL;
+ struct darwin_cached_device *cached_device;
+ UInt8 devSpeed;
+ int ret = 0;
+
+ do {
+ ret = darwin_get_cached_device (ctx, service, &cached_device);
+
+ if (ret < 0 || !cached_device->can_enumerate) {
+ return ret;
+ }
+
+ /* check current active configuration (and cache the first configuration value--
+ which may be used by claim_interface) */
+ ret = darwin_check_configuration (ctx, cached_device);
+ if (ret)
break;
- dev->bus_number = locationID >> 24;
- dev->device_address = address;
+ usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ ctx, cached_device->session);
- (*device)->GetDeviceSpeed (device, &devSpeed);
+ dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
+ if (!dev) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ priv = (struct darwin_device_priv *)dev->os_priv;
+
+ priv->dev = cached_device;
+ darwin_ref_cached_device (priv->dev);
+
+ 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;
+ }
+ dev->port_number = cached_device->port;
+ dev->bus_number = cached_device->location >> 24;
+ dev->device_address = cached_device->address;
+
+ (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
switch (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 DeviceVersion >= 500
+ case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
+#endif
default:
usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
}
- /* save our location, we'll need this later */
- priv->location = locationID;
- snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, priv->dev_descriptor.idVendor, priv->dev_descriptor.idProduct,
- priv->dev_descriptor.bDeviceClass, priv->dev_descriptor.bDeviceSubClass);
-
ret = usbi_sanitize_device (dev);
if (ret < 0)
break;
- /* append the device to the list of discovered devices */
- discdevs = discovered_devs_append(*_discdevs, dev);
- if (!discdevs) {
- ret = LIBUSB_ERROR_NO_MEM;
- break;
- }
-
- *_discdevs = discdevs;
-
- usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path);
+ usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+ dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
} while (0);
- if (need_unref)
- libusb_unref_device(dev);
+ if (0 == ret) {
+ usbi_connect_device (dev);
+ } else {
+ libusb_unref_device (dev);
+ }
return ret;
}
-static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) {
- io_iterator_t deviceIterator;
- usb_device_t **device;
- kern_return_t kresult;
- UInt32 location;
+static int darwin_scan_devices(struct libusb_context *ctx) {
+ io_iterator_t deviceIterator;
+ io_service_t service;
+ kern_return_t kresult;
kresult = usb_setup_device_iterator (&deviceIterator, 0);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
- while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) {
- (void) process_new_device (ctx, device, location, _discdevs);
+ while ((service = IOIteratorNext (deviceIterator))) {
+ (void) process_new_device (ctx, service);
- (*(device))->Release(device);
+ IOObjectRelease(service);
}
IOObjectRelease(deviceIterator);
static int darwin_open (struct libusb_device_handle *dev_handle) {
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
- usb_device_t **darwin_device;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
if (0 == dpriv->open_count) {
- kresult = darwin_get_device (dpriv->location, &darwin_device);
- if (kresult) {
- usbi_err (HANDLE_CTX (dev_handle), "could not find device: %s", darwin_error_str (kresult));
- return darwin_to_libusb (kresult);
- }
-
- dpriv->device = darwin_device;
-
/* try to open the device */
kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
-
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult));
-
- switch (kresult) {
- case kIOReturnExclusiveAccess:
- /* it is possible to perform some actions on a device that is not open so do not return an error */
- priv->is_open = 0;
-
- break;
- default:
- (*(dpriv->device))->Release (dpriv->device);
- dpriv->device = NULL;
- return darwin_to_libusb (kresult);
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult));
+
+ if (kIOReturnExclusiveAccess != kresult) {
+ return darwin_to_libusb (kresult);
}
+
+ /* it is possible to perform some actions on a device that is not open so do not return an error */
+ priv->is_open = 0;
} else {
- /* create async event source */
- kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
- if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult));
+ priv->is_open = 1;
+ }
- (*(dpriv->device))->USBDeviceClose (dpriv->device);
- (*(dpriv->device))->Release (dpriv->device);
+ /* create async event source */
+ kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult));
- dpriv->device = NULL;
- return darwin_to_libusb (kresult);
+ if (priv->is_open) {
+ (*(dpriv->device))->USBDeviceClose (dpriv->device);
}
- priv->is_open = 1;
+ priv->is_open = 0;
- CFRetain (libusb_darwin_acfl);
-
- /* add the cfSource to the aync run loop */
- CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+ return darwin_to_libusb (kresult);
}
+
+ CFRetain (libusb_darwin_acfl);
+
+ /* add the cfSource to the aync run loop */
+ CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
}
/* device opened successfully */
usbi_add_pollfd(HANDLE_CTX(dev_handle), priv->fds[0], POLLIN);
- usbi_info (HANDLE_CTX (dev_handle), "device open for access");
+ usbi_dbg ("device open for access");
return 0;
}
static void darwin_close (struct libusb_device_handle *dev_handle) {
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
int i;
libusb_release_interface (dev_handle, i);
if (0 == dpriv->open_count) {
- if (priv->is_open) {
- /* delete the device's async event source */
- if (priv->cfSource) {
- CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
- CFRelease (priv->cfSource);
- }
+ /* delete the device's async event source */
+ if (priv->cfSource) {
+ CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
+ CFRelease (priv->cfSource);
+ priv->cfSource = NULL;
+ CFRelease (libusb_darwin_acfl);
+ }
+ if (priv->is_open) {
/* close the device */
kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
if (kresult) {
- /* Log the fact that we had a problem closing the file, however failing a
- * close isn't really an error, so return success anyway */
- usbi_err (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
+ /* Log the fact that we had a problem closing the file, however failing a
+ * close isn't really an error, so return success anyway */
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
}
}
-
- kresult = (*(dpriv->device))->Release(dpriv->device);
- if (kresult) {
- /* Log the fact that we had a problem closing the file, however failing a
- * close isn't really an error, so return success anyway */
- usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
- }
-
- dpriv->device = NULL;
}
/* file descriptors are maintained per-instance */
}
static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
*config = (int) dpriv->active_config;
}
static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
IOReturn kresult;
int i;
static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
IOUSBFindInterfaceRequest request;
- uint8_t current_interface;
kern_return_t kresult;
io_iterator_t interface_iterator;
+ UInt8 bInterfaceNumber;
+ int ret;
*usbInterfacep = IO_OBJECT_NULL;
if (kresult)
return kresult;
- for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
- *usbInterfacep = IOIteratorNext(interface_iterator);
- if (current_interface != ifc)
- (void) IOObjectRelease (*usbInterfacep);
+ while ((*usbInterfacep = IOIteratorNext(interface_iterator))) {
+ /* find the interface number */
+ ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type,
+ &bInterfaceNumber);
+
+ if (ret && bInterfaceNumber == ifc) {
+ break;
+ }
+
+ (void) IOObjectRelease (*usbInterfacep);
}
/* done with the interface iterator */
u_int16_t dont_care2;
int i;
- usbi_info (HANDLE_CTX (dev_handle), "building table of endpoints.");
+ usbi_dbg ("building table of endpoints.");
/* retrieve the total number of endpoints on this interface */
kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
/* iterate through pipe references */
for (i = 1 ; i <= numep ; i++) {
kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
- &dont_care2, &dont_care3);
+ &dont_care2, &dont_care3);
if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "error getting pipe information for pipe %d: %s", i, darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
- usbi_info (HANDLE_CTX (dev_handle), "interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number);
+ usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number);
- cInterface->endpoint_addrs[i - 1] = ((direction << 7 & LIBUSB_ENDPOINT_DIR_MASK) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
}
cInterface->num_endpoints = numep;
}
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
io_service_t usbInterface = IO_OBJECT_NULL;
IOReturn kresult;
/* get an interface to the device's interface */
kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID,
- kIOCFPlugInInterfaceID, &plugInInterface, &score);
+ kIOCFPlugInInterfaceID, &plugInInterface, &score);
/* ignore release error */
(void)IOObjectRelease (usbInterface);
/* Do the actual claim */
kresult = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
- (LPVOID)&cInterface->interface);
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID)&cInterface->interface);
/* We no longer need the intermediate plug-in */
- IODestroyPlugInInterface (plugInInterface);
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
if (kresult || !cInterface->interface) {
usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
/* add the cfSource to the async thread's run loop */
CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
- usbi_info (HANDLE_CTX (dev_handle), "interface opened");
+ usbi_dbg ("interface opened");
return 0;
}
kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
if (kresult)
- usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
+ usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
kresult = (*(cInterface->interface))->Release(cInterface->interface);
if (kresult != kIOReturnSuccess)
- usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
+ usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
cInterface->interface = IO_OBJECT_NULL;
}
static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
-
/* current interface */
struct darwin_interface *cInterface;
- uint8_t pipeRef, iface;
IOReturn kresult;
+ uint8_t pipeRef;
/* determine the interface/endpoint to use */
- if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, &iface) != 0) {
+ if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
- cInterface = &priv->interfaces[iface];
-
-#if (InterfaceVersion < 190)
- kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef);
-#else
/* newer versions of darwin support clearing additional bits on the device's endpoint */
kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
-#endif
if (kresult)
- usbi_err (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
+ usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
return darwin_to_libusb (kresult);
}
static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOUSBDeviceDescriptor descriptor;
+ IOUSBConfigurationDescriptorPtr cached_configuration;
+ IOUSBConfigurationDescriptor configuration;
+ bool reenumerate = false;
IOReturn kresult;
+ int i;
kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
- if (kresult)
+ if (kresult) {
usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
+ return darwin_to_libusb (kresult);
+ }
- return darwin_to_libusb (kresult);
+ do {
+ usbi_dbg ("darwin/reset_device: checking if device descriptor changed");
+
+ /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */
+ (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor));
+
+ /* check if the device descriptor has changed */
+ if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) {
+ reenumerate = true;
+ break;
+ }
+
+ /* check if any configuration descriptor has changed */
+ for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+ usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i);
+
+ (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration));
+ (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+
+ if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) {
+ reenumerate = true;
+ break;
+ }
+ }
+ } while (0);
+
+ if (reenumerate) {
+ usbi_dbg ("darwin/reset_device: device requires reenumeration");
+ (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg ("darwin/reset_device: device reset complete");
+
+ return LIBUSB_SUCCESS;
}
static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) {
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
io_service_t usbInterface;
CFTypeRef driver;
IOReturn kresult;
}
static void darwin_destroy_device(struct libusb_device *dev) {
- (void)dev;
+ struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv;
+
+ if (dpriv->dev) {
+ /* need to hold the lock in case this is the last reference to the device */
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ darwin_deref_cached_device (dpriv->dev);
+ dpriv->dev = NULL;
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+ }
}
static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
IOReturn ret;
- uint8_t is_read; /* 0 = we're reading, 1 = we're writing */
uint8_t transferType;
- /* None of the values below are used in libusb for bulk transfers */
- uint8_t direction, number, interval, pipeRef, iface;
+ /* None of the values below are used in libusbx for bulk transfers */
+ uint8_t direction, number, interval, pipeRef;
uint16_t maxPacketSize;
struct darwin_interface *cInterface;
- /* are we reading or writing? */
- is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-
- if (!is_read && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)
- return LIBUSB_ERROR_NOT_SUPPORTED;
-
- if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
- cInterface = &priv->interfaces[iface];
+ ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
- (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
- &transferType, &maxPacketSize, &interval);
+ if (ret) {
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+ return darwin_to_libusb (ret);
+ }
+
+ if (0 != (transfer->length % maxPacketSize)) {
+ /* do not need a zero packet */
+ transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET;
+ }
/* submit the request */
/* timeouts are unavailable on interrupt endpoints */
if (transferType == kUSBInterrupt) {
- if (is_read)
+ if (IS_XFERIN(transfer))
ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, darwin_async_io_callback, itransfer);
+ transfer->length, darwin_async_io_callback, itransfer);
else
ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, darwin_async_io_callback, itransfer);
+ transfer->length, darwin_async_io_callback, itransfer);
} else {
itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
- if (is_read)
+ if (IS_XFERIN(transfer))
ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, (void *)itransfer);
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
else
ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, (void *)itransfer);
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
+ }
+
+ if (ret)
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+
+ return darwin_to_libusb (ret);
+}
+
+#if InterfaceVersion >= 550
+static int submit_stream_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+ IOReturn ret;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
}
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+ else
+ ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+
if (ret)
- usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", is_read ? "In" : "Out",
- darwin_error_str(ret), ret);
+ usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
return darwin_to_libusb (ret);
}
+#endif
static int submit_iso_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
- IOReturn kresult;
- uint8_t is_read; /* 0 = we're writing, 1 = we're reading */
- uint8_t pipeRef, iface;
- UInt64 frame;
- AbsoluteTime atTime;
- int i;
+ IOReturn kresult;
+ uint8_t direction, number, interval, pipeRef, transferType;
+ uint16_t maxPacketSize;
+ UInt64 frame;
+ AbsoluteTime atTime;
+ int i;
struct darwin_interface *cInterface;
- /* are we reading or writing? */
- is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-
/* construct an array of IOUSBIsocFrames, reuse the old one if possible */
if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) {
free(tpriv->isoc_framelist);
tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length;
/* determine the interface/endpoint to use */
- if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
- cInterface = &priv->interfaces[iface];
+ /* determine the properties of this endpoint and the speed of the device */
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
/* Last but not least we need the bus frame number */
kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
return darwin_to_libusb (kresult);
}
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
/* schedule for a frame a little in the future */
frame += 4;
frame = cInterface->frames[transfer->endpoint];
/* submit the request */
- if (is_read)
+ if (IS_XFERIN(transfer))
kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
- itransfer);
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
else
kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
- itransfer);
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
- cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets / 8;
+ if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
+ /* Full speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1));
+ else
+ /* High/super speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8;
if (kresult != kIOReturnSuccess) {
- usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", is_read ? "In" : "Out",
- darwin_error_str(kresult));
+ usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(kresult));
free (tpriv->isoc_framelist);
tpriv->isoc_framelist = NULL;
}
static int submit_control_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
IOReturn kresult;
if (transfer->endpoint) {
struct darwin_interface *cInterface;
- uint8_t pipeRef, iface;
+ uint8_t pipeRef;
- if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
- cInterface = &priv->interfaces[iface];
-
kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer);
} else
/* control request on endpoint 0 */
return submit_bulk_transfer(itransfer);
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return submit_iso_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+#if InterfaceVersion >= 550
+ return submit_stream_transfer(itransfer);
+#else
+ usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+#endif
default:
usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
static int cancel_control_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
IOReturn kresult;
- usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions control pipe");
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe");
if (!dpriv->device)
return LIBUSB_ERROR_NO_DEVICE;
static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
- struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
struct darwin_interface *cInterface;
uint8_t pipeRef, iface;
IOReturn kresult;
- if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
- cInterface = &priv->interfaces[iface];
-
if (!dpriv->device)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef);
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
/* abort transactions */
- (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
+#if InterfaceVersion >= 550
+ if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type)
+ (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id);
+ else
+#endif
+ (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
- usbi_info (ITRANSFER_CTX (itransfer), "calling clear pipe stall to clear the data toggle bit");
+ usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
- /* clear the data toggle bit */
-#if (InterfaceVersion < 190)
- kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef);
-#else
/* newer versions of darwin support clearing additional bits on the device's endpoint */
kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
-#endif
return darwin_to_libusb (kresult);
}
struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
- UInt32 message, size;
+ struct darwin_msg_async_io_complete message = {.itransfer = itransfer, .result = result,
+ .size = (UInt32) (uintptr_t) arg0};
- usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed");
+ usbi_dbg ("an async io operation has completed");
- size = (UInt32) arg0;
+ /* if requested write a zero packet */
+ if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+
+ (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface);
+
+ (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0);
+ }
/* send a completion message to the device's file descriptor */
- message = MESSAGE_ASYNC_IO_COMPLETE;
write (priv->fds[1], &message, sizeof (message));
- write (priv->fds[1], &itransfer, sizeof (itransfer));
- write (priv->fds[1], &result, sizeof (IOReturn));
- write (priv->fds[1], &size, sizeof (size));
}
static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
case kIOReturnAborted:
return LIBUSB_TRANSFER_CANCELLED;
case kIOUSBPipeStalled:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: pipe is stalled");
+ usbi_dbg ("transfer error: pipe is stalled");
return LIBUSB_TRANSFER_STALL;
case kIOReturnOverrun:
- usbi_err (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
return LIBUSB_TRANSFER_OVERFLOW;
case kIOUSBTransactionTimeout:
- usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
return LIBUSB_TRANSFER_TIMED_OUT;
default:
- usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
return LIBUSB_TRANSFER_ERROR;
}
}
return;
}
- usbi_info (ITRANSFER_CTX (itransfer), "handling %s completion with kernel status %d",
- isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result);
+ usbi_dbg ("handling %s completion with kernel status %d",
+ isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result);
if (kIOReturnSuccess == result || kIOReturnUnderrun == result) {
if (isIsoc && tpriv->isoc_framelist) {
/* copy isochronous results back */
for (i = 0; i < transfer->num_iso_packets ; i++) {
- struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
- lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
- lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
+ struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
+ lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
+ lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
}
} else if (!isIsoc)
itransfer->transferred += io_size;
}
static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) {
- struct usbi_transfer *itransfer;
- UInt32 io_size;
- IOReturn kresult;
+ struct darwin_msg_async_io_complete message;
POLL_NFDS_TYPE i = 0;
ssize_t ret;
- UInt32 message;
usbi_mutex_lock(&ctx->open_devs_lock);
+
for (i = 0; i < nfds && num_ready > 0; i++) {
struct pollfd *pollfd = &fds[i];
- struct libusb_device_handle *handle;
- struct darwin_device_handle_priv *hpriv = NULL;
- usbi_info (ctx, "checking fd %i with revents = %x", fds[i], pollfd->revents);
+ usbi_dbg ("checking fd %i with revents = %x", pollfd->fd, pollfd->revents);
if (!pollfd->revents)
continue;
num_ready--;
- list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
- hpriv = (struct darwin_device_handle_priv *)handle->os_priv;
- if (hpriv->fds[0] == pollfd->fd)
- break;
- }
- if (!(pollfd->revents & POLLERR)) {
- ret = read (hpriv->fds[0], &message, sizeof (message));
- if (ret < (ssize_t)sizeof (message))
- continue;
- } else
- /* could not poll the device-- response is to delete the device (this seems a little heavy-handed) */
- message = MESSAGE_DEVICE_GONE;
-
- switch (message) {
- case MESSAGE_DEVICE_GONE:
- /* remove the device's async port from the runloop */
- if (hpriv->cfSource) {
- if (libusb_darwin_acfl)
- CFRunLoopRemoveSource (libusb_darwin_acfl, hpriv->cfSource, kCFRunLoopDefaultMode);
- CFRelease (hpriv->cfSource);
- hpriv->cfSource = NULL;
- }
-
- usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fds[0]);
- usbi_handle_disconnect(handle);
-
- /* done with this device */
+ if (pollfd->revents & POLLERR) {
+ /* this probably will never happen so ignore the error an move on. */
continue;
- case MESSAGE_ASYNC_IO_COMPLETE:
- read (hpriv->fds[0], &itransfer, sizeof (itransfer));
- read (hpriv->fds[0], &kresult, sizeof (IOReturn));
- read (hpriv->fds[0], &io_size, sizeof (UInt32));
+ }
- darwin_handle_callback (itransfer, kresult, io_size);
- break;
- default:
- usbi_err (ctx, "unknown message received from device pipe");
+ /* there is only one type of message */
+ ret = read (pollfd->fd, &message, sizeof (message));
+ if (ret < (ssize_t) sizeof (message)) {
+ usbi_dbg ("WARNING: short read on async io completion pipe\n");
+ continue;
}
+
+ darwin_handle_callback (message.itransfer, message.result, message.size);
}
usbi_mutex_unlock(&ctx->open_devs_lock);
return 0;
}
+#if InterfaceVersion >= 550
+static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
+ int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc, i;
+
+ /* find the mimimum 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;
+ }
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (num_streams > supportsStreams)
+ num_streams = supportsStreams;
+ }
+
+ /* it is an error if any endpoint in endpoints does not support streams */
+ if (0 == num_streams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ /* create the streams */
+ for (i = 0 ; i < num_endpoints ; ++i) {
+ (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface);
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return num_streams;
+}
+
+static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc;
+
+ for (int i = 0 ; i < num_endpoints ; ++i) {
+ if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface)))
+ return rc;
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (0 == supportsStreams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+#endif
+
const struct usbi_os_backend darwin_backend = {
- .name = "Darwin",
- .init = darwin_init,
- .exit = darwin_exit,
- .get_device_list = darwin_get_device_list,
- .get_device_descriptor = darwin_get_device_descriptor,
- .get_active_config_descriptor = darwin_get_active_config_descriptor,
- .get_config_descriptor = darwin_get_config_descriptor,
-
- .open = darwin_open,
- .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_reset_device,
-
- .kernel_driver_active = darwin_kernel_driver_active,
- .detach_kernel_driver = darwin_detach_kernel_driver,
- .attach_kernel_driver = darwin_attach_kernel_driver,
-
- .destroy_device = darwin_destroy_device,
-
- .submit_transfer = darwin_submit_transfer,
- .cancel_transfer = darwin_cancel_transfer,
- .clear_transfer_priv = darwin_clear_transfer_priv,
-
- .handle_events = op_handle_events,
-
- .clock_gettime = darwin_clock_gettime,
-
- .device_priv_size = sizeof(struct darwin_device_priv),
- .device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
- .transfer_priv_size = sizeof(struct darwin_transfer_priv),
- .add_iso_packet_size = 0,
-};
+ .name = "Darwin",
+ .caps = 0,
+ .init = darwin_init,
+ .exit = darwin_exit,
+ .get_device_list = NULL, /* not needed */
+ .get_device_descriptor = darwin_get_device_descriptor,
+ .get_active_config_descriptor = darwin_get_active_config_descriptor,
+ .get_config_descriptor = darwin_get_config_descriptor,
+ .hotplug_poll = darwin_hotplug_poll,
+
+ .open = darwin_open,
+ .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_reset_device,
+
+#if InterfaceVersion >= 550
+ .alloc_streams = darwin_alloc_streams,
+ .free_streams = darwin_free_streams,
+#endif
+
+ .kernel_driver_active = darwin_kernel_driver_active,
+ .detach_kernel_driver = darwin_detach_kernel_driver,
+ .attach_kernel_driver = darwin_attach_kernel_driver,
+
+ .destroy_device = darwin_destroy_device,
+ .submit_transfer = darwin_submit_transfer,
+ .cancel_transfer = darwin_cancel_transfer,
+ .clear_transfer_priv = darwin_clear_transfer_priv,
+
+ .handle_events = op_handle_events,
+
+ .clock_gettime = darwin_clock_gettime,
+
+ .device_priv_size = sizeof(struct darwin_device_priv),
+ .device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
+ .transfer_priv_size = sizeof(struct darwin_transfer_priv),
+ .add_iso_packet_size = 0,
+};