From 91f8d063d30358fcb76831c238071f7d4b13c35e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 16 Apr 2009 15:35:09 -0400 Subject: USB: consolidate usb_unbind_interface and usb_driver_release_interface This patch (as1230) consolidates code in usb_unbind_interface() and usb_driver_release_interface(). In fact, it makes release_interface call unbind_interface, thereby removing the need for duplicated code. It works like this: If the interface has already been registered with the driver core when a driver releases it, then the usual driver-core mechanism will call unbind_interface. If it hasn't been unregistered then we will make the call ourselves. As a nice bonus, drivers now don't have to worry about whether their disconnect method will get called when they release an interface -- it always will. Previously it would be called only if the interface was registered. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d0a21a5f8201..c115eed0fdc3 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -385,7 +385,6 @@ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { struct device *dev = &iface->dev; - struct usb_device *udev = interface_to_usbdev(iface); /* this should never happen, don't release something that's not ours */ if (!dev->driver || dev->driver != &driver->drvwrap.driver) @@ -394,23 +393,19 @@ void usb_driver_release_interface(struct usb_driver *driver, /* don't release from within disconnect() */ if (iface->condition != USB_INTERFACE_BOUND) return; + iface->condition = USB_INTERFACE_UNBINDING; - /* don't release if the interface hasn't been added yet */ + /* Release via the driver core only if the interface + * has already been registered + */ if (device_is_registered(dev)) { - iface->condition = USB_INTERFACE_UNBINDING; device_release_driver(dev); } else { - iface->condition = USB_INTERFACE_UNBOUND; - usb_cancel_queued_reset(iface); + down(&dev->sem); + usb_unbind_interface(dev); + dev->driver = NULL; + up(&dev->sem); } - dev->driver = NULL; - usb_set_intfdata(iface, NULL); - - usb_pm_lock(udev); - iface->condition = USB_INTERFACE_UNBOUND; - mark_quiesced(iface); - iface->needs_remote_wakeup = 0; - usb_pm_unlock(udev); } EXPORT_SYMBOL_GPL(usb_driver_release_interface); -- cgit From cc71329b3b89b4a5be849b617f2c4f151f0b9213 Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Tue, 21 Apr 2009 17:21:40 +0100 Subject: USB: usbfs: deprecate and hide option for !embedded Modern systems do not use usbfs; the entries within it are files, not device nodes, and do not support ACLs which are the default way to provide access to USB devices to untrusted users. It is replaced by device-nodes maintained by udev in /dev/bus/usb, libusb uses this device nodes. Mark the option as deprecated, and hide entirely for non-embedded builds (which may not be using udev but require raw USB device access). Signed-off-by: Scott James Remnant Acked-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Kconfig | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index e1759d17ac5d..69280c35b5cb 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -28,7 +28,7 @@ comment "Miscellaneous USB options" depends on USB config USB_DEVICEFS - bool "USB device filesystem" + bool "USB device filesystem (DEPRECATED)" if EMBEDDED depends on USB ---help--- If you say Y here (and to "/proc file system support" in the "File @@ -46,11 +46,15 @@ config USB_DEVICEFS For the format of the various /proc/bus/usb/ files, please read . - Usbfs files can't handle Access Control Lists (ACL), which are the - default way to grant access to USB devices for untrusted users of a - desktop system. The usbfs functionality is replaced by real - device-nodes managed by udev. These nodes live in /dev/bus/usb and - are used by libusb. + Modern Linux systems do not use this. + + Usbfs entries are files and not character devices; usbfs can't + handle Access Control Lists (ACL) which are the default way to + grant access to USB devices for untrusted users of a desktop + system. + + The usbfs functionality is replaced by real device-nodes managed by + udev. These nodes lived in /dev/bus/usb and are used by libusb. config USB_DEVICE_CLASS bool "USB device class-devices (DEPRECATED)" -- cgit From a864e3aa5d8e1278c05aa93c57dba0debbb46858 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 15 Apr 2009 21:34:40 -0400 Subject: USB: core/sysfs: fix sparse warnings Fix 3 sparse warning in drivers/usb/core/sysfs.c. warning: symbol '__mptr' shadows an earlier one Signed-off-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index c66789197927..b5c72e458943 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -552,8 +552,8 @@ static struct attribute *dev_string_attrs[] = { static mode_t dev_string_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct usb_device *udev = to_usb_device( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_device *udev = to_usb_device(dev); if (a == &dev_attr_manufacturer.attr) { if (udev->manufacturer == NULL) @@ -585,8 +585,8 @@ static ssize_t read_descriptors(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct usb_device *udev = to_usb_device( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_device *udev = to_usb_device(dev); size_t nleft = count; size_t srclen, n; int cfgno; @@ -786,8 +786,8 @@ static struct attribute *intf_assoc_attrs[] = { static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct usb_interface *intf = to_usb_interface( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_interface *intf = to_usb_interface(dev); if (intf->intf_assoc == NULL) return 0; -- cgit From d0f830d30c24175b463fa725ba369a5117fbdd8f Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 22 Apr 2009 16:03:05 -0400 Subject: USB: hub.c: fix sparse warnings Fix sparse warning in drivers/usb/core/hub.c. The following sparse warning is seen when building on ARM due do the macro raw_local_irq_save(): warning: symbol 'temp' shadows an earlier one Signed-off-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index be86ae3f4088..c7a1a1d4bcb1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -457,13 +457,13 @@ static void hub_tt_kevent (struct work_struct *work) spin_lock_irqsave (&hub->tt.lock, flags); while (--limit && !list_empty (&hub->tt.clear_list)) { - struct list_head *temp; + struct list_head *next; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; int status; - temp = hub->tt.clear_list.next; - clear = list_entry (temp, struct usb_tt_clear, clear_list); + next = hub->tt.clear_list.next; + clear = list_entry (next, struct usb_tt_clear, clear_list); list_del (&clear->clear_list); /* drop lock so HCD can concurrently report other TT errors */ -- cgit From 00048b8bde5a6cbd9c3a76f272cc9ddb55705e37 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Apr 2009 14:56:26 -0700 Subject: USB: add usb debugfs directory Add a common usb directory in debugfs that the usb subsystem can use. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7eee400d3e32..5f6873f5f268 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -1001,6 +1002,22 @@ static struct notifier_block usb_bus_nb = { .notifier_call = usb_bus_notify, }; +struct dentry *usb_debug_root; +EXPORT_SYMBOL_GPL(usb_debug_root); + +static int usb_debugfs_init(void) +{ + usb_debug_root = debugfs_create_dir("usb", NULL); + if (!usb_debug_root) + return -ENOENT; + return 0; +} + +static void usb_debugfs_cleanup(void) +{ + debugfs_remove(usb_debug_root); +} + /* * Init */ @@ -1012,6 +1029,10 @@ static int __init usb_init(void) return 0; } + retval = usb_debugfs_init(); + if (retval) + goto out; + retval = ksuspend_usb_init(); if (retval) goto out; @@ -1083,6 +1104,7 @@ static void __exit usb_exit(void) bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); ksuspend_usb_cleanup(); + usb_debugfs_cleanup(); } subsys_initcall(usb_init); -- cgit From 97d7b7a41bd462abceee7dbb2b3afacfd52438ed Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Apr 2009 15:16:04 -0700 Subject: USB: add the usbfs devices file to debugfs People are very used to the devices file in usbfs. Now that we have moved usbfs to be an "embedded" option only, the developers miss the file, they had grown quite attached to it over all of these years. This patch brings it back and puts it in the usb debugfs directory, so that the developers don't feel sad anymore. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Makefile | 4 ++-- drivers/usb/core/usb.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index b6078706fb93..ec16e6029905 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -4,14 +4,14 @@ usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ config.o file.o buffer.o sysfs.o endpoint.o \ - devio.o notify.o generic.o quirks.o + devio.o notify.o generic.o quirks.o devices.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o endif ifeq ($(CONFIG_USB_DEVICEFS),y) - usbcore-objs += inode.o devices.o + usbcore-objs += inode.o endif obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 5f6873f5f268..c71590666ade 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1005,16 +1005,29 @@ static struct notifier_block usb_bus_nb = { struct dentry *usb_debug_root; EXPORT_SYMBOL_GPL(usb_debug_root); +struct dentry *usb_debug_devices; + static int usb_debugfs_init(void) { usb_debug_root = debugfs_create_dir("usb", NULL); if (!usb_debug_root) return -ENOENT; + + usb_debug_devices = debugfs_create_file("devices", 0444, + usb_debug_root, NULL, + &usbfs_devices_fops); + if (!usb_debug_devices) { + debugfs_remove(usb_debug_root); + usb_debug_root = NULL; + return -ENOENT; + } + return 0; } static void usb_debugfs_cleanup(void) { + debugfs_remove(usb_debug_devices); debugfs_remove(usb_debug_root); } -- cgit From a853a3d4eb2edb066248a39f0634f6f5858816a0 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 24 Apr 2009 10:12:18 +0200 Subject: usb: return device strings in UTF-8 Change the encoding of strings returned by usb_string() from ISO 8859-1 to UTF-8. Signed-off-by: Clemens Ladisch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b62628377654..e98f928c08ea 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -759,7 +760,7 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, } /** - * usb_string - returns ISO 8859-1 version of a string descriptor + * usb_string - returns UTF-8 version of a string descriptor * @dev: the device whose string descriptor is being retrieved * @index: the number of the descriptor * @buf: where to put the string @@ -767,17 +768,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, * Context: !in_interrupt () * * This converts the UTF-16LE encoded strings returned by devices, from - * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones - * that are more usable in most kernel contexts. Note that all characters - * in the chosen descriptor that can't be encoded using ISO-8859-1 - * are converted to the question mark ("?") character, and this function + * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones + * that are more usable in most kernel contexts. Note that this function * chooses strings in the first language supported by the device. * - * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit - * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode, - * and is appropriate for use many uses of English and several other - * Western European languages. (But it doesn't include the "Euro" symbol.) - * * This call is synchronous, and may not be used in an interrupt context. * * Returns length of the string (>= 0) or usb_control_msg status (< 0). @@ -786,14 +780,14 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { unsigned char *tbuf; int err; - unsigned int u, idx; + unsigned int u; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; if (size <= 0 || !buf || !index) return -EINVAL; buf[0] = 0; - tbuf = kmalloc(256, GFP_NOIO); + tbuf = kmalloc(256 + 2, GFP_NOIO); if (!tbuf) return -ENOMEM; @@ -820,17 +814,13 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (err < 0) goto errout; + for (u = 2; u < err; u += 2) + le16_to_cpus((u16 *)&tbuf[u]); + tbuf[u] = 0; + tbuf[u + 1] = 0; size--; /* leave room for trailing NULL char in output buffer */ - for (idx = 0, u = 2; u < err; u += 2) { - if (idx >= size) - break; - if (tbuf[u+1]) /* high byte */ - buf[idx++] = '?'; /* non ISO-8859-1 character */ - else - buf[idx++] = tbuf[u]; - } - buf[idx] = 0; - err = idx; + err = utf8_wcstombs(buf, (u16 *)&tbuf[2], size); + buf[err] = 0; if (tbuf[1] != USB_DT_STRING) dev_dbg(&dev->dev, @@ -843,6 +833,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) } EXPORT_SYMBOL_GPL(usb_string); +/* one UTF-8-encoded 16-bit character has at most three bytes */ +#define MAX_USB_STRING_SIZE (127 * 3 + 1) + /** * usb_cache_string - read a string descriptor and cache it for later use * @udev: the device whose string descriptor is being read @@ -860,9 +853,9 @@ char *usb_cache_string(struct usb_device *udev, int index) if (index <= 0) return NULL; - buf = kmalloc(256, GFP_KERNEL); + buf = kmalloc(MAX_USB_STRING_SIZE, GFP_KERNEL); if (buf) { - len = usb_string(udev, index, buf, 256); + len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); if (len > 0) { smallbuf = kmalloc(++len, GFP_KERNEL); if (!smallbuf) -- cgit From 74675a58507e769beee7d949dbed788af3c4139d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 30 Apr 2009 10:08:18 -0400 Subject: NLS: update handling of Unicode This patch (as1239) updates the kernel's treatment of Unicode. The character-set conversion routines are well behind the current state of the Unicode specification: They don't recognize the existence of code points beyond plane 0 or of surrogate pairs in the UTF-16 encoding. The old wchar_t 16-bit type is retained because it's still used in lots of places. This shouldn't cause any new problems; if a conversion now results in an invalid 16-bit code then before it must have yielded an undefined code. Difficult-to-read names like "utf_mbstowcs" are replaced with more transparent names like "utf8s_to_utf16s" and the ordering of the parameters is rationalized (buffer lengths come immediate after the pointers they refer to, and the inputs precede the outputs). Fortunately the low-level conversion routines are used in only a few places; the interfaces to the higher-level uni2char and char2uni methods have been left unchanged. Signed-off-by: Alan Stern Acked-by: Clemens Ladisch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index e98f928c08ea..9bd26dec7599 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -780,14 +780,13 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { unsigned char *tbuf; int err; - unsigned int u; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; if (size <= 0 || !buf || !index) return -EINVAL; buf[0] = 0; - tbuf = kmalloc(256 + 2, GFP_NOIO); + tbuf = kmalloc(256, GFP_NOIO); if (!tbuf) return -ENOMEM; @@ -814,12 +813,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (err < 0) goto errout; - for (u = 2; u < err; u += 2) - le16_to_cpus((u16 *)&tbuf[u]); - tbuf[u] = 0; - tbuf[u + 1] = 0; size--; /* leave room for trailing NULL char in output buffer */ - err = utf8_wcstombs(buf, (u16 *)&tbuf[2], size); + err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2, + UTF16_LITTLE_ENDIAN, buf, size); buf[err] = 0; if (tbuf[1] != USB_DT_STRING) -- cgit From 820d7a253c5e59a786d5b608f6e8d0419fdc2f6e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Apr 2009 13:17:21 -0700 Subject: USB: remove unused usb_host class The usb_host class isn't used for anything anymore (it was used for debug files, but they have moved to debugfs a few kernel releases ago), so let's delete it before someone accidentally puts a file in it. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 27 --------------------------- drivers/usb/core/usb.c | 6 ------ drivers/usb/core/usb.h | 2 -- 3 files changed, 35 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 42b93da1085d..c65d95604028 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -755,23 +755,6 @@ static struct attribute_group usb_bus_attr_group = { /*-------------------------------------------------------------------------*/ -static struct class *usb_host_class; - -int usb_host_init(void) -{ - int retval = 0; - - usb_host_class = class_create(THIS_MODULE, "usb_host"); - if (IS_ERR(usb_host_class)) - retval = PTR_ERR(usb_host_class); - return retval; -} - -void usb_host_cleanup(void) -{ - class_destroy(usb_host_class); -} - /** * usb_bus_init - shared initialization code * @bus: the bus structure being initialized @@ -818,12 +801,6 @@ static int usb_register_bus(struct usb_bus *bus) set_bit (busnum, busmap.busmap); bus->busnum = busnum; - bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0), - bus, "usb_host%d", busnum); - result = PTR_ERR(bus->dev); - if (IS_ERR(bus->dev)) - goto error_create_class_dev; - /* Add it to the local list of buses */ list_add (&bus->bus_list, &usb_bus_list); mutex_unlock(&usb_bus_list_lock); @@ -834,8 +811,6 @@ static int usb_register_bus(struct usb_bus *bus) "number %d\n", bus->busnum); return 0; -error_create_class_dev: - clear_bit(busnum, busmap.busmap); error_find_busnum: mutex_unlock(&usb_bus_list_lock); return result; @@ -865,8 +840,6 @@ static void usb_deregister_bus (struct usb_bus *bus) usb_notify_remove_bus(bus); clear_bit (bus->busnum, busmap.busmap); - - device_unregister(bus->dev); } /** diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index c71590666ade..eb810bbe7bbc 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1055,9 +1055,6 @@ static int __init usb_init(void) retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); if (retval) goto bus_notifier_failed; - retval = usb_host_init(); - if (retval) - goto host_init_failed; retval = usb_major_init(); if (retval) goto major_init_failed; @@ -1087,8 +1084,6 @@ usb_devio_init_failed: driver_register_failed: usb_major_cleanup(); major_init_failed: - usb_host_cleanup(); -host_init_failed: bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_notifier_failed: bus_unregister(&usb_bus_type); @@ -1113,7 +1108,6 @@ static void __exit usb_exit(void) usb_deregister(&usbfs_driver); usb_devio_cleanup(); usb_hub_cleanup(); - usb_host_cleanup(); bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); ksuspend_usb_cleanup(); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 79d8a9ea559b..dabf9255a10e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -41,8 +41,6 @@ extern int usb_hub_init(void); extern void usb_hub_cleanup(void); extern int usb_major_init(void); extern void usb_major_cleanup(void); -extern int usb_host_init(void); -extern void usb_host_cleanup(void); #ifdef CONFIG_PM -- cgit From abb306416a7ec2386678de0da6b632a6cb068af0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 27 Apr 2009 13:33:24 -0400 Subject: USB: move PCI host controllers to new PM framework This patch (as1236) converts the USB PCI power management routines over to the new PM framework. Signed-off-by: Alan Stern Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 242 +++++++++++++++++++++++---------------------- drivers/usb/core/hcd.h | 9 +- 2 files changed, 126 insertions(+), 125 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index a4301dc02d27..5db4d40db832 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -185,194 +185,198 @@ void usb_hcd_pci_remove(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); - -#ifdef CONFIG_PM - /** - * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD - * @dev: USB Host Controller being suspended - * @message: Power Management message describing this state transition - * - * Store this function in the HCD's struct pci_driver as .suspend. + * usb_hcd_pci_shutdown - shutdown host controller + * @dev: USB Host Controller being shutdown */ -int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) +void usb_hcd_pci_shutdown(struct pci_dev *dev) +{ + struct usb_hcd *hcd; + + hcd = pci_get_drvdata(dev); + if (!hcd) + return; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} +EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); + +#ifdef CONFIG_PM_SLEEP + +static int check_root_hub_suspended(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + + if (!(hcd->state == HC_STATE_SUSPENDED || + hcd->state == HC_STATE_HALT)) { + dev_warn(dev, "Root hub is not suspended\n"); + return -EBUSY; + } + return 0; +} + +static int hcd_pci_suspend(struct device *dev) { - struct usb_hcd *hcd = pci_get_drvdata(dev); - int retval = 0; - int wake, w; - int has_pci_pm; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + int retval; /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface * and the stub usb_device (which we check here). But maybe it * didn't; writing sysfs power/state files ignores such rules... - * - * We must ignore the FREEZE vs SUSPEND distinction here, because - * otherwise the swsusp will save (and restore) garbage state. */ - if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) { - dev_warn(&dev->dev, "Root hub is not suspended\n"); - retval = -EBUSY; - goto done; - } + retval = check_root_hub_suspended(dev); + if (retval) + return retval; /* We might already be suspended (runtime PM -- not yet written) */ - if (dev->current_state != PCI_D0) - goto done; + if (pci_dev->current_state != PCI_D0) + return retval; if (hcd->driver->pci_suspend) { - retval = hcd->driver->pci_suspend(hcd, message); + retval = hcd->driver->pci_suspend(hcd, PMSG_SUSPEND); suspend_report_result(hcd->driver->pci_suspend, retval); if (retval) - goto done; + return retval; } - synchronize_irq(dev->irq); + synchronize_irq(pci_dev->irq); /* Downstream ports from this root hub should already be quiesced, so * there will be no DMA activity. Now we can shut down the upstream - * link (except maybe for PME# resume signaling) and enter some PCI - * low power state, if the hardware allows. + * link (except maybe for PME# resume signaling). We'll enter a + * low power state during suspend_noirq, if the hardware allows. */ - pci_disable_device(dev); + pci_disable_device(pci_dev); + return retval; +} + +static int hcd_pci_suspend_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + int retval; + + retval = check_root_hub_suspended(dev); + if (retval) + return retval; - pci_save_state(dev); + pci_save_state(pci_dev); - /* Don't fail on error to enable wakeup. We rely on pci code - * to reject requests the hardware can't implement, rather - * than coding the same thing. + /* If the root hub is HALTed rather than SUSPENDed, + * disallow remote wakeup. */ - wake = (hcd->state == HC_STATE_SUSPENDED && - device_may_wakeup(&dev->dev)); - w = pci_wake_from_d3(dev, wake); - if (w < 0) - wake = w; - dev_dbg(&dev->dev, "wakeup: %d\n", wake); - - /* Don't change state if we don't need to */ - if (message.event == PM_EVENT_FREEZE || - message.event == PM_EVENT_PRETHAW) { - dev_dbg(&dev->dev, "--> no state change\n"); - goto done; - } + if (hcd->state == HC_STATE_HALT) + device_set_wakeup_enable(dev, 0); + dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); - has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (!has_pci_pm) { - dev_dbg(&dev->dev, "--> PCI D0 legacy\n"); + /* Possibly enable remote wakeup, + * choose the appropriate low-power state, and go to that state. + */ + retval = pci_prepare_to_sleep(pci_dev); + if (retval == -EIO) { /* Low-power not supported */ + dev_dbg(dev, "--> PCI D0 legacy\n"); + retval = 0; + } else if (retval == 0) { + dev_dbg(dev, "--> PCI %s\n", + pci_power_name(pci_dev->current_state)); } else { - - /* NOTE: dev->current_state becomes nonzero only here, and - * only for devices that support PCI PM. Also, exiting - * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset - * some device state (e.g. as part of clock reinit). - */ - retval = pci_set_power_state(dev, PCI_D3hot); - suspend_report_result(pci_set_power_state, retval); - if (retval == 0) { - dev_dbg(&dev->dev, "--> PCI D3\n"); - } else { - dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n", - retval); - pci_restore_state(dev); - } + suspend_report_result(pci_prepare_to_sleep, retval); + return retval; } #ifdef CONFIG_PPC_PMAC - if (retval == 0) { - /* Disable ASIC clocks for USB */ - if (machine_is(powermac)) { - struct device_node *of_node; - - of_node = pci_device_to_OF_node(dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, - of_node, 0, 0); - } + /* Disable ASIC clocks for USB */ + if (machine_is(powermac)) { + struct device_node *of_node; + + of_node = pci_device_to_OF_node(pci_dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); } #endif - - done: return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); -/** - * usb_hcd_pci_resume - power management resume of a PCI-based HCD - * @dev: USB Host Controller being resumed - * - * Store this function in the HCD's struct pci_driver as .resume. - */ -int usb_hcd_pci_resume(struct pci_dev *dev) +static int hcd_pci_resume_noirq(struct device *dev) { - struct usb_hcd *hcd; - int retval; + struct pci_dev *pci_dev = to_pci_dev(dev); #ifdef CONFIG_PPC_PMAC /* Reenable ASIC clocks for USB */ if (machine_is(powermac)) { struct device_node *of_node; - of_node = pci_device_to_OF_node(dev); + of_node = pci_device_to_OF_node(pci_dev); if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); } #endif - pci_restore_state(dev); + /* Go back to D0 and disable remote wakeup */ + pci_back_from_sleep(pci_dev); + return 0; +} + +static int resume_common(struct device *dev, bool hibernated) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + int retval; - hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { - dev_dbg(hcd->self.controller, - "can't resume, not suspended!\n"); + dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } - pci_enable_wake(dev, PCI_D0, false); - - retval = pci_enable_device(dev); + retval = pci_enable_device(pci_dev); if (retval < 0) { - dev_err(&dev->dev, "can't re-enable after resume, %d!\n", - retval); + dev_err(dev, "can't re-enable after resume, %d!\n", retval); return retval; } - pci_set_master(dev); - - /* yes, ignore this result too... */ - (void) pci_wake_from_d3(dev, 0); + pci_set_master(pci_dev); clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->driver->pci_resume) { retval = hcd->driver->pci_resume(hcd); if (retval) { - dev_err(hcd->self.controller, - "PCI post-resume error %d!\n", retval); + dev_err(dev, "PCI post-resume error %d!\n", retval); usb_hc_died(hcd); } } return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_pci_resume); -#endif /* CONFIG_PM */ - -/** - * usb_hcd_pci_shutdown - shutdown host controller - * @dev: USB Host Controller being shutdown - */ -void usb_hcd_pci_shutdown(struct pci_dev *dev) +static int hcd_pci_resume(struct device *dev) { - struct usb_hcd *hcd; - - hcd = pci_get_drvdata(dev); - if (!hcd) - return; + return resume_common(dev, false); +} - if (hcd->driver->shutdown) - hcd->driver->shutdown(hcd); +static int hcd_pci_restore(struct device *dev) +{ + return resume_common(dev, true); } -EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); +struct dev_pm_ops usb_hcd_pci_pm_ops = { + .suspend = hcd_pci_suspend, + .suspend_noirq = hcd_pci_suspend_noirq, + .resume_noirq = hcd_pci_resume_noirq, + .resume = hcd_pci_resume, + .freeze = check_root_hub_suspended, + .freeze_noirq = check_root_hub_suspended, + .thaw_noirq = NULL, + .thaw = NULL, + .poweroff = hcd_pci_suspend, + .poweroff_noirq = hcd_pci_suspend_noirq, + .restore_noirq = hcd_pci_resume_noirq, + .restore = hcd_pci_restore, +}; +EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); + +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index e7d4479de41c..7f068d6e6940 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -261,14 +261,11 @@ struct pci_device_id; extern int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id); extern void usb_hcd_pci_remove(struct pci_dev *dev); - -#ifdef CONFIG_PM -extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_resume(struct pci_dev *dev); -#endif /* CONFIG_PM */ - extern void usb_hcd_pci_shutdown(struct pci_dev *dev); +#ifdef CONFIG_PM_SLEEP +extern struct dev_pm_ops usb_hcd_pci_pm_ops; +#endif #endif /* CONFIG_PCI */ /* pci-ish (pdev null is ok) buffer alloc/mapping support */ -- cgit From 6ec4beb5c701f728548b587082c83ef62eb36035 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 27 Apr 2009 13:33:41 -0400 Subject: USB: new flag for resume-from-hibernation This patch (as1237) changes the way the PCI host controller drivers avoid retaining bogus hardware states during resume-from-hibernation. Previously we had reset the hardware as part of preparing to reinstate the memory image. But we can do better now with the new PM framework, since we know exactly which resume operations are from hibernation. The pci_resume method is changed to accept a flag indicating whether the system is resuming from hibernation. When this flag is set, the drivers will reset the hardware to get rid of any existing state. Similarly, the pci_suspend method is changed to remove the pm_message_t argument. It's no longer needed, since no special action has to be taken when preparing to reinstate the memory image. Signed-off-by: Alan Stern Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 4 ++-- drivers/usb/core/hcd.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 5db4d40db832..91f2885b6ee1 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -237,7 +237,7 @@ static int hcd_pci_suspend(struct device *dev) return retval; if (hcd->driver->pci_suspend) { - retval = hcd->driver->pci_suspend(hcd, PMSG_SUSPEND); + retval = hcd->driver->pci_suspend(hcd); suspend_report_result(hcd->driver->pci_suspend, retval); if (retval) return retval; @@ -344,7 +344,7 @@ static int resume_common(struct device *dev, bool hibernated) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->driver->pci_resume) { - retval = hcd->driver->pci_resume(hcd); + retval = hcd->driver->pci_resume(hcd, hibernated); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); usb_hc_died(hcd); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 7f068d6e6940..174170f14f06 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -182,10 +182,10 @@ struct hc_driver { * a whole, not just the root hub; they're for PCI bus glue. */ /* called after suspending the hub, before entering D3 etc */ - int (*pci_suspend) (struct usb_hcd *hcd, pm_message_t message); + int (*pci_suspend)(struct usb_hcd *hcd); /* called after entering D0 (etc), before resuming the hub */ - int (*pci_resume) (struct usb_hcd *hcd); + int (*pci_resume)(struct usb_hcd *hcd, bool hibernated); /* cleanly make HCD stop writing memory and doing I/O */ void (*stop) (struct usb_hcd *hcd); -- cgit From 5512966643adb17483efc5f61481a38fc33088bb Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 4 May 2009 19:48:32 +0200 Subject: usb: convert endpoint devices to bus-less childs of the usb interface The endpoint devices look like simple attribute groups now, and no longer like devices with a specific subsystem. They will also no longer emit uevents. It also removes the device node requests for endpoint devices, which are not implemented for now. Signed-off-by: Kay Sievers Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 25 +++---- drivers/usb/core/endpoint.c | 160 +++----------------------------------------- drivers/usb/core/usb.c | 3 +- drivers/usb/core/usb.h | 11 +++ 4 files changed, 29 insertions(+), 170 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index c115eed0fdc3..77de8d65862c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -154,16 +154,11 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in static int usb_probe_device(struct device *dev) { struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); - struct usb_device *udev; + struct usb_device *udev = to_usb_device(dev); int error = -ENODEV; dev_dbg(dev, "%s\n", __func__); - if (!is_usb_device(dev)) /* Sanity check */ - return error; - - udev = to_usb_device(dev); - /* TODO: Add real matching code */ /* The device should always appear to be in use @@ -203,18 +198,13 @@ static void usb_cancel_queued_reset(struct usb_interface *iface) static int usb_probe_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); - struct usb_interface *intf; - struct usb_device *udev; + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); const struct usb_device_id *id; int error = -ENODEV; dev_dbg(dev, "%s\n", __func__); - if (is_usb_device(dev)) /* Sanity check */ - return error; - - intf = to_usb_interface(dev); - udev = interface_to_usbdev(intf); intf->needs_binding = 0; if (udev->authorized == 0) { @@ -593,7 +583,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) /* TODO: Add real matching code */ return 1; - } else { + } else if (is_usb_interface(dev)) { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; @@ -625,11 +615,14 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) /* driver is often null here; dev_dbg() would oops */ pr_debug("usb %s: uevent\n", dev_name(dev)); - if (is_usb_device(dev)) + if (is_usb_device(dev)) { usb_dev = to_usb_device(dev); - else { + } else if (is_usb_interface(dev)) { struct usb_interface *intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev(intf); + } else { + return 0; } if (usb_dev->devnum < 0) { diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 40dee2ac0133..bc39fc40bbde 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -15,19 +15,18 @@ #include #include "usb.h" -#define MAX_ENDPOINT_MINORS (64*128*32) -static int usb_endpoint_major; -static DEFINE_IDR(endpoint_idr); - struct ep_device { struct usb_endpoint_descriptor *desc; struct usb_device *udev; struct device dev; - int minor; }; #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev) +struct device_type usb_ep_device_type = { + .name = "usb_endpoint", +}; + struct ep_attribute { struct attribute attr; ssize_t (*show)(struct usb_device *, @@ -160,118 +159,10 @@ static struct attribute_group *ep_dev_groups[] = { NULL }; -static int usb_endpoint_major_init(void) -{ - dev_t dev; - int error; - - error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS, - "usb_endpoint"); - if (error) { - printk(KERN_ERR "Unable to get a dynamic major for " - "usb endpoints.\n"); - return error; - } - usb_endpoint_major = MAJOR(dev); - - return error; -} - -static void usb_endpoint_major_cleanup(void) -{ - unregister_chrdev_region(MKDEV(usb_endpoint_major, 0), - MAX_ENDPOINT_MINORS); -} - -static int endpoint_get_minor(struct ep_device *ep_dev) -{ - static DEFINE_MUTEX(minor_lock); - int retval = -ENOMEM; - int id; - - mutex_lock(&minor_lock); - if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0) - goto exit; - - retval = idr_get_new(&endpoint_idr, ep_dev, &id); - if (retval < 0) { - if (retval == -EAGAIN) - retval = -ENOMEM; - goto exit; - } - ep_dev->minor = id & MAX_ID_MASK; -exit: - mutex_unlock(&minor_lock); - return retval; -} - -static void endpoint_free_minor(struct ep_device *ep_dev) -{ - idr_remove(&endpoint_idr, ep_dev->minor); -} - -static struct endpoint_class { - struct kref kref; - struct class *class; -} *ep_class; - -static int init_endpoint_class(void) -{ - int result = 0; - - if (ep_class != NULL) { - kref_get(&ep_class->kref); - goto exit; - } - - ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL); - if (!ep_class) { - result = -ENOMEM; - goto exit; - } - - kref_init(&ep_class->kref); - ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); - if (IS_ERR(ep_class->class)) { - result = PTR_ERR(ep_class->class); - goto class_create_error; - } - - result = usb_endpoint_major_init(); - if (result) - goto endpoint_major_error; - - goto exit; - -endpoint_major_error: - class_destroy(ep_class->class); -class_create_error: - kfree(ep_class); - ep_class = NULL; -exit: - return result; -} - -static void release_endpoint_class(struct kref *kref) -{ - /* Ok, we cheat as we know we only have one ep_class */ - class_destroy(ep_class->class); - kfree(ep_class); - ep_class = NULL; - usb_endpoint_major_cleanup(); -} - -static void destroy_endpoint_class(void) -{ - if (ep_class) - kref_put(&ep_class->kref, release_endpoint_class); -} - static void ep_device_release(struct device *dev) { struct ep_device *ep_dev = to_ep_device(dev); - endpoint_free_minor(ep_dev); kfree(ep_dev); } @@ -279,62 +170,32 @@ int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev) { - char name[8]; struct ep_device *ep_dev; int retval; - retval = init_endpoint_class(); - if (retval) - goto exit; - ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); if (!ep_dev) { retval = -ENOMEM; - goto error_alloc; - } - - retval = endpoint_get_minor(ep_dev); - if (retval) { - dev_err(parent, "can not allocate minor number for %s\n", - dev_name(&ep_dev->dev)); - goto error_register; + goto exit; } ep_dev->desc = &endpoint->desc; ep_dev->udev = udev; ep_dev->dev.groups = ep_dev_groups; - ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor); - ep_dev->dev.class = ep_class->class; + ep_dev->dev.type = &usb_ep_device_type; ep_dev->dev.parent = parent; ep_dev->dev.release = ep_device_release; - dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x", - udev->bus->busnum, udev->devnum, - endpoint->desc.bEndpointAddress); + dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); retval = device_register(&ep_dev->dev); if (retval) - goto error_chrdev; + goto error_register; - /* create the symlink to the old-style "ep_XX" directory */ - sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); - retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name); - if (retval) - goto error_link; endpoint->ep_dev = ep_dev; return retval; -error_link: - device_unregister(&ep_dev->dev); - destroy_endpoint_class(); - return retval; - -error_chrdev: - endpoint_free_minor(ep_dev); - error_register: kfree(ep_dev); -error_alloc: - destroy_endpoint_class(); exit: return retval; } @@ -344,12 +205,7 @@ void usb_remove_ep_devs(struct usb_host_endpoint *endpoint) struct ep_device *ep_dev = endpoint->ep_dev; if (ep_dev) { - char name[8]; - - sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); - sysfs_remove_link(&ep_dev->dev.parent->kobj, name); device_unregister(&ep_dev->dev); endpoint->ep_dev = NULL; - destroy_endpoint_class(); } } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index eb810bbe7bbc..c32811ae128f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -140,8 +140,7 @@ static int __find_interface(struct device *dev, void *data) struct find_interface_arg *arg = data; struct usb_interface *intf; - /* can't look at usb devices, only interfaces */ - if (is_usb_device(dev)) + if (!is_usb_interface(dev)) return 0; intf = to_usb_interface(dev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index dabf9255a10e..e2a8cfaade1d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -104,6 +104,7 @@ extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; extern struct device_type usb_device_type; extern struct device_type usb_if_device_type; +extern struct device_type usb_ep_device_type; extern struct usb_device_driver usb_generic_driver; static inline int is_usb_device(const struct device *dev) @@ -111,6 +112,16 @@ static inline int is_usb_device(const struct device *dev) return dev->type == &usb_device_type; } +static inline int is_usb_interface(const struct device *dev) +{ + return dev->type == &usb_if_device_type; +} + +static inline int is_usb_endpoint(const struct device *dev) +{ + return dev->type == &usb_ep_device_type; +} + /* Do the same for device drivers and interface drivers. */ static inline int is_usb_device_driver(struct device_driver *drv) -- cgit From 9b8e7ba68ad0e4273f4897950de65bc311552cd1 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 May 2009 10:10:44 +0900 Subject: USB: replace dma_sync_single and dma_sync_sg with dma_sync_single_for_cpu and dma_sync_sg_for_cpu This replaces dma_sync_single() and dma_sync_sg() with dma_sync_single_for_cpu() and dma_sync_sg_for_cpu() respectively because they is an obsolete API; include/linux/dma-mapping.h says: /* Backwards compat, remove in 2.7.x */ #define dma_sync_single dma_sync_single_for_cpu #define dma_sync_sg dma_sync_sg_for_cpu Signed-off-by: FUJITA Tomonori Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index c32811ae128f..020b58528d90 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -799,12 +799,12 @@ void usb_buffer_dmasync(struct urb *urb) return; if (controller->dma_mask) { - dma_sync_single(controller, + dma_sync_single_for_cpu(controller, urb->transfer_dma, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (usb_pipecontrol(urb->pipe)) - dma_sync_single(controller, + dma_sync_single_for_cpu(controller, urb->setup_dma, sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); @@ -922,8 +922,8 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in, || !controller->dma_mask) return; - dma_sync_sg(controller, sg, n_hw_ents, - is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_sync_sg_for_cpu(controller, sg, n_hw_ents, + is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg); #endif -- cgit From 23a54e567534d4895056df558e2564114513524a Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Thu, 4 Jun 2009 22:30:48 +0200 Subject: USB: Avoid PM error messages during resume if a device was disconnected Currently if a laptop is suspended e.g. while docked and then resumed after undocking it, the following errors get generated because the USB hub in the docking station and the devices connected to it are no longer available: pm_op(): usb_dev_resume+0x0/0x10 returns -19 PM: Device 1-2 failed to resume: error -19 pm_op(): usb_dev_resume+0x0/0x10 returns -19 PM: Device 1-2.2 failed to resume: error -19 pm_op(): usb_dev_resume+0x0/0x10 returns -19 PM: Device 1-2.3 failed to resume: error -19 As the removal of USB devices while a system is suspended is a relatively common use case and in most cases not an error, just return success on -ENODEV. The user gets informed anyway as the USB subsystem generates regular disconnect messages for the devices shortly afterwards: usb 1-2: USB disconnect, address 3 usb 1-2.2: USB disconnect, address 4 usblp0: removed usb 1-2.3: USB disconnect, address 5 Signed-off-by: Frans Pop Acked-by: Alan Stern Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 77de8d65862c..69e5773abfce 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1750,6 +1750,7 @@ int usb_suspend(struct device *dev, pm_message_t msg) int usb_resume(struct device *dev, pm_message_t msg) { struct usb_device *udev; + int status; udev = to_usb_device(dev); @@ -1759,7 +1760,14 @@ int usb_resume(struct device *dev, pm_message_t msg) */ if (udev->skip_sys_resume) return 0; - return usb_external_resume_device(udev, msg); + status = usb_external_resume_device(udev, msg); + + /* Avoid PM error messages for devices disconnected while suspended + * as we'll display regular disconnect messages just a bit later. + */ + if (status == -ENODEV) + return 0; + return status; } #endif /* CONFIG_PM */ -- cgit From 66d4eadd8d067269ea8fead1a50fe87c2979a80d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:52:28 -0700 Subject: USB: xhci: BIOS handoff and HW initialization. Add PCI initialization code to take control of the xHCI host controller away from the BIOS, halt, and reset the host controller. The xHCI spec says that BIOSes must give up the host controller within 5 seconds. Add some host controller glue functions to handle hardware initialization and memory allocation for the host controller. The current xHCI prototypes use PCI interrupts, but the xHCI spec requires MSI-X interrupts. Add code to support MSI-X interrupts, but use the PCI interrupts for now. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 174170f14f06..7a47498b0aac 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -173,6 +173,7 @@ struct hc_driver { #define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */ #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ +#define HCD_USB3 0x0040 /* USB 3.0 */ /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); -- cgit From 6b403b020c1f42180b14d28d832da61167cff822 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:54:10 -0700 Subject: USB: Add SuperSpeed to the list of USB device speeds. Modify the USB core to handle the new USB 3.0 speed, "SuperSpeed". This is 5.0 Gbps (wire speed). There are probably more places that check for speed that I've missed. SuperSpeed devices have a 512 byte endpoint 0 max packet size. This shows up as a bMaxPacketSize0 set to 0x09 (see table 9-8 of the USB 3.0 bus spec). xHCI spec says that the xHC can handle intervals up to 2^15 microframes. That might change when real silicon becomes available. Add FIXME note for SuperSpeed isochronous endpoints. They can transmit up to 16 packets in one "burst" before they wait for an acknowledgment of the packets. They can do up to 3 bursts per microframe (determined by the mult value in the endpoint companion descriptor). The xHCI driver doesn't have support for isoc yet, so fix this later. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 1 + drivers/usb/core/hcd.c | 16 ++++++++++++++-- drivers/usb/core/hcd.h | 1 + drivers/usb/core/hub.c | 11 +++++++++-- drivers/usb/core/urb.c | 6 ++++++ 5 files changed, 31 insertions(+), 4 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 568244c99bdc..e9426acf5682 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -92,6 +92,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c65d95604028..120bb56c4b84 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1870,8 +1870,20 @@ int usb_add_hcd(struct usb_hcd *hcd, retval = -ENOMEM; goto err_allocate_root_hub; } - rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : - USB_SPEED_FULL; + + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB11: + rhdev->speed = USB_SPEED_FULL; + break; + case HCD_USB2: + rhdev->speed = USB_SPEED_HIGH; + break; + case HCD_USB3: + rhdev->speed = USB_SPEED_SUPER; + break; + default: + goto err_allocate_root_hub; + } hcd->self.root_hub = rhdev; /* wakeup flag init defaults to "everything works" for root hubs, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 7a47498b0aac..4f6ee60d97c6 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -174,6 +174,7 @@ struct hc_driver { #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ #define HCD_USB3 0x0040 /* USB 3.0 */ +#define HCD_MASK 0x0070 /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c7a1a1d4bcb1..fa0208e0da6a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2471,6 +2471,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { + case USB_SPEED_SUPER: case USB_SPEED_VARIABLE: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); break; @@ -2496,6 +2497,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; + case USB_SPEED_SUPER: + speed = "super"; + break; case USB_SPEED_VARIABLE: speed = "variable"; type = "Wireless "; @@ -2634,8 +2638,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - i = udev->descriptor.bMaxPacketSize0 == 0xff? /* wusb device? */ - 512 : udev->descriptor.bMaxPacketSize0; + if (udev->descriptor.bMaxPacketSize0 == 0xff || + udev->speed == USB_SPEED_SUPER) + i = 512; + else + i = udev->descriptor.bMaxPacketSize0; if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { if (udev->speed != USB_SPEED_FULL || !(i == 8 || i == 16 || i == 32 || i == 64)) { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3376055f36e7..02eb0ef7a4c3 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -351,6 +351,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (xfertype == USB_ENDPOINT_XFER_ISOC) { int n, len; + /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */ /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) { int mult = 1 + ((max >> 11) & 0x03); @@ -426,6 +427,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) return -EINVAL; /* too big? */ switch (dev->speed) { + case USB_SPEED_SUPER: /* units are 125us */ + /* Handle up to 2^(16-1) microframes */ + if (urb->interval > (1 << 15)) + return -EINVAL; + max = 1 << 15; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) -- cgit From d2e9b4d6734db2327af3149d8ca7555307e10828 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:55:01 -0700 Subject: USB: Add USB 3.0 roothub support to USB core. Add USB 3.0 root hub descriptors. This is a kludge because I reused the old USB 2.0 descriptors, instead of using the new USB 3.0 hub descriptors with endpoint companion descriptors and other descriptors. I did this because I wasn't ready to add USB 3.0 hub changes to khubd. For now, a USB 3.0 roothub looks like a USB 2.0 roothub, with a higher speed. USB 3.0 hubs have no transaction translator (TT). Make USB core debugging handle super speed ports. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/core/hub.c | 5 ++++ drivers/usb/core/hub.h | 3 +++ 3 files changed, 77 insertions(+), 2 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 120bb56c4b84..823744d80cb9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -128,6 +128,27 @@ static inline int is_root_hub(struct usb_device *udev) #define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff) #define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff) +/* usb 3.0 root hub device descriptor */ +static const u8 usb3_rh_dev_descriptor[18] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, 0x03, /* __le16 bcdUSB; v3.0 */ + + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */ + 0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */ + + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ + 0x02, 0x00, /* __le16 idProduct; device 0x0002 */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + /* usb 2.0 root hub device descriptor */ static const u8 usb2_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ @@ -273,6 +294,47 @@ static const u8 hs_rh_config_descriptor [] = { 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ }; +static const u8 ss_rh_config_descriptor[] = { + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __le16 wTotalLength; FIXME */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) + * see hub.c:hub_configure() for details. */ + (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, + 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ + /* + * All 3.0 hubs should have an endpoint companion descriptor, + * but we're ignoring that for now. FIXME? + */ +}; + /*-------------------------------------------------------------------------*/ /* @@ -426,7 +488,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (wValue & 0xff00) { case USB_DT_DEVICE << 8: - if (hcd->driver->flags & HCD_USB2) + if (hcd->driver->flags & HCD_USB3) + bufp = usb3_rh_dev_descriptor; + else if (hcd->driver->flags & HCD_USB2) bufp = usb2_rh_dev_descriptor; else if (hcd->driver->flags & HCD_USB11) bufp = usb11_rh_dev_descriptor; @@ -437,7 +501,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) patch_protocol = 1; break; case USB_DT_CONFIG << 8: - if (hcd->driver->flags & HCD_USB2) { + if (hcd->driver->flags & HCD_USB3) { + bufp = ss_rh_config_descriptor; + len = sizeof ss_rh_config_descriptor; + } else if (hcd->driver->flags & HCD_USB2) { bufp = hs_rh_config_descriptor; len = sizeof hs_rh_config_descriptor; } else { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index fa0208e0da6a..f3fe8dfaaad1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -155,6 +155,8 @@ static inline char *portspeed(int portstatus) return "480 Mb/s"; else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) return "1.5 Mb/s"; + else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)) + return "5.0 Gb/s"; else return "12 Mb/s"; } @@ -951,6 +953,9 @@ static int hub_configure(struct usb_hub *hub, ret); hub->tt.hub = hdev; break; + case 3: + /* USB 3.0 hubs don't have a TT */ + break; default: dev_dbg(hub_dev, "Unrecognized hub protocol %d\n", hdev->descriptor.bDeviceProtocol); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 2a116ce53c9b..889c0f32a40b 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -47,7 +47,10 @@ #define USB_PORT_FEAT_L1 5 /* L1 suspend */ #define USB_PORT_FEAT_POWER 8 #define USB_PORT_FEAT_LOWSPEED 9 +/* This value was never in Table 11-17 */ #define USB_PORT_FEAT_HIGHSPEED 10 +/* This value is also fake */ +#define USB_PORT_FEAT_SUPERSPEED 11 #define USB_PORT_FEAT_C_CONNECTION 16 #define USB_PORT_FEAT_C_ENABLE 17 #define USB_PORT_FEAT_C_SUSPEND 18 -- cgit From e7b7717247f61e2cf18ec47f91999065c59d1607 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:54:26 -0700 Subject: USB: Don't reset USB 3.0 devices on port change detection. The USB 3.0 bus specification defines a new connection sequence for USB 3.0 hubs and roothubs. USB 3.0 devices are reset and link trained by the hub before the port status change notification is sent to the host OS. This means that an entire tree of devices can be trained in parallel on power up, and the OS no longer needs to reset USB 3.0 devices. Change the USB core's hub port init sequence so that it does not reset USB 3.0 devices. The port status change from the roothub and from the USB 3.0 hub will report the SuperSpeed connect correctly. This patch currently only handles the roothub case. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f3fe8dfaaad1..3c28bde6cbd5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2435,6 +2435,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, static DEFINE_MUTEX(usb_address0_mutex); struct usb_device *hdev = hub->hdev; + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; @@ -2457,11 +2458,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, mutex_lock(&usb_address0_mutex); - /* Reset the device; full speed may morph to high speed */ - retval = hub_port_reset(hub, port1, udev, delay); - if (retval < 0) /* error or disconnect */ + if ((hcd->driver->flags & HCD_USB3) && udev->config) { + /* FIXME this will need special handling by the xHCI driver. */ + dev_dbg(&udev->dev, + "xHCI reset of configured device " + "not supported yet.\n"); + retval = -EINVAL; goto fail; - /* success, speed is known */ + } else if (!udev->config && oldspeed == USB_SPEED_SUPER) { + /* Don't reset USB 3.0 devices during an initial setup */ + usb_set_device_state(udev, USB_STATE_DEFAULT); + } else { + /* Reset the device; full speed may morph to high speed */ + /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ + retval = hub_port_reset(hub, port1, udev, delay); + if (retval < 0) /* error or disconnect */ + goto fail; + /* success, speed is known */ + } retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { @@ -2859,7 +2873,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } usb_set_device_state(udev, USB_STATE_POWERED); - udev->speed = USB_SPEED_UNKNOWN; udev->bus_mA = hub->mA_per_port; udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); @@ -2871,7 +2884,24 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, goto loop; } - /* reset and get descriptor */ + /* + * USB 3.0 devices are reset automatically before the connect + * port status change appears, and the root hub port status + * shows the correct speed. We also get port change + * notifications for USB 3.0 devices from the USB 3.0 portion of + * an external USB 3.0 hub, but this isn't handled correctly yet + * FIXME. + */ + + if (!(hcd->driver->flags & HCD_USB3)) + udev->speed = USB_SPEED_UNKNOWN; + else if ((hdev->parent == NULL) && + (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))) + udev->speed = USB_SPEED_SUPER; + else + udev->speed = USB_SPEED_UNKNOWN; + + /* reset (non-USB 3.0 devices) and get descriptor */ status = hub_port_init(hub, udev, port1, i); if (status < 0) goto loop; -- cgit From 7206b00164a1c3ca533e01db285955617e1019f8 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:54:49 -0700 Subject: USB: Add route string to struct usb_device. This patch adds a hex route string to each USB device. The route string is used by the USB 3.0 host controller to send packets through the device tree. USB 3.0 hubs use this string to route packets to the correct port. This is fundamental bus change from USB 2.0, where all packets were broadcast across the bus. Devices (including hubs) under a root port receive the route string 0x0. Every four bits in the route string represent a port on a hub. This length works because USB 3.0 hubs are limited to 15 ports, and USB 2.0 hubs (with potentially more ports) will never see packets with a route string. A port number of 0 means the packet is destined for that hub. For example, a peripheral device might have a route string of 0x00097. This means the device is connected to port 9 of the hub at depth 1. The hub at depth 1 is connected to port 7 of a hub at depth 0. The hub at depth 0 is connected to a root port. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 020b58528d90..f026991d0bdf 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -375,18 +375,24 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, */ if (unlikely(!parent)) { dev->devpath[0] = '0'; + dev->route = 0; dev->dev.parent = bus->controller; dev_set_name(&dev->dev, "usb%d", bus->busnum); root_hub = 1; } else { /* match any labeling on the hubs; it's one-based */ - if (parent->devpath[0] == '0') + if (parent->devpath[0] == '0') { snprintf(dev->devpath, sizeof dev->devpath, "%d", port1); - else + /* Root ports are not counted in route string */ + dev->route = 0; + } else { snprintf(dev->devpath, sizeof dev->devpath, "%s.%d", parent->devpath, port1); + dev->route = parent->route + + (port1 << ((parent->level - 1)*4)); + } dev->dev.parent = &parent->dev; dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath); -- cgit From c6515272b858742962c1de0f3bf497a048b9abd7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:57:26 -0700 Subject: USB: Support for addressing a USB device under xHCI Add host controller driver API and a slot_id variable to struct usb_device. This allows the xHCI host controller driver to ask the hardware to allocate a slot for the device when a struct usb_device is allocated. The slot needs to be allocated at that point because the hardware can run out of internal resources, and we want to know that very early in the device connection process. Don't call this new API for root hubs, since they aren't real devices. Add HCD API to let the host controller choose the device address. This is especially important for xHCI hardware running in a virtualized environment. The guests running under the VM don't need to know which addresses on the bus are taken, because the hardware picks the address for them. Announce SuperSpeed USB devices after the address has been assigned by the hardware. Don't use the new get descriptor/set address scheme with xHCI. Unless special handling is done in the host controller driver, the xHC can't issue control transfers before you set the device address. Support for the older addressing scheme will be added when the xHCI driver supports the Block Set Address Request (BSR) flag in the Address Device command. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.h | 8 ++++++ drivers/usb/core/hub.c | 74 +++++++++++++++++++++++++++++++++++++------------- drivers/usb/core/usb.c | 14 +++++++++- 3 files changed, 76 insertions(+), 20 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 4f6ee60d97c6..ae6d9db41ca9 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -226,6 +226,14 @@ struct hc_driver { void (*relinquish_port)(struct usb_hcd *, int); /* has a port been handed over to a companion? */ int (*port_handed_over)(struct usb_hcd *, int); + + /* xHCI specific functions */ + /* Called by usb_alloc_dev to alloc HC device structures */ + int (*alloc_dev)(struct usb_hcd *, struct usb_device *); + /* Called by usb_release_dev to free HC device structures */ + void (*free_dev)(struct usb_hcd *, struct usb_device *); + /* Returns the hardware-chosen device address */ + int (*address_device)(struct usb_hcd *, struct usb_device *udev); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3c28bde6cbd5..2af3b4f06054 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1328,6 +1328,11 @@ EXPORT_SYMBOL_GPL(usb_set_device_state); * 0 is reserved by USB for default address; (b) Linux's USB stack * uses always #1 for the root hub of the controller. So USB stack's * port #1, which is wusb virtual-port #0 has address #2. + * + * Devices connected under xHCI are not as simple. The host controller + * supports virtualization, so the hardware assigns device addresses and + * the HCD must setup data structures before issuing a set address + * command to the hardware. */ static void choose_address(struct usb_device *udev) { @@ -1647,6 +1652,9 @@ int usb_new_device(struct usb_device *udev) err = usb_configure_device(udev); /* detect & probe dev/intfs */ if (err < 0) goto fail; + dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n", + udev->devnum, udev->bus->busnum, + (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); /* export the usbdev device-node for libusb */ udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); @@ -2400,19 +2408,29 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit); static int hub_set_address(struct usb_device *udev, int devnum) { int retval; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (devnum <= 1) + /* + * The host controller will choose the device address, + * instead of the core having chosen it earlier + */ + if (!hcd->driver->address_device && devnum <= 1) return -EINVAL; if (udev->state == USB_STATE_ADDRESS) return 0; if (udev->state != USB_STATE_DEFAULT) return -EINVAL; - retval = usb_control_msg(udev, usb_sndaddr0pipe(), - USB_REQ_SET_ADDRESS, 0, devnum, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); + if (hcd->driver->address_device) { + retval = hcd->driver->address_device(hcd, udev); + } else { + retval = usb_control_msg(udev, usb_sndaddr0pipe(), + USB_REQ_SET_ADDRESS, 0, devnum, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (retval == 0) + update_address(udev, devnum); + } if (retval == 0) { /* Device now using proper address. */ - update_address(udev, devnum); usb_set_device_state(udev, USB_STATE_ADDRESS); usb_ep0_reinit(udev); } @@ -2525,10 +2543,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, break; default: speed = "?"; break; } - dev_info (&udev->dev, - "%s %s speed %sUSB device using %s and address %d\n", - (udev->config) ? "reset" : "new", speed, type, - udev->bus->controller->driver->name, devnum); + if (udev->speed != USB_SPEED_SUPER) + dev_info(&udev->dev, + "%s %s speed %sUSB device using %s and address %d\n", + (udev->config) ? "reset" : "new", speed, type, + udev->bus->controller->driver->name, devnum); /* Set up TT records, if needed */ if (hdev->tt) { @@ -2553,7 +2572,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * value. */ for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { - if (USE_NEW_SCHEME(retry_counter)) { + /* + * An xHCI controller cannot send any packets to a device until + * a set address command successfully completes. + */ + if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) { struct usb_device_descriptor *buf; int r = 0; @@ -2619,7 +2642,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * unauthorized address in the Connect Ack sequence; * authorization will assign the final address. */ - if (udev->wusb == 0) { + if (udev->wusb == 0) { for (j = 0; j < SET_ADDRESS_TRIES; ++j) { retval = hub_set_address(udev, devnum); if (retval >= 0) @@ -2632,13 +2655,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, devnum, retval); goto fail; } + if (udev->speed == USB_SPEED_SUPER) { + devnum = udev->devnum; + dev_info(&udev->dev, + "%s SuperSpeed USB device using %s and address %d\n", + (udev->config) ? "reset" : "new", + udev->bus->controller->driver->name, devnum); + } /* cope with hardware quirkiness: * - let SET_ADDRESS settle, some device hardware wants it * - read ep0 maxpacket even for high and low speed, */ msleep(10); - if (USE_NEW_SCHEME(retry_counter)) + if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) break; } @@ -2877,13 +2907,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); - /* set the address */ - choose_address(udev); - if (udev->devnum <= 0) { - status = -ENOTCONN; /* Don't retry */ - goto loop; - } - /* * USB 3.0 devices are reset automatically before the connect * port status change appears, and the root hub port status @@ -2901,6 +2924,19 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, else udev->speed = USB_SPEED_UNKNOWN; + /* + * xHCI needs to issue an address device command later + * in the hub_port_init sequence for SS/HS/FS/LS devices. + */ + if (!(hcd->driver->flags & HCD_USB3)) { + /* set the address */ + choose_address(udev); + if (udev->devnum <= 0) { + status = -ENOTCONN; /* Don't retry */ + goto loop; + } + } + /* reset (non-USB 3.0 devices) and get descriptor */ status = hub_port_init(hub, udev, port1, i); if (status < 0) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f026991d0bdf..55b8d3a22d26 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -184,11 +184,16 @@ EXPORT_SYMBOL_GPL(usb_find_interface); static void usb_release_dev(struct device *dev) { struct usb_device *udev; + struct usb_hcd *hcd; udev = to_usb_device(dev); + hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); - usb_put_hcd(bus_to_hcd(udev->bus)); + /* Root hubs aren't real devices, so don't free HCD resources */ + if (hcd->driver->free_dev && udev->parent) + hcd->driver->free_dev(hcd, udev); + usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); kfree(udev->serial); @@ -348,6 +353,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, kfree(dev); return NULL; } + /* Root hubs aren't true devices, so don't allocate HCD resources */ + if (usb_hcd->driver->alloc_dev && parent && + !usb_hcd->driver->alloc_dev(usb_hcd, dev)) { + usb_put_hcd(bus_to_hcd(bus)); + kfree(dev); + return NULL; + } device_initialize(&dev->dev); dev->dev.bus = &usb_bus_type; -- cgit From 663c30d0829d556efabd5fbd98fb8473da7fe694 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:58:14 -0700 Subject: USB: Parse and store the SuperSpeed endpoint companion descriptors. The USB 3.0 bus specification added an "Endpoint Companion" descriptor that is supposed to follow all SuperSpeed Endpoint descriptors. This descriptor is used to extend the bus protocol to allow more packets to be sent to an endpoint per "microframe". The word microframe was removed from the USB 3.0 specification because the host controller does not send Start Of Frame (SOF) symbols down the USB 3.0 wires. The descriptor defines a bMaxBurst field, which indicates the number of packets of wMaxPacketSize that a SuperSpeed device can send or recieve in a service interval. All non-control endpoints may set this value as high as 16 packets (bMaxBurst = 15). The descriptor also allows isochronous endpoints to further specify that they can send and receive multiple bursts per service interval. The bmAttributes allows them to specify a "Mult" of up to 3 (bmAttributes = 2). Bulk endpoints use bmAttributes to report the number of "Streams" they support. This was an extension of the endpoint pipe concept to allow multiple mass storage device commands to be outstanding for one bulk endpoint at a time. This should allow USB 3.0 mass storage devices to support SCSI command queueing. Bulk endpoints can say they support up to 2^16 (65,536) streams. The information in the endpoint companion descriptor must be stored with the other device, config, interface, and endpoint descriptors because the host controller needs to access them quickly, and we need to install some default values if a SuperSpeed device doesn't provide an endpoint companion descriptor. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 189 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 180 insertions(+), 9 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index e9426acf5682..7103758bb486 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -19,6 +19,32 @@ static inline const char *plural(int n) return (n == 1 ? "" : "s"); } +/* FIXME: this is a kludge */ +static int find_next_descriptor_more(unsigned char *buffer, int size, + int dt1, int dt2, int dt3, int *num_skipped) +{ + struct usb_descriptor_header *h; + int n = 0; + unsigned char *buffer0 = buffer; + + /* Find the next descriptor of type dt1 or dt2 or dt3 */ + while (size > 0) { + h = (struct usb_descriptor_header *) buffer; + if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 || + h->bDescriptorType == dt3) + break; + buffer += h->bLength; + size -= h->bLength; + ++n; + } + + /* Store the number of descriptors skipped and return the + * number of bytes skipped */ + if (num_skipped) + *num_skipped = n; + return buffer - buffer0; +} + static int find_next_descriptor(unsigned char *buffer, int size, int dt1, int dt2, int *num_skipped) { @@ -43,6 +69,128 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } +static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, + int inum, int asnum, struct usb_host_endpoint *ep, + int num_ep, unsigned char *buffer, int size) +{ + unsigned char *buffer_start = buffer; + struct usb_ep_comp_descriptor *desc; + int retval; + int num_skipped; + int max_tx; + int i; + + /* Allocate space for the companion descriptor */ + ep->ep_comp = kzalloc(sizeof(struct usb_host_ep_comp), GFP_KERNEL); + if (!ep->ep_comp) + return -ENOMEM; + desc = (struct usb_ep_comp_descriptor *) buffer; + if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { + dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " + " interface %d altsetting %d ep %d: " + "using minimum values\n", + cfgno, inum, asnum, ep->desc.bEndpointAddress); + ep->ep_comp->desc.bLength = USB_DT_EP_COMP_SIZE; + ep->ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + ep->ep_comp->desc.bMaxBurst = 0; + /* + * Leave bmAttributes as zero, which will mean no streams for + * bulk, and isoc won't support multiple bursts of packets. + * With bursts of only one packet, and a Mult of 1, the max + * amount of data moved per endpoint service interval is one + * packet. + */ + if (usb_endpoint_xfer_isoc(&ep->desc) || + usb_endpoint_xfer_int(&ep->desc)) + ep->ep_comp->desc.wBytesPerInterval = + ep->desc.wMaxPacketSize; + /* + * The next descriptor is for an Endpoint or Interface, + * no extra descriptors to copy into the companion structure, + * and we didn't eat up any of the buffer. + */ + retval = 0; + goto valid; + } + memcpy(&ep->ep_comp->desc, desc, USB_DT_EP_COMP_SIZE); + desc = &ep->ep_comp->desc; + buffer += desc->bLength; + size -= desc->bLength; + + /* Eat up the other descriptors we don't care about */ + ep->ep_comp->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &num_skipped); + ep->ep_comp->extralen = i; + buffer += i; + size -= i; + retval = buffer - buffer_start + i; + if (num_skipped > 0) + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + num_skipped, plural(num_skipped), + "SuperSpeed endpoint companion"); + + /* Check the various values */ + if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) { + dev_warn(ddev, "Control endpoint with bMaxBurst = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to zero\n", desc->bMaxBurst, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bMaxBurst = 0; + } + if (desc->bMaxBurst > 15) { + dev_warn(ddev, "Endpoint with bMaxBurst = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to 15\n", desc->bMaxBurst, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bMaxBurst = 15; + } + if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) + && desc->bmAttributes != 0) { + dev_warn(ddev, "%s endpoint with bmAttributes = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to zero\n", + usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", + desc->bmAttributes, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 0; + } + if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { + dev_warn(ddev, "Bulk endpoint with more than 65536 streams in " + "config %d interface %d altsetting %d ep %d: " + "setting to max\n", + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 16; + } + if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) { + dev_warn(ddev, "Isoc endpoint has Mult of %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to 3\n", desc->bmAttributes + 1, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 2; + } + if (usb_endpoint_xfer_isoc(&ep->desc)) { + max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) * + (desc->bmAttributes + 1); + } else if (usb_endpoint_xfer_int(&ep->desc)) { + max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1); + } else { + goto valid; + } + if (desc->wBytesPerInterval > max_tx) { + dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to %d\n", + usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int", + desc->wBytesPerInterval, + cfgno, inum, asnum, ep->desc.bEndpointAddress, + max_tx); + desc->wBytesPerInterval = max_tx; + } +valid: + return retval; +} + static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) @@ -50,7 +198,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; - int n, i, j; + int n, i, j, retval; d = (struct usb_endpoint_descriptor *) buffer; buffer += d->bLength; @@ -162,17 +310,38 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress, maxp); } - - /* Skip over any Class Specific or Vendor Specific descriptors; - * find the next endpoint or interface descriptor */ - endpoint->extra = buffer; - i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, - USB_DT_INTERFACE, &n); - endpoint->extralen = i; + /* Allocate room for and parse any endpoint companion descriptors */ + if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) { + endpoint->extra = buffer; + i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP, + USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); + endpoint->extralen = i; + buffer += i; + size -= i; + + if (size > 0) { + retval = usb_parse_endpoint_companion(ddev, cfgno, inum, asnum, + endpoint, num_ep, buffer, size); + if (retval >= 0) { + buffer += retval; + retval = buffer - buffer0; + } + } else { + retval = buffer - buffer0; + } + } else { + /* Skip over any Class Specific or Vendor Specific descriptors; + * find the next endpoint or interface descriptor */ + endpoint->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &n); + endpoint->extralen = i; + retval = buffer - buffer0 + i; + } if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "endpoint"); - return buffer - buffer0 + i; + return retval; skip_to_next_endpoint_or_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, @@ -453,6 +622,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, kref_init(&intfc->ref); } + /* FIXME: parse the BOS descriptor */ + /* Skip over any Class Specific or Vendor Specific descriptors; * find the first interface descriptor */ config->extra = buffer; -- cgit From 79abb1ab13cee5ba488210798b6e7bbae0b391ac Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:58:26 -0700 Subject: USB: Support for bandwidth allocation. Originally, the USB core had no support for allocating bandwidth when a particular configuration or alternate setting for an interface was selected. Instead, the device driver's URB submission would fail if there was not enough bandwidth for a periodic endpoint. Drivers could work around this, by using the scatter-gather list API to guarantee bandwidth. This patch adds host controller API to allow the USB core to allocate or deallocate bandwidth for an endpoint. Endpoints are added to or dropped from a copy of the current schedule by calling add_endpoint() or drop_endpoint(), and then the schedule is atomically evaluated with a call to check_bandwidth(). This allows all the endpoints for a new configuration or alternate setting to be added at the same time that the endpoints from the old configuration or alt setting are dropped. Endpoints must be added to the schedule before any URBs are submitted to them. The HCD must be allowed to reject a new configuration or alt setting before the control transfer is sent to the device requesting the change. It may reject the change because there is not enough bandwidth, not enough internal resources (such as memory on an embedded host controller), or perhaps even for security reasons in a virtualized environment. If the call to check_bandwidth() fails, the USB core must call reset_bandwidth(). This causes the schedule to be reverted back to the state it was in just after the last successful check_bandwidth() call. If the call succeeds, the host controller driver (and hardware) will have changed its internal state to match the new configuration or alternate setting. The USB core can then issue a control transfer to the device to change the configuration or alt setting. This allows the core to test new configurations or alternate settings before unbinding drivers bound to interfaces in the old configuration. WIP: The USB core must add endpoints from all interfaces in a configuration to the schedule, because a driver may claim that interface at any time. A slight optimization might be to add the endpoints to the schedule once a driver claims that interface. FIXME This patch does not cover changing alternate settings, but it does handle a configuration change or de-configuration. FIXME The code for managing the schedule is currently HCD specific. A generic scheduling algorithm could be added for host controllers without built-in scheduling support. For now, if a host controller does not define the check_bandwidth() function, the call to usb_hcd_check_bandwidth() will always succeed. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/hcd.h | 32 +++++++++++++++++ drivers/usb/core/message.c | 20 +++++++++++ drivers/usb/core/urb.c | 6 ++++ 4 files changed, 144 insertions(+) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 823744d80cb9..b2da4753b12e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1560,6 +1560,92 @@ rescan: } } +/* Check whether a new configuration or alt setting for an interface + * will exceed the bandwidth for the bus (or the host controller resources). + * Only pass in a non-NULL config or interface, not both! + * Passing NULL for both new_config and new_intf means the device will be + * de-configured by issuing a set configuration 0 command. + */ +int usb_hcd_check_bandwidth(struct usb_device *udev, + struct usb_host_config *new_config, + struct usb_interface *new_intf) +{ + int num_intfs, i, j; + struct usb_interface_cache *intf_cache; + struct usb_host_interface *alt = 0; + int ret = 0; + struct usb_hcd *hcd; + struct usb_host_endpoint *ep; + + hcd = bus_to_hcd(udev->bus); + if (!hcd->driver->check_bandwidth) + return 0; + + /* Configuration is being removed - set configuration 0 */ + if (!new_config && !new_intf) { + for (i = 1; i < 16; ++i) { + ep = udev->ep_out[i]; + if (ep) + hcd->driver->drop_endpoint(hcd, udev, ep); + ep = udev->ep_in[i]; + if (ep) + hcd->driver->drop_endpoint(hcd, udev, ep); + } + hcd->driver->check_bandwidth(hcd, udev); + return 0; + } + /* Check if the HCD says there's enough bandwidth. Enable all endpoints + * each interface's alt setting 0 and ask the HCD to check the bandwidth + * of the bus. There will always be bandwidth for endpoint 0, so it's + * ok to exclude it. + */ + if (new_config) { + num_intfs = new_config->desc.bNumInterfaces; + /* Remove endpoints (except endpoint 0, which is always on the + * schedule) from the old config from the schedule + */ + for (i = 1; i < 16; ++i) { + ep = udev->ep_out[i]; + if (ep) { + ret = hcd->driver->drop_endpoint(hcd, udev, ep); + if (ret < 0) + goto reset; + } + ep = udev->ep_in[i]; + if (ep) { + ret = hcd->driver->drop_endpoint(hcd, udev, ep); + if (ret < 0) + goto reset; + } + } + for (i = 0; i < num_intfs; ++i) { + + /* Dig the endpoints for alt setting 0 out of the + * interface cache for this interface + */ + intf_cache = new_config->intf_cache[i]; + for (j = 0; j < intf_cache->num_altsetting; j++) { + if (intf_cache->altsetting[j].desc.bAlternateSetting == 0) + alt = &intf_cache->altsetting[j]; + } + if (!alt) { + printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i); + continue; + } + for (j = 0; j < alt->desc.bNumEndpoints; j++) { + ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]); + if (ret < 0) + goto reset; + } + } + } + ret = hcd->driver->check_bandwidth(hcd, udev); +reset: + if (ret < 0) + hcd->driver->reset_bandwidth(hcd, udev); + return ret; +} + /* Disables the endpoint: synchronizes with the hcd to make sure all * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must * have been called previously. Use for set_configuration, set_interface, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index ae6d9db41ca9..d397ecfd5b17 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -232,6 +232,35 @@ struct hc_driver { int (*alloc_dev)(struct usb_hcd *, struct usb_device *); /* Called by usb_release_dev to free HC device structures */ void (*free_dev)(struct usb_hcd *, struct usb_device *); + + /* Bandwidth computation functions */ + /* Note that add_endpoint() can only be called once per endpoint before + * check_bandwidth() or reset_bandwidth() must be called. + * drop_endpoint() can only be called once per endpoint also. + * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will + * add the endpoint to the schedule with possibly new parameters denoted by a + * different endpoint descriptor in usb_host_endpoint. + * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is + * not allowed. + */ + /* Allocate endpoint resources and add them to a new schedule */ + int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); + /* Drop an endpoint from a new schedule */ + int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); + /* Check that a new hardware configuration, set using + * endpoint_enable and endpoint_disable, does not exceed bus + * bandwidth. This must be called before any set configuration + * or set interface requests are sent to the device. + */ + int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); + /* Reset the device schedule to the last known good schedule, + * which was set from a previous successful call to + * check_bandwidth(). This reverts any add_endpoint() and + * drop_endpoint() calls since that last successful call. + * Used for when a check_bandwidth() call fails due to resource + * or bandwidth constraints. + */ + void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); /* Returns the hardware-chosen device address */ int (*address_device)(struct usb_hcd *, struct usb_device *udev); }; @@ -252,6 +281,9 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern void usb_hcd_reset_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); +extern int usb_hcd_check_bandwidth(struct usb_device *udev, + struct usb_host_config *new_config, + struct usb_interface *new_intf); extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9bd26dec7599..3a2e69ec2f29 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -510,6 +510,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init); * could be transferred. That capability is less useful for low or full * speed interrupt endpoints, which allow at most one packet per millisecond, * of at most 8 or 64 bytes (respectively). + * + * It is not necessary to call this function to reserve bandwidth for devices + * under an xHCI host controller, as the bandwidth is reserved when the + * configuration or interface alt setting is selected. */ void usb_sg_wait(struct usb_sg_request *io) { @@ -1653,6 +1657,21 @@ free_interfaces: if (ret) goto free_interfaces; + /* Make sure we have bandwidth (and available HCD resources) for this + * configuration. Remove endpoints from the schedule if we're dropping + * this configuration to set configuration 0. After this point, the + * host controller will not allow submissions to dropped endpoints. If + * this call fails, the device state is unchanged. + */ + if (cp) + ret = usb_hcd_check_bandwidth(dev, cp, NULL); + else + ret = usb_hcd_check_bandwidth(dev, NULL, NULL); + if (ret < 0) { + usb_autosuspend_device(dev); + goto free_interfaces; + } + /* if it's already configured, clear out old state first. * getting rid of old interfaces means unbinding their drivers. */ @@ -1675,6 +1694,7 @@ free_interfaces: dev->actconfig = cp; if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); + usb_hcd_check_bandwidth(dev, NULL, NULL); usb_autosuspend_device(dev); goto free_interfaces; } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 02eb0ef7a4c3..0885d4abdc62 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * If the USB subsystem can't allocate sufficient bandwidth to perform * the periodic request, submitting such a periodic request should fail. * + * For devices under xHCI, the bandwidth is reserved at configuration time, or + * when the alt setting is selected. If there is not enough bus bandwidth, the + * configuration/alt setting request will fail. Therefore, submissions to + * periodic endpoints on devices under xHCI should never fail due to bandwidth + * constraints. + * * Device drivers must explicitly request that repetition, by ensuring that * some URB is always on the endpoint's queue (except possibly for short * periods during completion callacks). When there is no longer an urb -- cgit From e04748e3a87271fcf30d383e3780c5d3ee1c1618 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:59:01 -0700 Subject: USB: Push scatter gather lists down to host controller drivers. This is the original patch I created before David Vrabel posted a better patch (http://marc.info/?l=linux-usb&m=123377477209109&w=2) that does basically the same thing. This patch will get replaced with his (modified) patch later. Allow USB device drivers that use usb_sg_init() and usb_sg_wait() to push bulk endpoint scatter gather lists down to the host controller drivers. This allows host controller drivers to more efficiently enqueue these transfers, and allows the xHCI host controller to better take advantage of USB 3.0 "bursts" for bulk endpoints. This patch currently only enables scatter gather lists for bulk endpoints. Other endpoint types that use the usb_sg_* functions will not have their scatter gather lists pushed down to the host controller. For periodic endpoints, we want each scatterlist entry to be a separate transfer. Eventually, HCDs could parse these scatter-gather lists for periodic endpoints also. For now, we use the old code and call usb_submit_urb() for each scatterlist entry. The caller of usb_sg_init() can request that all bytes in the scatter gather list be transferred by passing in a length of zero. Handle that request for a bulk endpoint under xHCI by walking the scatter gather list and calculating the length. We could let the HCD handle a zero length in this case, but I'm not sure if the core layers in between will get confused by this. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 3 +- drivers/usb/core/message.c | 139 +++++++++++++++++++++++++++++---------------- 2 files changed, 93 insertions(+), 49 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index b2da4753b12e..1609623ec829 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1239,7 +1239,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, /* Map the URB's buffers for DMA access. * Lower level HCD code should use *_dma exclusively, - * unless it uses pio or talks to another transport. + * unless it uses pio or talks to another transport, + * or uses the provided scatter gather list for bulk. */ if (is_root_hub(urb->dev)) return 0; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 3a2e69ec2f29..2bed83caacb1 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -365,6 +365,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, int i; int urb_flags; int dma; + int use_sg; if (!io || !dev || !sg || usb_pipecontrol(pipe) @@ -392,7 +393,19 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (io->entries <= 0) return io->entries; - io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); + /* If we're running on an xHCI host controller, queue the whole scatter + * gather list with one call to urb_enqueue(). This is only for bulk, + * as that endpoint type does not care how the data gets broken up + * across frames. + */ + if (usb_pipebulk(pipe) && + bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) { + io->urbs = kmalloc(sizeof *io->urbs, mem_flags); + use_sg = true; + } else { + io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); + use_sg = false; + } if (!io->urbs) goto nomem; @@ -402,62 +415,92 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (usb_pipein(pipe)) urb_flags |= URB_SHORT_NOT_OK; - for_each_sg(sg, sg, io->entries, i) { - unsigned len; - - io->urbs[i] = usb_alloc_urb(0, mem_flags); - if (!io->urbs[i]) { - io->entries = i; + if (use_sg) { + io->urbs[0] = usb_alloc_urb(0, mem_flags); + if (!io->urbs[0]) { + io->entries = 0; goto nomem; } - io->urbs[i]->dev = NULL; - io->urbs[i]->pipe = pipe; - io->urbs[i]->interval = period; - io->urbs[i]->transfer_flags = urb_flags; - - io->urbs[i]->complete = sg_complete; - io->urbs[i]->context = io; - - /* - * Some systems need to revert to PIO when DMA is temporarily - * unavailable. For their sakes, both transfer_buffer and - * transfer_dma are set when possible. However this can only - * work on systems without: - * - * - HIGHMEM, since DMA buffers located in high memory are - * not directly addressable by the CPU for PIO; - * - * - IOMMU, since dma_map_sg() is allowed to use an IOMMU to - * make virtually discontiguous buffers be "dma-contiguous" - * so that PIO and DMA need diferent numbers of URBs. - * - * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL - * to prevent stale pointers and to help spot bugs. - */ - if (dma) { - io->urbs[i]->transfer_dma = sg_dma_address(sg); - len = sg_dma_len(sg); + io->urbs[0]->dev = NULL; + io->urbs[0]->pipe = pipe; + io->urbs[0]->interval = period; + io->urbs[0]->transfer_flags = urb_flags; + + io->urbs[0]->complete = sg_complete; + io->urbs[0]->context = io; + /* A length of zero means transfer the whole sg list */ + io->urbs[0]->transfer_buffer_length = length; + if (length == 0) { + for_each_sg(sg, sg, io->entries, i) { + io->urbs[0]->transfer_buffer_length += + sg_dma_len(sg); + } + } + io->urbs[0]->sg = io; + io->urbs[0]->num_sgs = io->entries; + io->entries = 1; + } else { + for_each_sg(sg, sg, io->entries, i) { + unsigned len; + + io->urbs[i] = usb_alloc_urb(0, mem_flags); + if (!io->urbs[i]) { + io->entries = i; + goto nomem; + } + + io->urbs[i]->dev = NULL; + io->urbs[i]->pipe = pipe; + io->urbs[i]->interval = period; + io->urbs[i]->transfer_flags = urb_flags; + + io->urbs[i]->complete = sg_complete; + io->urbs[i]->context = io; + + /* + * Some systems need to revert to PIO when DMA is + * temporarily unavailable. For their sakes, both + * transfer_buffer and transfer_dma are set when + * possible. However this can only work on systems + * without: + * + * - HIGHMEM, since DMA buffers located in high memory + * are not directly addressable by the CPU for PIO; + * + * - IOMMU, since dma_map_sg() is allowed to use an + * IOMMU to make virtually discontiguous buffers be + * "dma-contiguous" so that PIO and DMA need diferent + * numbers of URBs. + * + * So when HIGHMEM or IOMMU are in use, transfer_buffer + * is NULL to prevent stale pointers and to help spot + * bugs. + */ + if (dma) { + io->urbs[i]->transfer_dma = sg_dma_address(sg); + len = sg_dma_len(sg); #if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU) - io->urbs[i]->transfer_buffer = NULL; + io->urbs[i]->transfer_buffer = NULL; #else - io->urbs[i]->transfer_buffer = sg_virt(sg); + io->urbs[i]->transfer_buffer = sg_virt(sg); #endif - } else { - /* hc may use _only_ transfer_buffer */ - io->urbs[i]->transfer_buffer = sg_virt(sg); - len = sg->length; - } + } else { + /* hc may use _only_ transfer_buffer */ + io->urbs[i]->transfer_buffer = sg_virt(sg); + len = sg->length; + } - if (length) { - len = min_t(unsigned, len, length); - length -= len; - if (length == 0) - io->entries = i + 1; + if (length) { + len = min_t(unsigned, len, length); + length -= len; + if (length == 0) + io->entries = i + 1; + } + io->urbs[i]->transfer_buffer_length = len; } - io->urbs[i]->transfer_buffer_length = len; + io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; } - io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; /* transaction state */ io->count = io->entries; -- cgit From f0058c627855ecb3b6c7185b7ad1910463c24c42 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 29 Apr 2009 19:06:20 -0700 Subject: USB: Change names of SuperSpeed ep companion descriptor structs. Differentiate between SuperSpeed endpoint companion descriptor and the wireless USB endpoint companion descriptor. Make all structure names for this descriptor have "ss" (SuperSpeed) in them. David Vrabel asked for this change in http://marc.info/?l=linux-usb&m=124091465109367&w=2 Reported-by: David Vrabel Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 7103758bb486..24dfb33f90cb 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -69,30 +69,31 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } -static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, +static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer_start = buffer; - struct usb_ep_comp_descriptor *desc; + struct usb_ss_ep_comp_descriptor *desc; int retval; int num_skipped; int max_tx; int i; - /* Allocate space for the companion descriptor */ - ep->ep_comp = kzalloc(sizeof(struct usb_host_ep_comp), GFP_KERNEL); - if (!ep->ep_comp) + /* Allocate space for the SS endpoint companion descriptor */ + ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), + GFP_KERNEL); + if (!ep->ss_ep_comp) return -ENOMEM; - desc = (struct usb_ep_comp_descriptor *) buffer; + desc = (struct usb_ss_ep_comp_descriptor *) buffer; if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " " interface %d altsetting %d ep %d: " "using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); - ep->ep_comp->desc.bLength = USB_DT_EP_COMP_SIZE; - ep->ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; - ep->ep_comp->desc.bMaxBurst = 0; + ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; + ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + ep->ss_ep_comp->desc.bMaxBurst = 0; /* * Leave bmAttributes as zero, which will mean no streams for * bulk, and isoc won't support multiple bursts of packets. @@ -102,7 +103,7 @@ static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) - ep->ep_comp->desc.wBytesPerInterval = + ep->ss_ep_comp->desc.wBytesPerInterval = ep->desc.wMaxPacketSize; /* * The next descriptor is for an Endpoint or Interface, @@ -112,16 +113,16 @@ static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, retval = 0; goto valid; } - memcpy(&ep->ep_comp->desc, desc, USB_DT_EP_COMP_SIZE); - desc = &ep->ep_comp->desc; + memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE); + desc = &ep->ss_ep_comp->desc; buffer += desc->bLength; size -= desc->bLength; /* Eat up the other descriptors we don't care about */ - ep->ep_comp->extra = buffer; + ep->ss_ep_comp->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &num_skipped); - ep->ep_comp->extralen = i; + ep->ss_ep_comp->extralen = i; buffer += i; size -= i; retval = buffer - buffer_start + i; @@ -310,7 +311,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress, maxp); } - /* Allocate room for and parse any endpoint companion descriptors */ + /* Allocate room for and parse any SS endpoint companion descriptors */ if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) { endpoint->extra = buffer; i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP, @@ -320,8 +321,9 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, size -= i; if (size > 0) { - retval = usb_parse_endpoint_companion(ddev, cfgno, inum, asnum, - endpoint, num_ep, buffer, size); + retval = usb_parse_ss_endpoint_companion(ddev, cfgno, + inum, asnum, endpoint, num_ep, buffer, + size); if (retval >= 0) { buffer += retval; retval = buffer - buffer0; -- cgit From 7dd19e69d131ea34f74397559b422511e54d2911 Mon Sep 17 00:00:00 2001 From: Viral Mehta Date: Tue, 5 May 2009 15:54:23 +0530 Subject: USB: xhci: replace if-elseif-else with switch-case Replace if-elseif-else with switch-case to keep the code consistent which is semantically same Switch-case is used here, http://www.spinics.net/lists/linux-usb/msg17201.html Making consistent at other places in usb/core Also easier to read and maintain when USB4.0, 5.0, ... comes Signed-off-by: Viral Mehta Tested-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 1609623ec829..ce3f453f02ef 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -488,28 +488,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (wValue & 0xff00) { case USB_DT_DEVICE << 8: - if (hcd->driver->flags & HCD_USB3) + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB3: bufp = usb3_rh_dev_descriptor; - else if (hcd->driver->flags & HCD_USB2) + break; + case HCD_USB2: bufp = usb2_rh_dev_descriptor; - else if (hcd->driver->flags & HCD_USB11) + break; + case HCD_USB11: bufp = usb11_rh_dev_descriptor; - else + break; + default: goto error; + } len = 18; if (hcd->has_tt) patch_protocol = 1; break; case USB_DT_CONFIG << 8: - if (hcd->driver->flags & HCD_USB3) { + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB3: bufp = ss_rh_config_descriptor; len = sizeof ss_rh_config_descriptor; - } else if (hcd->driver->flags & HCD_USB2) { + break; + case HCD_USB2: bufp = hs_rh_config_descriptor; len = sizeof hs_rh_config_descriptor; - } else { + break; + case HCD_USB11: bufp = fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor; + break; + default: + goto error; } if (device_can_wakeup(&hcd->self.root_hub->dev)) patch_wakeup = 1; -- cgit