diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 16 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 21 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 159 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 24 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 96 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 22 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 84 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 2 |
17 files changed, 211 insertions, 297 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 9eed5b52d9de..18d02e32a3d5 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -107,17 +107,25 @@ config USB_SUSPEND If you are unsure about this, say N here. config USB_OTG - bool + bool "OTG support" depends on USB && EXPERIMENTAL depends on USB_SUSPEND default n + help + The most notable feature of USB OTG is support for a + "Dual-Role" device, which can act as either a device + or a host. The initial role is decided by the type of + plug inserted and can be changed later when two dual + role devices talk to each other. + Select this only if your board has Mini-AB/Micro-AB + connector. config USB_OTG_WHITELIST bool "Rely on OTG Targeted Peripherals List" - depends on USB_OTG || EMBEDDED + depends on USB_OTG || EXPERT default y if USB_OTG - default n if EMBEDDED + 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 @@ -133,7 +141,7 @@ config USB_OTG_WHITELIST config USB_OTG_BLACKLIST_HUB bool "Disable external hubs" - depends on USB_OTG || EMBEDDED + depends on USB_OTG || EXPERT help If you say Y here, then Linux will refuse to enumerate external hubs. OTG hosts are allowed to reduce hardware diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index ec16e6029905..507a4e1b6360 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,20 +2,13 @@ # Makefile for USB Core files and filesystem # -usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ - config.o file.o buffer.o sysfs.o endpoint.o \ - devio.o notify.o generic.o quirks.o devices.o +ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG -ifeq ($(CONFIG_PCI),y) - usbcore-objs += hcd-pci.o -endif +usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o +usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o +usbcore-y += devio.o notify.o generic.o quirks.o devices.o -ifeq ($(CONFIG_USB_DEVICEFS),y) - usbcore-objs += inode.o -endif +usbcore-$(CONFIG_PCI) += hcd-pci.o +usbcore-$(CONFIG_USB_DEVICEFS) += inode.o -obj-$(CONFIG_USB) += usbcore.o - -ifeq ($(CONFIG_USB_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 3449742c00e1..a3d2e2399655 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -54,7 +54,6 @@ #include <linux/gfp.h> #include <linux/poll.h> #include <linux/usb.h> -#include <linux/smp_lock.h> #include <linux/usbdevice_fs.h> #include <linux/usb/hcd.h> #include <linux/mutex.h> @@ -66,8 +65,8 @@ #define ALLOW_SERIAL_NUMBER static const char *format_topo = -/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ -"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; +/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */ +"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n"; static const char *format_string_manufacturer = /* S: Manufacturer=xxxx */ @@ -520,11 +519,14 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, speed = "1.5"; break; case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ case USB_SPEED_FULL: - speed = "12 "; break; + speed = "12"; break; + case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */ case USB_SPEED_HIGH: speed = "480"; break; + case USB_SPEED_SUPER: + speed = "5000"; break; default: - speed = "?? "; + speed = "??"; } data_end = pages_start + sprintf(pages_start, format_topo, bus->busnum, level, parent_devnum, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index f1aaff6202a5..a7131ad630f9 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -37,7 +37,6 @@ #include <linux/fs.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/signal.h> #include <linux/poll.h> #include <linux/module.h> @@ -965,10 +964,11 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) static int proc_connectinfo(struct dev_state *ps, void __user *arg) { - struct usbdevfs_connectinfo ci; + struct usbdevfs_connectinfo ci = { + .devnum = ps->dev->devnum, + .slow = ps->dev->speed == USB_SPEED_LOW + }; - ci.devnum = ps->dev->devnum; - ci.slow = ps->dev->speed == USB_SPEED_LOW; if (copy_to_user(arg, &ci, sizeof(ci))) return -EFAULT; return 0; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d7a4401ef019..fca61720b873 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -27,7 +27,6 @@ #include <linux/usb.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> -#include <linux/pm_runtime.h> #include "usb.h" @@ -376,7 +375,7 @@ static int usb_unbind_interface(struct device *dev) * Just re-enable it without affecting the endpoint toggles. */ usb_enable_interface(udev, intf, false); - } else if (!error && intf->dev.power.status == DPM_ON) { + } else if (!error && !intf->dev.power.in_suspend) { r = usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); if (r < 0) @@ -961,7 +960,7 @@ void usb_rebind_intf(struct usb_interface *intf) } /* Try to rebind the interface */ - if (intf->dev.power.status == DPM_ON) { + if (!intf->dev.power.in_suspend) { intf->needs_binding = 0; rc = device_attach(&intf->dev); if (rc < 0) @@ -1108,8 +1107,7 @@ static int usb_resume_interface(struct usb_device *udev, if (intf->condition == USB_INTERFACE_UNBOUND) { /* Carry out a deferred switch to altsetting 0 */ - if (intf->needs_altsetting0 && - intf->dev.power.status == DPM_ON) { + if (intf->needs_altsetting0 && !intf->dev.power.in_suspend) { usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); intf->needs_altsetting0 = 0; @@ -1262,6 +1260,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) udev->reset_resume); } } + usb_mark_last_busy(udev); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -1329,7 +1328,6 @@ int usb_resume(struct device *dev, pm_message_t msg) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - udev->last_busy = jiffies; do_unbind_rebind(udev, DO_REBIND); } } @@ -1337,7 +1335,7 @@ int usb_resume(struct device *dev, pm_message_t msg) /* Avoid PM error messages for devices disconnected while suspended * as we'll display regular disconnect messages just a bit later. */ - if (status == -ENODEV) + if (status == -ENODEV || status == -ESHUTDOWN) status = 0; return status; } @@ -1397,33 +1395,8 @@ void usb_autosuspend_device(struct usb_device *udev) { int status; - udev->last_busy = jiffies; - status = pm_runtime_put_sync(&udev->dev); - dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", - __func__, atomic_read(&udev->dev.power.usage_count), - status); -} - -/** - * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces - * @udev: the usb_device to autosuspend - * - * This routine should be called when a core subsystem thinks @udev may - * be ready to autosuspend. - * - * @udev's usage counter left unchanged. If it is 0 and all the interfaces - * are inactive then an autosuspend will be attempted. The attempt may - * fail or be delayed. - * - * The caller must hold @udev's device lock. - * - * This routine can run only in process context. - */ -void usb_try_autosuspend_device(struct usb_device *udev) -{ - int status; - - status = pm_runtime_idle(&udev->dev); + usb_mark_last_busy(udev); + status = pm_runtime_put_sync_autosuspend(&udev->dev); dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&udev->dev.power.usage_count), status); @@ -1482,7 +1455,7 @@ void usb_autopm_put_interface(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); int status; - udev->last_busy = jiffies; + usb_mark_last_busy(udev); atomic_dec(&intf->pm_usage_cnt); status = pm_runtime_put_sync(&intf->dev); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", @@ -1509,32 +1482,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); void usb_autopm_put_interface_async(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - unsigned long last_busy; - int status = 0; + int status; - last_busy = udev->last_busy; - udev->last_busy = jiffies; + usb_mark_last_busy(udev); atomic_dec(&intf->pm_usage_cnt); - pm_runtime_put_noidle(&intf->dev); - - if (udev->dev.power.runtime_auto) { - /* Optimization: Don't schedule a delayed autosuspend if - * the timer is already running and the expiration time - * wouldn't change. - * - * We have to use the interface's timer. Attempts to - * schedule a suspend for the device would fail because - * the interface is still active. - */ - if (intf->dev.power.timer_expires == 0 || - round_jiffies_up(last_busy) != - round_jiffies_up(jiffies)) { - status = pm_schedule_suspend(&intf->dev, - jiffies_to_msecs( - round_jiffies_up_relative( - udev->autosuspend_delay))); - } - } + status = pm_runtime_put(&intf->dev); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); @@ -1554,7 +1506,7 @@ void usb_autopm_put_interface_no_suspend(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - udev->last_busy = jiffies; + usb_mark_last_busy(udev); atomic_dec(&intf->pm_usage_cnt); pm_runtime_put_noidle(&intf->dev); } @@ -1612,18 +1564,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface); */ int usb_autopm_get_interface_async(struct usb_interface *intf) { - int status = 0; - enum rpm_status s; - - /* Don't request a resume unless the interface is already suspending - * or suspended. Doing so would force a running suspend timer to be - * cancelled. - */ - pm_runtime_get_noresume(&intf->dev); - s = ACCESS_ONCE(intf->dev.power.runtime_status); - if (s == RPM_SUSPENDING || s == RPM_SUSPENDED) - status = pm_request_resume(&intf->dev); + int status; + status = pm_runtime_get(&intf->dev); if (status < 0 && status != -EINPROGRESS) pm_runtime_put_noidle(&intf->dev); else @@ -1650,7 +1593,7 @@ void usb_autopm_get_interface_no_resume(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - udev->last_busy = jiffies; + usb_mark_last_busy(udev); atomic_inc(&intf->pm_usage_cnt); pm_runtime_get_noresume(&intf->dev); } @@ -1661,7 +1604,6 @@ static int autosuspend_check(struct usb_device *udev) { int w, i; struct usb_interface *intf; - unsigned long suspend_time, j; /* Fail if autosuspend is disabled, or any interfaces are in use, or * any interface drivers require remote wakeup but it isn't available. @@ -1701,87 +1643,46 @@ static int autosuspend_check(struct usb_device *udev) return -EOPNOTSUPP; } udev->do_remote_wakeup = w; - - /* If everything is okay but the device hasn't been idle for long - * enough, queue a delayed autosuspend request. - */ - j = ACCESS_ONCE(jiffies); - suspend_time = udev->last_busy + udev->autosuspend_delay; - if (time_before(j, suspend_time)) { - pm_schedule_suspend(&udev->dev, jiffies_to_msecs( - round_jiffies_up_relative(suspend_time - j))); - return -EAGAIN; - } return 0; } static int usb_runtime_suspend(struct device *dev) { - int status = 0; + struct usb_device *udev = to_usb_device(dev); + int status; /* A USB device can be suspended if it passes the various autosuspend * checks. Runtime suspend for a USB device means suspending all the * interfaces and then the device itself. */ - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - if (autosuspend_check(udev) != 0) - return -EAGAIN; - - status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); - - /* If an interface fails the suspend, adjust the last_busy - * time so that we don't get another suspend attempt right - * away. - */ - if (status) { - udev->last_busy = jiffies + - (udev->autosuspend_delay == 0 ? - HZ/2 : 0); - } - - /* Prevent the parent from suspending immediately after */ - else if (udev->parent) - udev->parent->last_busy = jiffies; - } + if (autosuspend_check(udev) != 0) + return -EAGAIN; - /* Runtime suspend for a USB interface doesn't mean anything. */ + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); return status; } static int usb_runtime_resume(struct device *dev) { + struct usb_device *udev = to_usb_device(dev); + int status; + /* Runtime resume for a USB device means resuming both the device * and all its interfaces. */ - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - int status; - - status = usb_resume_both(udev, PMSG_AUTO_RESUME); - udev->last_busy = jiffies; - return status; - } - - /* Runtime resume for a USB interface doesn't mean anything. */ - return 0; + status = usb_resume_both(udev, PMSG_AUTO_RESUME); + return status; } static int usb_runtime_idle(struct device *dev) { + struct usb_device *udev = to_usb_device(dev); + /* An idle USB device can be suspended if it passes the various - * autosuspend checks. An idle interface can be suspended at - * any time. + * autosuspend checks. */ - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - if (autosuspend_check(udev) != 0) - return 0; - } - - pm_runtime_suspend(dev); + if (autosuspend_check(udev) == 0) + pm_runtime_autosuspend(dev); return 0; } diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 3788e738e265..df502a98d0df 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -192,17 +192,17 @@ int usb_create_ep_devs(struct device *parent, ep_dev->dev.parent = parent; ep_dev->dev.release = ep_device_release; dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); - device_enable_async_suspend(&ep_dev->dev); retval = device_register(&ep_dev->dev); if (retval) goto error_register; + device_enable_async_suspend(&ep_dev->dev); endpoint->ep_dev = ep_dev; return retval; error_register: - kfree(ep_dev); + put_device(&ep_dev->dev); exit: return retval; } diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 1e6ccef2cf0c..cf6a5423de09 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -19,7 +19,6 @@ #include <linux/errno.h> #include <linux/rwsem.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/usb.h> #include "usb.h" @@ -59,6 +58,7 @@ static int usb_open(struct inode * inode, struct file * file) static const struct file_operations usb_fops = { .owner = THIS_MODULE, .open = usb_open, + .llseek = noop_llseek, }; static struct usb_class { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c3f98543caaf..f71e8e307e0f 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -329,8 +328,10 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) return; if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && - hcd->driver->shutdown) + hcd->driver->shutdown) { hcd->driver->shutdown(hcd); + pci_disable_device(dev); + } } EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); @@ -404,7 +405,12 @@ static int suspend_common(struct device *dev, bool do_wakeup) return retval; } - synchronize_irq(pci_dev->irq); + /* If MSI-X is enabled, the driver will have synchronized all vectors + * in pci_suspend(). If MSI or legacy PCI is enabled, that will be + * synchronized here. + */ + if (!hcd->msix_enabled) + synchronize_irq(pci_dev->irq); /* Downstream ports from this root hub should already be quiesced, so * there will be no DMA activity. Now we can shut down the upstream diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5cca00a6d09d..e935f71d7a34 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -38,7 +38,6 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> -#include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -1263,10 +1262,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { - enum dma_data_direction dir; - if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) dma_unmap_single(hcd->self.controller, urb->setup_dma, @@ -1279,6 +1276,17 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); + /* Make it safe to call this routine more than once */ + urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL); +} +EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma); + +void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + enum dma_data_direction dir; + + unmap_urb_setup_for_dma(hcd, urb); + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) dma_unmap_sg(hcd->self.controller, @@ -1303,10 +1311,10 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) dir); /* Make it safe to call this routine more than once */ - urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL | - URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | + urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } +EXPORT_SYMBOL_GPL(unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) @@ -1321,6 +1329,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, */ if (usb_endpoint_xfer_control(&urb->ep->desc)) { + if (hcd->self.uses_pio_for_control) + return ret; if (hcd->self.uses_dma) { urb->setup_dma = dma_map_single( hcd->self.controller, @@ -1945,7 +1955,6 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); - clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) @@ -1953,6 +1962,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) hcd->state = HC_STATE_RESUMING; status = hcd->driver->bus_resume(hcd); + clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 84c1897188d2..0f299b7aad60 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -24,7 +24,6 @@ #include <linux/kthread.h> #include <linux/mutex.h> #include <linux/freezer.h> -#include <linux/pm_runtime.h> #include <asm/uaccess.h> #include <asm/byteorder.h> @@ -677,6 +676,8 @@ static void hub_init_func3(struct work_struct *ws); static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) { struct usb_device *hdev = hub->hdev; + struct usb_hcd *hcd; + int ret; int port1; int status; bool need_debounce_delay = false; @@ -715,6 +716,25 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) usb_autopm_get_interface_no_resume( to_usb_interface(hub->intfdev)); return; /* Continues at init2: below */ + } else if (type == HUB_RESET_RESUME) { + /* The internal host controller state for the hub device + * may be gone after a host power loss on system resume. + * Update the device's info so the HW knows it's a hub. + */ + hcd = bus_to_hcd(hdev->bus); + if (hcd->driver->update_hub_device) { + ret = hcd->driver->update_hub_device(hcd, hdev, + &hub->tt, GFP_NOIO); + if (ret < 0) { + dev_err(hub->intfdev, "Host not " + "accepting hub info " + "update.\n"); + dev_err(hub->intfdev, "LS/FS devices " + "and hubs may not work " + "under this hub\n."); + } + } + hub_power_on(hub, true); } else { hub_power_on(hub, true); } @@ -758,6 +778,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); portstatus &= ~USB_PORT_STAT_ENABLE; + } else { + /* Pretend that power was lost for USB3 devs */ + portstatus &= ~USB_PORT_STAT_ENABLE; } } @@ -1801,8 +1824,15 @@ int usb_new_device(struct usb_device *udev) /* Tell the runtime-PM framework the device is active */ pm_runtime_set_active(&udev->dev); + pm_runtime_get_noresume(&udev->dev); + pm_runtime_use_autosuspend(&udev->dev); pm_runtime_enable(&udev->dev); + /* By default, forbid autosuspend for all devices. It will be + * allowed for hubs during binding. + */ + usb_disable_autosuspend(udev); + err = usb_enumerate_device(udev); /* Read descriptors */ if (err < 0) goto fail; @@ -1828,6 +1858,8 @@ int usb_new_device(struct usb_device *udev) } (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); + usb_mark_last_busy(udev); + pm_runtime_put_sync_autosuspend(&udev->dev); return err; fail: @@ -2218,6 +2250,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } + usb_mark_last_busy(hub->hdev); return status; } @@ -2594,16 +2627,14 @@ static int hub_set_address(struct usb_device *udev, int devnum) return 0; if (udev->state != USB_STATE_DEFAULT) return -EINVAL; - if (hcd->driver->address_device) { + if (hcd->driver->address_device) retval = hcd->driver->address_device(hcd, udev); - } else { + else retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (retval == 0) - update_address(udev, devnum); - } if (retval == 0) { + update_address(udev, devnum); /* Device now using proper address. */ usb_set_device_state(udev, USB_STATE_ADDRESS); usb_ep0_reinit(udev); @@ -2650,17 +2681,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, mutex_lock(&usb_address0_mutex); - if (!udev->config && oldspeed == USB_SPEED_SUPER) { - /* Don't reset USB 3.0 devices during an initial setup */ - usb_set_device_state(udev, USB_STATE_DEFAULT); - } else { - /* Reset the device; full speed may morph to high speed */ - /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); - if (retval < 0) /* error or disconnect */ - goto fail; - /* success, speed is known */ - } + /* Reset the device; full speed may morph to high speed */ + /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ + retval = hub_port_reset(hub, port1, udev, delay); + if (retval < 0) /* error or disconnect */ + goto fail; + /* success, speed is known */ + retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { @@ -2722,6 +2749,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, udev->ttport = hdev->ttport; } else if (udev->speed != USB_SPEED_HIGH && hdev->speed == USB_SPEED_HIGH) { + if (!hub->tt.hub) { + dev_err(&udev->dev, "parent hub has no TT\n"); + retval = -EINVAL; + goto fail; + } udev->tt = &hub->tt; udev->ttport = port1; } @@ -2860,13 +2892,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, else i = udev->descriptor.bMaxPacketSize0; if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { - if (udev->speed != USB_SPEED_FULL || + if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { - dev_err(&udev->dev, "ep0 maxpacket = %d\n", i); + dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); retval = -EMSGSIZE; goto fail; } - dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); + if (udev->speed == USB_SPEED_FULL) + dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); + else + dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i); udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); usb_ep0_reinit(udev); } @@ -3097,16 +3132,17 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, udev->speed = USB_SPEED_UNKNOWN; /* - * xHCI needs to issue an address device command later - * in the hub_port_init sequence for SS/HS/FS/LS devices. + * Set the address. + * Note xHCI needs to issue an address device command later + * in the hub_port_init sequence for SS/HS/FS/LS devices, + * and xHC will assign an address to the device. But use + * kernel assigned address here, to avoid any address conflict + * issue. */ - if (!(hcd->driver->flags & HCD_USB3)) { - /* set the address */ - choose_address(udev); - if (udev->devnum <= 0) { - status = -ENOTCONN; /* Don't retry */ - goto loop; - } + choose_address(udev); + if (udev->devnum <= 0) { + status = -ENOTCONN; /* Don't retry */ + goto loop; } /* reset (non-USB 3.0 devices) and get descriptor */ @@ -3629,7 +3665,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } if (!parent_hdev) { - /* this requires hcd-specific logic; see OHCI hc_restart() */ + /* this requires hcd-specific logic; see ohci_restart() */ dev_dbg(&udev->dev, "%s for root hub!\n", __func__); return -EISDIR; } diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 095fa5366690..1b125c224dcf 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -39,7 +39,6 @@ #include <linux/parser.h> #include <linux/notifier.h> #include <linux/seq_file.h> -#include <linux/smp_lock.h> #include <linux/usb/hcd.h> #include <asm/byteorder.h> #include "usb.h" @@ -276,6 +275,7 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de struct inode *inode = new_inode(sb); if (inode) { + inode->i_ino = get_next_ino(); inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); @@ -343,17 +343,19 @@ static int usbfs_empty (struct dentry *dentry) { struct list_head *list; - spin_lock(&dcache_lock); - + 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(&dcache_lock); + spin_unlock(&de->d_lock); + spin_unlock(&dentry->d_lock); return 0; } + spin_unlock(&de->d_lock); } - - spin_unlock(&dcache_lock); + spin_unlock(&dentry->d_lock); return 1; } @@ -573,16 +575,16 @@ static void fs_remove_file (struct dentry *dentry) /* --------------------------------------------------------------------- */ -static int usb_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) +static struct dentry *usb_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_single(fs_type, flags, data, usbfs_fill_super, mnt); + return mount_single(fs_type, flags, data, usbfs_fill_super); } static struct file_system_type usb_fs_type = { .owner = THIS_MODULE, .name = "usbfs", - .get_sb = usb_get_sb, + .mount = usb_mount, .kill_sb = kill_litter_super, }; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9f0ce7de0e36..832487423826 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1140,13 +1140,6 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) { int i; - dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, - skip_ep0 ? "non-ep0" : "all"); - for (i = skip_ep0; i < 16; ++i) { - usb_disable_endpoint(dev, i, true); - usb_disable_endpoint(dev, i + USB_DIR_IN, true); - } - /* getting rid of interfaces will disconnect * any drivers bound to them (a key side effect) */ @@ -1176,6 +1169,13 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); } + + dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, + skip_ep0 ? "non-ep0" : "all"); + for (i = skip_ep0; i < 16; ++i) { + usb_disable_endpoint(dev, i, true); + usb_disable_endpoint(dev, i + USB_DIR_IN, true); + } } /** @@ -1804,6 +1804,7 @@ free_interfaces: INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); intf->minor = -1; device_initialize(&intf->dev); + pm_runtime_no_callbacks(&intf->dev); dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 25719da45e33..81ce6a8e1d94 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -48,6 +48,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04b4, 0x0526), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ + { USB_DEVICE(0x04e8, 0x6601), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Roland SC-8820 */ { USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -68,6 +72,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Keytouch QWERTY Panel keyboard */ + { USB_DEVICE(0x0926, 0x3333), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* X-Rite/Gretag-Macbeth Eye-One Pro display colorimeter */ { USB_DEVICE(0x0971, 0x2000), .driver_info = USB_QUIRK_NO_SET_INTF }, @@ -117,21 +125,6 @@ void usb_detect_quirks(struct usb_device *udev) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", udev->quirks); -#ifdef CONFIG_USB_SUSPEND - - /* By default, disable autosuspend for all devices. The hub driver - * will enable it for hubs. - */ - usb_disable_autosuspend(udev); - - /* Autosuspend can also be disabled if the initial autosuspend_delay - * is negative. - */ - if (udev->autosuspend_delay < 0) - usb_autoresume_device(udev); - -#endif - /* For the present, all devices default to USB-PERSIST enabled */ #if 0 /* was: #ifdef CONFIG_PM */ /* Hubs are automatically enabled for USB-PERSIST */ diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 448f5b47fc48..6781c369ce2d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -233,8 +233,6 @@ static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); #ifdef CONFIG_PM -static const char power_group[] = "power"; - static ssize_t show_persist(struct device *dev, struct device_attribute *attr, char *buf) { @@ -278,7 +276,7 @@ static int add_persist_attributes(struct device *dev) if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) rc = sysfs_add_file_to_group(&dev->kobj, &dev_attr_persist.attr, - power_group); + power_group_name); } return rc; } @@ -287,7 +285,7 @@ static void remove_persist_attributes(struct device *dev) { sysfs_remove_file_from_group(&dev->kobj, &dev_attr_persist.attr, - power_group); + power_group_name); } #else @@ -336,44 +334,20 @@ static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL); static ssize_t show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) { - struct usb_device *udev = to_usb_device(dev); - - return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ); + return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000); } static ssize_t set_autosuspend(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct usb_device *udev = to_usb_device(dev); - int value, old_delay; - int rc; + int value; - if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || - value <= - INT_MAX/HZ) + if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/1000 || + value <= -INT_MAX/1000) return -EINVAL; - value *= HZ; - - usb_lock_device(udev); - old_delay = udev->autosuspend_delay; - udev->autosuspend_delay = value; - - if (old_delay < 0) { /* Autosuspend wasn't allowed */ - if (value >= 0) - usb_autosuspend_device(udev); - } else { /* Autosuspend was allowed */ - if (value < 0) { - rc = usb_autoresume_device(udev); - if (rc < 0) { - count = rc; - udev->autosuspend_delay = old_delay; - } - } else { - usb_try_autosuspend_device(udev); - } - } - usb_unlock_device(udev); + pm_runtime_set_autosuspend_delay(dev, value * 1000); return count; } @@ -438,44 +412,30 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); +static struct attribute *power_attrs[] = { + &dev_attr_autosuspend.attr, + &dev_attr_level.attr, + &dev_attr_connected_duration.attr, + &dev_attr_active_duration.attr, + NULL, +}; +static struct attribute_group power_attr_group = { + .name = power_group_name, + .attrs = power_attrs, +}; + static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) { - rc = sysfs_add_file_to_group(&dev->kobj, - &dev_attr_autosuspend.attr, - power_group); - if (rc == 0) - rc = sysfs_add_file_to_group(&dev->kobj, - &dev_attr_level.attr, - power_group); - if (rc == 0) - rc = sysfs_add_file_to_group(&dev->kobj, - &dev_attr_connected_duration.attr, - power_group); - if (rc == 0) - rc = sysfs_add_file_to_group(&dev->kobj, - &dev_attr_active_duration.attr, - power_group); - } + if (is_usb_device(dev)) + rc = sysfs_merge_group(&dev->kobj, &power_attr_group); return rc; } static void remove_power_attributes(struct device *dev) { - sysfs_remove_file_from_group(&dev->kobj, - &dev_attr_active_duration.attr, - power_group); - sysfs_remove_file_from_group(&dev->kobj, - &dev_attr_connected_duration.attr, - power_group); - sysfs_remove_file_from_group(&dev->kobj, - &dev_attr_level.attr, - power_group); - sysfs_remove_file_from_group(&dev->kobj, - &dev_attr_autosuspend.attr, - power_group); + sysfs_unmerge_group(&dev->kobj, &power_attr_group); } #else diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 419e6b34e2fe..c14fc082864f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -401,8 +401,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) }; /* Check that the pipe's type matches the endpoint's type */ - if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) + if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) { + dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", + usb_pipetype(urb->pipe), pipetypes[xfertype]); return -EPIPE; /* The most suitable error code :-) */ + } /* enforce simple/standard policy */ allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK | diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index fdd4130fbb7d..079cb57bab4f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -445,7 +445,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, INIT_LIST_HEAD(&dev->filelist); #ifdef CONFIG_PM - dev->autosuspend_delay = usb_autosuspend_delay * HZ; + pm_runtime_set_autosuspend_delay(&dev->dev, + usb_autosuspend_delay * 1000); dev->connect_time = jiffies; dev->active_duration = -jiffies; #endif diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index cd882203ad34..b975450f403e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -75,14 +75,12 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) #ifdef CONFIG_USB_SUSPEND extern void usb_autosuspend_device(struct usb_device *udev); -extern void usb_try_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); extern int usb_remote_wakeup(struct usb_device *dev); #else #define usb_autosuspend_device(udev) do {} while (0) -#define usb_try_autosuspend_device(udev) do {} while (0) static inline int usb_autoresume_device(struct usb_device *udev) { return 0; |