diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 83 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 127 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 930 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 127 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 13 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 121 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 85 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 13 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 38 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 43 |
16 files changed, 846 insertions, 792 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index ad925946f869..97a819c23ef3 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -91,8 +91,8 @@ config USB_DYNAMIC_MINORS If you are unsure about this, say N here. config USB_SUSPEND - bool "USB selective suspend/resume and wakeup" - depends on USB && PM + bool "USB runtime power management (suspend/resume and wakeup)" + depends on USB && PM_RUNTIME help If you say Y here, you can use driver calls or the sysfs "power/level" file to suspend or resume individual USB diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 355dffcc23b0..d41811bfef2a 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -117,12 +117,20 @@ static const char *format_endpt = * However, these will come from functions that return ptrs to each of them. */ -static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq); -static unsigned int conndiscevcnt; - -/* this struct stores the poll state for <mountpoint>/devices pollers */ -struct usb_device_status { - unsigned int lastev; +/* + * Wait for an connect/disconnect event to happen. We initialize + * the event counter with an odd number, and each event will increment + * the event counter by two, so it will always _stay_ odd. That means + * that it will never be zero, so "event 0" will never match a current + * event, and thus 'poll' will always trigger as readable for the first + * time it gets called. + */ +static struct device_connect_event { + atomic_t count; + wait_queue_head_t wait; +} device_event = { + .count = ATOMIC_INIT(1), + .wait = __WAIT_QUEUE_HEAD_INITIALIZER(device_event.wait) }; struct class_info { @@ -156,8 +164,8 @@ static const struct class_info clas_info[] = void usbfs_conn_disc_event(void) { - conndiscevcnt++; - wake_up(&deviceconndiscwq); + atomic_add(2, &device_event.count); + wake_up(&device_event.wait); } static const char *class_decode(const int class) @@ -629,55 +637,16 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait) { - struct usb_device_status *st = file->private_data; - unsigned int mask = 0; - - lock_kernel(); - if (!st) { - st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); - - /* we may have dropped BKL - - * need to check for having lost the race */ - if (file->private_data) { - kfree(st); - st = file->private_data; - goto lost_race; - } - /* we haven't lost - check for allocation failure now */ - if (!st) { - unlock_kernel(); - return POLLIN; - } + unsigned int event_count; - /* - * need to prevent the module from being unloaded, since - * proc_unregister does not call the release method and - * we would have a memory leak - */ - st->lastev = conndiscevcnt; - file->private_data = st; - mask = POLLIN; - } -lost_race: - if (file->f_mode & FMODE_READ) - poll_wait(file, &deviceconndiscwq, wait); - if (st->lastev != conndiscevcnt) - mask |= POLLIN; - st->lastev = conndiscevcnt; - unlock_kernel(); - return mask; -} + poll_wait(file, &device_event.wait, wait); -static int usb_device_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} + event_count = atomic_read(&device_event.count); + if (file->f_version != event_count) { + file->f_version = event_count; + return POLLIN | POLLRDNORM; + } -static int usb_device_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); - file->private_data = NULL; return 0; } @@ -685,7 +654,7 @@ static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig) { loff_t ret; - lock_kernel(); + mutex_lock(&file->f_dentry->d_inode->i_mutex); switch (orig) { case 0: @@ -701,7 +670,7 @@ static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig) ret = -EINVAL; } - unlock_kernel(); + mutex_unlock(&file->f_dentry->d_inode->i_mutex); return ret; } @@ -709,6 +678,4 @@ const struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, - .open = usb_device_open, - .release = usb_device_release, }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a678186f218f..e909ff7b9094 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -122,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) { loff_t ret; - lock_kernel(); + mutex_lock(&file->f_dentry->d_inode->i_mutex); switch (orig) { case 0: @@ -138,7 +138,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) ret = -EINVAL; } - unlock_kernel(); + mutex_unlock(&file->f_dentry->d_inode->i_mutex); return ret; } @@ -310,7 +310,8 @@ static struct async *async_getpending(struct dev_state *ps, static void snoop_urb(struct usb_device *udev, void __user *userurb, int pipe, unsigned length, - int timeout_or_status, enum snoop_when when) + int timeout_or_status, enum snoop_when when, + unsigned char *data, unsigned data_len) { static const char *types[] = {"isoc", "int", "ctrl", "bulk"}; static const char *dirs[] = {"out", "in"}; @@ -344,6 +345,11 @@ static void snoop_urb(struct usb_device *udev, "status %d\n", ep, t, d, length, timeout_or_status); } + + if (data && data_len > 0) { + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + data, data_len, 1); + } } #define AS_CONTINUATION 1 @@ -410,7 +416,9 @@ static void async_completed(struct urb *urb) } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, - as->status, COMPLETE); + as->status, COMPLETE, + ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? + NULL : urb->transfer_buffer, urb->actual_length); if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && as->status != -ENOENT) cancel_bulk_urbs(ps, as->bulk_addr); @@ -653,20 +661,20 @@ static int usbdev_open(struct inode *inode, struct file *file) const struct cred *cred = current_cred(); int ret; - lock_kernel(); - /* Protect against simultaneous removal or release */ - mutex_lock(&usbfs_mutex); - ret = -ENOMEM; ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL); if (!ps) - goto out; + goto out_free_ps; ret = -ENODEV; + /* Protect against simultaneous removal or release */ + mutex_lock(&usbfs_mutex); + /* usbdev device-node */ if (imajor(inode) == USB_DEVICE_MAJOR) dev = usbdev_lookup_by_devt(inode->i_rdev); + #ifdef CONFIG_USB_DEVICEFS /* procfs file */ if (!dev) { @@ -678,13 +686,19 @@ static int usbdev_open(struct inode *inode, struct file *file) dev = NULL; } #endif - if (!dev || dev->state == USB_STATE_NOTATTACHED) - goto out; + mutex_unlock(&usbfs_mutex); + + if (!dev) + goto out_free_ps; + + usb_lock_device(dev); + if (dev->state == USB_STATE_NOTATTACHED) + goto out_unlock_device; + ret = usb_autoresume_device(dev); if (ret) - goto out; + goto out_unlock_device; - ret = 0; ps->dev = dev; ps->file = file; spin_lock_init(&ps->lock); @@ -702,15 +716,16 @@ static int usbdev_open(struct inode *inode, struct file *file) smp_wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; + usb_unlock_device(dev); snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current), current->comm); - out: - if (ret) { - kfree(ps); - usb_put_dev(dev); - } - mutex_unlock(&usbfs_mutex); - unlock_kernel(); + return ret; + + out_unlock_device: + usb_unlock_device(dev); + usb_put_dev(dev); + out_free_ps: + kfree(ps); return ret; } @@ -724,10 +739,7 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_lock_device(dev); usb_hub_release_all_ports(dev, ps); - /* Protect against simultaneous open */ - mutex_lock(&usbfs_mutex); list_del_init(&ps->list); - mutex_unlock(&usbfs_mutex); for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) { @@ -770,6 +782,13 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (!tbuf) return -ENOMEM; tmo = ctrl.timeout; + snoop(&dev->dev, "control urb: bRequestType=%02x " + "bRequest=%02x wValue=%04x " + "wIndex=%04x wLength=%04x\n", + ctrl.bRequestType, ctrl.bRequest, + __le16_to_cpup(&ctrl.wValue), + __le16_to_cpup(&ctrl.wIndex), + __le16_to_cpup(&ctrl.wLength)); if (ctrl.bRequestType & 0x80) { if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) { @@ -777,15 +796,15 @@ static int proc_control(struct dev_state *ps, void __user *arg) return -EINVAL; } pipe = usb_rcvctrlpipe(dev, 0); - snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0); usb_unlock_device(dev); i = usb_control_msg(dev, pipe, ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); - snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); - + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, + tbuf, i); if ((i > 0) && ctrl.wLength) { if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); @@ -800,14 +819,15 @@ static int proc_control(struct dev_state *ps, void __user *arg) } } pipe = usb_sndctrlpipe(dev, 0); - snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, + tbuf, ctrl.wLength); usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); - snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0); } free_page((unsigned long)tbuf); if (i < 0 && i != -EPIPE) { @@ -853,12 +873,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0); usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); - snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { @@ -873,12 +893,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1); usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); - snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0); } kfree(tbuf); if (i < 0) @@ -1097,6 +1117,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, is_in = 0; uurb->endpoint &= ~USB_DIR_IN; } + snoop(&ps->dev->dev, "control urb: bRequestType=%02x " + "bRequest=%02x wValue=%04x " + "wIndex=%04x wLength=%04x\n", + dr->bRequestType, dr->bRequest, + __le16_to_cpup(&dr->wValue), + __le16_to_cpup(&dr->wIndex), + __le16_to_cpup(&dr->wLength)); break; case USBDEVFS_URB_TYPE_BULK: @@ -1104,13 +1131,25 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_ISOC: return -EINVAL; - /* allow single-shot interrupt transfers, at bogus rates */ + case USB_ENDPOINT_XFER_INT: + /* allow single-shot interrupt transfers */ + uurb->type = USBDEVFS_URB_TYPE_INTERRUPT; + goto interrupt_urb; } uurb->number_of_packets = 0; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; break; + case USBDEVFS_URB_TYPE_INTERRUPT: + if (!usb_endpoint_xfer_int(&ep->desc)) + return -EINVAL; + interrupt_urb: + uurb->number_of_packets = 0; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; + break; + case USBDEVFS_URB_TYPE_ISO: /* arbitrary limit */ if (uurb->number_of_packets < 1 || @@ -1143,14 +1182,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->buffer_length = totlen; break; - case USBDEVFS_URB_TYPE_INTERRUPT: - uurb->number_of_packets = 0; - if (!usb_endpoint_xfer_int(&ep->desc)) - return -EINVAL; - if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) - return -EINVAL; - break; - default: return -EINVAL; } @@ -1236,7 +1267,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, } } snoop_urb(ps->dev, as->userurb, as->urb->pipe, - as->urb->transfer_buffer_length, 0, SUBMIT); + as->urb->transfer_buffer_length, 0, SUBMIT, + is_in ? NULL : as->urb->transfer_buffer, + uurb->buffer_length); async_newpending(as); if (usb_endpoint_xfer_bulk(&ep->desc)) { @@ -1274,7 +1307,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); snoop_urb(ps->dev, as->userurb, as->urb->pipe, - 0, ret, COMPLETE); + 0, ret, COMPLETE, NULL, 0); async_removepending(as); free_async(as); return ret; @@ -1628,7 +1661,10 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) if (driver == NULL || driver->ioctl == NULL) { retval = -ENOTTY; } else { + /* keep API that guarantees BKL */ + lock_kernel(); retval = driver->ioctl(intf, ctl->ioctl_code, buf); + unlock_kernel(); if (retval == -ENOIOCTLCMD) retval = -ENOTTY; } @@ -1711,6 +1747,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, if (!(file->f_mode & FMODE_WRITE)) return -EPERM; + usb_lock_device(dev); if (!connected(ps)) { usb_unlock_device(dev); @@ -1877,9 +1914,7 @@ static long usbdev_ioctl(struct file *file, unsigned int cmd, { int ret; - lock_kernel(); ret = usbdev_do_ioctl(file, cmd, (void __user *)arg); - unlock_kernel(); return ret; } @@ -1890,9 +1925,7 @@ static long usbdev_compat_ioctl(struct file *file, unsigned int cmd, { int ret; - lock_kernel(); ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg)); - unlock_kernel(); return ret; } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 60a45f1e3a67..f3c233806fa3 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -25,7 +25,7 @@ #include <linux/device.h> #include <linux/usb.h> #include <linux/usb/quirks.h> -#include <linux/workqueue.h> +#include <linux/pm_runtime.h> #include "hcd.h" #include "usb.h" @@ -221,7 +221,7 @@ static int usb_probe_device(struct device *dev) { struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); struct usb_device *udev = to_usb_device(dev); - int error = -ENODEV; + int error = 0; dev_dbg(dev, "%s\n", __func__); @@ -230,18 +230,23 @@ static int usb_probe_device(struct device *dev) /* The device should always appear to be in use * unless the driver suports autosuspend. */ - udev->pm_usage_cnt = !(udriver->supports_autosuspend); + if (!udriver->supports_autosuspend) + error = usb_autoresume_device(udev); - error = udriver->probe(udev); + if (!error) + error = udriver->probe(udev); return error; } /* called from driver core with dev locked */ static int usb_unbind_device(struct device *dev) { + struct usb_device *udev = to_usb_device(dev); struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); - udriver->disconnect(to_usb_device(dev)); + udriver->disconnect(udev); + if (!udriver->supports_autosuspend) + usb_autosuspend_device(udev); return 0; } @@ -274,60 +279,62 @@ static int usb_probe_interface(struct device *dev) intf->needs_binding = 0; if (usb_device_is_owned(udev)) - return -ENODEV; + return error; if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); - return -ENODEV; + return error; } id = usb_match_id(intf, driver->id_table); if (!id) id = usb_match_dynamic_id(intf, driver); - if (id) { - dev_dbg(dev, "%s - got id\n", __func__); - - error = usb_autoresume_device(udev); - if (error) - return error; + if (!id) + return error; - /* Interface "power state" doesn't correspond to any hardware - * state whatsoever. We use it to record when it's bound to - * a driver that may start I/0: it's not frozen/quiesced. - */ - mark_active(intf); - intf->condition = USB_INTERFACE_BINDING; + dev_dbg(dev, "%s - got id\n", __func__); - /* The interface should always appear to be in use - * unless the driver suports autosuspend. - */ - atomic_set(&intf->pm_usage_cnt, !driver->supports_autosuspend); - - /* Carry out a deferred switch to altsetting 0 */ - if (intf->needs_altsetting0) { - error = usb_set_interface(udev, intf->altsetting[0]. - desc.bInterfaceNumber, 0); - if (error < 0) - goto err; + error = usb_autoresume_device(udev); + if (error) + return error; - intf->needs_altsetting0 = 0; - } + intf->condition = USB_INTERFACE_BINDING; - error = driver->probe(intf, id); - if (error) + /* Bound interfaces are initially active. They are + * runtime-PM-enabled only if the driver has autosuspend support. + * They are sensitive to their children's power states. + */ + pm_runtime_set_active(dev); + pm_suspend_ignore_children(dev, false); + if (driver->supports_autosuspend) + pm_runtime_enable(dev); + + /* Carry out a deferred switch to altsetting 0 */ + if (intf->needs_altsetting0) { + error = usb_set_interface(udev, intf->altsetting[0]. + desc.bInterfaceNumber, 0); + if (error < 0) goto err; - - intf->condition = USB_INTERFACE_BOUND; - usb_autosuspend_device(udev); + intf->needs_altsetting0 = 0; } + error = driver->probe(intf, id); + if (error) + goto err; + + intf->condition = USB_INTERFACE_BOUND; + usb_autosuspend_device(udev); return error; -err: - mark_quiesced(intf); + err: intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; usb_cancel_queued_reset(intf); + + /* Unbound interfaces are always runtime-PM-disabled and -suspended */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + usb_autosuspend_device(udev); return error; } @@ -377,9 +384,17 @@ static int usb_unbind_interface(struct device *dev) usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; - mark_quiesced(intf); intf->needs_remote_wakeup = 0; + /* Unbound interfaces are always runtime-PM-disabled and -suspended */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + /* Undo any residual pm_autopm_get_interface_* calls */ + for (r = atomic_read(&intf->pm_usage_cnt); r > 0; --r) + usb_autopm_put_interface_no_suspend(intf); + atomic_set(&intf->pm_usage_cnt, 0); + if (!error) usb_autosuspend_device(udev); @@ -410,7 +425,6 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) { struct device *dev = &iface->dev; - struct usb_device *udev = interface_to_usbdev(iface); int retval = 0; if (dev->driver) @@ -420,11 +434,16 @@ int usb_driver_claim_interface(struct usb_driver *driver, usb_set_intfdata(iface, priv); iface->needs_binding = 0; - usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; - mark_active(iface); - atomic_set(&iface->pm_usage_cnt, !driver->supports_autosuspend); - usb_pm_unlock(udev); + + /* Bound interfaces are initially active. They are + * runtime-PM-enabled only if the driver has autosuspend support. + * They are sensitive to their children's power states. + */ + pm_runtime_set_active(dev); + pm_suspend_ignore_children(dev, false); + if (driver->supports_autosuspend) + pm_runtime_enable(dev); /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -470,10 +489,10 @@ void usb_driver_release_interface(struct usb_driver *driver, if (device_is_registered(dev)) { device_release_driver(dev); } else { - down(&dev->sem); + device_lock(dev); usb_unbind_interface(dev); dev->driver = NULL; - up(&dev->sem); + device_unlock(dev); } } EXPORT_SYMBOL_GPL(usb_driver_release_interface); @@ -691,9 +710,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) { struct usb_device *usb_dev; - /* driver is often null here; dev_dbg() would oops */ - pr_debug("usb %s: uevent\n", dev_name(dev)); - if (is_usb_device(dev)) { usb_dev = to_usb_device(dev); } else if (is_usb_interface(dev)) { @@ -705,6 +721,7 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) } if (usb_dev->devnum < 0) { + /* driver is often null here; dev_dbg() would oops */ pr_debug("usb %s: already deleted?\n", dev_name(dev)); return -ENODEV; } @@ -983,7 +1000,6 @@ static void do_unbind_rebind(struct usb_device *udev, int action) } } -/* Caller has locked udev's pm_mutex */ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1007,7 +1023,6 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) return status; } -/* Caller has locked udev's pm_mutex */ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1022,6 +1037,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) goto done; } + /* Non-root devices on a full/low-speed bus must wait for their + * companion high-speed root hub, in case a handoff is needed. + */ + if (!(msg.event & PM_EVENT_AUTO) && udev->parent && + udev->bus->hs_companion) + device_pm_wait_for_dev(&udev->dev, + &udev->bus->hs_companion->root_hub->dev); + if (udev->quirks & USB_QUIRK_RESET_RESUME) udev->reset_resume = 1; @@ -1033,27 +1056,20 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) return status; } -/* Caller has locked intf's usb_device's pm mutex */ static int usb_suspend_interface(struct usb_device *udev, struct usb_interface *intf, pm_message_t msg) { struct usb_driver *driver; int status = 0; - /* with no hardware, USB interfaces only use FREEZE and ON states */ - if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf)) - goto done; - - /* This can happen; see usb_driver_release_interface() */ - if (intf->condition == USB_INTERFACE_UNBOUND) + if (udev->state == USB_STATE_NOTATTACHED || + intf->condition == USB_INTERFACE_UNBOUND) goto done; driver = to_usb_driver(intf->dev.driver); if (driver->suspend) { status = driver->suspend(intf, msg); - if (status == 0) - mark_quiesced(intf); - else if (!(msg.event & PM_EVENT_AUTO)) + if (status && !(msg.event & PM_EVENT_AUTO)) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { @@ -1061,7 +1077,6 @@ static int usb_suspend_interface(struct usb_device *udev, intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "suspend", driver->name); - mark_quiesced(intf); } done: @@ -1069,14 +1084,13 @@ static int usb_suspend_interface(struct usb_device *udev, return status; } -/* Caller has locked intf's usb_device's pm_mutex */ static int usb_resume_interface(struct usb_device *udev, struct usb_interface *intf, pm_message_t msg, int reset_resume) { struct usb_driver *driver; int status = 0; - if (udev->state == USB_STATE_NOTATTACHED || is_active(intf)) + if (udev->state == USB_STATE_NOTATTACHED) goto done; /* Don't let autoresume interfere with unbinding */ @@ -1127,90 +1141,11 @@ static int usb_resume_interface(struct usb_device *udev, done: dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); - if (status == 0 && intf->condition == USB_INTERFACE_BOUND) - mark_active(intf); /* Later we will unbind the driver and/or reprobe, if necessary */ return status; } -#ifdef CONFIG_USB_SUSPEND - -/* Internal routine to check whether we may autosuspend a device. */ -static int autosuspend_check(struct usb_device *udev, int reschedule) -{ - int i; - struct usb_interface *intf; - unsigned long suspend_time, j; - - /* For autosuspend, fail fast if anything is in use or autosuspend - * is disabled. Also fail if any interfaces require remote wakeup - * but it isn't available. - */ - if (udev->pm_usage_cnt > 0) - return -EBUSY; - if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) - return -EPERM; - - suspend_time = udev->last_busy + udev->autosuspend_delay; - if (udev->actconfig) { - for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { - intf = udev->actconfig->interface[i]; - if (!is_active(intf)) - continue; - if (atomic_read(&intf->pm_usage_cnt) > 0) - return -EBUSY; - if (intf->needs_remote_wakeup && - !udev->do_remote_wakeup) { - dev_dbg(&udev->dev, "remote wakeup needed " - "for autosuspend\n"); - return -EOPNOTSUPP; - } - - /* Don't allow autosuspend if the device will need - * a reset-resume and any of its interface drivers - * doesn't include support. - */ - if (udev->quirks & USB_QUIRK_RESET_RESUME) { - struct usb_driver *driver; - - driver = to_usb_driver(intf->dev.driver); - if (!driver->reset_resume || - intf->needs_remote_wakeup) - return -EOPNOTSUPP; - } - } - } - - /* If everything is okay but the device hasn't been idle for long - * enough, queue a delayed autosuspend request. If the device - * _has_ been idle for long enough and the reschedule flag is set, - * likewise queue a delayed (1 second) autosuspend request. - */ - j = jiffies; - if (time_before(j, suspend_time)) - reschedule = 1; - else - suspend_time = j + HZ; - if (reschedule) { - if (!timer_pending(&udev->autosuspend.timer)) { - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - round_jiffies_up_relative(suspend_time - j)); - } - return -EAGAIN; - } - return 0; -} - -#else - -static inline int autosuspend_check(struct usb_device *udev, int reschedule) -{ - return 0; -} - -#endif /* CONFIG_USB_SUSPEND */ - /** * usb_suspend_both - suspend a USB device and its interfaces * @udev: the usb_device to suspend @@ -1222,27 +1157,12 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule) * all the interfaces which were suspended are resumed so that they remain * in the same state as the device. * - * If an autosuspend is in progress the routine checks first to make sure - * that neither the device itself or any of its active interfaces is in use - * (pm_usage_cnt is greater than 0). If they are, the autosuspend fails. - * - * If the suspend succeeds, the routine recursively queues an autosuspend - * request for @udev's parent device, thereby propagating the change up - * the device tree. If all of the parent's children are now suspended, - * the parent will autosuspend in turn. - * - * The suspend method calls are subject to mutual exclusion under control - * of @udev's pm_mutex. Many of these calls are also under the protection - * of @udev's device lock (including all requests originating outside the - * USB subsystem), but autosuspend requests generated by a child device or - * interface driver may not be. Usbcore will insure that the method calls - * do not arrive during bind, unbind, or reset operations. However, drivers - * must be prepared to handle suspend calls arriving at unpredictable times. - * The only way to block such calls is to do an autoresume (preventing - * autosuspends) while holding @udev's device lock (preventing outside - * suspends). - * - * The caller must hold @udev->pm_mutex. + * Autosuspend requests originating from a child device or an interface + * driver may be made without the protection of @udev's device lock, but + * all other suspend calls will hold the lock. Usbcore will insure that + * method calls do not arrive during bind, unbind, or reset operations. + * However drivers must be prepared to handle suspend calls arriving at + * unpredictable times. * * This routine can run only in process context. */ @@ -1251,20 +1171,11 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i = 0; struct usb_interface *intf; - struct usb_device *parent = udev->parent; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) goto done; - udev->do_remote_wakeup = device_may_wakeup(&udev->dev); - - if (msg.event & PM_EVENT_AUTO) { - status = autosuspend_check(udev, 0); - if (status < 0) - goto done; - } - /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { for (; i < udev->actconfig->desc.bNumInterfaces; i++) { @@ -1279,35 +1190,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { - pm_message_t msg2; - - msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME); + msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); while (--i >= 0) { intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, msg2, 0); + usb_resume_interface(udev, intf, msg, 0); } - /* Try another autosuspend when the interfaces aren't busy */ - if (msg.event & PM_EVENT_AUTO) - autosuspend_check(udev, status == -EBUSY); - - /* If the suspend succeeded then prevent any more URB submissions, - * flush any outstanding URBs, and propagate the suspend up the tree. + /* If the suspend succeeded then prevent any more URB submissions + * and flush any outstanding URBs. */ } else { - cancel_delayed_work(&udev->autosuspend); udev->can_submit = 0; for (i = 0; i < 16; ++i) { usb_hcd_flush_endpoint(udev, udev->ep_out[i]); usb_hcd_flush_endpoint(udev, udev->ep_in[i]); } - - /* If this is just a FREEZE or a PRETHAW, udev might - * not really be suspended. Only true suspends get - * propagated up the device tree. - */ - if (parent && udev->state == USB_STATE_SUSPENDED) - usb_autosuspend_device(parent); } done: @@ -1324,23 +1221,12 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * the resume method for @udev and then calls the resume methods for all * the interface drivers in @udev. * - * Before starting the resume, the routine calls itself recursively for - * the parent device of @udev, thereby propagating the change up the device - * tree and assuring that @udev will be able to resume. If the parent is - * unable to resume successfully, the routine fails. - * - * The resume method calls are subject to mutual exclusion under control - * of @udev's pm_mutex. Many of these calls are also under the protection - * of @udev's device lock (including all requests originating outside the - * USB subsystem), but autoresume requests generated by a child device or - * interface driver may not be. Usbcore will insure that the method calls - * do not arrive during bind, unbind, or reset operations. However, drivers - * must be prepared to handle resume calls arriving at unpredictable times. - * The only way to block such calls is to do an autoresume (preventing - * other autoresumes) while holding @udev's device lock (preventing outside - * resumes). - * - * The caller must hold @udev->pm_mutex. + * Autoresume requests originating from a child device or an interface + * driver may be made without the protection of @udev's device lock, but + * all other resume calls will hold the lock. Usbcore will insure that + * method calls do not arrive during bind, unbind, or reset operations. + * However drivers must be prepared to handle resume calls arriving at + * unpredictable times. * * This routine can run only in process context. */ @@ -1349,48 +1235,18 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i; struct usb_interface *intf; - struct usb_device *parent = udev->parent; - cancel_delayed_work(&udev->autosuspend); if (udev->state == USB_STATE_NOTATTACHED) { status = -ENODEV; goto done; } udev->can_submit = 1; - /* Propagate the resume up the tree, if necessary */ - if (udev->state == USB_STATE_SUSPENDED) { - if (parent) { - status = usb_autoresume_device(parent); - if (status == 0) { - status = usb_resume_device(udev, msg); - if (status || udev->state == - USB_STATE_NOTATTACHED) { - usb_autosuspend_device(parent); - - /* It's possible usb_resume_device() - * failed after the port was - * unsuspended, causing udev to be - * logically disconnected. We don't - * want usb_disconnect() to autosuspend - * the parent again, so tell it that - * udev disconnected while still - * suspended. */ - if (udev->state == - USB_STATE_NOTATTACHED) - udev->discon_suspended = 1; - } - } - } else { - - /* We can't progagate beyond the USB subsystem, - * so if a root hub's controller is suspended - * then we're stuck. */ - status = usb_resume_device(udev, msg); - } - } else if (udev->reset_resume) + /* Resume the device */ + if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) status = usb_resume_device(udev, msg); + /* Resume the interfaces */ if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -1406,55 +1262,94 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) return status; } -#ifdef CONFIG_USB_SUSPEND +/* The device lock is held by the PM core */ +int usb_suspend(struct device *dev, pm_message_t msg) +{ + struct usb_device *udev = to_usb_device(dev); -/* Internal routine to adjust a device's usage counter and change - * its autosuspend state. - */ -static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) + do_unbind_rebind(udev, DO_UNBIND); + udev->do_remote_wakeup = device_may_wakeup(&udev->dev); + return usb_suspend_both(udev, msg); +} + +/* The device lock is held by the PM core */ +int usb_resume(struct device *dev, pm_message_t msg) { - int status = 0; + struct usb_device *udev = to_usb_device(dev); + int status; - usb_pm_lock(udev); - udev->pm_usage_cnt += inc_usage_cnt; - WARN_ON(udev->pm_usage_cnt < 0); - if (inc_usage_cnt) - udev->last_busy = jiffies; - if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { - if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev, PMSG_AUTO_RESUME); - if (status != 0) - udev->pm_usage_cnt -= inc_usage_cnt; - else if (inc_usage_cnt) + /* For PM complete calls, all we do is rebind interfaces */ + if (msg.event == PM_EVENT_ON) { + if (udev->state != USB_STATE_NOTATTACHED) + do_unbind_rebind(udev, DO_REBIND); + status = 0; + + /* For all other calls, take the device back to full power and + * tell the PM core in case it was autosuspended previously. + */ + } else { + status = usb_resume_both(udev, msg); + if (status == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); udev->last_busy = jiffies; - } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { - status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); + } } - usb_pm_unlock(udev); + + /* Avoid PM error messages for devices disconnected while suspended + * as we'll display regular disconnect messages just a bit later. + */ + if (status == -ENODEV) + status = 0; return status; } -/* usb_autosuspend_work - callback routine to autosuspend a USB device */ -void usb_autosuspend_work(struct work_struct *work) -{ - struct usb_device *udev = - container_of(work, struct usb_device, autosuspend.work); +#endif /* CONFIG_PM */ - usb_autopm_do_device(udev, 0); +#ifdef CONFIG_USB_SUSPEND + +/** + * usb_enable_autosuspend - allow a USB device to be autosuspended + * @udev: the USB device which may be autosuspended + * + * This routine allows @udev to be autosuspended. An autosuspend won't + * take place until the autosuspend_delay has elapsed and all the other + * necessary conditions are satisfied. + * + * The caller must hold @udev's device lock. + */ +int usb_enable_autosuspend(struct usb_device *udev) +{ + if (udev->autosuspend_disabled) { + udev->autosuspend_disabled = 0; + usb_autosuspend_device(udev); + } + return 0; } +EXPORT_SYMBOL_GPL(usb_enable_autosuspend); -/* usb_autoresume_work - callback routine to autoresume a USB device */ -void usb_autoresume_work(struct work_struct *work) +/** + * usb_disable_autosuspend - prevent a USB device from being autosuspended + * @udev: the USB device which may not be autosuspended + * + * This routine prevents @udev from being autosuspended and wakes it up + * if it is already autosuspended. + * + * The caller must hold @udev's device lock. + */ +int usb_disable_autosuspend(struct usb_device *udev) { - struct usb_device *udev = - container_of(work, struct usb_device, autoresume); + int rc = 0; - /* Wake it up, let the drivers do their thing, and then put it - * back to sleep. - */ - if (usb_autopm_do_device(udev, 1) == 0) - usb_autopm_do_device(udev, -1); + if (!udev->autosuspend_disabled) { + rc = usb_autoresume_device(udev); + if (rc == 0) + udev->autosuspend_disabled = 1; + } + return rc; } +EXPORT_SYMBOL_GPL(usb_disable_autosuspend); /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces @@ -1464,15 +1359,11 @@ void usb_autoresume_work(struct work_struct *work) * @udev and wants to allow it to autosuspend. Examples would be when * @udev's device file in usbfs is closed or after a configuration change. * - * @udev's usage counter is decremented. If it or any of the usage counters - * for an active interface is greater than 0, no autosuspend request will be - * queued. (If an interface driver does not support autosuspend then its - * usage counter is permanently positive.) Furthermore, if an interface - * driver requires remote-wakeup capability during autosuspend but remote - * wakeup is disabled, the autosuspend will fail. + * @udev's usage counter is decremented; if it drops to 0 and all the + * interfaces are inactive then a delayed autosuspend will be attempted. + * The attempt may fail (see autosuspend_check()). * - * Often the caller will hold @udev's device lock, but this is not - * necessary. + * The caller must hold @udev's device lock. * * This routine can run only in process context. */ @@ -1480,9 +1371,11 @@ void usb_autosuspend_device(struct usb_device *udev) { int status; - status = usb_autopm_do_device(udev, -1); - dev_vdbg(&udev->dev, "%s: cnt %d\n", - __func__, udev->pm_usage_cnt); + 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); } /** @@ -1492,17 +1385,22 @@ void usb_autosuspend_device(struct usb_device *udev) * This routine should be called when a core subsystem thinks @udev may * be ready to autosuspend. * - * @udev's usage counter left unchanged. If it or any of the usage counters - * for an active interface is greater than 0, or autosuspend is not allowed - * for any other reason, no autosuspend request will be queued. + * @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) { - usb_autopm_do_device(udev, 0); - dev_vdbg(&udev->dev, "%s: cnt %d\n", - __func__, udev->pm_usage_cnt); + int status; + + status = pm_runtime_idle(&udev->dev); + dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&udev->dev.power.usage_count), + status); } /** @@ -1511,16 +1409,15 @@ void usb_try_autosuspend_device(struct usb_device *udev) * * This routine should be called when a core subsystem wants to use @udev * and needs to guarantee that it is not suspended. No autosuspend will - * occur until usb_autosuspend_device is called. (Note that this will not - * prevent suspend events originating in the PM core.) Examples would be - * when @udev's device file in usbfs is opened or when a remote-wakeup + * occur until usb_autosuspend_device() is called. (Note that this will + * not prevent suspend events originating in the PM core.) Examples would + * be when @udev's device file in usbfs is opened or when a remote-wakeup * request is received. * * @udev's usage counter is incremented to prevent subsequent autosuspends. * However if the autoresume fails then the usage counter is re-decremented. * - * Often the caller will hold @udev's device lock, but this is not - * necessary (and attempting it might cause deadlock). + * The caller must hold @udev's device lock. * * This routine can run only in process context. */ @@ -1528,42 +1425,14 @@ int usb_autoresume_device(struct usb_device *udev) { int status; - status = usb_autopm_do_device(udev, 1); - dev_vdbg(&udev->dev, "%s: status %d cnt %d\n", - __func__, status, udev->pm_usage_cnt); - return status; -} - -/* Internal routine to adjust an interface's usage counter and change - * its device's autosuspend state. - */ -static int usb_autopm_do_interface(struct usb_interface *intf, - int inc_usage_cnt) -{ - struct usb_device *udev = interface_to_usbdev(intf); - int status = 0; - - usb_pm_lock(udev); - if (intf->condition == USB_INTERFACE_UNBOUND) - status = -ENODEV; - else { - atomic_add(inc_usage_cnt, &intf->pm_usage_cnt); - udev->last_busy = jiffies; - if (inc_usage_cnt >= 0 && - atomic_read(&intf->pm_usage_cnt) > 0) { - if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev, - PMSG_AUTO_RESUME); - if (status != 0) - atomic_sub(inc_usage_cnt, &intf->pm_usage_cnt); - else - udev->last_busy = jiffies; - } else if (inc_usage_cnt <= 0 && - atomic_read(&intf->pm_usage_cnt) <= 0) { - status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); - } - } - usb_pm_unlock(udev); + status = pm_runtime_get_sync(&udev->dev); + if (status < 0) + pm_runtime_put_sync(&udev->dev); + dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&udev->dev.power.usage_count), + status); + if (status > 0) + status = 0; return status; } @@ -1577,34 +1446,25 @@ static int usb_autopm_do_interface(struct usb_interface *intf, * closed. * * The routine decrements @intf's usage counter. When the counter reaches - * 0, a delayed autosuspend request for @intf's device is queued. When - * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all - * the other usage counters for the sibling interfaces and @intf's - * usb_device, the device and all its interfaces will be autosuspended. - * - * Note that @intf->pm_usage_cnt is owned by the interface driver. The - * core will not change its value other than the increment and decrement - * in usb_autopm_get_interface and usb_autopm_put_interface. The driver - * may use this simple counter-oriented discipline or may set the value - * any way it likes. + * 0, a delayed autosuspend request for @intf's device is attempted. The + * attempt may fail (see autosuspend_check()). * * If the driver has set @intf->needs_remote_wakeup then autosuspend will * take place only if the device's remote-wakeup facility is enabled. * - * Suspend method calls queued by this routine can arrive at any time - * while @intf is resumed and its usage counter is equal to 0. They are - * not protected by the usb_device's lock but only by its pm_mutex. - * Drivers must provide their own synchronization. - * * This routine can run only in process context. */ void usb_autopm_put_interface(struct usb_interface *intf) { - int status; + struct usb_device *udev = interface_to_usbdev(intf); + int status; - status = usb_autopm_do_interface(intf, -1); - dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, atomic_read(&intf->pm_usage_cnt)); + udev->last_busy = jiffies; + atomic_dec(&intf->pm_usage_cnt); + status = pm_runtime_put_sync(&intf->dev); + dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&intf->dev.power.usage_count), + status); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface); @@ -1612,11 +1472,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); * usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be decremented * - * This routine does essentially the same thing as - * usb_autopm_put_interface(): it decrements @intf's usage counter and - * queues a delayed autosuspend request if the counter is <= 0. The - * difference is that it does not acquire the device's pm_mutex; - * callers must handle all synchronization issues themselves. + * This routine does much the same thing as usb_autopm_put_interface(): + * It decrements @intf's usage counter and schedules a delayed + * autosuspend request if the counter is <= 0. The difference is that it + * does not perform any synchronization; callers should hold a private + * lock and handle all synchronization issues themselves. * * Typically a driver would call this routine during an URB's completion * handler, if no more URBs were pending. @@ -1626,28 +1486,58 @@ 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; - if (intf->condition == USB_INTERFACE_UNBOUND) { - status = -ENODEV; - } else { - udev->last_busy = jiffies; - atomic_dec(&intf->pm_usage_cnt); - if (udev->autosuspend_disabled || udev->autosuspend_delay < 0) - status = -EPERM; - else if (atomic_read(&intf->pm_usage_cnt) <= 0 && - !timer_pending(&udev->autosuspend.timer)) { - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + last_busy = udev->last_busy; + udev->last_busy = jiffies; + atomic_dec(&intf->pm_usage_cnt); + pm_runtime_put_noidle(&intf->dev); + + if (!udev->autosuspend_disabled) { + /* 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)); + udev->autosuspend_delay))); } } - dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, atomic_read(&intf->pm_usage_cnt)); + dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&intf->dev.power.usage_count), + status); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); /** + * usb_autopm_put_interface_no_suspend - decrement a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be decremented + * + * This routine decrements @intf's usage counter but does not carry out an + * autosuspend. + * + * This routine can run in atomic context. + */ +void usb_autopm_put_interface_no_suspend(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + udev->last_busy = jiffies; + atomic_dec(&intf->pm_usage_cnt); + pm_runtime_put_noidle(&intf->dev); +} +EXPORT_SYMBOL_GPL(usb_autopm_put_interface_no_suspend); + +/** * usb_autopm_get_interface - increment a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be incremented * @@ -1659,25 +1549,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); * or @intf is unbound. A typical example would be a character-device * driver when its device file is opened. * - * - * The routine increments @intf's usage counter. (However if the - * autoresume fails then the counter is re-decremented.) So long as the - * counter is greater than 0, autosuspend will not be allowed for @intf - * or its usb_device. When the driver is finished using @intf it should - * call usb_autopm_put_interface() to decrement the usage counter and - * queue a delayed autosuspend request (if the counter is <= 0). - * - * - * Note that @intf->pm_usage_cnt is owned by the interface driver. The - * core will not change its value other than the increment and decrement - * in usb_autopm_get_interface and usb_autopm_put_interface. The driver - * may use this simple counter-oriented discipline or may set the value - * any way it likes. - * - * Resume method calls generated by this routine can arrive at any time - * while @intf is suspended. They are not protected by the usb_device's - * lock but only by its pm_mutex. Drivers must provide their own - * synchronization. + * @intf's usage counter is incremented to prevent subsequent autosuspends. + * However if the autoresume fails then the counter is re-decremented. * * This routine can run only in process context. */ @@ -1685,9 +1558,16 @@ int usb_autopm_get_interface(struct usb_interface *intf) { int status; - status = usb_autopm_do_interface(intf, 1); - dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, atomic_read(&intf->pm_usage_cnt)); + status = pm_runtime_get_sync(&intf->dev); + if (status < 0) + pm_runtime_put_sync(&intf->dev); + else + atomic_inc(&intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&intf->dev.power.usage_count), + status); + if (status > 0) + status = 0; return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface); @@ -1697,149 +1577,207 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface); * @intf: the usb_interface whose counter should be incremented * * This routine does much the same thing as - * usb_autopm_get_interface(): it increments @intf's usage counter and - * queues an autoresume request if the result is > 0. The differences - * are that it does not acquire the device's pm_mutex (callers must - * handle all synchronization issues themselves), and it does not - * autoresume the device directly (it only queues a request). After a - * successful call, the device will generally not yet be resumed. + * usb_autopm_get_interface(): It increments @intf's usage counter and + * queues an autoresume request if the device is suspended. The + * differences are that it does not perform any synchronization (callers + * should hold a private lock and handle all synchronization issues + * themselves), and it does not autoresume the device directly (it only + * queues a request). After a successful call, the device may not yet be + * resumed. * * This routine can run in atomic context. */ int usb_autopm_get_interface_async(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); - int status = 0; + int status = 0; + enum rpm_status s; - if (intf->condition == USB_INTERFACE_UNBOUND) - status = -ENODEV; - else { + /* 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); + + if (status < 0 && status != -EINPROGRESS) + pm_runtime_put_noidle(&intf->dev); + else atomic_inc(&intf->pm_usage_cnt); - if (atomic_read(&intf->pm_usage_cnt) > 0 && - udev->state == USB_STATE_SUSPENDED) - queue_work(ksuspend_usb_wq, &udev->autoresume); - } - dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, atomic_read(&intf->pm_usage_cnt)); + dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", + __func__, atomic_read(&intf->dev.power.usage_count), + status); + if (status > 0) + status = 0; return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async); -#else - -void usb_autosuspend_work(struct work_struct *work) -{} - -void usb_autoresume_work(struct work_struct *work) -{} - -#endif /* CONFIG_USB_SUSPEND */ - /** - * usb_external_suspend_device - external suspend of a USB device and its interfaces - * @udev: the usb_device to suspend - * @msg: Power Management message describing this state transition + * usb_autopm_get_interface_no_resume - increment a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be incremented * - * This routine handles external suspend requests: ones not generated - * internally by a USB driver (autosuspend) but rather coming from the user - * (via sysfs) or the PM core (system sleep). The suspend will be carried - * out regardless of @udev's usage counter or those of its interfaces, - * and regardless of whether or not remote wakeup is enabled. Of course, - * interface drivers still have the option of failing the suspend (if - * there are unsuspended children, for example). + * This routine increments @intf's usage counter but does not carry out an + * autoresume. * - * The caller must hold @udev's device lock. + * This routine can run in atomic context. */ -int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) +void usb_autopm_get_interface_no_resume(struct usb_interface *intf) { - int status; + struct usb_device *udev = interface_to_usbdev(intf); - do_unbind_rebind(udev, DO_UNBIND); - usb_pm_lock(udev); - status = usb_suspend_both(udev, msg); - usb_pm_unlock(udev); - return status; + udev->last_busy = jiffies; + atomic_inc(&intf->pm_usage_cnt); + pm_runtime_get_noresume(&intf->dev); } +EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume); -/** - * usb_external_resume_device - external resume of a USB device and its interfaces - * @udev: the usb_device to resume - * @msg: Power Management message describing this state transition - * - * This routine handles external resume requests: ones not generated - * internally by a USB driver (autoresume) but rather coming from the user - * (via sysfs), the PM core (system resume), or the device itself (remote - * wakeup). @udev's usage counter is unaffected. - * - * The caller must hold @udev's device lock. - */ -int usb_external_resume_device(struct usb_device *udev, pm_message_t msg) +/* Internal routine to check whether we may autosuspend a device. */ +static int autosuspend_check(struct usb_device *udev) { - int status; + int i; + struct usb_interface *intf; + unsigned long suspend_time, j; - usb_pm_lock(udev); - status = usb_resume_both(udev, msg); - udev->last_busy = jiffies; - usb_pm_unlock(udev); - if (status == 0) - do_unbind_rebind(udev, DO_REBIND); + /* Fail if autosuspend is disabled, or any interfaces are in use, or + * any interface drivers require remote wakeup but it isn't available. + */ + udev->do_remote_wakeup = device_may_wakeup(&udev->dev); + if (udev->actconfig) { + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; - /* Now that the device is awake, we can start trying to autosuspend - * it again. */ - if (status == 0) - usb_try_autosuspend_device(udev); - return status; + /* We don't need to check interfaces that are + * disabled for runtime PM. Either they are unbound + * or else their drivers don't support autosuspend + * and so they are permanently active. + */ + if (intf->dev.power.disable_depth) + continue; + if (atomic_read(&intf->dev.power.usage_count) > 0) + return -EBUSY; + if (intf->needs_remote_wakeup && + !udev->do_remote_wakeup) { + dev_dbg(&udev->dev, "remote wakeup needed " + "for autosuspend\n"); + return -EOPNOTSUPP; + } + + /* Don't allow autosuspend if the device will need + * a reset-resume and any of its interface drivers + * doesn't include support or needs remote wakeup. + */ + if (udev->quirks & USB_QUIRK_RESET_RESUME) { + struct usb_driver *driver; + + driver = to_usb_driver(intf->dev.driver); + if (!driver->reset_resume || + intf->needs_remote_wakeup) + return -EOPNOTSUPP; + } + } + } + + /* 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; } -int usb_suspend(struct device *dev, pm_message_t msg) +static int usb_runtime_suspend(struct device *dev) { - struct usb_device *udev; - - udev = to_usb_device(dev); + int status = 0; - /* If udev is already suspended, we can skip this suspend and - * we should also skip the upcoming system resume. High-speed - * root hubs are an exception; they need to resume whenever the - * system wakes up in order for USB-PERSIST port handover to work - * properly. + /* 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 (udev->state == USB_STATE_SUSPENDED) { - if (udev->parent || udev->speed != USB_SPEED_HIGH) - udev->skip_sys_resume = 1; - return 0; + 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; + } } - udev->skip_sys_resume = 0; - return usb_external_suspend_device(udev, msg); + /* Runtime suspend for a USB interface doesn't mean anything. */ + return status; } -int usb_resume(struct device *dev, pm_message_t msg) +static int usb_runtime_resume(struct device *dev) { - struct usb_device *udev; - 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; - udev = to_usb_device(dev); + status = usb_resume_both(udev, PMSG_AUTO_RESUME); + udev->last_busy = jiffies; + return status; + } - /* If udev->skip_sys_resume is set then udev was already suspended - * when the system sleep started, so we don't want to resume it - * during this system wakeup. - */ - if (udev->skip_sys_resume) - return 0; - status = usb_external_resume_device(udev, msg); + /* Runtime resume for a USB interface doesn't mean anything. */ + return 0; +} - /* Avoid PM error messages for devices disconnected while suspended - * as we'll display regular disconnect messages just a bit later. +static int usb_runtime_idle(struct 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. */ - if (status == -ENODEV) - return 0; - return status; + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); + + if (autosuspend_check(udev) != 0) + return 0; + } + + pm_runtime_suspend(dev); + return 0; } -#endif /* CONFIG_PM */ +static struct dev_pm_ops usb_bus_pm_ops = { + .runtime_suspend = usb_runtime_suspend, + .runtime_resume = usb_runtime_resume, + .runtime_idle = usb_runtime_idle, +}; + +#else + +#define usb_bus_pm_ops (*(struct dev_pm_ops *) NULL) + +#endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, + .pm = &usb_bus_pm_ops, }; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index fdfaa7885515..d26b9ea981f9 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -186,6 +186,7 @@ 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) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index bfc6c2eea647..c3536f151f02 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -34,7 +34,6 @@ static int usb_open(struct inode * inode, struct file * file) int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; - lock_kernel(); down_read(&minor_rwsem); c = usb_minors[minor]; @@ -53,7 +52,6 @@ static int usb_open(struct inode * inode, struct file * file) fops_put(old_fops); done: up_read(&minor_rwsem); - unlock_kernel(); return err; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 2dcf906df569..15286533c15a 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pm_runtime.h> #include <linux/usb.h> #include <asm/io.h> @@ -37,6 +38,122 @@ /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ +#ifdef CONFIG_PM_SLEEP + +/* Coordinate handoffs between EHCI and companion controllers + * during system resume + */ + +static DEFINE_MUTEX(companions_mutex); + +#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI +#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI +#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI + +enum companion_action { + SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS +}; + +static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, + enum companion_action action) +{ + struct pci_dev *companion; + struct usb_hcd *companion_hcd; + unsigned int slot = PCI_SLOT(pdev->devfn); + + /* Iterate through other PCI functions in the same slot. + * If pdev is OHCI or UHCI then we are looking for EHCI, and + * vice versa. + */ + companion = NULL; + for (;;) { + companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion); + if (!companion) + break; + if (companion->bus != pdev->bus || + PCI_SLOT(companion->devfn) != slot) + continue; + + companion_hcd = pci_get_drvdata(companion); + if (!companion_hcd) + continue; + + /* For SET_HS_COMPANION, store a pointer to the EHCI bus in + * the OHCI/UHCI companion bus structure. + * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus + * in the OHCI/UHCI companion bus structure. + * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI + * companion controllers have fully resumed. + */ + + if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) && + companion->class == CL_EHCI) { + /* action must be SET_HS_COMPANION */ + dev_dbg(&companion->dev, "HS companion for %s\n", + dev_name(&pdev->dev)); + hcd->self.hs_companion = &companion_hcd->self; + + } else if (pdev->class == CL_EHCI && + (companion->class == CL_OHCI || + companion->class == CL_UHCI)) { + switch (action) { + case SET_HS_COMPANION: + dev_dbg(&pdev->dev, "HS companion for %s\n", + dev_name(&companion->dev)); + companion_hcd->self.hs_companion = &hcd->self; + break; + case CLEAR_HS_COMPANION: + companion_hcd->self.hs_companion = NULL; + break; + case WAIT_FOR_COMPANIONS: + device_pm_wait_for_dev(&pdev->dev, + &companion->dev); + break; + } + } + } +} + +static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) +{ + mutex_lock(&companions_mutex); + dev_set_drvdata(&pdev->dev, hcd); + companion_common(pdev, hcd, SET_HS_COMPANION); + mutex_unlock(&companions_mutex); +} + +static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) +{ + mutex_lock(&companions_mutex); + dev_set_drvdata(&pdev->dev, NULL); + + /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */ + if (pdev->class == CL_OHCI || pdev->class == CL_UHCI) + hcd->self.hs_companion = NULL; + + /* Otherwise search for companion buses and clear their pointers */ + else + companion_common(pdev, hcd, CLEAR_HS_COMPANION); + mutex_unlock(&companions_mutex); +} + +static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd) +{ + /* Only EHCI controllers need to wait. + * No locking is needed because a controller cannot be resumed + * while one of its companions is getting unbound. + */ + if (pdev->class == CL_EHCI) + companion_common(pdev, hcd, WAIT_FOR_COMPANIONS); +} + +#else /* !CONFIG_PM_SLEEP */ + +static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} +static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} +static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {} + +#endif /* !CONFIG_PM_SLEEP */ /*-------------------------------------------------------------------------*/ @@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (region == PCI_ROM_RESOURCE) { dev_dbg(&dev->dev, "no i/o regions available\n"); retval = -EBUSY; - goto err1; + goto err2; } } @@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) goto err4; + set_hs_companion(dev, hcd); return retval; err4: @@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } else release_region(hcd->rsrc_start, hcd->rsrc_len); err2: + clear_hs_companion(dev, hcd); usb_put_hcd(hcd); err1: pci_disable_device(dev); @@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev) } else { release_region(hcd->rsrc_start, hcd->rsrc_len); } + clear_hs_companion(dev, hcd); usb_put_hcd(hcd); pci_disable_device(dev); } @@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->driver->pci_resume) { + /* This call should be made only during system resume, + * not during runtime resume. + */ + wait_for_companions(pci_dev, hcd); + retval = hcd->driver->pci_resume(hcd, hibernated); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 80995ef0868c..2f8cedda8007 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -39,6 +39,7 @@ #include <linux/platform_device.h> #include <linux/workqueue.h> #include <linux/mutex.h> +#include <linux/pm_runtime.h> #include <linux/usb.h> @@ -141,7 +142,7 @@ static const u8 usb3_rh_dev_descriptor[18] = { 0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */ 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ - 0x02, 0x00, /* __le16 idProduct; device 0x0002 */ + 0x03, 0x00, /* __le16 idProduct; device 0x0003 */ KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ 0x03, /* __u8 iManufacturer; */ @@ -1670,11 +1671,16 @@ int usb_hcd_alloc_bandwidth(struct usb_device *udev, } } for (i = 0; i < num_intfs; ++i) { + struct usb_host_interface *first_alt; + int iface_num; + + first_alt = &new_config->intf_cache[i]->altsetting[0]; + iface_num = first_alt->desc.bInterfaceNumber; /* Set up endpoints for alternate interface setting 0 */ - alt = usb_find_alt_setting(new_config, i, 0); + alt = usb_find_alt_setting(new_config, iface_num, 0); if (!alt) /* No alt setting 0? Pick the first setting. */ - alt = &new_config->intf_cache[i]->altsetting[0]; + alt = first_alt; for (j = 0; j < alt->desc.bNumEndpoints; j++) { ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]); @@ -1853,6 +1859,10 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) return status; } +#endif /* CONFIG_PM */ + +#ifdef CONFIG_USB_SUSPEND + /* Workqueue routine for root-hub remote wakeup */ static void hcd_resume_work(struct work_struct *work) { @@ -1860,8 +1870,7 @@ static void hcd_resume_work(struct work_struct *work) struct usb_device *udev = hcd->self.root_hub; usb_lock_device(udev); - usb_mark_last_busy(udev); - usb_external_resume_device(udev, PMSG_REMOTE_RESUME); + usb_remote_wakeup(udev); usb_unlock_device(udev); } @@ -1880,12 +1889,12 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) - queue_work(ksuspend_usb_wq, &hcd->wakeup_work); + queue_work(pm_wq, &hcd->wakeup_work); spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); -#endif +#endif /* CONFIG_USB_SUSPEND */ /*-------------------------------------------------------------------------*/ @@ -2030,7 +2039,7 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif mutex_init(&hcd->bandwidth_mutex); @@ -2230,7 +2239,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND cancel_work_sync(&hcd->wakeup_work); #endif diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index bbe2b924aae8..a3cdb09734ab 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -80,7 +80,7 @@ struct usb_hcd { struct timer_list rh_timer; /* drives root-hub polling */ struct urb *status_urb; /* the current status urb */ -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND struct work_struct wakeup_work; /* for remote wakeup */ #endif @@ -248,7 +248,7 @@ struct hc_driver { /* xHCI specific functions */ /* Called by usb_alloc_dev to alloc HC device structures */ int (*alloc_dev)(struct usb_hcd *, struct usb_device *); - /* Called by usb_release_dev to free HC device structures */ + /* Called by usb_disconnect to free HC device structures */ void (*free_dev)(struct usb_hcd *, struct usb_device *); /* Bandwidth computation functions */ @@ -286,6 +286,7 @@ struct hc_driver { */ int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); + int (*reset_device)(struct usb_hcd *, struct usb_device *); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); @@ -463,16 +464,20 @@ extern int usb_find_interface_driver(struct usb_device *dev, #define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) #ifdef CONFIG_PM -extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); extern void usb_root_hub_lost_power(struct usb_device *rhdev); extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg); extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg); +#endif /* CONFIG_PM */ + +#ifdef CONFIG_USB_SUSPEND +extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { return; } -#endif /* CONFIG_PM */ +#endif /* CONFIG_USB_SUSPEND */ + /* * USB device fs stuff diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 35cc8b9ba1f5..0940ccd6f4f4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -22,6 +22,7 @@ #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> @@ -71,7 +72,6 @@ struct usb_hub { unsigned mA_per_port; /* current for each child */ - unsigned init_done:1; unsigned limited_power:1; unsigned quiescing:1; unsigned disconnected:1; @@ -820,7 +820,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) } init3: hub->quiescing = 0; - hub->init_done = 1; status = usb_submit_urb(hub->urb, GFP_NOIO); if (status < 0) @@ -861,11 +860,6 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) int i; cancel_delayed_work_sync(&hub->init_work); - if (!hub->init_done) { - hub->init_done = 1; - usb_autopm_put_interface_no_suspend( - to_usb_interface(hub->intfdev)); - } /* khubd and related activity won't re-trigger */ hub->quiescing = 1; @@ -1224,6 +1218,9 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); + /* Hubs have proper suspend/resume support */ + usb_enable_autosuspend(hdev); + if (hdev->level == MAX_TOPO_LEVEL) { dev_err(&intf->dev, "Unsupported bus topology: hub nested too deep\n"); @@ -1402,10 +1399,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (udev->children[i]) recursively_mark_NOTATTACHED(udev->children[i]); } - if (udev->state == USB_STATE_SUSPENDED) { - udev->discon_suspended = 1; + if (udev->state == USB_STATE_SUSPENDED) udev->active_duration -= jiffies; - } udev->state = USB_STATE_NOTATTACHED; } @@ -1448,11 +1443,11 @@ void usb_set_device_state(struct usb_device *udev, || new_state == USB_STATE_SUSPENDED) ; /* No change to wakeup settings */ else if (new_state == USB_STATE_CONFIGURED) - device_init_wakeup(&udev->dev, + device_set_wakeup_capable(&udev->dev, (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP)); else - device_init_wakeup(&udev->dev, 0); + device_set_wakeup_capable(&udev->dev, 0); } if (udev->state == USB_STATE_SUSPENDED && new_state != USB_STATE_SUSPENDED) @@ -1529,31 +1524,15 @@ static void update_address(struct usb_device *udev, int devnum) udev->devnum = devnum; } -#ifdef CONFIG_USB_SUSPEND - -static void usb_stop_pm(struct usb_device *udev) +static void hub_free_dev(struct usb_device *udev) { - /* Synchronize with the ksuspend thread to prevent any more - * autosuspend requests from being submitted, and decrement - * the parent's count of unsuspended children. - */ - usb_pm_lock(udev); - if (udev->parent && !udev->discon_suspended) - usb_autosuspend_device(udev->parent); - usb_pm_unlock(udev); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); - /* Stop any autosuspend or autoresume requests already submitted */ - cancel_delayed_work_sync(&udev->autosuspend); - cancel_work_sync(&udev->autoresume); + /* Root hubs aren't real devices, so don't free HCD resources */ + if (hcd->driver->free_dev && udev->parent) + hcd->driver->free_dev(hcd, udev); } -#else - -static inline void usb_stop_pm(struct usb_device *udev) -{ } - -#endif - /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected @@ -1622,7 +1601,7 @@ void usb_disconnect(struct usb_device **pdev) *pdev = NULL; spin_unlock_irq(&device_state_lock); - usb_stop_pm(udev); + hub_free_dev(udev); put_device(&udev->dev); } @@ -1799,9 +1778,18 @@ int usb_new_device(struct usb_device *udev) { int err; - /* Increment the parent's count of unsuspended children */ - if (udev->parent) - usb_autoresume_device(udev->parent); + if (udev->parent) { + /* Initialize non-root-hub device wakeup to disabled; + * device (un)configuration controls wakeup capable + * sysfs power/wakeup controls wakeup enabled/disabled + */ + device_init_wakeup(&udev->dev, 0); + device_set_wakeup_enable(&udev->dev, 1); + } + + /* Tell the runtime-PM framework the device is active */ + pm_runtime_set_active(&udev->dev); + pm_runtime_enable(&udev->dev); usb_detect_quirks(udev); err = usb_enumerate_device(udev); /* Read descriptors */ @@ -1817,6 +1805,7 @@ int usb_new_device(struct usb_device *udev) /* Tell the world! */ announce_device(udev); + device_enable_async_suspend(&udev->dev); /* Register the device. The device driver is responsible * for configuring the device and invoking the add-device * notifier chain (used by usbfs and possibly others). @@ -1832,7 +1821,8 @@ int usb_new_device(struct usb_device *udev) fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); - usb_stop_pm(udev); + pm_runtime_disable(&udev->dev); + pm_runtime_set_suspended(&udev->dev); return err; } @@ -1981,7 +1971,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (!(portstatus & USB_PORT_STAT_RESET) && (portstatus & USB_PORT_STAT_ENABLE)) { if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_VARIABLE; + udev->speed = USB_SPEED_WIRELESS; else if (portstatus & USB_PORT_STAT_HIGH_SPEED) udev->speed = USB_SPEED_HIGH; else if (portstatus & USB_PORT_STAT_LOW_SPEED) @@ -2007,7 +1997,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay) { int i, status; + struct usb_hcd *hcd; + hcd = bus_to_hcd(udev->bus); /* Block EHCI CF initialization during the port reset. * Some companion controllers don't like it when they mix. */ @@ -2035,6 +2027,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* TRSTRCY = 10 ms; plus some extra */ msleep(10 + 40); update_address(udev, 0); + if (hcd->driver->reset_device) { + status = hcd->driver->reset_device(hcd, udev); + if (status < 0) { + dev_err(&udev->dev, "Cannot reset " + "HCD device state\n"); + break; + } + } /* FALL THROUGH */ case -ENOTCONN: case -ENODEV: @@ -2380,14 +2380,17 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) } /* caller has locked udev */ -static int remote_wakeup(struct usb_device *udev) +int usb_remote_wakeup(struct usb_device *udev) { int status = 0; if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); - usb_mark_last_busy(udev); - status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME); + status = usb_autoresume_device(udev); + if (status == 0) { + /* Let the drivers do their thing, then... */ + usb_autosuspend_device(udev); + } } return status; } @@ -2424,11 +2427,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) return status; } -static inline int remote_wakeup(struct usb_device *udev) -{ - return 0; -} - #endif static int hub_suspend(struct usb_interface *intf, pm_message_t msg) @@ -2495,11 +2493,6 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); #else /* CONFIG_PM */ -static inline int remote_wakeup(struct usb_device *udev) -{ - return 0; -} - #define hub_suspend NULL #define hub_resume NULL #define hub_reset_resume NULL @@ -2644,14 +2637,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, mutex_lock(&usb_address0_mutex); - if ((hcd->driver->flags & HCD_USB3) && udev->config) { - /* FIXME this will need special handling by the xHCI driver. */ - dev_dbg(&udev->dev, - "xHCI reset of configured device " - "not supported yet.\n"); - retval = -EINVAL; - goto fail; - } else if (!udev->config && oldspeed == USB_SPEED_SUPER) { + 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 { @@ -2677,7 +2663,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, */ switch (udev->speed) { case USB_SPEED_SUPER: - case USB_SPEED_VARIABLE: /* fixed at 512 */ + case USB_SPEED_WIRELESS: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); break; case USB_SPEED_HIGH: /* fixed at 64 */ @@ -2705,7 +2691,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, case USB_SPEED_SUPER: speed = "super"; break; - case USB_SPEED_VARIABLE: + case USB_SPEED_WIRELESS: speed = "variable"; type = "Wireless "; break; @@ -3005,7 +2991,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* For a suspended device, treat this as a * remote wakeup event. */ - status = remote_wakeup(udev); + status = usb_remote_wakeup(udev); #endif } else { @@ -3191,6 +3177,7 @@ loop_disable: loop: usb_ep0_reinit(udev); release_address(udev); + hub_free_dev(udev); usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; @@ -3258,7 +3245,7 @@ static void hub_events(void) * disconnected while waiting for the lock to succeed. */ usb_lock_device(hdev); if (unlikely(hub->disconnected)) - goto loop2; + goto loop_disconnected; /* If the hub has died, clean up after it */ if (hdev->state == USB_STATE_NOTATTACHED) { @@ -3351,7 +3338,7 @@ static void hub_events(void) msleep(10); usb_lock_device(udev); - ret = remote_wakeup(hdev-> + ret = usb_remote_wakeup(hdev-> children[i-1]); usb_unlock_device(udev); if (ret < 0) @@ -3418,7 +3405,7 @@ static void hub_events(void) * kick_khubd() and allow autosuspend. */ usb_autopm_put_interface(intf); - loop2: + loop_disconnected: usb_unlock_device(hdev); kref_put(&hub->kref, hub_release); @@ -3445,7 +3432,7 @@ static int hub_thread(void *__unused) return 0; } -static struct usb_device_id hub_id_table [] = { +static const struct usb_device_id hub_id_table[] = { { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, .bDeviceClass = USB_CLASS_HUB}, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9bc95fec793f..cd220277c6c3 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1316,7 +1316,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) alt = usb_altnum_to_altsetting(iface, alternate); if (!alt) { - dev_warn(&dev->dev, "selecting invalid altsetting %d", + dev_warn(&dev->dev, "selecting invalid altsetting %d\n", alternate); return -EINVAL; } @@ -1471,7 +1471,7 @@ int usb_reset_configuration(struct usb_device *dev) /* If not, reinstate the old alternate settings */ if (retval < 0) { reset_old_alts: - for (; i >= 0; i--) { + for (i--; i >= 0; i--) { struct usb_interface *intf = config->interface[i]; struct usb_host_interface *alt; @@ -1843,7 +1843,6 @@ free_interfaces: intf->dev.dma_mask = dev->dev.dma_mask; INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); device_initialize(&intf->dev); - mark_quiesced(intf); dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); @@ -1867,6 +1866,7 @@ free_interfaces: "adding %s (config #%d, interface %d)\n", dev_name(&intf->dev), configuration, intf->cur_altsetting->desc.bInterfaceNumber); + device_enable_async_suspend(&intf->dev); ret = device_add(&intf->dev); if (ret != 0) { dev_err(&dev->dev, "device_add(%s) --> %d\n", diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index ab93918d9207..f073c5cb4e7b 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -103,10 +103,19 @@ void usb_detect_quirks(struct usb_device *udev) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", udev->quirks); - /* By default, disable autosuspend for all non-hubs */ #ifdef CONFIG_USB_SUSPEND - if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) - udev->autosuspend_disabled = 1; + + /* 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 */ @@ -120,6 +129,7 @@ void usb_detect_quirks(struct usb_device *udev) * for all devices. It will affect things like hub resets * and EMF-related port disables. */ - udev->persist_enabled = 1; + if (!(udev->quirks & USB_QUIRK_RESET_MORPHS)) + udev->persist_enabled = 1; #endif /* CONFIG_PM */ } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5f3908f6e2dc..43c002e3a9aa 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -115,7 +115,7 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf) case USB_SPEED_HIGH: speed = "480"; break; - case USB_SPEED_VARIABLE: + case USB_SPEED_WIRELESS: speed = "480"; break; case USB_SPEED_SUPER: @@ -191,6 +191,36 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); static ssize_t +show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS)); +} + +static ssize_t +set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int config; + + if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1) + return -EINVAL; + usb_lock_device(udev); + if (config) + udev->quirks |= USB_QUIRK_RESET_MORPHS; + else + udev->quirks &= ~USB_QUIRK_RESET_MORPHS; + usb_unlock_device(udev); + return count; +} + +static DEVICE_ATTR(avoid_reset_quirk, S_IRUGO | S_IWUSR, + show_avoid_reset_quirk, set_avoid_reset_quirk); + +static ssize_t show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -226,9 +256,10 @@ set_persist(struct device *dev, struct device_attribute *attr, if (sscanf(buf, "%d", &value) != 1) return -EINVAL; - usb_pm_lock(udev); + + usb_lock_device(udev); udev->persist_enabled = !!value; - usb_pm_unlock(udev); + usb_unlock_device(udev); return count; } @@ -315,20 +346,34 @@ 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; + int value, old_delay; + int rc; if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || value <= - INT_MAX/HZ) return -EINVAL; value *= HZ; + usb_lock_device(udev); + old_delay = udev->autosuspend_delay; udev->autosuspend_delay = value; - if (value >= 0) - usb_try_autosuspend_device(udev); - else { - if (usb_autoresume_device(udev) == 0) + + 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); return count; } @@ -356,34 +401,25 @@ set_level(struct device *dev, struct device_attribute *attr, struct usb_device *udev = to_usb_device(dev); int len = count; char *cp; - int rc = 0; - int old_autosuspend_disabled; + int rc; cp = memchr(buf, '\n', count); if (cp) len = cp - buf; usb_lock_device(udev); - old_autosuspend_disabled = udev->autosuspend_disabled; - /* Setting the flags without calling usb_pm_lock is a subject to - * races, but who cares... - */ if (len == sizeof on_string - 1 && - strncmp(buf, on_string, len) == 0) { - udev->autosuspend_disabled = 1; - rc = usb_external_resume_device(udev, PMSG_USER_RESUME); + strncmp(buf, on_string, len) == 0) + rc = usb_disable_autosuspend(udev); - } else if (len == sizeof auto_string - 1 && - strncmp(buf, auto_string, len) == 0) { - udev->autosuspend_disabled = 0; - rc = usb_external_resume_device(udev, PMSG_USER_RESUME); + else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) + rc = usb_enable_autosuspend(udev); - } else + else rc = -EINVAL; - if (rc) - udev->autosuspend_disabled = old_autosuspend_disabled; usb_unlock_device(udev); return (rc < 0 ? rc : count); } @@ -558,6 +594,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_version.attr, &dev_attr_maxchild.attr, &dev_attr_quirks.attr, + &dev_attr_avoid_reset_quirk.attr, &dev_attr_authorized.attr, &dev_attr_remove.attr, NULL, diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index e7cae1334693..27080561a1c2 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -387,6 +387,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { unsigned int orig_flags = urb->transfer_flags; unsigned int allowed; + static int pipetypes[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT + }; + + /* Check that the pipe's type matches the endpoint's type */ + if (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_SETUP_DMA_MAP | @@ -430,7 +437,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) case USB_ENDPOINT_XFER_INT: /* too small? */ switch (dev->speed) { - case USB_SPEED_VARIABLE: + case USB_SPEED_WIRELESS: if (urb->interval < 6) return -EINVAL; break; @@ -446,7 +453,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (urb->interval > (1 << 15)) return -EINVAL; max = 1 << 15; - case USB_SPEED_VARIABLE: + case USB_SPEED_WIRELESS: if (urb->interval > 16) return -EINVAL; break; @@ -473,7 +480,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) default: return -EINVAL; } - if (dev->speed != USB_SPEED_VARIABLE) { + if (dev->speed != USB_SPEED_WIRELESS) { /* Round down to a power of 2, no more than max */ urb->interval = min(max, 1 << ilog2(urb->interval)); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0daff0d968ba..1297e9b16a51 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -49,9 +49,6 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ -/* Workqueue for autosuspend and for remote wakeup of root hubs */ -struct workqueue_struct *ksuspend_usb_wq; - #ifdef CONFIG_USB_SUSPEND static int usb_autosuspend_delay = 2; /* Default delay value, * in seconds */ @@ -228,9 +225,6 @@ static void usb_release_dev(struct device *dev) hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); - /* Root hubs aren't real devices, so don't free HCD resources */ - if (hcd->driver->free_dev && udev->parent) - hcd->driver->free_dev(hcd, udev); usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); @@ -264,23 +258,6 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env) #ifdef CONFIG_PM -static int ksuspend_usb_init(void) -{ - /* This workqueue is supposed to be both freezable and - * singlethreaded. Its job doesn't justify running on more - * than one CPU. - */ - ksuspend_usb_wq = create_freezeable_workqueue("ksuspend_usbd"); - if (!ksuspend_usb_wq) - return -ENOMEM; - return 0; -} - -static void ksuspend_usb_cleanup(void) -{ - destroy_workqueue(ksuspend_usb_wq); -} - /* USB device Power-Management thunks. * There's no need to distinguish here between quiescing a USB device * and powering it down; the generic_suspend() routine takes care of @@ -296,7 +273,7 @@ static int usb_dev_prepare(struct device *dev) static void usb_dev_complete(struct device *dev) { /* Currently used only for rebinding interfaces */ - usb_resume(dev, PMSG_RESUME); /* Message event is meaningless */ + usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */ } static int usb_dev_suspend(struct device *dev) @@ -342,9 +319,7 @@ static const struct dev_pm_ops usb_device_pm_ops = { #else -#define ksuspend_usb_init() 0 -#define ksuspend_usb_cleanup() do {} while (0) -#define usb_device_pm_ops (*(struct dev_pm_ops *)0) +#define usb_device_pm_ops (*(struct dev_pm_ops *) NULL) #endif /* CONFIG_PM */ @@ -472,9 +447,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, INIT_LIST_HEAD(&dev->filelist); #ifdef CONFIG_PM - mutex_init(&dev->pm_mutex); - INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); - INIT_WORK(&dev->autoresume, usb_autoresume_work); dev->autosuspend_delay = usb_autosuspend_delay * HZ; dev->connect_time = jiffies; dev->active_duration = -jiffies; @@ -1117,9 +1089,6 @@ static int __init usb_init(void) if (retval) goto out; - retval = ksuspend_usb_init(); - if (retval) - goto out; retval = bus_register(&usb_bus_type); if (retval) goto bus_register_failed; @@ -1159,7 +1128,7 @@ major_init_failed: bus_notifier_failed: bus_unregister(&usb_bus_type); bus_register_failed: - ksuspend_usb_cleanup(); + usb_debugfs_cleanup(); out: return retval; } @@ -1181,7 +1150,6 @@ static void __exit usb_exit(void) usb_hub_cleanup(); bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); - ksuspend_usb_cleanup(); usb_debugfs_cleanup(); } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 4c36c7f512a0..cd882203ad34 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -55,24 +55,8 @@ extern void usb_major_cleanup(void); extern int usb_suspend(struct device *dev, pm_message_t msg); extern int usb_resume(struct device *dev, pm_message_t msg); -extern void usb_autosuspend_work(struct work_struct *work); -extern void usb_autoresume_work(struct work_struct *work); extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); -extern int usb_external_suspend_device(struct usb_device *udev, - pm_message_t msg); -extern int usb_external_resume_device(struct usb_device *udev, - pm_message_t msg); - -static inline void usb_pm_lock(struct usb_device *udev) -{ - mutex_lock_nested(&udev->pm_mutex, udev->level); -} - -static inline void usb_pm_unlock(struct usb_device *udev) -{ - mutex_unlock(&udev->pm_mutex); -} #else @@ -86,9 +70,6 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) return 0; } -static inline void usb_pm_lock(struct usb_device *udev) {} -static inline void usb_pm_unlock(struct usb_device *udev) {} - #endif #ifdef CONFIG_USB_SUSPEND @@ -96,6 +77,7 @@ static inline void usb_pm_unlock(struct usb_device *udev) {} 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 @@ -106,9 +88,13 @@ static inline int usb_autoresume_device(struct usb_device *udev) return 0; } +static inline int usb_remote_wakeup(struct usb_device *udev) +{ + return 0; +} + #endif -extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; extern struct device_type usb_device_type; extern struct device_type usb_if_device_type; @@ -138,23 +124,6 @@ static inline int is_usb_device_driver(struct device_driver *drv) for_devices; } -/* Interfaces and their "power state" are owned by usbcore */ - -static inline void mark_active(struct usb_interface *f) -{ - f->is_active = 1; -} - -static inline void mark_quiesced(struct usb_interface *f) -{ - f->is_active = 0; -} - -static inline int is_active(const struct usb_interface *f) -{ - return f->is_active; -} - /* for labeling diagnostics */ extern const char *usbcore_name; |