diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 53 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 93 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 103 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 676 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 748 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 38 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 21 | ||||
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 117 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 7 |
14 files changed, 983 insertions, 906 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 18d02e32a3d..9981984b365 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -27,58 +27,6 @@ config USB_ANNOUNCE_NEW_DEVICES comment "Miscellaneous USB options" depends on USB -config USB_DEVICEFS - bool "USB device filesystem (DEPRECATED)" - depends on USB - ---help--- - If you say Y here (and to "/proc file system support" in the "File - systems" section, above), you will get a file /proc/bus/usb/devices - which lists the devices currently connected to your USB bus or - busses, and for every connected device a file named - "/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the - device number; the latter files can be used by user space programs - to talk directly to the device. These files are "virtual", meaning - they are generated on the fly and not stored on the hard drive. - - You may need to mount the usbfs file system to see the files, use - mount -t usbfs none /proc/bus/usb - - For the format of the various /proc/bus/usb/ files, please read - <file:Documentation/usb/proc_usb_info.txt>. - - 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)" - depends on USB - default y - ---help--- - Userspace access to USB devices is granted by device-nodes exported - directly from the usbdev in sysfs. Old versions of the driver - core and udev needed additional class devices to export device nodes. - - These additional devices are difficult to handle in userspace, if - information about USB interfaces must be available. One device - contains the device node, the other device contains the interface - data. Both devices are at the same level in sysfs (siblings) and one - can't access the other. The device node created directly by the - usb device is the parent device of the interface and therefore - easily accessible from the interface event. - - This option provides backward compatibility for libusb device - nodes (lsusb) when usbfs is not used, and the following udev rule - doesn't exist: - SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \ - NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644" - config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation" depends on USB @@ -125,7 +73,6 @@ config USB_OTG_WHITELIST bool "Rely on OTG Targeted Peripherals List" depends on USB_OTG || EXPERT default y if USB_OTG - default n if EXPERT help If you say Y here, the "otg_whitelist.h" file will be used as a product whitelist, so USB peripherals not listed there will be diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 507a4e1b636..26059b93dbf 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -9,6 +9,6 @@ usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o usbcore-$(CONFIG_PCI) += hcd-pci.o -usbcore-$(CONFIG_USB_DEVICEFS) += inode.o +usbcore-$(CONFIG_ACPI) += usb-acpi.o obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 8df4b76465a..e0f107948eb 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -333,17 +333,14 @@ static struct async *async_getcompleted(struct dev_state *ps) static struct async *async_getpending(struct dev_state *ps, void __user *userurb) { - unsigned long flags; struct async *as; - spin_lock_irqsave(&ps->lock, flags); list_for_each_entry(as, &ps->async_pending, asynclist) if (as->userurb == userurb) { list_del_init(&as->asynclist); - spin_unlock_irqrestore(&ps->lock, flags); return as; } - spin_unlock_irqrestore(&ps->lock, flags); + return NULL; } @@ -398,6 +395,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr) __releases(ps->lock) __acquires(ps->lock) { + struct urb *urb; struct async *as; /* Mark all the pending URBs that match bulk_addr, up to but not @@ -420,8 +418,11 @@ __acquires(ps->lock) list_for_each_entry(as, &ps->async_pending, asynclist) { if (as->bulk_status == AS_UNLINK) { as->bulk_status = 0; /* Only once */ + urb = as->urb; + usb_get_urb(urb); spin_unlock(&ps->lock); /* Allow completions */ - usb_unlink_urb(as->urb); + usb_unlink_urb(urb); + usb_put_urb(urb); spin_lock(&ps->lock); goto rescan; } @@ -472,6 +473,7 @@ static void async_completed(struct urb *urb) static void destroy_async(struct dev_state *ps, struct list_head *list) { + struct urb *urb; struct async *as; unsigned long flags; @@ -479,10 +481,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list) while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); + urb = as->urb; + usb_get_urb(urb); /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - usb_kill_urb(as->urb); + usb_kill_urb(urb); + usb_put_urb(urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -727,17 +732,6 @@ static int usbdev_open(struct inode *inode, struct file *file) if (imajor(inode) == USB_DEVICE_MAJOR) dev = usbdev_lookup_by_devt(inode->i_rdev); -#ifdef CONFIG_USB_DEVICEFS - /* procfs file */ - if (!dev) { - dev = inode->i_private; - if (dev && dev->usbfs_dentry && - dev->usbfs_dentry->d_inode == inode) - usb_get_dev(dev); - else - dev = NULL; - } -#endif mutex_unlock(&usbfs_mutex); if (!dev) @@ -1410,12 +1404,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) static int proc_unlinkurb(struct dev_state *ps, void __user *arg) { + struct urb *urb; struct async *as; + unsigned long flags; + spin_lock_irqsave(&ps->lock, flags); as = async_getpending(ps, arg); - if (!as) + if (!as) { + spin_unlock_irqrestore(&ps->lock, flags); return -EINVAL; - usb_kill_urb(as->urb); + } + + urb = as->urb; + usb_get_urb(urb); + spin_unlock_irqrestore(&ps->lock, flags); + + usb_kill_urb(urb); + usb_put_urb(urb); + return 0; } @@ -2062,44 +2068,13 @@ static void usbdev_remove(struct usb_device *udev) } } -#ifdef CONFIG_USB_DEVICE_CLASS -static struct class *usb_classdev_class; - -static int usb_classdev_add(struct usb_device *dev) -{ - struct device *cldev; - - cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt, - NULL, "usbdev%d.%d", dev->bus->busnum, - dev->devnum); - if (IS_ERR(cldev)) - return PTR_ERR(cldev); - dev->usb_classdev = cldev; - return 0; -} - -static void usb_classdev_remove(struct usb_device *dev) -{ - if (dev->usb_classdev) - device_unregister(dev->usb_classdev); -} - -#else -#define usb_classdev_add(dev) 0 -#define usb_classdev_remove(dev) do {} while (0) - -#endif - static int usbdev_notify(struct notifier_block *self, unsigned long action, void *dev) { switch (action) { case USB_DEVICE_ADD: - if (usb_classdev_add(dev)) - return NOTIFY_BAD; break; case USB_DEVICE_REMOVE: - usb_classdev_remove(dev); usbdev_remove(dev); break; } @@ -2129,21 +2104,6 @@ int __init usb_devio_init(void) USB_DEVICE_MAJOR); goto error_cdev; } -#ifdef CONFIG_USB_DEVICE_CLASS - usb_classdev_class = class_create(THIS_MODULE, "usb_device"); - if (IS_ERR(usb_classdev_class)) { - printk(KERN_ERR "Unable to register usb_device class\n"); - retval = PTR_ERR(usb_classdev_class); - cdev_del(&usb_device_cdev); - usb_classdev_class = NULL; - goto out; - } - /* devices of this class shadow the major:minor of their parent - * device, so clear ->dev_kobj to prevent adding duplicate entries - * to /sys/dev - */ - usb_classdev_class->dev_kobj = NULL; -#endif usb_register_notify(&usbdev_nb); out: return retval; @@ -2156,9 +2116,6 @@ error_cdev: void usb_devio_cleanup(void) { usb_unregister_notify(&usbdev_nb); -#ifdef CONFIG_USB_DEVICE_CLASS - class_destroy(usb_classdev_class); -#endif cdev_del(&usb_device_cdev); unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 9a56635dc19..f536aebc958 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -79,6 +79,30 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, } EXPORT_SYMBOL_GPL(usb_store_new_id); +ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf) +{ + struct usb_dynid *dynid; + size_t count = 0; + + list_for_each_entry(dynid, &dynids->list, node) + if (dynid->id.bInterfaceClass != 0) + count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n", + dynid->id.idVendor, dynid->id.idProduct, + dynid->id.bInterfaceClass); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n", + dynid->id.idVendor, dynid->id.idProduct); + return count; +} +EXPORT_SYMBOL_GPL(usb_show_dynids); + +static ssize_t show_dynids(struct device_driver *driver, char *buf) +{ + struct usb_driver *usb_drv = to_usb_driver(driver); + + return usb_show_dynids(&usb_drv->dynids, buf); +} + static ssize_t store_new_id(struct device_driver *driver, const char *buf, size_t count) { @@ -86,7 +110,7 @@ static ssize_t store_new_id(struct device_driver *driver, return usb_store_new_id(&usb_drv->dynids, driver, buf, count); } -static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); +static DRIVER_ATTR(new_id, S_IRUGO | S_IWUSR, show_dynids, store_new_id); /** * store_remove_id - remove a USB device ID from this driver @@ -127,7 +151,7 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) return retval; return count; } -static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); +static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id); static int usb_create_newid_files(struct usb_driver *usb_drv) { @@ -264,6 +288,7 @@ static int usb_probe_interface(struct device *dev) struct usb_device *udev = interface_to_usbdev(intf); const struct usb_device_id *id; int error = -ENODEV; + int lpm_disable_error; dev_dbg(dev, "%s\n", __func__); @@ -300,6 +325,25 @@ static int usb_probe_interface(struct device *dev) if (driver->supports_autosuspend) pm_runtime_enable(dev); + /* If the new driver doesn't allow hub-initiated LPM, and we can't + * disable hub-initiated LPM, then fail the probe. + * + * Otherwise, leaving LPM enabled should be harmless, because the + * endpoint intervals should remain the same, and the U1/U2 timeouts + * should remain the same. + * + * If we need to install alt setting 0 before probe, or another alt + * setting during probe, that should also be fine. usb_set_interface() + * will attempt to disable LPM, and fail if it can't disable it. + */ + lpm_disable_error = usb_unlocked_disable_lpm(udev); + if (lpm_disable_error && driver->disable_hub_initiated_lpm) { + dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.", + __func__, driver->name); + error = lpm_disable_error; + goto err; + } + /* Carry out a deferred switch to altsetting 0 */ if (intf->needs_altsetting0) { error = usb_set_interface(udev, intf->altsetting[0]. @@ -314,6 +358,11 @@ static int usb_probe_interface(struct device *dev) goto err; intf->condition = USB_INTERFACE_BOUND; + + /* If the LPM disable succeeded, balance the ref counts. */ + if (!lpm_disable_error) + usb_unlocked_enable_lpm(udev); + usb_autosuspend_device(udev); return error; @@ -337,7 +386,7 @@ static int usb_unbind_interface(struct device *dev) struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); struct usb_device *udev; - int error, r; + int error, r, lpm_disable_error; intf->condition = USB_INTERFACE_UNBINDING; @@ -345,6 +394,13 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); + /* Hub-initiated LPM policy may change, so attempt to disable LPM until + * the driver is unbound. If LPM isn't disabled, that's fine because it + * wouldn't be enabled unless all the bound interfaces supported + * hub-initiated LPM. + */ + lpm_disable_error = usb_unlocked_disable_lpm(udev); + /* Terminate all URBs for this interface unless the driver * supports "soft" unbinding. */ @@ -378,6 +434,10 @@ static int usb_unbind_interface(struct device *dev) intf->condition = USB_INTERFACE_UNBOUND; intf->needs_remote_wakeup = 0; + /* Attempt to re-enable USB3 LPM, if the disable succeeded. */ + if (!lpm_disable_error) + usb_unlocked_enable_lpm(udev); + /* Unbound interfaces are always runtime-PM-disabled and -suspended */ if (driver->supports_autosuspend) pm_runtime_disable(dev); @@ -418,17 +478,29 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) { struct device *dev = &iface->dev; + struct usb_device *udev; int retval = 0; + int lpm_disable_error; if (dev->driver) return -EBUSY; + udev = interface_to_usbdev(iface); + dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); iface->needs_binding = 0; iface->condition = USB_INTERFACE_BOUND; + /* Disable LPM until this driver is bound. */ + lpm_disable_error = usb_unlocked_disable_lpm(udev); + if (lpm_disable_error && driver->disable_hub_initiated_lpm) { + dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.", + __func__, driver->name); + return -ENOMEM; + } + /* Claimed interfaces are initially inactive (suspended) and * runtime-PM-enabled, but only if the driver has autosuspend * support. Otherwise they are marked active, to prevent the @@ -447,6 +519,10 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (device_is_registered(dev)) retval = device_bind_driver(dev); + /* Attempt to re-enable USB3 LPM, if the disable was successful. */ + if (!lpm_disable_error) + usb_unlocked_enable_lpm(udev); + return retval; } EXPORT_SYMBOL_GPL(usb_driver_claim_interface); @@ -726,16 +802,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) return -ENODEV; } -#ifdef CONFIG_USB_DEVICEFS - /* If this is available, userspace programs can directly read - * all the device descriptors we don't tell them about. Or - * act as usermode drivers. - */ - if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d", - usb_dev->bus->busnum, usb_dev->devnum)) - return -ENOMEM; -#endif - /* per-device configurations are common */ if (add_uevent_var(env, "PRODUCT=%x/%x/%x", le16_to_cpu(usb_dev->descriptor.idVendor), @@ -788,15 +854,13 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, retval = driver_register(&new_udriver->drvwrap.driver); - if (!retval) { + if (!retval) pr_info("%s: registered new device driver %s\n", usbcore_name, new_udriver->name); - usbfs_update_special(); - } else { + else printk(KERN_ERR "%s: error %d registering device " " driver %s\n", usbcore_name, retval, new_udriver->name); - } return retval; } @@ -815,7 +879,6 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver) usbcore_name, udriver->name); driver_unregister(&udriver->drvwrap.driver); - usbfs_update_special(); } EXPORT_SYMBOL_GPL(usb_deregister_device_driver); @@ -856,8 +919,6 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, if (retval) goto out; - usbfs_update_special(); - retval = usb_create_newid_files(new_driver); if (retval) goto out_newid; @@ -897,8 +958,6 @@ void usb_deregister(struct usb_driver *driver) usb_remove_newid_files(driver); driver_unregister(&driver->drvwrap.driver); usb_free_dynids(driver); - - usbfs_update_special(); } EXPORT_SYMBOL_GPL(usb_deregister); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index d95760de9e8..e673b26e598 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -183,7 +183,7 @@ int usb_register_dev(struct usb_interface *intf, if (retval) return retval; - dev_dbg(&intf->dev, "looking for a minor, starting at %d", minor_base); + dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base); down_write(&minor_rwsem); for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { @@ -239,7 +239,7 @@ void usb_deregister_dev(struct usb_interface *intf, if (intf->minor == -1) return; - dbg ("removing %d minor", intf->minor); + dev_dbg(&intf->dev, "removing %d minor\n", intf->minor); down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 140d3e11f21..190b1ec7bdc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -138,7 +138,7 @@ static const u8 usb3_rh_dev_descriptor[18] = { 0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */ 0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */ - 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ 0x03, 0x00, /* __le16 idProduct; device 0x0003 */ KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ @@ -159,7 +159,7 @@ static const u8 usb2_rh_dev_descriptor [18] = { 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */ 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ - 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ 0x02, 0x00, /* __le16 idProduct; device 0x0002 */ KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ @@ -182,7 +182,7 @@ static const u8 usb11_rh_dev_descriptor [18] = { 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ - 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ 0x01, 0x00, /* __le16 idProduct; device 0x0001 */ KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ @@ -997,6 +997,15 @@ static int register_root_hub(struct usb_hcd *hcd) dev_name(&usb_dev->dev), retval); return (retval < 0) ? retval : -EMSGSIZE; } + if (usb_dev->speed == USB_SPEED_SUPER) { + retval = usb_get_bos_descriptor(usb_dev); + if (retval < 0) { + mutex_unlock(&usb_bus_list_lock); + dev_dbg(parent_dev, "can't read %s bos descriptor %d\n", + dev_name(&usb_dev->dev), retval); + return retval; + } + } retval = usb_new_device (usb_dev); if (retval) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ec6c97dadbe..04fb834c3fa 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -177,6 +177,228 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev) return usb_get_intfdata(hdev->actconfig->interface[0]); } +static int usb_device_supports_lpm(struct usb_device *udev) +{ + /* USB 2.1 (and greater) devices indicate LPM support through + * their USB 2.0 Extended Capabilities BOS descriptor. + */ + if (udev->speed == USB_SPEED_HIGH) { + if (udev->bos->ext_cap && + (USB_LPM_SUPPORT & + le32_to_cpu(udev->bos->ext_cap->bmAttributes))) + return 1; + return 0; + } + + /* All USB 3.0 must support LPM, but we need their max exit latency + * information from the SuperSpeed Extended Capabilities BOS descriptor. + */ + if (!udev->bos->ss_cap) { + dev_warn(&udev->dev, "No LPM exit latency info found. " + "Power management will be impacted.\n"); + return 0; + } + if (udev->parent->lpm_capable) + return 1; + + dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. " + "Power management will be impacted.\n"); + return 0; +} + +/* + * Set the Maximum Exit Latency (MEL) for the host to initiate a transition from + * either U1 or U2. + */ +static void usb_set_lpm_mel(struct usb_device *udev, + struct usb3_lpm_parameters *udev_lpm_params, + unsigned int udev_exit_latency, + struct usb_hub *hub, + struct usb3_lpm_parameters *hub_lpm_params, + unsigned int hub_exit_latency) +{ + unsigned int total_mel; + unsigned int device_mel; + unsigned int hub_mel; + + /* + * Calculate the time it takes to transition all links from the roothub + * to the parent hub into U0. The parent hub must then decode the + * packet (hub header decode latency) to figure out which port it was + * bound for. + * + * The Hub Header decode latency is expressed in 0.1us intervals (0x1 + * means 0.1us). Multiply that by 100 to get nanoseconds. + */ + total_mel = hub_lpm_params->mel + + (hub->descriptor->u.ss.bHubHdrDecLat * 100); + + /* + * How long will it take to transition the downstream hub's port into + * U0? The greater of either the hub exit latency or the device exit + * latency. + * + * The BOS U1/U2 exit latencies are expressed in 1us intervals. + * Multiply that by 1000 to get nanoseconds. + */ + device_mel = udev_exit_latency * 1000; + hub_mel = hub_exit_latency * 1000; + if (device_mel > hub_mel) + total_mel += device_mel; + else + total_mel += hub_mel; + + udev_lpm_params->mel = total_mel; +} + +/* + * Set the maximum Device to Host Exit Latency (PEL) for the device to initiate + * a transition from either U1 or U2. + */ +static void usb_set_lpm_pel(struct usb_device *udev, + struct usb3_lpm_parameters *udev_lpm_params, + unsigned int udev_exit_latency, + struct usb_hub *hub, + struct usb3_lpm_parameters *hub_lpm_params, + unsigned int hub_exit_latency, + unsigned int port_to_port_exit_latency) +{ + unsigned int first_link_pel; + unsigned int hub_pel; + + /* + * First, the device sends an LFPS to transition the link between the + * device and the parent hub into U0. The exit latency is the bigger of + * the device exit latency or the hub exit latency. + */ + if (udev_exit_latency > hub_exit_latency) + first_link_pel = udev_exit_latency * 1000; + else + first_link_pel = hub_exit_latency * 1000; + + /* + * When the hub starts to receive the LFPS, there is a slight delay for + * it to figure out that one of the ports is sending an LFPS. Then it + * will forward the LFPS to its upstream link. The exit latency is the + * delay, plus the PEL that we calculated for this hub. + */ + hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel; + + /* + * According to figure C-7 in the USB 3.0 spec, the PEL for this device + * is the greater of the two exit latencies. + */ + if (first_link_pel > hub_pel) + udev_lpm_params->pel = first_link_pel; + else + udev_lpm_params->pel = hub_pel; +} + +/* + * Set the System Exit Latency (SEL) to indicate the total worst-case time from + * when a device initiates a transition to U0, until when it will receive the + * first packet from the host controller. + * + * Section C.1.5.1 describes the four components to this: + * - t1: device PEL + * - t2: time for the ERDY to make it from the device to the host. + * - t3: a host-specific delay to process the ERDY. + * - t4: time for the packet to make it from the host to the device. + * + * t3 is specific to both the xHCI host and the platform the host is integrated + * into. The Intel HW folks have said it's negligible, FIXME if a different + * vendor says otherwise. + */ +static void usb_set_lpm_sel(struct usb_device *udev, + struct usb3_lpm_parameters *udev_lpm_params) +{ + struct usb_device *parent; + unsigned int num_hubs; + unsigned int total_sel; + + /* t1 = device PEL */ + total_sel = udev_lpm_params->pel; + /* How many external hubs are in between the device & the root port. */ + for (parent = udev->parent, num_hubs = 0; parent->parent; + parent = parent->parent) + num_hubs++; + /* t2 = 2.1us + 250ns * (num_hubs - 1) */ + if (num_hubs > 0) + total_sel += 2100 + 250 * (num_hubs - 1); + + /* t4 = 250ns * num_hubs */ + total_sel += 250 * num_hubs; + + udev_lpm_params->sel = total_sel; +} + +static void usb_set_lpm_parameters(struct usb_device *udev) +{ + struct usb_hub *hub; + unsigned int port_to_port_delay; + unsigned int udev_u1_del; + unsigned int udev_u2_del; + unsigned int hub_u1_del; + unsigned int hub_u2_del; + + if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER) + return; + + hub = hdev_to_hub(udev->parent); + /* It doesn't take time to transition the roothub into U0, since it + * doesn't have an upstream link. + */ + if (!hub) + return; + + udev_u1_del = udev->bos->ss_cap->bU1devExitLat; + udev_u2_del = udev->bos->ss_cap->bU2DevExitLat; + hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat; + hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat; + + usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del, + hub, &udev->parent->u1_params, hub_u1_del); + + usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del, + hub, &udev->parent->u2_params, hub_u2_del); + + /* + * Appendix C, section C.2.2.2, says that there is a slight delay from + * when the parent hub notices the downstream port is trying to + * transition to U0 to when the hub initiates a U0 transition on its + * upstream port. The section says the delays are tPort2PortU1EL and + * tPort2PortU2EL, but it doesn't define what they are. + * + * The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking + * about the same delays. Use the maximum delay calculations from those + * sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For + * U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I + * assume the device exit latencies they are talking about are the hub + * exit latencies. + * + * What do we do if the U2 exit latency is less than the U1 exit + * latency? It's possible, although not likely... + */ + port_to_port_delay = 1; + + usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del, + hub, &udev->parent->u1_params, hub_u1_del, + port_to_port_delay); + + if (hub_u2_del > hub_u1_del) + port_to_port_delay = 1 + hub_u2_del - hub_u1_del; + else + port_to_port_delay = 1 + hub_u1_del; + + usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del, + hub, &udev->parent->u2_params, hub_u2_del, + port_to_port_delay); + + /* Now that we've got PEL, calculate SEL. */ + usb_set_lpm_sel(udev, &udev->u1_params); + usb_set_lpm_sel(udev, &udev->u2_params); +} + /* USB 2.0 spec Section 11.24.4.5 */ static int get_hub_descriptor(struct usb_device *hdev, void *data) { @@ -2480,6 +2702,12 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_unlocked_disable_lpm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", + __func__); + return -ENOMEM; + } + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2499,6 +2727,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) NULL, 0, USB_CTRL_SET_TIMEOUT); + /* Try to enable USB2 hardware LPM again */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); + + /* Try to enable USB3 LPM again */ + usb_unlocked_enable_lpm(udev); + /* System sleep transitions should never fail */ if (!PMSG_IS_AUTO(msg)) status = 0; @@ -2696,6 +2931,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) /* Try to enable USB2 hardware LPM */ if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); + + /* Try to enable USB3 LPM */ + usb_unlocked_enable_lpm(udev); } return status; @@ -2824,11 +3062,429 @@ void usb_root_hub_lost_power(struct usb_device *rhdev) } EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); +static const char * const usb3_lpm_names[] = { + "U0", + "U1", + "U2", + "U3", +}; + +/* + * Send a Set SEL control transfer to the device, prior to enabling + * device-initiated U1 or U2. This lets the device know the exit latencies from + * the time the device initiates a U1 or U2 exit, to the time it will receive a + * packet from the host. + * + * This function will fail if the SEL or PEL values for udev are greater than + * the maximum allowed values for the link state to be enabled. + */ +static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) +{ + struct usb_set_sel_req *sel_values; + unsigned long long u1_sel; + unsigned long long u1_pel; + unsigned long long u2_sel; + unsigned long long u2_pel; + int ret; + + /* Convert SEL and PEL stored in ns to us */ + u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000); + u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000); + u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000); + u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000); + + /* + * Make sure that the calculated SEL and PEL values for the link + * state we're enabling aren't bigger than the max SEL/PEL + * value that will fit in the SET SEL control transfer. + * Otherwise the device would get an incorrect idea of the exit + * latency for the link state, and could start a device-initiated + * U1/U2 when the exit latencies are too high. + */ + if ((state == USB3_LPM_U1 && + (u1_sel > USB3_LPM_MAX_U1_SEL_PEL || + u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) || + (state == USB3_LPM_U2 && + (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || + u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { + dev_dbg(&udev->dev, "Device-initiated %s disabled due " + "to long SEL %llu ms or PEL %llu ms\n", + usb3_lpm_names[state], u1_sel, u1_pel); + return -EINVAL; + } + + /* + * If we're enabling device-initiated LPM for one link state, + * but the other link state has a too high SEL or PEL value, + * just set those values to the max in the Set SEL request. + */ + if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL) + u1_sel = USB3_LPM_MAX_U1_SEL_PEL; + + if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL) + u1_pel = USB3_LPM_MAX_U1_SEL_PEL; + + if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL) + u2_sel = USB3_LPM_MAX_U2_SEL_PEL; + + if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL) + u2_pel = USB3_LPM_MAX_U2_SEL_PEL; + + /* + * usb_enable_lpm() can be called as part of a failed device reset, + * which may be initiated by an error path of a mass storage driver. + * Therefore, use GFP_NOIO. + */ + sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO); + if (!sel_values) + return -ENOMEM; + + sel_values->u1_sel = u1_sel; + sel_values->u1_pel = u1_pel; + sel_values->u2_sel = cpu_to_le16(u2_sel); + sel_values->u2_pel = cpu_to_le16(u2_pel); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_SEL, + USB_RECIP_DEVICE, + 0, 0, + sel_values, sizeof *(sel_values), + USB_CTRL_SET_TIMEOUT); + kfree(sel_values); + return ret; +} + +/* + * Enable or disable device-initiated U1 or U2 transitions. + */ +static int usb_set_device_initiated_lpm(struct usb_device *udev, + enum usb3_link_state state, bool enable) +{ + int ret; + int feature; + + switch (state) { + case USB3_LPM_U1: + feature = USB_DEVICE_U1_ENABLE; + break; + case USB3_LPM_U2: + feature = USB_DEVICE_U2_ENABLE; + break; + default: + dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n", + __func__, enable ? "enable" : "disable"); + return -EINVAL; + } + + if (udev->state != USB_STATE_CONFIGURED) { + dev_dbg(&udev->dev, "%s: Can't %s %s state " + "for unconfigured device.\n", + __func__, enable ? "enable" : "disable", + usb3_lpm_names[state]); + retur |