diff options
-rw-r--r-- | libusb/os/darwin_usb.c | 472 | ||||
-rw-r--r-- | libusb/os/darwin_usb.h | 65 |
2 files changed, 294 insertions, 243 deletions
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index f9bd479..0e06afb 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -52,6 +52,11 @@ static clock_serv_t clock_monotonic; 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; @@ -62,8 +67,7 @@ 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, usb_device_t **device, UInt32 locationID, - UInt32 parent_location, UInt8 port); +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) { @@ -96,6 +100,8 @@ static const char *darwin_error_str (int result) { 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"; } @@ -127,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; @@ -204,77 +225,43 @@ static int get_ioregistry_value_number (io_service_t service, CFStringRef proper return ret; } -static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp, UInt8 *portp, UInt32 *parent_locationp) { +static usb_device_t **darwin_device_from_service (io_service_t service) +{ io_cf_plugin_ref_t *plugInInterface = NULL; usb_device_t **device; - io_service_t usbDevice, parent; kern_return_t result; SInt32 score; - if (!IOIteratorIsValid (deviceIterator)) - return NULL; - - - while ((usbDevice = IOIteratorNext(deviceIterator))) { - result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, &plugInInterface, - &score); - - /* we are done with the usb_device_t */ - (void)IOObjectRelease(usbDevice); - - if (portp) { - *portp = 0; - (void) get_ioregistry_value_number (usbDevice, CFSTR("PortNum"), kCFNumberSInt8Type, portp); - } - - if (parent_locationp) { - *parent_locationp = 0; + result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); - result = IORegistryEntryGetParentEntry (usbDevice, kIOUSBPlane, &parent); - - if (kIOReturnSuccess == result) { - (void) get_ioregistry_value_number (parent, CFSTR("locationID"), kCFNumberLongType, parent_locationp); - } - } - - 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) + 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); /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ (*plugInInterface)->Release (plugInInterface); - /* get the location from the device */ - if (locationp) - (*(device))->GetLocationID(device, locationp); - return device; } static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { struct libusb_context *ctx; - usb_device_t **device; - UInt32 location, parent_location; - UInt8 port; + io_service_t service; usbi_mutex_lock(&active_contexts_lock); - while ((device = usb_get_next_device (add_devices, &location, &port, &parent_location))) { + 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, device, location, parent_location, port); + process_new_device (ctx, service);; } - /* release extra reference */ - (*device)->Release (device); + IOObjectRelease(service); } usbi_mutex_unlock(&active_contexts_lock); @@ -285,40 +272,27 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { struct libusb_context *ctx; io_service_t device; - bool locationValid; - UInt32 location; - CFTypeRef locationCF; + 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) - continue; - - locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() && - CFNumberGetValue(locationCF, kCFNumberSInt32Type, &location); - - CFRelease (locationCF); - - if (!locationValid) + if (!ret) continue; usbi_mutex_lock(&active_contexts_lock); list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { - usbi_dbg ("libusb/darwin.c darwin_devices_detached: notifying context %p of device disconnect", ctx); + usbi_dbg ("notifying context %p of device disconnect", ctx); - dev = usbi_get_device_by_session_id(ctx, location); - if (!dev) { - continue; + 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); } - - /* 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(&active_contexts_lock); @@ -341,13 +315,11 @@ static void *darwin_event_thread_main (void *arg0) { and crash reports. */ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 pthread_setname_np ("org.libusb.device-hotplug"); -#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 @@ -425,8 +397,19 @@ static void *darwin_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); @@ -437,6 +420,11 @@ static int darwin_init(struct libusb_context *ctx) { 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); @@ -450,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) { @@ -465,7 +453,7 @@ static void darwin_exit (void) { } 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); @@ -476,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; @@ -498,7 +486,7 @@ static int get_configuration_index (struct libusb_device *dev, int config_value) } 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) @@ -512,7 +500,7 @@ 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; int ret; @@ -540,8 +528,8 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi } /* 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; @@ -549,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 @@ -582,23 +570,23 @@ 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_dbg ("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_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { - IOUSBDevRequest req; + IOUSBDevRequestTO req; memset (buffer, 0, buffer_size); @@ -609,37 +597,37 @@ static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 d req.wIndex = desc_index; req.wLength = buffer_size; req.pData = buffer; + req.noDataTimeout = 20; + req.completionTimeout = 100; - return (*device)->DeviceRequest (device, &req); + return (*device)->DeviceRequestTO (device, &req); } -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 = 2, delay = 30000; +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; 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 { - /* 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 = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &priv->dev_descriptor, sizeof(priv->dev_descriptor)); - + /**** retrieve device descriptor ****/ + ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor)); - if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType) + if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType) /* received an overrun error but we still received a device descriptor */ ret = kIOReturnSuccess; @@ -648,8 +636,8 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li break; } - if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.bNumConfigurations || - 0 == priv->dev_descriptor.bcdUSB)) { + 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..."); @@ -667,16 +655,18 @@ 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 */ + /* try to unsuspend the device */ ret2 = (*device)->USBDeviceSuspend (device, 0); if (kIOReturnSuccess != ret2) { /* prevent log spew from poorly behaving devices. this indicates the @@ -706,78 +696,176 @@ 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, - UInt32 parent_location, UInt8 port) { +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 { + 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 (*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; + } + + device = darwin_device_from_service (service); + if (!device) { + ret = LIBUSB_ERROR_NO_DEVICE; + free (new_device); + new_device = NULL; + break; + } + + /* add this device to the cached device list */ + list_add(&new_device->list, &darwin_cached_devices); + + (*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; + + 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, *parent = NULL; + struct libusb_device *dev = NULL; + struct darwin_cached_device *cached_device; UInt8 devSpeed; - UInt16 address; int ret = 0; do { - usbi_dbg ("allocating new device for location 0x%08x", locationID); + 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; + + usbi_dbg ("allocating new device in context %p for with session 0x%08x", + ctx, cached_device->session); - dev = usbi_alloc_device(ctx, locationID); + dev = usbi_alloc_device(ctx, cached_device->session); if (!dev) { return LIBUSB_ERROR_NO_MEM; } priv = (struct darwin_device_priv *)dev->os_priv; - priv->device = device; - /* increment the device's reference count (it is decremented in darwin_destroy_device) */ - (*device)->AddRef (device); + priv->dev = cached_device; + darwin_ref_cached_device (priv->dev); - (*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->parent_dev = libusb_ref_device (usbi_get_device_by_session_id (ctx, parent_location)); - dev->port_number = port; - dev->bus_number = locationID >> 24; - dev->device_address = address; + 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; - (*device)->GetDeviceSpeed (device, &devSpeed); + (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); switch (devSpeed) { case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; @@ -790,17 +878,12 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device 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; usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address, - dev->port_number, (void *) parent, priv->sys_path); + dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path); } while (0); if (0 == ret) { @@ -813,22 +896,18 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device } static int darwin_scan_devices(struct libusb_context *ctx) { - io_iterator_t deviceIterator; - usb_device_t **device; - kern_return_t kresult; - UInt32 location, parent_location; - UInt8 port; + 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, &port, &parent_location)) != NULL) { - (void) process_new_device (ctx, device, location, parent_location, port); + while ((service = IOIteratorNext (deviceIterator))) { + (void) process_new_device (ctx, service); - /* process_new_device added a reference so we need to release the one - from QueryInterface */ - (*device)->Release (device); + IOObjectRelease(service); } IOObjectRelease(deviceIterator); @@ -838,7 +917,7 @@ static int darwin_scan_devices(struct libusb_context *ctx) { 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; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; if (0 == dpriv->open_count) { @@ -895,7 +974,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { 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; @@ -941,7 +1020,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; @@ -949,7 +1028,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; @@ -977,8 +1056,8 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s IOUSBFindInterfaceRequest request; kern_return_t kresult; io_iterator_t interface_iterator; - CFTypeRef bInterfaceNumberCF; - int bInterfaceNumber; + UInt8 bInterfaceNumber; + int ret; *usbInterfacep = IO_OBJECT_NULL; @@ -994,17 +1073,10 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { /* find the interface number */ - bInterfaceNumberCF = IORegistryEntryCreateCFProperty (*usbInterfacep, CFSTR("bInterfaceNumber"), - kCFAllocatorDefault, 0); - if (!bInterfaceNumberCF) { - continue; - } - - CFNumberGetValue(bInterfaceNumberCF, kCFNumberIntType, &bInterfaceNumber); - - CFRelease(bInterfaceNumberCF); + ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type, + &bInterfaceNumber); - if ((uint8_t) bInterfaceNumber == ifc) { + if (ret && bInterfaceNumber == ifc) { break; } @@ -1061,7 +1133,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; @@ -1240,12 +1312,8 @@ 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_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); @@ -1253,7 +1321,7 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c } 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; @@ -1305,7 +1373,7 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) { } 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; @@ -1346,11 +1414,12 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, static void darwin_destroy_device(struct libusb_device *dev) { struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv; - if (dpriv->device) { - /* it is an internal error if the reference count of a device is < 0 after release */ - assert(0 <= (*(dpriv->device))->Release(dpriv->device)); - - dpriv->device = NULL; + 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); } } @@ -1504,7 +1573,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); @@ -1570,7 +1639,7 @@ 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_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe"); @@ -1585,7 +1654,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; @@ -1609,13 +1678,8 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { 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); } diff --git a/libusb/os/darwin_usb.h b/libusb/os/darwin_usb.h index a80aa8b..53b8542 100644 --- a/libusb/os/darwin_usb.h +++ b/libusb/os/darwin_usb.h @@ -28,7 +28,19 @@ #include <IOKit/IOCFPlugIn.h> /* IOUSBInterfaceInferface */ -#if defined (kIOUSBInterfaceInterfaceID300) +#if defined (kIOUSBInterfaceInterfaceID550) + +#define usb_interface_t IOUSBInterfaceInterface550 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 +#define InterfaceVersion 550 + +#elif defined (kIOUSBInterfaceInterfaceID500) + +#define usb_interface_t IOUSBInterfaceInterface500 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 +#define InterfaceVersion 500 + +#elif defined (kIOUSBInterfaceInterfaceID300) #define usb_interface_t IOUSBInterfaceInterface300 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 @@ -46,24 +58,6 @@ #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 #define InterfaceVersion 220 -#elif defined (kIOUSBInterfaceInterfaceID197) - -#define usb_interface_t IOUSBInterfaceInterface197 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 -#define InterfaceVersion 197 - -#elif defined (kIOUSBInterfaceInterfaceID190) - -#define usb_interface_t IOUSBInterfaceInterface190 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 -#define InterfaceVersion 190 - -#elif defined (kIOUSBInterfaceInterfaceID182) - -#define usb_interface_t IOUSBInterfaceInterface182 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 -#define InterfaceVersion 182 - #else #error "IOUSBFamily is too old. Please upgrade your OS" @@ -95,24 +89,11 @@ #define DeviceInterfaceID kIOUSBDeviceInterfaceID245 #define DeviceVersion 245 -#elif defined (kIOUSBDeviceInterfaceID197) - +#elif defined (kIOUSBDeviceInterfaceID220) #define usb_device_t IOUSBDeviceInterface197 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197 #define DeviceVersion 197 -#elif defined (kIOUSBDeviceInterfaceID187) - -#define usb_device_t IOUSBDeviceInterface187 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 -#define DeviceVersion 187 - -#elif defined (kIOUSBDeviceInterfaceID182) - -#define usb_device_t IOUSBDeviceInterface182 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 -#define DeviceVersion 182 - #else #error "IOUSBFamily is too old. Please upgrade your OS" @@ -127,13 +108,23 @@ typedef IOCFPlugInInterface *io_cf_plugin_ref_t; typedef IONotificationPortRef io_notification_port_t; /* private structures */ -struct darwin_device_priv { +struct darwin_cached_device { + struct list_head list; IOUSBDeviceDescriptor dev_descriptor; UInt32 location; + UInt64 parent_session; + UInt64 session; + UInt16 address; char sys_path[21]; usb_device_t **device; int open_count; - UInt8 first_config, active_config; + UInt8 first_config, active_config, port; + int can_enumerate; + int refcount; +}; + +struct darwin_device_priv { + struct darwin_cached_device *dev; }; struct darwin_device_handle_priv { @@ -156,11 +147,7 @@ struct darwin_transfer_priv { int num_iso_packets; /* Control */ -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) IOUSBDevRequestTO req; -#else - IOUSBDevRequest req; -#endif /* Bulk */ }; |