diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 20:06:10 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 20:06:10 (GMT) |
commit | e1f5b94fd0c93c3e27ede88b7ab652d086dc960f (patch) | |
tree | e8de7a132eb88521dd1c19e128eba2d5349bdf4f /drivers/usb/core | |
parent | 6fd03301d76bc439382710e449f58efbb233df1b (diff) | |
parent | 1b6ed69f974f6f32c8be0d9a7fc952822eb83b6f (diff) | |
download | linux-e1f5b94fd0c93c3e27ede88b7ab652d086dc960f.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (143 commits)
USB: xhci depends on PCI.
USB: xhci: Add Makefile, MAINTAINERS, and Kconfig entries.
USB: xhci: Respect critical sections.
USB: xHCI: Fix interrupt moderation.
USB: xhci: Remove packed attribute from structures.
usb; xhci: Fix TRB offset calculations.
USB: xhci: replace if-elseif-else with switch-case
USB: xhci: Make xhci-mem.c include linux/dmapool.h
USB: xhci: drop spinlock in xhci_urb_enqueue() error path.
USB: Change names of SuperSpeed ep companion descriptor structs.
USB: xhci: Avoid compiler reordering in Link TRB giveback.
USB: xhci: Clean up xhci_irq() function.
USB: xhci: Avoid global namespace pollution.
USB: xhci: Fix Link TRB handoff bit twiddling.
USB: xhci: Fix register write order.
USB: xhci: fix some compiler warnings in xhci.h
USB: xhci: fix lots of compiler warnings.
USB: xhci: use xhci_handle_event instead of handle_event
USB: xhci: URB cancellation support.
USB: xhci: Scatter gather list support for bulk transfers.
...
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 16 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 4 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 192 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 56 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 160 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 244 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 220 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 55 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 134 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 3 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 194 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 76 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 13 |
15 files changed, 907 insertions, 484 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index e1759d1..69280c3 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 <file:Documentation/usb/proc_usb_info.txt>. - 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)" diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index b607870..ec16e60 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/config.c b/drivers/usb/core/config.c index 568244c..24dfb33 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,129 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } +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_ss_ep_comp_descriptor *desc; + int retval; + int num_skipped; + int max_tx; + int i; + + /* 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_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->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. + * 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->ss_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->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->ss_ep_comp->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &num_skipped); + ep->ss_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 +199,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; @@ -92,6 +241,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 @@ -161,17 +311,39 @@ 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 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, + USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); + endpoint->extralen = i; + buffer += i; + size -= i; + + if (size > 0) { + retval = usb_parse_ss_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, @@ -452,6 +624,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; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d0a21a5..69e5773 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) { @@ -385,7 +375,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 +383,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); @@ -598,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; @@ -630,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) { @@ -1762,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); @@ -1771,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 */ diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 40dee2a..bc39fc4 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -15,19 +15,18 @@ #include <linux/usb.h> #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/hcd-pci.c b/drivers/usb/core/hcd-pci.c index a4301dc..91f2885 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); 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); + retval = hcd->driver->pci_resume(hcd, hibernated); 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.c b/drivers/usb/core/hcd.c index 42b93da..ce3f453 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,23 +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_USB2) + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB3: + bufp = usb3_rh_dev_descriptor; + 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_USB2) { + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB3: + bufp = ss_rh_config_descriptor; + len = sizeof ss_rh_config_descriptor; + 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; @@ -755,23 +833,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 +879,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 +889,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 +918,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); } /** @@ -1199,7 +1250,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; @@ -1520,6 +1572,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, @@ -1897,8 +2035,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 e7d4479..d397ecf 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -173,6 +173,8 @@ 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 */ +#define HCD_MASK 0x0070 /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); @@ -182,10 +184,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); @@ -224,6 +226,43 @@ 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 *); + + /* 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); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); @@ -242,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, @@ -261,14 +303,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 */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index be86ae3..2af3b4f 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"; } @@ -457,13 +459,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 */ @@ -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); @@ -1323,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) { @@ -1642,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))); @@ -2395,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); } @@ -2430,6 +2453,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; @@ -2452,11 +2476,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) { @@ -2471,6 +2508,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,16 +2534,20 @@ 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 "; 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) { @@ -2530,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; @@ -2596,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) @@ -2609,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; } @@ -2634,8 +2687,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)) { @@ -2847,19 +2903,41 @@ 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); - /* 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 + * 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; + + /* + * 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 and get descriptor */ + /* reset (non-USB 3.0 devices) and get descriptor */ status = hub_port_init(hub, udev, port1, i); if (status < 0) goto loop; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 2a116ce..889c0f3 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 diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b626283..2bed83c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/timer.h> #include <linux/ctype.h> +#include <linux/nls.h> #include <linux/device.h> #include <linux/scatterlist.h> #include <linux/usb/quirks.h> @@ -364,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) @@ -391,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; @@ -401,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; @@ -509,6 +553,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) { @@ -759,7 +807,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 +815,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,7 +827,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { unsigned char *tbuf; int err; - unsigned int u, idx; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; @@ -821,16 +861,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) goto errout; 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 = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2, + UTF16_LITTLE_ENDIAN, buf, size); + buf[err] = 0; if (tbuf[1] != USB_DT_STRING) dev_dbg(&dev->dev, @@ -843,6 +876,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 +896,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) @@ -1664,6 +1700,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. */ @@ -1686,6 +1737,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/sysfs.c b/drivers/usb/core/sysfs.c index c667891..b5c72e4 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; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3376055..0885d4a 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 @@ -351,6 +357,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 +433,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)) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 927a27d..a26f738 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -34,6 +34,7 @@ #include <linux/usb.h> #include <linux/mutex.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> #include <asm/io.h> #include <linux/scatterlist.h> @@ -139,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); @@ -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); @@ -359,6 +364,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; @@ -386,18 +398,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); @@ -810,12 +828,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); @@ -933,8 +951,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 @@ -1012,6 +1030,35 @@ static struct notifier_block usb_bus_nb = { .notifier_call = usb_bus_notify, }; +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); +} + /* * Init */ @@ -1023,6 +1070,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; @@ -1032,9 +1083,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; @@ -1064,8 +1112,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); @@ -1090,10 +1136,10 @@ 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(); + usb_debugfs_cleanup(); } subsys_initcall(usb_init); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 79d8a9e..e2a8cfa 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 @@ -106,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) @@ -113,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) |