Darwin: Fix incorrect mutex protection of libusb_darwin_acfl global
[platform/upstream/libusb.git] / libusb / os / darwin_usb.c
index 3a0f0be..f092562 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * darwin backend for libusb 1.0
- * Copyright (C) 2008-2009 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright (C) 2008-2011 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
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <libkern/OSAtomic.h>
 
 #include <mach/clock.h>
 #include <mach/clock_types.h>
 #include <mach/mach_host.h>
-
 #include <mach/mach_port.h>
+
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+  #include <objc/objc-auto.h>
+#endif
+
 #include <IOKit/IOCFBundle.h>
 #include <IOKit/usb/IOUSBLib.h>
 #include <IOKit/IOCFPlugIn.h>
 
 #include "darwin_usb.h"
 
-static mach_port_t  libusb_darwin_mp = 0; /* master port */
+/* async event thread */
+static pthread_mutex_t libusb_darwin_at_mutex;
+static pthread_cond_t  libusb_darwin_at_cond;
+
+static clock_serv_t clock_realtime;
+static clock_serv_t clock_monotonic;
+
 static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */
+static volatile int32_t initCount = 0;
 
 /* async event thread */
 static pthread_t libusb_darwin_at;
@@ -52,12 +65,9 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi
 static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
 static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
 static int darwin_reset_device(struct libusb_device_handle *dev_handle);
-static void darwin_bulk_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size);
-static void darwin_isoc_callback (struct usbi_transfer *itransfer, kern_return_t result);
-static void darwin_control_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size);
 static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
 
-static char *darwin_error_str (int result) {
+static const char *darwin_error_str (int result) {
   switch (result) {
   case kIOReturnSuccess:
     return "no error";
@@ -81,6 +91,10 @@ static char *darwin_error_str (int result) {
     return "transaction aborted";
   case kIOReturnNotResponding:
     return "device not responding";
+  case kIOReturnOverrun:
+    return "data overrun";
+  case kIOReturnCannotWire:
+    return "physical memory can not be wired down";
   default:
     return "unknown error";
   }
@@ -88,6 +102,7 @@ static char *darwin_error_str (int result) {
 
 static int darwin_to_libusb (int result) {
   switch (result) {
+  case kIOReturnUnderrun:
   case kIOReturnSuccess:
     return LIBUSB_SUCCESS;
   case kIOReturnNotOpen:
@@ -115,11 +130,11 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   int8_t i, iface;
 
-  _usbi_log (HANDLE_CTX(dev_handle), LOG_LEVEL_INFO, "converting ep address 0x%02x to pipeRef and interface", ep);
+  usbi_info (HANDLE_CTX(dev_handle), "converting ep address 0x%02x to pipeRef and interface", ep);
 
   for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
     cInterface = &priv->interfaces[iface];
@@ -129,7 +144,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
        if (cInterface->endpoint_addrs[i] == ep) {
          *pipep = i + 1;
          *ifcp = iface;
-         _usbi_log (HANDLE_CTX(dev_handle), LOG_LEVEL_INFO, "pipe %d on interface %d matches", *pipep, *ifcp);
+         usbi_info (HANDLE_CTX(dev_handle), "pipe %d on interface %d matches", *pipep, *ifcp);
          return 0;
        }
       }
@@ -137,42 +152,75 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
   }
 
   /* No pipe found with the correct endpoint address */
-  _usbi_log (HANDLE_CTX(dev_handle), LOG_LEVEL_WARNING, "no pipeRef found with endpoint address 0x%02x.", ep);
-  
+  usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep);
+
   return -1;
 }
 
-static int usb_setup_device_iterator (io_iterator_t *deviceIterator) {
-  return IOServiceGetMatchingServices(libusb_darwin_mp, IOServiceMatching(kIOUSBDeviceClassName), deviceIterator);
+static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long location) {
+  CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+  if (!matchingDict)
+    return kIOReturnError;
+
+  if (location) {
+    CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                                                         &kCFTypeDictionaryKeyCallBacks,
+                                                                         &kCFTypeDictionaryValueCallBacks);
+
+    if (propertyMatchDict) {
+      CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberLongType, &location);
+
+      CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
+      /* release our reference to the CFNumber (CFDictionarySetValue retains it) */
+      CFRelease (locationCF);
+
+      CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
+      /* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */
+      CFRelease (propertyMatchDict);
+    }
+    /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */
+  }
+
+  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, score;
+  long result;
+  SInt32 score;
 
-  if (!IOIteratorIsValid (deviceIterator) || !(usbDevice = IOIteratorNext(deviceIterator)))
+  if (!IOIteratorIsValid (deviceIterator))
     return NULL;
-  
-  result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
-                                            kIOCFPlugInInterfaceID, &plugInInterface,
-                                            &score);
-  
-  if (result || !plugInInterface) {
+
+
+  while ((usbDevice = IOIteratorNext(deviceIterator))) {
+    result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
+                                              kIOCFPlugInInterfaceID, &plugInInterface,
+                                              &score);
+
+    /* we are done with the usb_device_t */
+    (void)IOObjectRelease(usbDevice);
+    if (kIOReturnSuccess == result && plugInInterface)
+      break;
+
     usbi_dbg ("libusb/darwin.c usb_get_next_device: could not set up plugin for service: %s\n", darwin_error_str (result));
+  }
 
+  if (!usbDevice)
     return NULL;
-  }
 
-  (void)IOObjectRelease(usbDevice);
   (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
                                           (LPVOID)&device);
 
   (*plugInInterface)->Stop(plugInInterface);
   IODestroyPlugInInterface (plugInInterface);
-  
-  (*(device))->GetLocationID(device, locationp);
+
+  /* get the location from the device */
+  if (locationp)
+    (*(device))->GetLocationID(device, locationp);
 
   return device;
 }
@@ -182,7 +230,7 @@ static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***d
   UInt32        location;
   io_iterator_t deviceIterator;
 
-  kresult = usb_setup_device_iterator (&deviceIterator);
+  kresult = usb_setup_device_iterator (&deviceIterator, dev_location);
   if (kresult)
     return kresult;
 
@@ -204,7 +252,7 @@ static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***d
 
 
 
-static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {  
+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;
@@ -212,35 +260,43 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
 
   io_service_t device;
   long location;
+  bool locationValid;
   CFTypeRef locationCF;
   UInt32 message;
 
-  _usbi_log (ctx, LOG_LEVEL_INFO, "a device has been detached");
+  usbi_info (ctx, "a device has been detached");
 
   while ((device = IOIteratorNext (rem_devices)) != 0) {
     /* get the location from the i/o registry */
     locationCF = IORegistryEntryCreateCFProperty (device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
 
-    CFNumberGetValue(locationCF, kCFNumberLongType, &location);
-    CFRelease (locationCF);
     IOObjectRelease (device);
 
-    list_for_each_entry(handle, &ctx->open_devs, list) {
-      dpriv = (struct darwin_device_priv *)handle->dev->os_priv;
-      if (dpriv->location == location)
-       break;
-    }
+    if (!locationCF)
+      continue;
 
-    if (handle) {
-      priv  = (struct darwin_device_handle_priv *)handle->os_priv;
+    locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() &&
+           CFNumberGetValue(locationCF, kCFNumberLongType, &location);
 
-      message = MESSAGE_DEVICE_GONE;
-      write (priv->fds[1], &message, sizeof (message));
+    CFRelease (locationCF);
 
-      CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
-      CFRelease (priv->cfSource);
-      priv->cfSource = NULL;
+    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;
+
+       message = MESSAGE_DEVICE_GONE;
+       write (priv->fds[1], &message, sizeof (message));
+      }
     }
+
+    usbi_mutex_unlock(&ctx->open_devs_lock);
   }
 }
 
@@ -254,83 +310,110 @@ static void darwin_clear_iterator (io_iterator_t iter) {
 static void *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
+
+  /* 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 */
   CFRunLoopSourceRef     libusb_notification_cfsource;
   io_notification_port_t libusb_notification_port;
   io_iterator_t          libusb_rem_device_iterator;
 
-  _usbi_log (ctx, LOG_LEVEL_INFO, "creating hotplug event source");
+  usbi_info (ctx, "creating hotplug event source");
+
+  runloop = CFRunLoopGetCurrent ();
+  CFRetain (runloop);
 
   /* add the notification port to the run loop */
-  libusb_notification_port     = IONotificationPortCreate (libusb_darwin_mp);
+  libusb_notification_port     = IONotificationPortCreate (kIOMasterPortDefault);
   libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
-  CFRunLoopAddSource(CFRunLoopGetCurrent (), libusb_notification_cfsource, kCFRunLoopDefaultMode);
+  CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
 
   /* create notifications for removed devices */
   kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
                                              IOServiceMatching(kIOUSBDeviceClassName),
                                              (IOServiceMatchingCallback)darwin_devices_detached,
                                              (void *)ctx, &libusb_rem_device_iterator);
+
   if (kresult != kIOReturnSuccess) {
-    _usbi_log (ctx, LOG_LEVEL_ERROR, "could not add hotplug event source: %s", darwin_error_str (kresult));
+    usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
 
-    pthread_exit ((void *)kresult);
+    pthread_exit (NULL);
   }
 
   /* arm notifiers */
   darwin_clear_iterator (libusb_rem_device_iterator);
 
-  /* let the main thread know about the async runloop */
-  libusb_darwin_acfl = CFRunLoopGetCurrent ();
+  usbi_info (ctx, "thread ready to receive events");
 
-  _usbi_log (ctx, LOG_LEVEL_INFO, "libopenusb/darwin.c event_thread_main: thread ready to receive events");
+  /* signal the main thread */
+  pthread_mutex_lock (&libusb_darwin_at_mutex);
+  libusb_darwin_acfl = runloop;
+  pthread_cond_signal (&libusb_darwin_at_cond);
+  pthread_mutex_unlock (&libusb_darwin_at_mutex);
 
   /* run the runloop */
   CFRunLoopRun();
 
-  _usbi_log (ctx, LOG_LEVEL_INFO, "libopenusb/darwin.c event_thread_main: thread exiting");
+  usbi_info (ctx, "thread exiting");
 
   /* delete notification port */
-  CFRunLoopSourceInvalidate (libusb_notification_cfsource);
   IONotificationPortDestroy (libusb_notification_port);
-  
+  IOObjectRelease (libusb_rem_device_iterator);
+
+  CFRelease (runloop);
+
   libusb_darwin_acfl = NULL;
 
-  pthread_exit (0);
+  pthread_exit (NULL);
 }
 
 static int darwin_init(struct libusb_context *ctx) {
-  IOReturn kresult;
+  host_name_port_t host_self;
 
-  /* Create the master port for talking to IOKit */
-  if (!libusb_darwin_mp) {
-    kresult = IOMasterPort (MACH_PORT_NULL, &libusb_darwin_mp);
+  if (OSAtomicIncrement32Barrier(&initCount) == 1) {
+    /* create the clocks that will be used */
 
-    if (kresult != kIOReturnSuccess || !libusb_darwin_mp)
-      return darwin_to_libusb (kresult);
-  }
+    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, event_thread_main, (void *)ctx);
 
-  while (!libusb_darwin_acfl)
-    usleep (10);
+    pthread_mutex_lock (&libusb_darwin_at_mutex);
+    while (!libusb_darwin_acfl)
+      pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+    pthread_mutex_unlock (&libusb_darwin_at_mutex);
+  }
 
   return 0;
 }
 
 static void darwin_exit (void) {
-  void *ret;
-
-  /* stop the async runloop */
-  CFRunLoopStop (libusb_darwin_acfl);
-  pthread_join (libusb_darwin_at, &ret);
-
-  if (libusb_darwin_mp)
-    mach_port_deallocate(mach_task_self(), libusb_darwin_mp);
+  if (OSAtomicDecrement32Barrier(&initCount) == 0) {
+    mach_port_deallocate(mach_task_self(), clock_realtime);
+    mach_port_deallocate(mach_task_self(), clock_monotonic);
 
-  libusb_darwin_mp = 0;
+    /* stop the async runloop */
+    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) {
@@ -344,82 +427,201 @@ static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char
   return 0;
 }
 
-static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
+static int get_configuration_index (struct libusb_device *dev, int config_value) {
   struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
-  UInt8 configNum;
+  UInt8 i, numConfig;
+  IOUSBConfigurationDescriptorPtr desc;
   IOReturn kresult;
 
-  kresult = (*(priv->device))->GetConfiguration (priv->device, &configNum);
+  /* is there a simpler way to determine the index? */
+  kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
-  return darwin_get_config_descriptor (dev, configNum, buffer, len, host_endian);
+  for (i = 0 ; i < numConfig ; i++) {
+    (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc);
+
+    if (desc->bConfigurationValue == config_value)
+      return i;
+  }
+
+  /* configuration not found */
+  return LIBUSB_ERROR_OTHER;
+}
+
+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;
+  int config_index;
+
+  if (0 == priv->active_config)
+    return LIBUSB_ERROR_INVALID_PARAM;
+
+  config_index = get_configuration_index (dev, priv->active_config);
+  if (config_index < 0)
+    return config_index;
+
+  return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian);
 }
 
 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;
   IOUSBConfigurationDescriptorPtr desc;
   IOReturn kresult;
+  usb_device_t **device = NULL;
 
-  kresult = (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, config_index, &desc);
-  if (kresult != kIOReturnSuccess)
+  if (!priv)
+    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);
+  if (kresult == kIOReturnSuccess) {
+    /* copy descriptor */
+    if (libusb_le16_to_cpu(desc->wTotalLength) < len)
+      len = libusb_le16_to_cpu(desc->wTotalLength);
+
+    memmove (buffer, desc, len);
+
+    /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */
+    *host_endian = 0;
+  }
+
+  if (!priv->device)
+    (*device)->Release (device);
+
+  return darwin_to_libusb (kresult);
+}
+
+/* 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;
+
+  IOUSBConfigurationDescriptorPtr configDesc;
+  IOUSBFindInterfaceRequest request;
+  kern_return_t             kresult;
+  io_iterator_t             interface_iterator;
+  io_service_t              firstInterface;
+
+  if (priv->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;
+
+  /* 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
+     might lock up on the device request) */
+
+  /* Setup the Interface Request */
+  request.bInterfaceClass    = kIOUSBFindInterfaceDontCare;
+  request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+  request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+  request.bAlternateSetting  = kIOUSBFindInterfaceDontCare;
+
+  kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+  if (kresult)
     return darwin_to_libusb (kresult);
 
-  /* sanity check. is the buffer larger than the returned descriptor */
-  if (len > sizeof (*desc))
-    len = sizeof (*desc);
+  /* iterate once */
+  firstInterface = IOIteratorNext(interface_iterator);
 
-  memmove (buffer, desc, len);
+  /* done with the interface iterator */
+  IOObjectRelease(interface_iterator);
 
-  /* GetConfigurationDescriptorPtr returns the descriptor is USB bus order */
-  *host_endian = 0;
+  if (firstInterface) {
+    IOObjectRelease (firstInterface);
+
+    /* device is configured */
+    if (priv->dev_descriptor.bNumConfigurations == 1)
+      /* to avoid problems with some devices get the configurations value from the configuration descriptor */
+      priv->active_config = priv->first_config;
+    else
+      /* devices with more than one configuration should work with GetConfiguration */
+      (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config);
+  } else
+    /* not configured */
+    priv->active_config = 0;
+  
+  usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config);
 
   return 0;
 }
 
-static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) {
+static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **device) {
   struct darwin_device_priv *priv;
-  struct libusb_device *dev; 
-  struct discovered_devs *discdevs;
-  UInt16                address, idVendor, idProduct;
-  UInt8                 bDeviceClass, bDeviceSubClass;
-  IOUSBDevRequest      req;
-  int ret;
-
-  dev = usbi_get_device_by_session_id(ctx, locationID);
-  if (!dev) {
-    _usbi_log (ctx, LOG_LEVEL_INFO, "allocating new device for location 0x%08x", locationID);
-    dev = usbi_alloc_device(ctx, locationID);
-    if (!dev)
-      return LIBUSB_ERROR_NO_MEM;
+  int retries = 5, 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;
 
-    priv = (struct darwin_device_priv *)dev->os_priv;
+  (*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) */
+  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(IOUSBDeviceDescriptor);
+    req.wLength       = sizeof(priv->dev_descriptor);
     req.pData         = &(priv->dev_descriptor);
-    
-    (*(device))->GetDeviceAddress (device, (USBDeviceAddress *)&address);
-    (*(device))->GetDeviceProduct (device, &idProduct);
-    (*(device))->GetDeviceVendor (device, &idVendor);
-    (*(device))->GetDeviceClass (device, &bDeviceClass);
-    (*(device))->GetDeviceSubClass (device, &bDeviceSubClass);
-
-    /**** retrieve device descriptors ****/
-    /* device must be open for DeviceRequest */
-    (*device)->USBDeviceOpen(device);
-    
+
+    /* 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 (ret != kIOReturnSuccess) {
-      int try_unsuspend = 1;
-      UInt32 info;
 
+    if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType)
+      /* received an overrun error but we still received a device descriptor */
+      ret = kIOReturnSuccess;
+
+    if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.idProduct ||
+                                   0 == priv->dev_descriptor.bNumConfigurations ||
+                                   0 == priv->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...");
+
+       /* set the first configuration */
+       (*device)->SetConfiguration(device, 1);
+
+       /* 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;
+
       /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
       (void)(*device)->GetUSBDeviceInformation (device, &info);
 
@@ -428,62 +630,141 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
 
       if (try_unsuspend) {
        /* resume the device */
-       (void)(*device)->USBDeviceSuspend (device, 0);
-
-       ret = (*(device))->DeviceRequest (device, &req);
-
-       /* resuspend the device */
-       (void)(*device)->USBDeviceSuspend (device, 1);
+       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;
       }
     }
 
-    (*device)->USBDeviceClose (device);
-
-    if (ret != kIOReturnSuccess) {
-      _usbi_log (ctx, LOG_LEVEL_WARNING, "could not retrieve device descriptor: %s. skipping device", darwin_error_str (ret));
-      libusb_unref_device(dev);
-      return -1;
+    if (kIOReturnSuccess != ret) {
+      usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000);
+      /* sleep for a little while before trying again */
+      usleep (delay);
     }
-    /**** end: retrieve device descriptors ****/
-    
-    /* 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) {
-      /* not a valid device */
-      _usbi_log (ctx, LOG_LEVEL_WARNING, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
-                idProduct, libusb_le16_to_cpu (priv->dev_descriptor.idProduct));
-      libusb_unref_device(dev);
-      return -1;
+  } while (kIOReturnSuccess != ret && retries--);
+
+  if (unsuspended)
+    /* resuspend the device */
+    (void)(*device)->USBDeviceSuspend (device, 1);
+
+  if (is_open)
+    (void) (*device)->USBDeviceClose (device);
+
+  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));
+    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);
+
+  /* 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) {
+    /* 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;
+  }
+
+  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;
+
+  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);
+
+    if (!dev) {
+      ret = LIBUSB_ERROR_NO_MEM;
+      break;
     }
 
+    priv = (struct darwin_device_priv *)dev->os_priv;
+
+    (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address);
+
+    ret = darwin_cache_device_descriptor (ctx, dev, device);
+    if (ret < 0)
+      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)
+      break;
+
     dev->bus_number     = locationID >> 24;
     dev->device_address = address;
 
+    (*device)->GetDeviceSpeed (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;
+    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, idVendor, idProduct, bDeviceClass, bDeviceSubClass);
+    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);
+    ret = usbi_sanitize_device (dev);
+    if (ret < 0)
+      break;
 
-    if (ret < 0) {
-      libusb_unref_device(dev);
-      return -1;
+    /* append the device to the list of discovered devices */
+    discdevs = discovered_devs_append(*_discdevs, dev);
+    if (!discdevs) {
+      ret = LIBUSB_ERROR_NO_MEM;
+      break;
     }
-  } else {
-    priv = (struct darwin_device_priv *)dev->os_priv;
 
-    _usbi_log (ctx, LOG_LEVEL_INFO, "using existing device for location 0x%08x", locationID);
-  }
+    *_discdevs = discdevs;
 
-  /* append the device to the list of discovered devices */
-  discdevs = discovered_devs_append(*_discdevs, dev);
-  if (!discdevs)
-    return LIBUSB_ERROR_NO_MEM;
-    
-  *_discdevs = discdevs;
-  
-  _usbi_log (ctx, LOG_LEVEL_INFO, "found device with address %d at %s", dev->device_address, priv->sys_path);
-  
-  return 0;
+    usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path);
+  } while (0);
+
+  if (need_unref)
+    libusb_unref_device(dev);
+
+  return ret;
 }
 
 static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) {
@@ -492,10 +773,7 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_
   kern_return_t        kresult;
   UInt32               location;
 
-  if (!libusb_darwin_mp)
-    return LIBUSB_ERROR_INVALID_PARAM;
-  
-  kresult = usb_setup_device_iterator (&deviceIterator);
+  kresult = usb_setup_device_iterator (&deviceIterator, 0);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
@@ -516,43 +794,57 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
   usb_device_t  **darwin_device;
   IOReturn kresult;
 
-  kresult = darwin_get_device (dpriv->location, &darwin_device);
-  if (kresult) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "could not find device: %s", darwin_error_str (kresult));
-    return darwin_to_libusb (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;
+    dpriv->device = darwin_device;
 
-  /* try to open the device */
-  kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
+    /* try to open the device */
+    kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
 
-  if (kresult != kIOReturnSuccess) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "USBDeviceOpen: %s", darwin_error_str(kresult));
+    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;
+      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);
-    }
-  } else {
-    priv->is_open = 1;
+       break;
+      default:
+       (*(dpriv->device))->Release (dpriv->device);
+       dpriv->device = NULL;
+       return darwin_to_libusb (kresult);
+      }
+    } 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));
 
-    /* create async event source */
-    kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
+       (*(dpriv->device))->USBDeviceClose (dpriv->device);
+       (*(dpriv->device))->Release (dpriv->device);
 
-    CFRetain (libusb_darwin_acfl);
+       dpriv->device = NULL;
+       return darwin_to_libusb (kresult);
+      }
+
+      priv->is_open = 1;
+
+      CFRetain (libusb_darwin_acfl);
 
-    /* add the cfSource to the aync run loop */
-    CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+      /* add the cfSource to the aync run loop */
+      CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+    }
   }
 
+  /* device opened successfully */
+  dpriv->open_count++;
+
   /* create a file descriptor for notifications */
   pipe (priv->fds);
 
@@ -561,7 +853,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
 
   usbi_add_pollfd(HANDLE_CTX(dev_handle), priv->fds[0], POLLIN);
 
-  _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "device open for access");
+  usbi_info (HANDLE_CTX (dev_handle), "device open for access");
 
   return 0;
 }
@@ -572,49 +864,58 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
   IOReturn kresult;
   int i;
 
+  if (dpriv->open_count == 0) {
+    /* something is probably very wrong if this is the case */
+    usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!\n");
+    return;
+  }
+
+  dpriv->open_count--;
+
   /* make sure all interfaces are released */
   for (i = 0 ; i < USB_MAXINTERFACES ; i++)
     if (dev_handle->claimed_interfaces & (1 << i))
       libusb_release_interface (dev_handle, i);
 
-  if (priv->is_open) {
-    /* delete the device's async event source */
-    if (priv->cfSource) {
-      CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
-      CFRelease (priv->cfSource);
+  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);
+      }
+
+      /* 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));
+      }
     }
 
-    /* close the device */
-    kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
+    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_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "USBDeviceClose: %s", darwin_error_str(kresult));
+      usbi_err (HANDLE_CTX (dev_handle), "Release: %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_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "Release: %s", darwin_error_str(kresult));
+
+    dpriv->device = NULL;
   }
 
+  /* file descriptors are maintained per-instance */
   usbi_remove_pollfd (HANDLE_CTX (dev_handle), priv->fds[0]);
   close (priv->fds[1]);
   close (priv->fds[0]);
+
+  priv->fds[0] = priv->fds[1] = -1;
 }
 
 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;
-  UInt8 configNum;
-  IOReturn kresult;
-
-  kresult = (*(dpriv->device))->GetConfiguration (dpriv->device, &configNum);
-  if (kresult != kIOReturnSuccess)
-    return darwin_to_libusb (kresult);
 
-  *config = (int) configNum;
+  *config = (int) dpriv->active_config;
 
   return 0;
 }
@@ -639,6 +940,8 @@ static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int
     if (dev_handle->claimed_interfaces & (1 << i))
       darwin_claim_interface (dev_handle, i);
 
+  dpriv->active_config = config;
+
   return 0;
 }
 
@@ -649,7 +952,7 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s
   io_iterator_t             interface_iterator;
 
   *usbInterfacep = IO_OBJECT_NULL;
-  
+
   /* Setup the Interface Request */
   request.bInterfaceClass    = kIOUSBFindInterfaceDontCare;
   request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
@@ -660,12 +963,15 @@ 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++ )
+  for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
     *usbInterfacep = IOIteratorNext(interface_iterator);
-  
+    if (current_interface != ifc)
+      (void) IOObjectRelease (*usbInterfacep);
+  }
+
   /* done with the interface iterator */
   IOObjectRelease(interface_iterator);
-  
+
   return 0;
 }
 
@@ -673,7 +979,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   kern_return_t kresult;
 
@@ -682,12 +988,12 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
   u_int16_t dont_care2;
   int i;
 
-  _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "building table of endpoints.");
-  
+  usbi_info (HANDLE_CTX (dev_handle), "building table of endpoints.");
+
   /* retrieve the total number of endpoints on this interface */
   kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
   if (kresult) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
@@ -697,18 +1003,18 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
                                                            &dont_care2, &dont_care3);
 
     if (kresult != kIOReturnSuccess) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "error getting pipe information for pipe %d: %s", i, darwin_error_str(kresult));
+      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_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number);
+    usbi_info (HANDLE_CTX (dev_handle), "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->num_endpoints = numep;
-  
+
   return 0;
 }
 
@@ -718,94 +1024,70 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
   io_service_t          usbInterface = IO_OBJECT_NULL;
   IOReturn kresult;
   IOCFPlugInInterface **plugInInterface = NULL;
-  long                  score;
-  uint8_t               new_config;
+  SInt32                score;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
   /* make sure we have an interface */
-  if (!usbInterface) {
-    u_int8_t nConfig;     /* Index of configuration to use */
-    IOUSBConfigurationDescriptorPtr configDesc; /* to describe which configuration to select */
-    /* Only a composite class device with no vendor-specific driver will
-       be configured. Otherwise, we need to do it ourselves, or there
-       will be no interfaces for the device. */
-
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "no interface found; selecting configuration" );
-
-    kresult = (*(dpriv->device))->GetNumberOfConfigurations (dpriv->device, &nConfig);
-    if (kresult != kIOReturnSuccess) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "GetNumberOfConfigurations: %s", darwin_error_str(kresult));
-      return darwin_to_libusb(kresult);
-    }
-    
-    if (nConfig < 1) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "GetNumberOfConfigurations: no configurations");
-      return LIBUSB_ERROR_OTHER;
-    }
-
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "device has %d configuration%s. using the first",
-             (int)nConfig, (nConfig > 1 ? "s" : "") );
-
-    /* Always use the first configuration */
-    kresult = (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, 0, &configDesc);
-    if (kresult != kIOReturnSuccess) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "GetConfigurationDescriptorPtr: %s",
-               darwin_error_str(kresult));
-
-      new_config = 1;
-    } else
-      new_config = configDesc->bConfigurationValue;
-
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "new configuration value is %d", new_config);
+  if (!usbInterface && dpriv->first_config != 0) {
+    usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
 
     /* set the configuration */
-    kresult = darwin_set_configuration (dev_handle, new_config);
+    kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
     if (kresult != LIBUSB_SUCCESS) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "could not set configuration");
+      usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
       return kresult;
     }
 
     kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
     if (kresult) {
-      _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "darwin_get_interface: %s", darwin_error_str(kresult));
+      usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
       return darwin_to_libusb (kresult);
     }
   }
 
   if (!usbInterface) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "interface not found");
+    usbi_err (HANDLE_CTX (dev_handle), "interface not found");
     return LIBUSB_ERROR_NOT_FOUND;
   }
-  
+
   /* get an interface to the device's interface */
   kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID,
                                               kIOCFPlugInInterfaceID, &plugInInterface, &score);
-  kresult = IOObjectRelease(usbInterface);
-  if (kresult || !plugInInterface)
+
+  /* ignore release error */
+  (void)IOObjectRelease (usbInterface);
+
+  if (kresult) {
+    usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
-  
+  }
+
+  if (!plugInInterface) {
+    usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+    return LIBUSB_ERROR_NOT_FOUND;
+  }
+
   /* Do the actual claim */
   kresult = (*plugInInterface)->QueryInterface(plugInInterface,
                                               CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
                                               (LPVOID)&cInterface->interface);
+  /* We no longer need the intermediate plug-in */
+  IODestroyPlugInInterface (plugInInterface);
   if (kresult || !cInterface->interface) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "QueryInterface: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
-  
-  /* We no longer need the intermediate plug-in */
-  (*plugInInterface)->Release(plugInInterface);
 
   /* claim the interface */
   kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
   if (kresult) {
-    _usbi_log(HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "USBInterfaceOpen: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
@@ -814,18 +1096,27 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
   if (kresult) {
     /* this should not happen */
     darwin_release_interface (dev_handle, iface);
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "could not build endpoint table");
+    usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
     return kresult;
   }
 
+  cInterface->cfSource = NULL;
+
   /* create async event source */
   kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
-  
-  /* add the cfSource to the aync run loop */
-  CFRetain (libusb_darwin_acfl);
+  if (kresult != kIOReturnSuccess) {
+    usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+
+    /* can't continue without an async event source */
+    (void)darwin_release_interface (dev_handle, iface);
+
+    return darwin_to_libusb (kresult);
+  }
+
+  /* add the cfSource to the async thread's run loop */
   CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
 
-  _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_INFO, "interface opened");
+  usbi_info (HANDLE_CTX (dev_handle), "interface opened");
 
   return 0;
 }
@@ -835,7 +1126,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
   IOReturn kresult;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   /* Check to see if an interface is open */
   if (!cInterface->interface)
@@ -843,18 +1134,20 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
 
   /* clean up endpoint data */
   cInterface->num_endpoints = 0;
-  
+
   /* delete the interface's async event source */
-  CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
-  CFRelease (cInterface->cfSource);
+  if (cInterface->cfSource) {
+    CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+    CFRelease (cInterface->cfSource);
+  }
 
   kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
   if (kresult)
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "USBInterfaceClose: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
 
   kresult = (*(cInterface->interface))->Release(cInterface->interface);
   if (kresult != kIOReturnSuccess)
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "Release: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
 
   cInterface->interface = IO_OBJECT_NULL;
 
@@ -866,7 +1159,7 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
   IOReturn kresult;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   if (!cInterface->interface)
     return LIBUSB_ERROR_NO_DEVICE;
@@ -875,6 +1168,15 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
   if (kresult != kIOReturnSuccess)
     darwin_reset_device (dev_handle);
 
+  /* update list of endpoints */
+  kresult = get_endpoints (dev_handle, iface);
+  if (kresult) {
+    /* this should not happen */
+    darwin_release_interface (dev_handle, iface);
+    usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+    return kresult;
+  }
+
   return darwin_to_libusb (kresult);
 }
 
@@ -882,13 +1184,13 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
   uint8_t pipeRef, iface;
   IOReturn kresult;
 
   /* determine the interface/endpoint to use */
   if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, &iface) != 0) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "endpoint not found on any open interface");
+    usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface");
 
     return LIBUSB_ERROR_NOT_FOUND;
   }
@@ -902,7 +1204,7 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c
   kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
 #endif
   if (kresult)
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "ClearPipeStall: %s", darwin_error_str (kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
 
   return darwin_to_libusb (kresult);
 }
@@ -913,7 +1215,7 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
 
   kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
   if (kresult)
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "ResetDevice: %s", darwin_error_str (kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
 
   return darwin_to_libusb (kresult);
 }
@@ -926,11 +1228,11 @@ static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle,
 
   kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
   if (kresult) {
-    _usbi_log (HANDLE_CTX (dev_handle), LOG_LEVEL_ERROR, "darwin_get_interface: %s", darwin_error_str(kresult));
+    usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
 
     return darwin_to_libusb (kresult);
   }
-  
+
   driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
   IOObjectRelease (usbInterface);
 
@@ -946,18 +1248,23 @@ static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle,
 
 /* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
 static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+  (void)dev_handle;
+  (void)interface;
   return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+  (void)dev_handle;
+  (void)interface;
   return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 static void darwin_destroy_device(struct libusb_device *dev) {
+  (void)dev;
 }
 
 static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_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;
@@ -967,13 +1274,13 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
   uint8_t                direction, number, interval, pipeRef, iface;
   uint16_t               maxPacketSize;
 
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   /* are we reading or writing? */
   is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-  
+
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "endpoint not found on any open interface");
+    usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
 
     return LIBUSB_ERROR_NOT_FOUND;
   }
@@ -982,7 +1289,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
 
   (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
                                                 &transferType, &maxPacketSize, &interval);
-  
+
   /* submit the request */
   /* timeouts are unavailable on interrupt endpoints */
   if (transferType == kUSBInterrupt) {
@@ -993,6 +1300,8 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
       ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
                                                       transfer->length, darwin_async_io_callback, itransfer);
   } else {
+    itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
     if (is_read)
       ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
                                                        transfer->length, transfer->timeout, transfer->timeout,
@@ -1004,14 +1313,14 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
   }
 
   if (ret)
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "bulk transfer failed (dir = %s): %s", is_read ? "In" : "Out",
-              darwin_error_str(ret));
+    usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", is_read ? "In" : "Out",
+              darwin_error_str(ret), ret);
 
   return darwin_to_libusb (ret);
 }
 
 static int submit_iso_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_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;
 
@@ -1022,15 +1331,23 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   AbsoluteTime            atTime;
   int                     i;
 
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   /* are we reading or writing? */
   is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-  
-  /* construct an array of IOUSBIsocFrames */
-  tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
-  if (!tpriv->isoc_framelist)
-    return LIBUSB_ERROR_NO_MEM;
+
+  /* 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 = NULL;
+  }
+
+  if (!tpriv->isoc_framelist) {
+    tpriv->num_iso_packets = transfer->num_iso_packets;
+    tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
+    if (!tpriv->isoc_framelist)
+      return LIBUSB_ERROR_NO_MEM;
+  }
 
   /* copy the frame list from the libusb descriptor (the structures differ only is member order) */
   for (i = 0 ; i < transfer->num_iso_packets ; i++)
@@ -1038,7 +1355,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
 
   /* determine the interface/endpoint to use */
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "endpoint not found on any open interface");
+    usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
 
     return LIBUSB_ERROR_NOT_FOUND;
   }
@@ -1048,7 +1365,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   /* Last but not least we need the bus frame number */
   kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
   if (kresult) {
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "failed to get bus frame number: %d", kresult);
+    usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
     free(tpriv->isoc_framelist);
     tpriv->isoc_framelist = NULL;
 
@@ -1056,8 +1373,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   }
 
   /* schedule for a frame a little in the future */
-  frame += 2;
-       
+  frame += 4;
+
+  if (cInterface->frames[transfer->endpoint] && frame < cInterface->frames[transfer->endpoint])
+    frame = cInterface->frames[transfer->endpoint];
+
   /* submit the request */
   if (is_read)
     kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
@@ -1068,8 +1388,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
                                                              transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
                                                              itransfer);
 
+  cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets / 8;
+
   if (kresult != kIOReturnSuccess) {
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "isochronous transfer failed (dir: %s): %s", is_read ? "In" : "Out",
+    usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", is_read ? "In" : "Out",
               darwin_error_str(kresult));
     free (tpriv->isoc_framelist);
     tpriv->isoc_framelist = NULL;
@@ -1078,10 +1400,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   return darwin_to_libusb (kresult);
 }
 
-static int submit_control_transfer(struct usbi_transfer *itransfer) {  
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_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_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);
 
   IOReturn               kresult;
@@ -1091,26 +1414,44 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) {
   /* IOUSBDeviceInterface expects the request in cpu endianess */
   tpriv->req.bmRequestType     = setup->bmRequestType;
   tpriv->req.bRequest          = setup->bRequest;
-  /* these values should already be in bus order */
-  tpriv->req.wValue            = setup->wValue;
-  tpriv->req.wIndex            = setup->wIndex;
-  tpriv->req.wLength           = setup->wLength;
+  /* these values should be in bus order from libusb_fill_control_setup */
+  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 */
   tpriv->req.pData             = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
   tpriv->req.completionTimeout = transfer->timeout;
   tpriv->req.noDataTimeout     = transfer->timeout;
 
+  itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
   /* all transfers in libusb-1.0 are async */
-  kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
+
+  if (transfer->endpoint) {
+    struct darwin_interface *cInterface;
+    uint8_t                 pipeRef, iface;
+
+    if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 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 */
+    kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
 
   if (kresult != kIOReturnSuccess)
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "control request failed: %s", darwin_error_str(kresult));
-  
+    usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult));
+
   return darwin_to_libusb (kresult);
 }
 
 static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
   switch (transfer->type) {
   case LIBUSB_TRANSFER_TYPE_CONTROL:
@@ -1121,17 +1462,20 @@ static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
   case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
     return submit_iso_transfer(itransfer);
   default:
-    _usbi_log (TRANSFER_CTX(transfer), LOG_LEVEL_ERROR, "unknown endpoint type %d", transfer->type);
+    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 libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
   IOReturn kresult;
 
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "WARNING: aborting all transactions control pipe");
+  usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions control pipe");
+
+  if (!dpriv->device)
+    return LIBUSB_ERROR_NO_DEVICE;
 
   kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device);
 
@@ -1139,26 +1483,30 @@ 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 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_interface *cInterface;
+  struct darwin_interface *cInterface;
   uint8_t pipeRef, iface;
   IOReturn kresult;
 
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
-    _usbi_log (TRANSFER_CTX (transfer), LOG_LEVEL_ERROR, "endpoint not found on any open interface");
+    usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
 
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
   cInterface = &priv->interfaces[iface];
 
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef);
+  if (!dpriv->device)
+    return LIBUSB_ERROR_NO_DEVICE;
+
+  usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef);
 
   /* abort transactions */
   (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
-  
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "calling clear pipe stall to clear the data toggle bit");
+
+  usbi_info (ITRANSFER_CTX (itransfer), "calling clear pipe stall to clear the data toggle bit");
 
   /* clear the data toggle bit */
 #if (InterfaceVersion < 190)
@@ -1172,7 +1520,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
 }
 
 static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
   switch (transfer->type) {
   case LIBUSB_TRANSFER_TYPE_CONTROL:
@@ -1182,13 +1530,13 @@ static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
   case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
     return darwin_abort_transfers (itransfer);
   default:
-    _usbi_log (TRANSFER_CTX(transfer), LOG_LEVEL_ERROR, "unknown endpoint type %d", transfer->type);
+    usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
     return LIBUSB_ERROR_INVALID_PARAM;
   }
 }
 
 static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
 
   if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
@@ -1199,170 +1547,129 @@ static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
 
 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 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;
+  UInt32 message, size;
+
+  usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed");
 
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "an async io operation has completed");
+  size = (UInt32) arg0;
 
   /* 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], &arg0, sizeof (UInt32));
+  write (priv->fds[1], &size, sizeof (size));
 }
 
-static void darwin_bulk_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
-  enum libusb_transfer_status status;
-
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "handling bulk completion with status %d", result);
+static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+  if (itransfer->flags & USBI_TRANSFER_TIMED_OUT)
+    result = kIOUSBTransactionTimeout;
 
   switch (result) {
+  case kIOReturnUnderrun:
   case kIOReturnSuccess:
-    status = LIBUSB_TRANSFER_COMPLETED;
-    itransfer->transferred += io_size;
-    break;
+    return LIBUSB_TRANSFER_COMPLETED;
   case kIOReturnAborted:
-    usbi_handle_transfer_cancellation(itransfer);
-    return;
+    return LIBUSB_TRANSFER_CANCELLED;
   case kIOUSBPipeStalled:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_WARNING, "unsupported control request");
-    status = LIBUSB_TRANSFER_STALL;
-    
-    break;
+    usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: pipe is stalled");
+    return LIBUSB_TRANSFER_STALL;
+  case kIOReturnOverrun:
+    usbi_err (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+    return LIBUSB_TRANSFER_OVERFLOW;
+  case kIOUSBTransactionTimeout:
+    usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+    itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
+    return LIBUSB_TRANSFER_TIMED_OUT;
   default:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_ERROR, "control error = %s", darwin_error_str (result));
-    status = LIBUSB_TRANSFER_ERROR;
+    usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+    return LIBUSB_TRANSFER_ERROR;
   }
-
-  usbi_handle_transfer_completion(itransfer, status);
 }
 
-static void darwin_isoc_callback (struct usbi_transfer *itransfer, kern_return_t result) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
-  int i, status;
-
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "handling isoc completion with status %d", result);
-
-  if (result == kIOReturnSuccess && 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;
-      lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
-      lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
-    }
-  }
+  int isIsoc      = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+  int isBulk      = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
+  int isControl   = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
+  int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
+  int i;
 
-  switch (result) {
-  case kIOReturnSuccess:
-    status = LIBUSB_TRANSFER_COMPLETED;
-    break;
-  case kIOReturnAborted:
-    usbi_handle_transfer_cancellation(itransfer);
+  if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+    usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
     return;
-  case kIOUSBPipeStalled:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_WARNING, "unsupported control request");
-    status = LIBUSB_TRANSFER_STALL;
-
-    break;
-  default:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_ERROR, "control error = %s", darwin_error_str (result));
-    status = LIBUSB_TRANSFER_ERROR;
   }
 
-  usbi_handle_transfer_completion(itransfer, status);
-}
-
-static void darwin_control_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
-  int status;
+  usbi_info (ITRANSFER_CTX (itransfer), "handling %s completion with kernel status %d",
+            isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result);
 
-  _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_INFO, "handling control completion with status %d", result);
+  if (kIOReturnSuccess == result || kIOReturnUnderrun == result) {
+    if (isIsoc && tpriv->isoc_framelist) {
+      /* copy isochronous results back */
 
-  switch (result) {
-  case kIOReturnSuccess:
-    status = LIBUSB_TRANSFER_COMPLETED;
-    itransfer->transferred += io_size;
-    break;
-  case kIOReturnAborted:
-    usbi_handle_transfer_cancellation(itransfer);
-    return;
-  case kIOUSBPipeStalled:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_WARNING, "unsupported control request");
-    status = LIBUSB_TRANSFER_STALL;
-    
-    break;
-  default:
-    _usbi_log (ITRANSFER_CTX (itransfer), LOG_LEVEL_ERROR, "control error = %s", darwin_error_str (result));
-    status = LIBUSB_TRANSFER_ERROR;
+      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;
+      }
+    } else if (!isIsoc)
+      itransfer->transferred += io_size;
   }
 
-  usbi_handle_transfer_completion(itransfer, status);
+  /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
+  usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, result));
 }
 
-static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-
-  switch (transfer->type) {
-  case LIBUSB_TRANSFER_TYPE_CONTROL:
-    darwin_control_callback (itransfer, result, io_size);
-    break;
-  case LIBUSB_TRANSFER_TYPE_BULK:
-  case LIBUSB_TRANSFER_TYPE_INTERRUPT:
-    darwin_bulk_callback (itransfer, result, io_size);
-    break;
-  case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-    darwin_isoc_callback (itransfer, result);
-    break;
-  default:
-    _usbi_log (TRANSFER_CTX(transfer), LOG_LEVEL_ERROR, "unknown endpoint type %d", transfer->type);
-  }
-}
-
-static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds, int num_ready) {
+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;
-  int i = 0, ret;
+  POLL_NFDS_TYPE i = 0;
+  ssize_t ret;
   UInt32 message;
 
-  pthread_mutex_lock(&ctx->open_devs_lock);
+  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_log (ctx, LOG_LEVEL_INFO, "checking fd %i with revents = %x", fds[i], pollfd->revents);
+    usbi_info (ctx, "checking fd %i with revents = %x", fds[i], pollfd->revents);
 
     if (!pollfd->revents)
       continue;
 
     num_ready--;
-    list_for_each_entry(handle, &ctx->open_devs, list) {
+    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) {
-      usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fds[0]);
-      usbi_handle_disconnect(handle);
-
-      /* done with this device */
-      continue;
-    }
-
-    ret = read (hpriv->fds[0], &message, sizeof (message));
-    if (ret < sizeof (message))
-      continue;
+    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 */
       continue;
     case MESSAGE_ASYNC_IO_COMPLETE:
@@ -1373,11 +1680,11 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds
       darwin_handle_callback (itransfer, kresult, io_size);
       break;
     default:
-      _usbi_log (ctx, LOG_LEVEL_ERROR, "unknown message received from device pipe");
+      usbi_err (ctx, "unknown message received from device pipe");
     }
   }
 
-  pthread_mutex_unlock(&ctx->open_devs_lock);
+  usbi_mutex_unlock(&ctx->open_devs_lock);
 
   return 0;
 }
@@ -1387,13 +1694,13 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
   clock_serv_t clock_ref;
 
   switch (clk_id) {
-  case LIBUSB_CLOCK_REALTIME:
+  case USBI_CLOCK_REALTIME:
     /* CLOCK_REALTIME represents time since the epoch */
-    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock_ref);
+    clock_ref = clock_realtime;
     break;
-  case LIBUSB_CLOCK_MONOTONIC:
+  case USBI_CLOCK_MONOTONIC:
     /* use system boot time as reference for the monotonic clock */
-    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_ref);
+    clock_ref = clock_monotonic;
     break;
   default:
     return LIBUSB_ERROR_INVALID_PARAM;