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/sysfs.c | 6 | ||||
-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 |
15 files changed, 986 insertions, 909 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]); + return 0; + } + + if (enable) { + /* + * First, let the device know about the exit latencies + * associated with the link state we're about to enable. + */ + ret = usb_req_set_sel(udev, state); + if (ret < 0) { + dev_warn(&udev->dev, "Set SEL for device-initiated " + "%s failed.\n", usb3_lpm_names[state]); + return -EBUSY; + } + /* + * Now send the control transfer to enable device-initiated LPM + * for either U1 or U2. + */ + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, + USB_RECIP_DEVICE, + feature, + 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_DEVICE, + feature, + 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + } + if (ret < 0) { + dev_warn(&udev->dev, "%s of device-initiated %s failed.\n", + enable ? "Enable" : "Disable", + usb3_lpm_names[state]); + return -EBUSY; + } + return 0; +} + +static int usb_set_lpm_timeout(struct usb_device *udev, + enum usb3_link_state state, int timeout) +{ + int ret; + int feature; + + switch (state) { + case USB3_LPM_U1: + feature = USB_PORT_FEAT_U1_TIMEOUT; + break; + case USB3_LPM_U2: + feature = USB_PORT_FEAT_U2_TIMEOUT; + break; + default: + dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n", + __func__); + return -EINVAL; + } + + if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT && + timeout != USB3_LPM_DEVICE_INITIATED) { + dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, " + "which is a reserved value.\n", + usb3_lpm_names[state], timeout); + return -EINVAL; + } + + ret = set_port_feature(udev->parent, + USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum, + feature); + if (ret < 0) { + dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x," + "error code %i\n", usb3_lpm_names[state], + timeout, ret); + return -EBUSY; + } + if (state == USB3_LPM_U1) + udev->u1_params.timeout = timeout; + else + udev->u2_params.timeout = timeout; + return 0; +} + +/* + * Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated + * U1/U2 entry. + * + * We will attempt to enable U1 or U2, but there are no guarantees that the + * control transfers to set the hub timeout or enable device-initiated U1/U2 + * will be successful. + * + * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI + * driver know about it. If that call fails, it should be harmless, and just + * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency. + */ +static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, + enum usb3_link_state state) +{ + int timeout; + + /* We allow the host controller to set the U1/U2 timeout internally + * first, so that it can change its schedule to account for the + * additional latency to send data to a device in a lower power + * link state. + */ + timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state); + + /* xHCI host controller doesn't want to enable this LPM state. */ + if (timeout == 0) + return; + + if (timeout < 0) { + dev_warn(&udev->dev, "Could not enable %s link state, " + "xHCI error %i.\n", usb3_lpm_names[state], + timeout); + return; + } + + if (usb_set_lpm_timeout(udev, state, timeout)) + /* If we can't set the parent hub U1/U2 timeout, + * device-initiated LPM won't be allowed either, so let the xHCI + * host know that this link state won't be enabled. + */ + hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state); + + /* Only a configured device will accept the Set Feature U1/U2_ENABLE */ + else if (udev->actconfig) + usb_set_device_initiated_lpm(udev, state, true); + +} + +/* + * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated + * U1/U2 entry. + * + * If this function returns -EBUSY, the parent hub will still allow U1/U2 entry. + * If zero is returned, the parent will not allow the link to go into U1/U2. + * + * If zero is returned, device-initiated U1/U2 entry may still be enabled, but + * it won't have an effect on the bus link state because the parent hub will + * still disallow device-initiated U1/U2 entry. + * + * If zero is returned, the xHCI host controller may still think U1/U2 entry is + * possible. The result will be slightly more bus bandwidth will be taken up + * (to account for U1/U2 exit latency), but it should be harmless. + */ +static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev, + enum usb3_link_state state) +{ + int feature; + + switch (state) { + case USB3_LPM_U1: + feature = USB_PORT_FEAT_U1_TIMEOUT; + break; + case USB3_LPM_U2: + feature = USB_PORT_FEAT_U2_TIMEOUT; + break; + default: + dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n", + __func__); + return -EINVAL; + } + + if (usb_set_lpm_timeout(udev, state, 0)) + return -EBUSY; + + usb_set_device_initiated_lpm(udev, state, false); + + if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state)) + dev_warn(&udev->dev, "Could not disable xHCI %s timeout, " + "bus schedule bandwidth may be impacted.\n", + usb3_lpm_names[state]); + return 0; +} + +/* + * Disable hub-initiated and device-initiated U1 and U2 entry. + * Caller must own the bandwidth_mutex. + * + * This will call usb_enable_lpm() on failure, which will decrement + * lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero. + */ +int usb_disable_lpm(struct usb_device *udev) +{ + struct usb_hcd *hcd; + + if (!udev || !udev->parent || + udev->speed != USB_SPEED_SUPER || + !udev->lpm_capable) + return 0; + + hcd = bus_to_hcd(udev->bus); + if (!hcd || !hcd->driver->disable_usb3_lpm_timeout) + return 0; + + udev->lpm_disable_count++; + if ((udev->u1_params.timeout == 0 && udev->u1_params.timeout == 0)) + return 0; + + /* If LPM is enabled, attempt to disable it. */ + if (usb_disable_link_state(hcd, udev, USB3_LPM_U1)) + goto enable_lpm; + if (usb_disable_link_state(hcd, udev, USB3_LPM_U2)) + goto enable_lpm; + + return 0; + +enable_lpm: + usb_enable_lpm(udev); + return -EBUSY; +} +EXPORT_SYMBOL_GPL(usb_disable_lpm); + +/* Grab the bandwidth_mutex before calling usb_disable_lpm() */ +int usb_unlocked_disable_lpm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret; + + if (!hcd) + return -EINVAL; + + mutex_lock(hcd->bandwidth_mutex); + ret = usb_disable_lpm(udev); + mutex_unlock(hcd->bandwidth_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); + +/* + * Attempt to enable device-initiated and hub-initiated U1 and U2 entry. The + * xHCI host policy may prevent U1 or U2 from being enabled. + * + * Other callers may have disabled link PM, so U1 and U2 entry will be disabled + * until the lpm_disable_count drops to zero. Caller must own the + * bandwidth_mutex. + */ +void usb_enable_lpm(struct usb_device *udev) +{ + struct usb_hcd *hcd; + + if (!udev || !udev->parent || + udev->speed != USB_SPEED_SUPER || + !udev->lpm_capable) + return; + + udev->lpm_disable_count--; + hcd = bus_to_hcd(udev->bus); + /* Double check that we can both enable and disable LPM. + * Device must be configured to accept set feature U1/U2 timeout. + */ + if (!hcd || !hcd->driver->enable_usb3_lpm_timeout || + !hcd->driver->disable_usb3_lpm_timeout) + return; + + if (udev->lpm_disable_count > 0) + return; + + usb_enable_link_state(hcd, udev, USB3_LPM_U1); + usb_enable_link_state(hcd, udev, USB3_LPM_U2); +} +EXPORT_SYMBOL_GPL(usb_enable_lpm); + +/* Grab the bandwidth_mutex before calling usb_enable_lpm() */ +void usb_unlocked_enable_lpm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!hcd) + return; + + mutex_lock(hcd->bandwidth_mutex); + usb_enable_lpm(udev); + mutex_unlock(hcd->bandwidth_mutex); +} +EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + + #else /* CONFIG_PM */ #define hub_suspend NULL #define hub_resume NULL #define hub_reset_resume NULL + +int usb_disable_lpm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_disable_lpm); + +void usb_enable_lpm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_enable_lpm); + +int usb_unlocked_disable_lpm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); + +void usb_unlocked_enable_lpm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); #endif @@ -3208,9 +3864,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { retval = usb_get_bos_descriptor(udev); if (!retval) { - if (udev->bos->ext_cap && (USB_LPM_SUPPORT & - le32_to_cpu(udev->bos->ext_cap->bmAttributes))) - udev->lpm_capable = 1; + udev->lpm_capable = usb_device_supports_lpm(udev); + usb_set_lpm_parameters(udev); } } @@ -4042,11 +4697,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev) goto done; mutex_lock(hcd->bandwidth_mutex); + /* Disable LPM while we reset the device and reinstall the alt settings. + * Device-initiated LPM settings, and system exit latency settings are + * cleared when the device is reset, so we have to set them up again. + */ + ret = usb_disable_lpm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); + mutex_unlock(hcd->bandwidth_mutex); + goto done; + } ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); if (ret < 0) { dev_warn(&udev->dev, "Busted HC? Not enough HCD resources for " "old configuration.\n"); + usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4058,6 +4724,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) dev_err(&udev->dev, "can't restore configuration #%d (error=%d)\n", udev->actconfig->desc.bConfigurationValue, ret); + usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4096,10 +4763,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev) desc->bInterfaceNumber, desc->bAlternateSetting, ret); + usb_unlocked_enable_lpm(udev); goto re_enumerate; } } + /* Now that the alt settings are re-installed, enable LPM. */ + usb_unlocked_enable_lpm(udev); done: return 0; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c deleted file mode 100644 index d2b9af59cba..00000000000 --- a/drivers/usb/core/inode.c +++ /dev/null @@ -1,748 +0,0 @@ -/*****************************************************************************/ - -/* - * inode.c -- Inode/Dentry functions for the USB device file system. - * - * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * 0.1 04.01.2000 Created - * 0.2 10.12.2001 converted to use the vfs layer better - */ - -/*****************************************************************************/ - -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/pagemap.h> -#include <linux/init.h> -#include <linux/proc_fs.h> -#include <linux/usb.h> -#include <linux/namei.h> -#include <linux/usbdevice_fs.h> -#include <linux/parser.h> -#include <linux/notifier.h> -#include <linux/seq_file.h> -#include <linux/usb/hcd.h> -#include <asm/byteorder.h> -#include "usb.h" - -#define USBFS_DEFAULT_DEVMODE (S_IWUSR | S_IRUGO) -#define USBFS_DEFAULT_BUSMODE (S_IXUGO | S_IRUGO) -#define USBFS_DEFAULT_LISTMODE S_IRUGO - -static const struct file_operations default_file_operations; -static struct vfsmount *usbfs_mount; -static int usbfs_mount_count; /* = 0 */ - -static struct dentry *devices_usbfs_dentry; -static int num_buses; /* = 0 */ - -static uid_t devuid; /* = 0 */ -static uid_t busuid; /* = 0 */ -static uid_t listuid; /* = 0 */ -static gid_t devgid; /* = 0 */ -static gid_t busgid; /* = 0 */ -static gid_t listgid; /* = 0 */ -static umode_t devmode = USBFS_DEFAULT_DEVMODE; -static umode_t busmode = USBFS_DEFAULT_BUSMODE; -static umode_t listmode = USBFS_DEFAULT_LISTMODE; - -static int usbfs_show_options(struct seq_file *seq, struct dentry *root) -{ - if (devuid != 0) - seq_printf(seq, ",devuid=%u", devuid); - if (devgid != 0) - seq_printf(seq, ",devgid=%u", devgid); - if (devmode != USBFS_DEFAULT_DEVMODE) - seq_printf(seq, ",devmode=%o", devmode); - if (busuid != 0) - seq_printf(seq, ",busuid=%u", busuid); - if (busgid != 0) - seq_printf(seq, ",busgid=%u", busgid); - if (busmode != USBFS_DEFAULT_BUSMODE) - seq_printf(seq, ",busmode=%o", busmode); - if (listuid != 0) - seq_printf(seq, ",listuid=%u", listuid); - if (listgid != 0) - seq_printf(seq, ",listgid=%u", listgid); - if (listmode != USBFS_DEFAULT_LISTMODE) - seq_printf(seq, ",listmode=%o", listmode); - - return 0; -} - -enum { - Opt_devuid, Opt_devgid, Opt_devmode, - Opt_busuid, Opt_busgid, Opt_busmode, - Opt_listuid, Opt_listgid, Opt_listmode, - Opt_err, -}; - -static const match_table_t tokens = { - {Opt_devuid, "devuid=%u"}, - {Opt_devgid, "devgid=%u"}, - {Opt_devmode, "devmode=%o"}, - {Opt_busuid, "busuid=%u"}, - {Opt_busgid, "busgid=%u"}, - {Opt_busmode, "busmode=%o"}, - {Opt_listuid, "listuid=%u"}, - {Opt_listgid, "listgid=%u"}, - {Opt_listmode, "listmode=%o"}, - {Opt_err, NULL} -}; - -static int parse_options(struct super_block *s, char *data) -{ - char *p; - int option; - - /* (re)set to defaults. */ - devuid = 0; - busuid = 0; - listuid = 0; - devgid = 0; - busgid = 0; - listgid = 0; - devmode = USBFS_DEFAULT_DEVMODE; - busmode = USBFS_DEFAULT_BUSMODE; - listmode = USBFS_DEFAULT_LISTMODE; - - while ((p = strsep(&data, ",")) != NULL) { - substring_t args[MAX_OPT_ARGS]; - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_devuid: - if (match_int(&args[0], &option)) - return -EINVAL; - devuid = option; - break; - case Opt_devgid: - if (match_int(&args[0], &option)) - return -EINVAL; - devgid = option; - break; - case Opt_devmode: - if (match_octal(&args[0], &option)) - return -EINVAL; - devmode = option & S_IRWXUGO; - break; - case Opt_busuid: - if (match_int(&args[0], &option)) - return -EINVAL; - busuid = option; - break; - case Opt_busgid: - if (match_int(&args[0], &option)) - return -EINVAL; - busgid = option; - break; - case Opt_busmode: - if (match_octal(&args[0], &option)) - return -EINVAL; - busmode = option & S_IRWXUGO; - break; - case Opt_listuid: - if (match_int(&args[0], &option)) - return -EINVAL; - listuid = option; - break; - case Opt_listgid: - if (match_int(&args[0], &option)) - return -EINVAL; - listgid = option; - break; - case Opt_listmode: - if (match_octal(&args[0], &option)) - return -EINVAL; - listmode = option & S_IRWXUGO; - break; - default: - printk(KERN_ERR "usbfs: unrecognised mount option " - "\"%s\" or missing value\n", p); - return -EINVAL; - } - } - - return 0; -} - -static void update_special(struct dentry *special) -{ - special->d_inode->i_uid = listuid; - special->d_inode->i_gid = listgid; - special->d_inode->i_mode = S_IFREG | listmode; -} - -static void update_dev(struct dentry *dev) -{ - dev->d_inode->i_uid = devuid; - dev->d_inode->i_gid = devgid; - dev->d_inode->i_mode = S_IFREG | devmode; -} - -static void update_bus(struct dentry *bus) -{ - struct dentry *dev = NULL; - - bus->d_inode->i_uid = busuid; - bus->d_inode->i_gid = busgid; - bus->d_inode->i_mode = S_IFDIR | busmode; - - mutex_lock(&bus->d_inode->i_mutex); - - list_for_each_entry(dev, &bus->d_subdirs, d_u.d_child) - if (dev->d_inode) - update_dev(dev); - - mutex_unlock(&bus->d_inode->i_mutex); -} - -static void update_sb(struct super_block *sb) -{ - struct dentry *root = sb->s_root; - struct dentry *bus = NULL; - - if (!root) - return; - - mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_PARENT); - - list_for_each_entry(bus, &root->d_subdirs, d_u.d_child) { - if (bus->d_inode) { - switch (S_IFMT & bus->d_inode->i_mode) { - case S_IFDIR: - update_bus(bus); - break; - case S_IFREG: - update_special(bus); - break; - default: - printk(KERN_WARNING "usbfs: Unknown node %s " - "mode %x found on remount!\n", - bus->d_name.name, bus->d_inode->i_mode); - break; - } - } - } - - mutex_unlock(&root->d_inode->i_mutex); -} - -static int remount(struct super_block *sb, int *flags, char *data) -{ - /* If this is not a real mount, - * i.e. it's a simple_pin_fs from create_special_files, - * then ignore it. - */ - if (*flags & MS_KERNMOUNT) - return 0; - - if (parse_options(sb, data)) { - printk(KERN_WARNING "usbfs: mount parameter error.\n"); - return -EINVAL; - } - - if (usbfs_mount) - update_sb(usbfs_mount->mnt_sb); - - return 0; -} - -static struct inode *usbfs_get_inode (struct super_block *sb, umode_t mode, dev_t dev) -{ - struct inode *inode = new_inode(sb); - - if (inode) { - inode->i_ino = get_next_ino(); - inode_init_owner(inode, NULL, mode); - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - switch (mode & S_IFMT) { - default: - init_special_inode(inode, mode, dev); - break; - case S_IFREG: - inode->i_fop = &default_file_operations; - break; - case S_IFDIR: - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - break; - } - } - return inode; -} - -/* SMP-safe */ -static int usbfs_mknod (struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev); - int error = -EPERM; - - if (dentry->d_inode) - return -EEXIST; - - if (inode) { - d_instantiate(dentry, inode); - dget(dentry); - error = 0; - } - return error; -} - -static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int res; - - mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; - res = usbfs_mknod (dir, dentry, mode, 0); - if (!res) - inc_nlink(dir); - return res; -} - -static int usbfs_create (struct inode *dir, struct dentry *dentry, umode_t mode) -{ - mode = (mode & S_IALLUGO) | S_IFREG; - return usbfs_mknod (dir, dentry, mode, 0); -} - -static inline int usbfs_positive (struct dentry *dentry) -{ - return dentry->d_inode && !d_unhashed(dentry); -} - -static int usbfs_empty (struct dentry *dentry) -{ - struct list_head *list; - - spin_lock(&dentry->d_lock); - list_for_each(list, &dentry->d_subdirs) { - struct dentry *de = list_entry(list, struct dentry, d_u.d_child); - - spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED); - if (usbfs_positive(de)) { - spin_unlock(&de->d_lock); - spin_unlock(&dentry->d_lock); - return 0; - } - spin_unlock(&de->d_lock); - } - spin_unlock(&dentry->d_lock); - return 1; -} - -static int usbfs_unlink (struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - mutex_lock(&inode->i_mutex); - drop_nlink(dentry->d_inode); - dput(dentry); - mutex_unlock(&inode->i_mutex); - d_delete(dentry); - return 0; -} - -static int usbfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int error = -ENOTEMPTY; - struct inode * inode = dentry->d_inode; - - mutex_lock(&inode->i_mutex); - dentry_unhash(dentry); - if (usbfs_empty(dentry)) { - dont_mount(dentry); - drop_nlink(dentry->d_inode); - drop_nlink(dentry->d_inode); - dput(dentry); - inode->i_flags |= S_DEAD; - drop_nlink(dir); - error = 0; - } - mutex_unlock(&inode->i_mutex); - if (!error) - d_delete(dentry); - return error; -} - - -/* default file operations */ -static ssize_t default_read_file (struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - return 0; -} - -static ssize_t default_write_file (struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return count; -} - -static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) -{ - loff_t retval = -EINVAL; - - mutex_lock(&file->f_path.dentry->d_inode->i_mutex); - switch(orig) { - case 0: - if (offset > 0) { - file->f_pos = offset; - retval = file->f_pos; - } - break; - case 1: - if ((offset + file->f_pos) > 0) { - file->f_pos += offset; - retval = file->f_pos; - } - break; - default: - break; - } - mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); - return retval; -} - -static const struct file_operations default_file_operations = { - .read = default_read_file, - .write = default_write_file, - .open = simple_open, - .llseek = default_file_lseek, -}; - -static const struct super_operations usbfs_ops = { - .statfs = simple_statfs, - .drop_inode = generic_delete_inode, - .remount_fs = remount, - .show_options = usbfs_show_options, -}; - -static int usbfs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct inode *inode; - - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_magic = USBDEVICE_SUPER_MAGIC; - sb->s_op = &usbfs_ops; - sb->s_time_gran = 1; - inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0); - sb->s_root = d_make_root(inode); - if (!sb->s_root) { - dbg("%s: could not get root dentry!",__func__); - return -ENOMEM; - } - return 0; -} - -/* - * fs_create_by_name - create a file, given a name - * @name: name of file - * @mode: type of file - * @parent: dentry of directory to create it in - * @dentry: resulting dentry of file - * - * This function handles both regular files and directories. - */ -static int fs_create_by_name (const char *name, umode_t mode, - struct dentry *parent, struct dentry **dentry) -{ - int error = 0; - - /* If the parent is not specified, we create it in the root. - * We need the root dentry to do this, which is in the super - * block. A pointer to that is in the struct vfsmount that we - * have around. - */ - if (!parent ) { - if (usbfs_mount) - parent = usbfs_mount->mnt_root; - } - - if (!parent) { - dbg("Ah! can not find a parent!"); - return -EFAULT; - } - - *dentry = NULL; - mutex_lock(&parent->d_inode->i_mutex); - *dentry = lookup_one_len(name, parent, strlen(name)); - if (!IS_ERR(*dentry)) { - if (S_ISDIR(mode)) - error = usbfs_mkdir (parent->d_inode, *dentry, mode); - else - error = usbfs_create (parent->d_inode, *dentry, mode); - } else - error = PTR_ERR(*dentry); - mutex_unlock(&parent->d_inode->i_mutex); - - return error; -} - -static struct dentry *fs_create_file (const char *name, umode_t mode, - struct dentry *parent, void *data, - const struct file_operations *fops, - uid_t uid, gid_t gid) -{ - struct dentry *dentry; - int error; - - dbg("creating file '%s'",name); - - error = fs_create_by_name (name, mode, parent, &dentry); - if (error) { - dentry = NULL; - } else { - if (dentry->d_inode) { - if (data) - dentry->d_inode->i_private = data; - if (fops) - dentry->d_inode->i_fop = fops; - dentry->d_inode->i_uid = uid; - dentry->d_inode->i_gid = gid; - } - } - - return dentry; -} - -static void fs_remove_file (struct dentry *dentry) -{ - struct dentry *parent = dentry->d_parent; - - if (!parent || !parent->d_inode) - return; - - mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT); - if (usbfs_positive(dentry)) { - if (dentry->d_inode) { - if (S_ISDIR(dentry->d_inode->i_mode)) - usbfs_rmdir(parent->d_inode, dentry); - else - usbfs_unlink(parent->d_inode, dentry); - dput(dentry); - } - } - mutex_unlock(&parent->d_inode->i_mutex); -} - -/* --------------------------------------------------------------------- */ - -static struct dentry *usb_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) -{ - return mount_single(fs_type, flags, data, usbfs_fill_super); -} - -static struct file_system_type usb_fs_type = { - .owner = THIS_MODULE, - .name = "usbfs", - .mount = usb_mount, - .kill_sb = kill_litter_super, -}; - -/* --------------------------------------------------------------------- */ - -static int create_special_files (void) -{ - struct dentry *parent; - int retval; - - /* create the devices special file */ - retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count); - if (retval) { - printk(KERN_ERR "Unable to get usbfs mount\n"); - goto exit; - } - - parent = usbfs_mount->mnt_root; - devices_usbfs_dentry = fs_create_file ("devices", - listmode | S_IFREG, parent, - NULL, &usbfs_devices_fops, - listuid, listgid); - if (devices_usbfs_dentry == NULL) { - printk(KERN_ERR "Unable to create devices usbfs file\n"); - retval = -ENODEV; - goto error_clean_mounts; - } - - goto exit; - -error_clean_mounts: - simple_release_fs(&usbfs_mount, &usbfs_mount_count); -exit: - return retval; -} - -static void remove_special_files (void) -{ - if (devices_usbfs_dentry) - fs_remove_file (devices_usbfs_dentry); - devices_usbfs_dentry = NULL; - simple_release_fs(&usbfs_mount, &usbfs_mount_count); -} - -void usbfs_update_special (void) -{ - struct inode *inode; - - if (devices_usbfs_dentry) { - inode = devices_usbfs_dentry->d_inode; - if (inode) - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - } -} - -static void usbfs_add_bus(struct usb_bus *bus) -{ - struct dentry *parent; - char name[8]; - int retval; - - /* create the special files if this is the first bus added */ - if (num_buses == 0) { - retval = create_special_files(); - if (retval) - return; - } - ++num_buses; - - sprintf (name, "%03d", bus->busnum); - - parent = usbfs_mount->mnt_root; - bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, - bus, NULL, busuid, busgid); - if (bus->usbfs_dentry == NULL) { - printk(KERN_ERR "Error creating usbfs bus entry\n"); - return; - } -} - -static void usbfs_remove_bus(struct usb_bus *bus) -{ - if (bus->usbfs_dentry) { - fs_remove_file (bus->usbfs_dentry); - bus->usbfs_dentry = NULL; - } - - --num_buses; - if (num_buses <= 0) { - remove_special_files(); - num_buses = 0; - } -} - -static void usbfs_add_device(struct usb_device *dev) -{ - char name[8]; - int i; - int i_size; - - sprintf (name, "%03d", dev->devnum); - dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, - dev->bus->usbfs_dentry, dev, - &usbdev_file_operations, - devuid, devgid); - if (dev->usbfs_dentry == NULL) { - printk(KERN_ERR "Error creating usbfs device entry\n"); - return; - } - - /* Set the size of the device's file to be - * equal to the size of the device descriptors. */ - i_size = sizeof (struct usb_device_descriptor); - for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) { - struct usb_config_descriptor *config = - (struct usb_config_descriptor *)dev->rawdescriptors[i]; - i_size += le16_to_cpu(config->wTotalLength); - } - if (dev->usbfs_dentry->d_inode) - dev->usbfs_dentry->d_inode->i_size = i_size; -} - -static void usbfs_remove_device(struct usb_device *dev) -{ - if (dev->usbfs_dentry) { - fs_remove_file (dev->usbfs_dentry); - dev->usbfs_dentry = NULL; - } -} - -static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev) -{ - switch (action) { - case USB_DEVICE_ADD: - usbfs_add_device(dev); - break; - case USB_DEVICE_REMOVE: - usbfs_remove_device(dev); - break; - case USB_BUS_ADD: - usbfs_add_bus(dev); - break; - case USB_BUS_REMOVE: - usbfs_remove_bus(dev); - } - - usbfs_update_special(); - usbfs_conn_disc_event(); - return NOTIFY_OK; -} - -static struct notifier_block usbfs_nb = { - .notifier_call = usbfs_notify, -}; - -/* --------------------------------------------------------------------- */ - -static struct proc_dir_entry *usbdir = NULL; - -int __init usbfs_init(void) -{ - int retval; - - retval = register_filesystem(&usb_fs_type); - if (retval) - return retval; - - usb_register_notify(&usbfs_nb); - - /* create mount point for usbfs */ - usbdir = proc_mkdir("bus/usb", NULL); - - return 0; -} - -void usbfs_cleanup(void) -{ - usb_unregister_notify(&usbfs_nb); - unregister_filesystem(&usb_fs_type); - if (usbdir) - remove_proc_entry("bus/usb", NULL); -} - diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index ca717da3be9..b548cf1dbc6 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1308,10 +1308,19 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * Remove the current alt setting and add the new alt setting. */ mutex_lock(hcd->bandwidth_mutex); + /* Disable LPM, and re-enable it once the new alt setting is installed, + * so that the xHCI driver can recalculate the U1/U2 timeouts. + */ + if (usb_disable_lpm(dev)) { + dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__); + mutex_unlock(hcd->bandwidth_mutex); + return -ENOMEM; + } ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", alternate); + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } @@ -1334,6 +1343,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } else if (ret < 0) { /* Re-instate the old alt setting */ usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } @@ -1354,6 +1364,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) iface->cur_altsetting = alt; + /* Now that the interface is installed, re-enable LPM. */ + usb_unlocked_enable_lpm(dev); + /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action * by manually clearing the HALT feature for each endpoint in the @@ -1437,6 +1450,14 @@ int usb_reset_configuration(struct usb_device *dev) config = dev->actconfig; retval = 0; mutex_lock(hcd->bandwidth_mutex); + /* Disable LPM, and re-enable it once the configuration is reset, so + * that the xHCI driver can recalculate the U1/U2 timeouts. + */ + if (usb_disable_lpm(dev)) { + dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + mutex_unlock(hcd->bandwidth_mutex); + return -ENOMEM; + } /* Make sure we have enough bandwidth for each alternate setting 0 */ for (i = 0; i < config->desc.bNumInterfaces; i++) { struct usb_interface *intf = config->interface[i]; @@ -1465,6 +1486,7 @@ reset_old_alts: usb_hcd_alloc_bandwidth(dev, NULL, alt, intf->cur_altsetting); } + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return retval; } @@ -1502,6 +1524,8 @@ reset_old_alts: create_intf_ep_devs(intf); } } + /* Now that the interfaces are installed, re-enable LPM. */ + usb_unlocked_enable_lpm(dev); return 0; } EXPORT_SYMBOL_GPL(usb_reset_configuration); @@ -1763,8 +1787,18 @@ free_interfaces: * this call fails, the device state is unchanged. */ mutex_lock(hcd->bandwidth_mutex); + /* Disable LPM, and re-enable it once the new configuration is + * installed, so that the xHCI driver can recalculate the U1/U2 + * timeouts. + */ + if (usb_disable_lpm(dev)) { + dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + mutex_unlock(hcd->bandwidth_mutex); + return -ENOMEM; + } ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; @@ -1784,6 +1818,7 @@ free_interfaces: if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; @@ -1838,6 +1873,9 @@ free_interfaces: !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) cp->string = usb_cache_string(dev, cp->desc.iConfiguration); + /* Now that the interfaces are installed, re-enable LPM. */ + usb_unlocked_enable_lpm(dev); + /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() * routines may install different altsettings and may diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 4c65eb6a867..32d3adc315f 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -123,6 +123,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Guillemot Webcam Hercules Dualpix Exchange*/ { USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Midiman M-Audio Keystation 88es */ + { USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME }, + /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 566d9f94f73..9a56e3adf47 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -73,7 +73,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr, return (value < 0) ? value : count; } -static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR, +static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR, show_bConfigurationValue, set_bConfigurationValue); /* String fields */ @@ -595,7 +595,7 @@ static ssize_t usb_dev_authorized_store(struct device *dev, return result < 0? result : size; } -static DEVICE_ATTR(authorized, 0644, +static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, 0644, usb_dev_authorized_show, usb_dev_authorized_store); /* "Safely remove a device" */ @@ -618,7 +618,7 @@ static ssize_t usb_remove_store(struct device *dev, usb_unlock_device(udev); return rc; } -static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store); +static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, usb_remove_store); static struct attribute *dev_attrs[] = { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index cd9b3a2cd8a..9d912bfdcff 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -681,6 +681,27 @@ void usb_unpoison_urb(struct urb *urb) EXPORT_SYMBOL_GPL(usb_unpoison_urb); /** + * usb_block_urb - reliably prevent further use of an URB + * @urb: pointer to URB to be blocked, may be NULL + * + * After the routine has run, attempts to resubmit the URB will fail + * with error -EPERM. Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + */ +void usb_block_urb(struct urb *urb) +{ + if (!urb) + return; + + atomic_inc(&urb->reject); +} +EXPORT_SYMBOL_GPL(usb_block_urb); + +/** * usb_kill_anchored_urbs - cancel transfer requests en masse * @anchor: anchor the requests are bound to * diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c new file mode 100644 index 00000000000..8947b203d5a --- /dev/null +++ b/drivers/usb/core/usb-acpi.c @@ -0,0 +1,117 @@ +/* + * USB-ACPI glue code + * + * Copyright 2012 Red Hat <mjg@redhat.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + */ +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <acpi/acpi_bus.h> + +#include "usb.h" + +static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *upc; + int ret = 0; + + status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); + + if (ACPI_FAILURE(status)) + return -ENODEV; + + upc = buffer.pointer; + + if (!upc || (upc->type != ACPI_TYPE_PACKAGE) + || upc->package.count != 4) { + ret = -EINVAL; + goto out; + } + + if (upc->package.elements[0].integer.value) + udev->removable = USB_DEVICE_REMOVABLE; + else + udev->removable = USB_DEVICE_FIXED; + +out: + kfree(upc); + return ret; +} + +static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle) +{ + acpi_status status; + struct acpi_pld pld; + + status = acpi_get_physical_device_location(handle, &pld); + + if (ACPI_FAILURE(status)) + return -ENODEV; + + if (pld.user_visible) + udev->removable = USB_DEVICE_REMOVABLE; + else + udev->removable = USB_DEVICE_FIXED; + + return 0; +} + +static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) +{ + struct usb_device *udev; + struct device *parent; + acpi_handle *parent_handle; + + if (!is_usb_device(dev)) + return -ENODEV; + + udev = to_usb_device(dev); + parent = dev->parent; + parent_handle = DEVICE_ACPI_HANDLE(parent); + + if (!parent_handle) + return -ENODEV; + + *handle = acpi_get_child(parent_handle, udev->portnum); + + if (!*handle) + return -ENODEV; + + /* + * PLD will tell us whether a port is removable to the user or + * not. If we don't get an answer from PLD (it's not present + * or it's malformed) then try to infer it from UPC. If a + * device isn't connectable then it's probably not removable. + */ + if (usb_acpi_check_pld(udev, *handle) != 0) + usb_acpi_check_upc(udev, *handle); + + return 0; +} + +static struct acpi_bus_type usb_acpi_bus = { + .bus = &usb_bus_type, + .find_bridge = NULL, + .find_device = usb_acpi_find_device, +}; + +int usb_acpi_register(void) +{ + return register_acpi_bus_type(&usb_acpi_bus); +} + +void usb_acpi_unregister(void) +{ + unregister_acpi_bus_type(&usb_acpi_bus); +} diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index c74ba7bbc74..25d0c61c3f8 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1015,6 +1015,7 @@ static int __init usb_init(void) if (retval) goto out; + usb_acpi_register(); retval = bus_register(&usb_bus_type); if (retval) goto bus_register_failed; @@ -1030,9 +1031,6 @@ static int __init usb_init(void) retval = usb_devio_init(); if (retval) goto usb_devio_init_failed; - retval = usbfs_init(); - if (retval) - goto fs_init_failed; retval = usb_hub_init(); if (retval) goto hub_init_failed; @@ -1042,8 +1040,6 @@ static int __init usb_init(void) usb_hub_cleanup(); hub_init_failed: - usbfs_cleanup(); -fs_init_failed: usb_devio_cleanup(); usb_devio_init_failed: usb_deregister(&usbfs_driver); @@ -1054,6 +1050,7 @@ major_init_failed: bus_notifier_failed: bus_unregister(&usb_bus_type); bus_register_failed: + usb_acpi_unregister(); usb_debugfs_cleanup(); out: return retval; @@ -1070,12 +1067,12 @@ static void __exit usb_exit(void) usb_deregister_device_driver(&usb_generic_driver); usb_major_cleanup(); - usbfs_cleanup(); usb_deregister(&usbfs_driver); usb_devio_cleanup(); usb_hub_cleanup(); bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); + usb_acpi_unregister(); usb_debugfs_cleanup(); } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 71648dcbe43..5c5c538ea73 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -156,3 +156,10 @@ extern void usb_notify_remove_device(struct usb_device *udev); extern void usb_notify_add_bus(struct usb_bus *ubus); extern void usb_notify_remove_bus(struct usb_bus *ubus); +#ifdef CONFIG_ACPI +extern int usb_acpi_register(void); +extern void usb_acpi_unregister(void); +#else +static inline int usb_acpi_register(void) { return 0; }; +static inline void usb_acpi_unregister(void) { }; +#endif |