diff options
author | Nathan Hjelm <hjelmn@me.com> | 2011-09-06 10:42:00 +0000 |
---|---|---|
committer | Vitali Lovich <vlovich@aliph.com> | 2011-09-06 11:09:23 +0100 |
commit | bf12efb464a87bb52c8e69a98999e88f617865c7 (patch) | |
tree | 3cdc176ce0d6ae75d8060250433d373f05c4935d | |
parent | 4fe08970e24d481aaf7d27141629ed2b3440ee2b (diff) | |
download | libusb-bf12efb464a87bb52c8e69a98999e88f617865c7.tar.gz |
Darwin: Improve device enumeration performance and save device location
[stuge: Formatting fixes and split out libusb_get_device_speed() change]
-rw-r--r-- | libusb/os/darwin_usb.c | 190 |
1 files changed, 132 insertions, 58 deletions
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index 480ec52..6fcbb33 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -1,6 +1,6 @@ /* * darwin backend for libusb 1.0 - * Copyright (C) 2008-2010 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 @@ -517,105 +517,179 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct libusb 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 = 0, need_unref = 0; + 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; - 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); + (*device)->GetDeviceClass (device, &bDeviceClass); + (*device)->GetDeviceProduct (device, &idProduct); + (*device)->GetDeviceVendor (device, &idVendor); - if (!dev) { - ret = LIBUSB_ERROR_NO_MEM; - break; - } + priv = (struct darwin_device_priv *)dev->os_priv; - 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 ****/ /* 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 */ + * 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; + + 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; - /* device may be suspended. unsuspend it and try again */ /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ (void)(*device)->GetUSBDeviceInformation (device, &info); try_unsuspend = info & (1 << kUSBInformationDeviceIsSuspendedBit); #endif - /* the device should be open before to device is unsuspended */ - (void) (*device)->USBDeviceOpenSeize(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_warn (ctx, "could not retrieve device descriptor: %s. skipping device", darwin_error_str (ret)); - ret = -1; - break; + 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); } + } while (kIOReturnSuccess != ret && retries--); + + if (unsuspended) + /* resuspend the device */ + (void)(*device)->USBDeviceSuspend (device, 1); - /**** end: retrieve device descriptors ****/ + if (is_open) + (void) (*device)->USBDeviceClose (device); - /* 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)); - ret = -1; + 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; + 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; } - dev->bus_number = locationID >> 24; - dev->device_address = address; + 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; + /* 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); if (ret < 0) |