Index: third_party/libusb/src/libusb/os/darwin_usb.c |
diff --git a/third_party/libusb/src/libusb/os/darwin_usb.c b/third_party/libusb/src/libusb/os/darwin_usb.c |
index 8834d0f958b3693c94a62a9724f76dd1a2447b1b..a24558cb593b4ecdca032a45ddc8fcba7b0faabd 100644 |
--- a/third_party/libusb/src/libusb/os/darwin_usb.c |
+++ b/third_party/libusb/src/libusb/os/darwin_usb.c |
@@ -1,6 +1,7 @@ |
+/* -*- Mode: C; indent-tabs-mode:nil -*- */ |
/* |
- * darwin backend for libusb 1.0 |
- * Copyright (C) 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net> |
+ * darwin backend for libusbx 1.0 |
+ * Copyright © 2008-2013 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 |
@@ -17,19 +18,16 @@ |
* 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> |
@@ -42,22 +40,23 @@ |
#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; |
@@ -67,6 +66,10 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int |
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) { |
switch (result) { |
case kIOReturnSuccess: |
@@ -95,10 +98,15 @@ static const char *darwin_error_str (int result) { |
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"; |
} |
} |
+#endif |
static int darwin_to_libusb (int result) { |
switch (result) { |
@@ -125,6 +133,21 @@ static int darwin_to_libusb (int 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 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_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; |
@@ -134,19 +157,19 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui |
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; |
+ *ifcp = iface; |
+ usbi_dbg ("pipe %d on interface %d matches", *pipep, *ifcp); |
+ return 0; |
+ } |
} |
} |
} |
@@ -157,7 +180,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui |
return -1; |
} |
-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) |
@@ -169,7 +192,9 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati |
&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) */ |
@@ -185,118 +210,92 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati |
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; |
+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; |
- |
- usbi_info (ctx, "a device has been detached"); |
+ UInt64 session; |
+ int ret; |
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(&active_contexts_lock); |
- 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; |
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { |
+ usbi_dbg ("notifying context %p of device disconnect", ctx); |
- /* 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; |
- |
- message = MESSAGE_DEVICE_GONE; |
- write (priv->fds[1], &message, sizeof (message)); |
+ dev = usbi_get_device_by_session_id(ctx, 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); |
} |
} |
- usbi_mutex_unlock(&ctx->open_devs_lock); |
+ usbi_mutex_unlock(&active_contexts_lock); |
} |
} |
@@ -307,7 +306,7 @@ 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; |
@@ -315,23 +314,22 @@ static void *event_thread_main (void *arg0) { |
/* 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); |
@@ -343,9 +341,21 @@ static void *event_thread_main (void *arg0) { |
/* 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), |
+ (IOServiceMatchingCallback)darwin_devices_detached, |
+ (void *)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), |
+ (IOServiceMatchingCallback)darwin_devices_attached, |
+ (void *)ctx, &libusb_add_device_iterator); |
if (kresult != kIOReturnSuccess) { |
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); |
@@ -355,10 +365,11 @@ static void *event_thread_main (void *arg0) { |
/* 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); |
@@ -367,11 +378,17 @@ static void *event_thread_main (void *arg0) { |
/* 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); |
@@ -380,21 +397,40 @@ static void *event_thread_main (void *arg0) { |
pthread_exit (NULL); |
} |
+static void _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; |
+ static int initted = 0; |
+ int rc; |
+ |
+ rc = darwin_scan_devices (ctx); |
+ if (LIBUSB_SUCCESS != rc) { |
+ return rc; |
+ } |
if (OSAtomicIncrement32Barrier(&initCount) == 1) { |
/* create the clocks that will be used */ |
+ if (!initted) { |
+ initted = 1; |
+ atexit(_darwin_finalize); |
+ } |
+ |
host_self = mach_host_self(); |
host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); |
host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); |
mach_port_deallocate(mach_task_self(), host_self); |
- 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, (void *)ctx); |
pthread_mutex_lock (&libusb_darwin_at_mutex); |
while (!libusb_darwin_acfl) |
@@ -402,7 +438,7 @@ static int darwin_init(struct libusb_context *ctx) { |
pthread_mutex_unlock (&libusb_darwin_at_mutex); |
} |
- return 0; |
+ return rc; |
} |
static void darwin_exit (void) { |
@@ -410,14 +446,14 @@ 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); |
@@ -428,7 +464,7 @@ static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char |
} |
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; |
@@ -446,15 +482,15 @@ static int get_configuration_index (struct libusb_device *dev, int config_value) |
} |
/* 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) |
@@ -464,27 +500,15 @@ static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsign |
} |
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) |
@@ -496,15 +520,16 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi |
*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 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; |
@@ -512,14 +537,14 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct libusb |
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 |
@@ -545,72 +570,83 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct libusb |
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; |
@@ -619,25 +655,27 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li |
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; |
} |
} |
@@ -658,128 +696,223 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li |
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)); |
- |
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", |
+ idVendor, idProduct, darwin_error_str (ret), 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); |
- |
/* 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)); |
+ idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); |
return -1; |
} |
+ 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 0; |
} |
-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, parent_sessionID; |
+ int ret = LIBUSB_SUCCESS; |
+ usb_device_t **device; |
+ io_service_t parent; |
+ kern_return_t result; |
+ UInt8 port = 0; |
+ |
+ *cached_out = NULL; |
+ |
+ /* 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\n" 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); |
+ list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { |
+ usbi_dbg("matching sessionID 0x%x against cached device with sessionID 0x%x", 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%x\n", sessionID); |
+ |
+ new_device = calloc (1, sizeof (*new_device)); |
+ if (!new_device) { |
ret = LIBUSB_ERROR_NO_MEM; |
break; |
} |
- priv = (struct darwin_device_priv *)dev->os_priv; |
+ device = darwin_device_from_service (service); |
+ if (!device) { |
+ ret = LIBUSB_ERROR_NO_DEVICE; |
+ free (new_device); |
+ new_device = NULL; |
+ break; |
+ } |
- (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address); |
+ /* add this device to the cached device list */ |
+ list_add(&new_device->list, &darwin_cached_devices); |
- ret = darwin_cache_device_descriptor (ctx, dev, device); |
- if (ret < 0) |
+ (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address); |
+ |
+ /* 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 && !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%08x", |
+ ctx, cached_device->session); |
- (*device)->GetDeviceSpeed (device, &devSpeed); |
+ dev = usbi_alloc_device(ctx, 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, 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; |
+ |
+ /* need to add a reference to the parent device */ |
+ if (dev->parent_dev) { |
+ libusb_ref_device(dev->parent_dev); |
+ } |
+ |
+ (*(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); |
@@ -789,56 +922,43 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_ |
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; |
- |
- CFRetain (libusb_darwin_acfl); |
+ priv->is_open = 0; |
- /* 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 */ |
@@ -852,14 +972,14 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { |
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; |
@@ -877,30 +997,23 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { |
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 */ |
@@ -912,7 +1025,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { |
} |
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; |
@@ -920,7 +1033,7 @@ static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int |
} |
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; |
@@ -946,9 +1059,10 @@ static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int |
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; |
@@ -962,10 +1076,16 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s |
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 */ |
@@ -987,7 +1107,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { |
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); |
@@ -999,7 +1119,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { |
/* 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)); |
@@ -1007,7 +1127,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { |
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)); |
} |
@@ -1018,7 +1138,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { |
} |
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; |
@@ -1057,7 +1177,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i |
/* get an interface to the device's interface */ |
kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID, |
- kIOCFPlugInInterfaceID, &plugInInterface, &score); |
+ kIOCFPlugInInterfaceID, &plugInInterface, &score); |
/* ignore release error */ |
(void)IOObjectRelease (usbInterface); |
@@ -1074,10 +1194,11 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i |
/* 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); |
@@ -1115,7 +1236,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i |
/* 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; |
} |
@@ -1142,11 +1263,11 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int |
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; |
@@ -1196,31 +1317,68 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c |
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; |
@@ -1259,7 +1417,15 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, |
} |
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) { |
@@ -1268,15 +1434,12 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { |
IOReturn ret; |
uint8_t transferType; |
- /* None of the values below are used in libusb for bulk transfers */ |
+ /* None of the values below are used in libusbx for bulk transfers */ |
uint8_t direction, number, interval, pipeRef, iface; |
uint16_t maxPacketSize; |
struct darwin_interface *cInterface; |
- if (IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) |
- return LIBUSB_ERROR_NOT_SUPPORTED; |
- |
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { |
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); |
@@ -1286,33 +1449,38 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { |
cInterface = &priv->interfaces[iface]; |
(*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, |
- &transferType, &maxPacketSize, &interval); |
+ &transferType, &maxPacketSize, &interval); |
+ |
+ 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_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_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); |
+ darwin_error_str(ret), ret); |
return darwin_to_libusb (ret); |
} |
@@ -1322,11 +1490,12 @@ static int submit_iso_transfer(struct usbi_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 pipeRef, iface; |
- UInt64 frame; |
- AbsoluteTime atTime; |
- int i; |
+ IOReturn kresult; |
+ uint8_t direction, number, interval, pipeRef, iface, transferType; |
+ uint16_t maxPacketSize; |
+ UInt64 frame; |
+ AbsoluteTime atTime; |
+ int i; |
struct darwin_interface *cInterface; |
@@ -1343,7 +1512,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { |
return LIBUSB_ERROR_NO_MEM; |
} |
- /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ |
+ /* copy the frame list from the libusbx descriptor (the structures differ only is member order) */ |
for (i = 0 ; i < transfer->num_iso_packets ; i++) |
tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length; |
@@ -1356,6 +1525,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { |
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); |
if (kresult) { |
@@ -1366,6 +1539,9 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { |
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; |
@@ -1375,18 +1551,23 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { |
/* submit the request */ |
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_XFERIN(transfer) ? "In" : "Out", |
- darwin_error_str(kresult)); |
+ darwin_error_str(kresult)); |
free (tpriv->isoc_framelist); |
tpriv->isoc_framelist = NULL; |
} |
@@ -1397,7 +1578,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { |
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_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); |
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; |
struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); |
@@ -1412,7 +1593,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) { |
tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue); |
tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex); |
tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength); |
- /* data is stored after the libusb control block */ |
+ /* data is stored after the libusbx control block */ |
tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; |
tpriv->req.completionTimeout = transfer->timeout; |
tpriv->req.noDataTimeout = transfer->timeout; |
@@ -1463,10 +1644,10 @@ static int darwin_submit_transfer(struct usbi_transfer *itransfer) { |
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; |
@@ -1478,7 +1659,7 @@ static int cancel_control_transfer(struct usbi_transfer *itransfer) { |
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_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); |
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; |
struct darwin_interface *cInterface; |
uint8_t pipeRef, iface; |
@@ -1495,20 +1676,15 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { |
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); |
- 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); |
} |
@@ -1543,18 +1719,24 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) |
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_dbg ("an async io operation has completed"); |
+ |
+ /* 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 iface, pipeRef; |
- usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed"); |
+ (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface); |
+ cInterface = &priv->interfaces[iface]; |
- size = (UInt32) (uintptr_t) arg0; |
+ (*(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) { |
@@ -1568,17 +1750,17 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_ |
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; |
} |
} |
@@ -1597,17 +1779,17 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return |
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; |
@@ -1618,64 +1800,35 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return |
} |
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); |
@@ -1709,42 +1862,42 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) { |
} |
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, |
+ |
+ .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, |
}; |
- |