diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 85 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 277 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 1059 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 129 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 125 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 34 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 301 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 96 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 156 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 29 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 110 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 44 |
17 files changed, 1483 insertions, 990 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 96f11715cd26..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) @@ -494,7 +502,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, return 0; /* allocate 2^1 pages = 8K (on i386); * should be more than enough for one device */ - pages_start = (char *)__get_free_pages(GFP_KERNEL, 1); + pages_start = (char *)__get_free_pages(GFP_NOIO, 1); if (!pages_start) return -ENOMEM; @@ -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 181f78c84105..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; @@ -1312,9 +1345,9 @@ static int processcompl(struct async *as, void __user * __user *arg) void __user *addr = as->userurb; unsigned int i; - if (as->userbuffer) + if (as->userbuffer && urb->actual_length) if (copy_to_user(as->userbuffer, urb->transfer_buffer, - urb->transfer_buffer_length)) + urb->actual_length)) goto err_out; if (put_user(as->status, &userurb->status)) goto err_out; @@ -1334,14 +1367,11 @@ static int processcompl(struct async *as, void __user * __user *arg) } } - free_async(as); - if (put_user(addr, (void __user * __user *)arg)) return -EFAULT; return 0; err_out: - free_async(as); return -EFAULT; } @@ -1371,8 +1401,11 @@ static struct async *reap_as(struct dev_state *ps) static int proc_reapurb(struct dev_state *ps, void __user *arg) { struct async *as = reap_as(ps); - if (as) - return processcompl(as, (void __user * __user *)arg); + if (as) { + int retval = processcompl(as, (void __user * __user *)arg); + free_async(as); + return retval; + } if (signal_pending(current)) return -EINTR; return -EIO; @@ -1380,14 +1413,59 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) { + int retval; struct async *as; - if (!(as = async_getcompleted(ps))) - return -EAGAIN; - return processcompl(as, (void __user * __user *)arg); + as = async_getcompleted(ps); + retval = -EAGAIN; + if (as) { + retval = processcompl(as, (void __user * __user *)arg); + free_async(as); + } + return retval; } #ifdef CONFIG_COMPAT +static int proc_control_compat(struct dev_state *ps, + struct usbdevfs_ctrltransfer32 __user *p32) +{ + struct usbdevfs_ctrltransfer __user *p; + __u32 udata; + p = compat_alloc_user_space(sizeof(*p)); + if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) || + get_user(udata, &p32->data) || + put_user(compat_ptr(udata), &p->data)) + return -EFAULT; + return proc_control(ps, p); +} + +static int proc_bulk_compat(struct dev_state *ps, + struct usbdevfs_bulktransfer32 __user *p32) +{ + struct usbdevfs_bulktransfer __user *p; + compat_uint_t n; + compat_caddr_t addr; + + p = compat_alloc_user_space(sizeof(*p)); + + if (get_user(n, &p32->ep) || put_user(n, &p->ep) || + get_user(n, &p32->len) || put_user(n, &p->len) || + get_user(n, &p32->timeout) || put_user(n, &p->timeout) || + get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data)) + return -EFAULT; + + return proc_bulk(ps, p); +} +static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_disconnectsignal32 ds; + + if (copy_from_user(&ds, arg, sizeof(ds))) + return -EFAULT; + ps->discsignr = ds.signr; + ps->disccontext = compat_ptr(ds.context); + return 0; +} static int get_urb32(struct usbdevfs_urb *kurb, struct usbdevfs_urb32 __user *uurb) @@ -1435,9 +1513,9 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) void __user *addr = as->userurb; unsigned int i; - if (as->userbuffer) + if (as->userbuffer && urb->actual_length) if (copy_to_user(as->userbuffer, urb->transfer_buffer, - urb->transfer_buffer_length)) + urb->actual_length)) return -EFAULT; if (put_user(as->status, &userurb->status)) return -EFAULT; @@ -1457,7 +1535,6 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) } } - free_async(as); if (put_user(ptr_to_compat(addr), (u32 __user *)arg)) return -EFAULT; return 0; @@ -1466,8 +1543,11 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) { struct async *as = reap_as(ps); - if (as) - return processcompl_compat(as, (void __user * __user *)arg); + if (as) { + int retval = processcompl_compat(as, (void __user * __user *)arg); + free_async(as); + return retval; + } if (signal_pending(current)) return -EINTR; return -EIO; @@ -1475,13 +1555,19 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) { + int retval; struct async *as; - if (!(as = async_getcompleted(ps))) - return -EAGAIN; - return processcompl_compat(as, (void __user * __user *)arg); + retval = -EAGAIN; + as = async_getcompleted(ps); + if (as) { + retval = processcompl_compat(as, (void __user * __user *)arg); + free_async(as); + } + return retval; } + #endif static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) @@ -1575,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; } @@ -1648,16 +1737,17 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) * are assuming that somehow the configuration has been prevented from * changing. But there's no mechanism to ensure that... */ -static int usbdev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long usbdev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p) { struct dev_state *ps = file->private_data; + struct inode *inode = file->f_path.dentry->d_inode; struct usb_device *dev = ps->dev; - void __user *p = (void __user *)arg; int ret = -ENOTTY; if (!(file->f_mode & FMODE_WRITE)) return -EPERM; + usb_lock_device(dev); if (!connected(ps)) { usb_unlock_device(dev); @@ -1726,6 +1816,24 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; #ifdef CONFIG_COMPAT + case USBDEVFS_CONTROL32: + snoop(&dev->dev, "%s: CONTROL32\n", __func__); + ret = proc_control_compat(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_BULK32: + snoop(&dev->dev, "%s: BULK32\n", __func__); + ret = proc_bulk_compat(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_DISCSIGNAL32: + snoop(&dev->dev, "%s: DISCSIGNAL32\n", __func__); + ret = proc_disconnectsignal_compat(ps, p); + break; case USBDEVFS_SUBMITURB32: snoop(&dev->dev, "%s: SUBMITURB32\n", __func__); @@ -1745,7 +1853,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; case USBDEVFS_IOCTL32: - snoop(&dev->dev, "%s: IOCTL\n", __func__); + snoop(&dev->dev, "%s: IOCTL32\n", __func__); ret = proc_ioctl_compat(ps, ptr_to_compat(p)); break; #endif @@ -1801,6 +1909,28 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, return ret; } +static long usbdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + ret = usbdev_do_ioctl(file, cmd, (void __user *)arg); + + return ret; +} + +#ifdef CONFIG_COMPAT +static long usbdev_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg)); + + return ret; +} +#endif + /* No kernel lock - fine */ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) @@ -1817,13 +1947,16 @@ static unsigned int usbdev_poll(struct file *file, } const struct file_operations usbdev_file_operations = { - .owner = THIS_MODULE, - .llseek = usbdev_lseek, - .read = usbdev_read, - .poll = usbdev_poll, - .ioctl = usbdev_ioctl, - .open = usbdev_open, - .release = usbdev_release, + .owner = THIS_MODULE, + .llseek = usbdev_lseek, + .read = usbdev_read, + .poll = usbdev_poll, + .unlocked_ioctl = usbdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = usbdev_compat_ioctl, +#endif + .open = usbdev_open, + .release = usbdev_release, }; static void usbdev_remove(struct usb_device *udev) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4f864472c5c4..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" @@ -83,6 +83,47 @@ static ssize_t store_new_id(struct device_driver *driver, } static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); +/** + * store_remove_id - remove a USB device ID from this driver + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Removes a dynamic usb device ID from this driver. + */ +static ssize_t +store_remove_id(struct device_driver *driver, const char *buf, size_t count) +{ + struct usb_dynid *dynid, *n; + struct usb_driver *usb_driver = to_usb_driver(driver); + u32 idVendor = 0; + u32 idProduct = 0; + int fields = 0; + int retval = 0; + + fields = sscanf(buf, "%x %x", &idVendor, &idProduct); + if (fields < 2) + return -EINVAL; + + spin_lock(&usb_driver->dynids.lock); + list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) { + struct usb_device_id *id = &dynid->id; + if ((id->idVendor == idVendor) && + (id->idProduct == idProduct)) { + list_del(&dynid->node); + kfree(dynid); + retval = 0; + break; + } + } + spin_unlock(&usb_driver->dynids.lock); + + if (retval) + return retval; + return count; +} +static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); + static int usb_create_newid_file(struct usb_driver *usb_drv) { int error = 0; @@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) &driver_attr_new_id); } +static int +usb_create_removeid_file(struct usb_driver *drv) +{ + int error = 0; + if (drv->probe != NULL) + error = driver_create_file(&drv->drvwrap.driver, + &driver_attr_remove_id); + return error; +} + +static void usb_remove_removeid_file(struct usb_driver *drv) +{ + driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id); +} + static void usb_free_dynids(struct usb_driver *usb_drv) { struct usb_dynid *dynid, *n; @@ -128,6 +184,16 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) { } +static int +usb_create_removeid_file(struct usb_driver *drv) +{ + return 0; +} + +static void usb_remove_removeid_file(struct usb_driver *drv) +{ +} + static inline void usb_free_dynids(struct usb_driver *usb_drv) { } @@ -155,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__); @@ -164,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; } @@ -208,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; - - /* 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; + if (!id) + return error; - /* The interface should always appear to be in use - * unless the driver suports autosuspend. - */ - atomic_set(&intf->pm_usage_cnt, !driver->supports_autosuspend); + dev_dbg(dev, "%s - got id\n", __func__); - /* 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; } @@ -311,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); @@ -344,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) @@ -354,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() @@ -404,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); @@ -625,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)) { @@ -639,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; } @@ -774,19 +857,34 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, INIT_LIST_HEAD(&new_driver->dynids.list); retval = driver_register(&new_driver->drvwrap.driver); + if (retval) + goto out; - if (!retval) { - pr_info("%s: registered new interface driver %s\n", + usbfs_update_special(); + + retval = usb_create_newid_file(new_driver); + if (retval) + goto out_newid; + + retval = usb_create_removeid_file(new_driver); + if (retval) + goto out_removeid; + + pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name); - usbfs_update_special(); - usb_create_newid_file(new_driver); - } else { - printk(KERN_ERR "%s: error %d registering interface " - " driver %s\n", - usbcore_name, retval, new_driver->name); - } +out: return retval; + +out_removeid: + usb_remove_newid_file(new_driver); +out_newid: + driver_unregister(&new_driver->drvwrap.driver); + + printk(KERN_ERR "%s: error %d registering interface " + " driver %s\n", + usbcore_name, retval, new_driver->name); + goto out; } EXPORT_SYMBOL_GPL(usb_register_driver); @@ -806,6 +904,7 @@ void usb_deregister(struct usb_driver *driver) pr_info("%s: deregistering interface driver %s\n", usbcore_name, driver->name); + usb_remove_removeid_file(driver); usb_remove_newid_file(driver); usb_free_dynids(driver); driver_unregister(&driver->drvwrap.driver); @@ -901,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; @@ -925,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; @@ -940,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; @@ -948,32 +1053,23 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); - if (status == 0) - udev->autoresume_disabled = 0; 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 { @@ -981,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: @@ -989,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 */ @@ -1047,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 @@ -1142,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. */ @@ -1171,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++) { @@ -1199,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: @@ -1244,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. */ @@ -1269,53 +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 ((msg.event & PM_EVENT_AUTO) && - udev->autoresume_disabled) { - status = -EPERM; - goto done; - } - 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]; @@ -1331,56 +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->auto_pm = 1; - 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 @@ -1390,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. */ @@ -1406,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); } /** @@ -1418,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); } /** @@ -1437,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. */ @@ -1454,43 +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 { - udev->auto_pm = 1; - 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; } @@ -1504,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); @@ -1539,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. @@ -1553,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 * @@ -1586,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. */ @@ -1612,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); @@ -1624,175 +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 if (udev->autoresume_disabled) - status = -EPERM; - 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); /** - * usb_autopm_set_interface - set a USB interface's autosuspend state - * @intf: the usb_interface whose state should be set - * - * This routine sets the autosuspend state of @intf's device according - * to @intf's usage counter, which the caller must have set previously. - * If the counter is <= 0, the device is autosuspended (if it isn't - * already suspended and if nothing else prevents the autosuspend). If - * the counter is > 0, the device is autoresumed (if it isn't already - * awake). + * 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 increments @intf's usage counter but does not carry out an + * autoresume. + * + * This routine can run in atomic context. */ -int usb_autopm_set_interface(struct usb_interface *intf) +void usb_autopm_get_interface_no_resume(struct usb_interface *intf) { - int status; + struct usb_device *udev = interface_to_usbdev(intf); - status = usb_autopm_do_interface(intf, 0); - dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, atomic_read(&intf->pm_usage_cnt)); - return status; + udev->last_busy = jiffies; + atomic_inc(&intf->pm_usage_cnt); + pm_runtime_get_noresume(&intf->dev); } -EXPORT_SYMBOL_GPL(usb_autopm_set_interface); +EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume); -#else +/* Internal routine to check whether we may autosuspend a device. */ +static int autosuspend_check(struct usb_device *udev) +{ + int i; + struct usb_interface *intf; + unsigned long suspend_time, j; -void usb_autosuspend_work(struct work_struct *work) -{} + /* 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]; -void usb_autoresume_work(struct work_struct *work) -{} + /* 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; + } -#endif /* CONFIG_USB_SUSPEND */ + /* 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; -/** - * 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 - * - * 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). - * - * The caller must hold @udev's device lock. - */ -int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) -{ - int status; + driver = to_usb_driver(intf->dev.driver); + if (!driver->reset_resume || + intf->needs_remote_wakeup) + return -EOPNOTSUPP; + } + } + } - do_unbind_rebind(udev, DO_UNBIND); - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_suspend_both(udev, msg); - usb_pm_unlock(udev); - return status; + /* 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; } -/** - * 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) +static int usb_runtime_suspend(struct device *dev) { - int status; + int status = 0; - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_resume_both(udev, msg); - udev->last_busy = jiffies; - usb_pm_unlock(udev); - if (status == 0) - do_unbind_rebind(udev, DO_REBIND); + /* A USB device can be suspended if it passes the various autosuspend + * checks. Runtime suspend for a USB device means suspending all the + * interfaces and then the device itself. + */ + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); - /* Now that the device is awake, we can start trying to autosuspend - * it again. */ - if (status == 0) - usb_try_autosuspend_device(udev); - return status; -} + if (autosuspend_check(udev) != 0) + return -EAGAIN; -int usb_suspend(struct device *dev, pm_message_t msg) -{ - struct usb_device *udev; + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); - udev = to_usb_device(dev); + /* 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); + } - /* 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. - */ - if (udev->state == USB_STATE_SUSPENDED) { - if (udev->parent || udev->speed != USB_SPEED_HIGH) - udev->skip_sys_resume = 1; - return 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 222ee07ea680..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; } @@ -99,6 +97,7 @@ static int init_usb_class(void) printk(KERN_ERR "class_create failed for usb devices\n"); kfree(usb_class); usb_class = NULL; + goto exit; } usb_class->class->devnode = usb_devnode; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 05e6d313961e..bdf87a8414a1 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -139,7 +139,7 @@ int usb_choose_configuration(struct usb_device *udev) if (best) { i = best->desc.bConfigurationValue; - dev_info(&udev->dev, + dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 91f2885b6ee1..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); @@ -363,7 +488,7 @@ static int hcd_pci_restore(struct device *dev) return resume_common(dev, true); } -struct dev_pm_ops usb_hcd_pci_pm_ops = { +const struct dev_pm_ops usb_hcd_pci_pm_ops = { .suspend = hcd_pci_suspend, .suspend_noirq = hcd_pci_suspend_noirq, .resume_noirq = hcd_pci_resume_noirq, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 34de475f016e..2f8cedda8007 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -38,6 +38,8 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> #include <linux/usb.h> @@ -140,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; */ @@ -1275,13 +1277,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (usb_endpoint_xfer_control(&urb->ep->desc) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { - if (hcd->self.uses_dma) + if (hcd->self.uses_dma) { urb->setup_dma = dma_map_single( hcd->self.controller, urb->setup_packet, sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); - else if (hcd->driver->flags & HCD_LOCAL_MEM) + if (dma_mapping_error(hcd->self.controller, + urb->setup_dma)) + return -EAGAIN; + } else if (hcd->driver->flags & HCD_LOCAL_MEM) ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, &urb->setup_dma, @@ -1293,13 +1298,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (ret == 0 && urb->transfer_buffer_length != 0 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - if (hcd->self.uses_dma) + if (hcd->self.uses_dma) { urb->transfer_dma = dma_map_single ( hcd->self.controller, urb->transfer_buffer, urb->transfer_buffer_length, dir); - else if (hcd->driver->flags & HCD_LOCAL_MEM) { + if (dma_mapping_error(hcd->self.controller, + urb->transfer_dma)) + return -EAGAIN; + } else if (hcd->driver->flags & HCD_LOCAL_MEM) { ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, &urb->transfer_dma, @@ -1589,19 +1597,34 @@ rescan: } } -/* Check whether a new configuration or alt setting for an interface - * will exceed the bandwidth for the bus (or the host controller resources). - * Only pass in a non-NULL config or interface, not both! - * Passing NULL for both new_config and new_intf means the device will be - * de-configured by issuing a set configuration 0 command. +/** + * usb_hcd_alloc_bandwidth - check whether a new bandwidth setting exceeds + * the bus bandwidth + * @udev: target &usb_device + * @new_config: new configuration to install + * @cur_alt: the current alternate interface setting + * @new_alt: alternate interface setting that is being installed + * + * To change configurations, pass in the new configuration in new_config, + * and pass NULL for cur_alt and new_alt. + * + * To reset a device's configuration (put the device in the ADDRESSED state), + * pass in NULL for new_config, cur_alt, and new_alt. + * + * To change alternate interface settings, pass in NULL for new_config, + * pass in the current alternate interface setting in cur_alt, + * and pass in the new alternate interface setting in new_alt. + * + * Returns an error if the requested bandwidth change exceeds the + * bus bandwidth or host controller internal resources. */ -int usb_hcd_check_bandwidth(struct usb_device *udev, +int usb_hcd_alloc_bandwidth(struct usb_device *udev, struct usb_host_config *new_config, - struct usb_interface *new_intf) + struct usb_host_interface *cur_alt, + struct usb_host_interface *new_alt) { int num_intfs, i, j; - struct usb_interface_cache *intf_cache; - struct usb_host_interface *alt = 0; + struct usb_host_interface *alt = NULL; int ret = 0; struct usb_hcd *hcd; struct usb_host_endpoint *ep; @@ -1611,7 +1634,7 @@ int usb_hcd_check_bandwidth(struct usb_device *udev, return 0; /* Configuration is being removed - set configuration 0 */ - if (!new_config && !new_intf) { + if (!new_config && !cur_alt) { for (i = 1; i < 16; ++i) { ep = udev->ep_out[i]; if (ep) @@ -1648,19 +1671,17 @@ int usb_hcd_check_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, iface_num, 0); + if (!alt) + /* No alt setting 0? Pick the first setting. */ + alt = first_alt; - /* Dig the endpoints for alt setting 0 out of the - * interface cache for this interface - */ - intf_cache = new_config->intf_cache[i]; - for (j = 0; j < intf_cache->num_altsetting; j++) { - if (intf_cache->altsetting[j].desc.bAlternateSetting == 0) - alt = &intf_cache->altsetting[j]; - } - if (!alt) { - printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i); - continue; - } for (j = 0; j < alt->desc.bNumEndpoints; j++) { ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]); if (ret < 0) @@ -1668,6 +1689,40 @@ int usb_hcd_check_bandwidth(struct usb_device *udev, } } } + if (cur_alt && new_alt) { + struct usb_interface *iface = usb_ifnum_to_if(udev, + cur_alt->desc.bInterfaceNumber); + + if (iface->resetting_device) { + /* + * The USB core just reset the device, so the xHCI host + * and the device will think alt setting 0 is installed. + * However, the USB core will pass in the alternate + * setting installed before the reset as cur_alt. Dig + * out the alternate setting 0 structure, or the first + * alternate setting if a broken device doesn't have alt + * setting 0. + */ + cur_alt = usb_altnum_to_altsetting(iface, 0); + if (!cur_alt) + cur_alt = &iface->altsetting[0]; + } + + /* Drop all the endpoints in the current alt setting */ + for (i = 0; i < cur_alt->desc.bNumEndpoints; i++) { + ret = hcd->driver->drop_endpoint(hcd, udev, + &cur_alt->endpoint[i]); + if (ret < 0) + goto reset; + } + /* Add all the endpoints in the new alt setting */ + for (i = 0; i < new_alt->desc.bNumEndpoints; i++) { + ret = hcd->driver->add_endpoint(hcd, udev, + &new_alt->endpoint[i]); + if (ret < 0) + goto reset; + } + } ret = hcd->driver->check_bandwidth(hcd, udev); reset: if (ret < 0) @@ -1804,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) { @@ -1811,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); } @@ -1831,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 */ /*-------------------------------------------------------------------------*/ @@ -1981,9 +2039,10 @@ 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); hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -2180,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 79782a1c43f6..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 @@ -111,6 +111,20 @@ struct usb_hcd { u64 rsrc_len; /* memory/io resource length */ unsigned power_budget; /* in mA, 0 = no limit */ + /* bandwidth_mutex should be taken before adding or removing + * any new bus bandwidth constraints: + * 1. Before adding a configuration for a new device. + * 2. Before removing the configuration to put the device into + * the addressed state. + * 3. Before selecting a different configuration. + * 4. Before selecting an alternate interface setting. + * + * bandwidth_mutex should be dropped after a successful control message + * to the device, or resetting the bandwidth after a failed attempt. + */ + struct mutex bandwidth_mutex; + + #define HCD_BUFFER_POOLS 4 struct dma_pool *pool [HCD_BUFFER_POOLS]; @@ -234,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 */ @@ -272,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); @@ -290,9 +305,10 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern void usb_hcd_reset_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); -extern int usb_hcd_check_bandwidth(struct usb_device *udev, +extern int usb_hcd_alloc_bandwidth(struct usb_device *udev, struct usb_host_config *new_config, - struct usb_interface *new_intf); + struct usb_host_interface *old_alt, + struct usb_host_interface *new_alt); extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, @@ -315,7 +331,7 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); extern void usb_hcd_pci_shutdown(struct pci_dev *dev); #ifdef CONFIG_PM_SLEEP -extern struct dev_pm_ops usb_hcd_pci_pm_ops; +extern const struct dev_pm_ops usb_hcd_pci_pm_ops; #endif #endif /* CONFIG_PCI */ @@ -448,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 0f857e645058..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> @@ -45,7 +46,6 @@ struct usb_hub { /* buffer for urb ... with extra space in case of babble */ char (*buffer)[8]; - dma_addr_t buffer_dma; /* DMA address for buffer */ union { struct usb_hub_status hub; struct usb_port_status port; @@ -61,6 +61,8 @@ struct usb_hub { status change */ unsigned long busy_bits[1]; /* ports being reset or resumed */ + unsigned long removed_bits[1]; /* ports with a "removed" + device present */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -374,12 +376,13 @@ static void kick_khubd(struct usb_hub *hub) { unsigned long flags; - /* Suppress autosuspend until khubd runs */ - atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1); - spin_lock_irqsave(&hub_event_lock, flags); if (!hub->disconnected && list_empty(&hub->event_list)) { list_add_tail(&hub->event_list, &hub_event_list); + + /* Suppress autosuspend until khubd runs */ + usb_autopm_get_interface_no_resume( + to_usb_interface(hub->intfdev)); wake_up(&khubd_wait); } spin_unlock_irqrestore(&hub_event_lock, flags); @@ -636,8 +639,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) kick_khubd(hub); } +/** + * usb_remove_device - disable a device's port on its parent hub + * @udev: device to be disabled and removed + * Context: @udev locked, must be able to sleep. + * + * After @udev's port has been disabled, khubd is notified and it will + * see that the device has been disconnected. When the device is + * physically unplugged and something is plugged in, the events will + * be received and processed normally. + */ +int usb_remove_device(struct usb_device *udev) +{ + struct usb_hub *hub; + struct usb_interface *intf; + + if (!udev->parent) /* Can't remove a root hub */ + return -EINVAL; + hub = hdev_to_hub(udev->parent); + intf = to_usb_interface(hub->intfdev); + + usb_autopm_get_interface(intf); + set_bit(udev->portnum, hub->removed_bits); + hub_port_logical_disconnect(hub, udev->portnum); + usb_autopm_put_interface(intf); + return 0; +} + enum hub_activation_type { - HUB_INIT, HUB_INIT2, HUB_INIT3, + HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */ HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, }; @@ -682,8 +712,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) msecs_to_jiffies(delay)); /* Suppress autosuspend until init is done */ - atomic_set(&to_usb_interface(hub->intfdev)-> - pm_usage_cnt, 1); + usb_autopm_get_interface_no_resume( + to_usb_interface(hub->intfdev)); return; /* Continues at init2: below */ } else { hub_power_on(hub, true); @@ -731,6 +761,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) USB_PORT_FEAT_C_ENABLE); } + /* We can forget about a "removed" device when there's a + * physical disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (!udev || udev->state == USB_STATE_NOTATTACHED) { /* Tell khubd to disconnect the device or * check for a new connection @@ -792,6 +829,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Scan all ports that need attention */ kick_khubd(hub); + + /* Allow autosuspend if it was suppressed */ + if (type <= HUB_INIT3) + usb_autopm_put_interface_async(to_usb_interface(hub->intfdev)); } /* Implement the continuations for the delays above */ @@ -869,8 +910,7 @@ static int hub_configure(struct usb_hub *hub, int maxp, ret; char *message = "out of memory"; - hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, - &hub->buffer_dma); + hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); if (!hub->buffer) { ret = -ENOMEM; goto fail; @@ -1111,8 +1151,6 @@ static int hub_configure(struct usb_hub *hub, usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); - hub->urb->transfer_dma = hub->buffer_dma; - hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* maybe cycle the hub leds */ if (hub->has_indicators && blinkenlights) @@ -1144,7 +1182,10 @@ static void hub_disconnect(struct usb_interface *intf) /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); - list_del_init(&hub->event_list); + if (!list_empty(&hub->event_list)) { + list_del_init(&hub->event_list); + usb_autopm_put_interface_no_suspend(intf); + } hub->disconnected = 1; spin_unlock_irq(&hub_event_lock); @@ -1162,8 +1203,7 @@ static void hub_disconnect(struct usb_interface *intf) kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); - usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, - hub->buffer_dma); + kfree(hub->buffer); kref_put(&hub->kref, hub_release); } @@ -1178,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"); @@ -1356,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; } @@ -1402,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) @@ -1483,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 @@ -1576,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); } @@ -1612,12 +1637,12 @@ static inline void announce_device(struct usb_device *udev) { } #endif /** - * usb_configure_device_otg - FIXME (usbcore-internal) + * usb_enumerate_device_otg - FIXME (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) * - * Do configuration for On-The-Go devices + * Finish enumeration for On-The-Go devices */ -static int usb_configure_device_otg(struct usb_device *udev) +static int usb_enumerate_device_otg(struct usb_device *udev) { int err = 0; @@ -1630,7 +1655,7 @@ static int usb_configure_device_otg(struct usb_device *udev) if (!udev->bus->is_b_host && udev->config && udev->parent == udev->bus->root_hub) { - struct usb_otg_descriptor *desc = 0; + struct usb_otg_descriptor *desc = NULL; struct usb_bus *bus = udev->bus; /* descriptor may appear anywhere in config */ @@ -1688,7 +1713,7 @@ fail: /** - * usb_configure_device - Detect and probe device intfs/otg (usbcore-internal) + * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) * * This is only called by usb_new_device() and usb_authorize_device() @@ -1699,7 +1724,7 @@ fail: * the string descriptors, as they will be errored out by the device * until it has been authorized. */ -static int usb_configure_device(struct usb_device *udev) +static int usb_enumerate_device(struct usb_device *udev) { int err; @@ -1723,7 +1748,7 @@ static int usb_configure_device(struct usb_device *udev) udev->descriptor.iManufacturer); udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); } - err = usb_configure_device_otg(udev); + err = usb_enumerate_device_otg(udev); fail: return err; } @@ -1733,8 +1758,8 @@ fail: * usb_new_device - perform initial device setup (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) * - * This is called with devices which have been enumerated, but not yet - * configured. The device descriptor is available, but not descriptors + * This is called with devices which have been detected but not fully + * enumerated. The device descriptor is available, but not descriptors * for any device configuration. The caller must have locked either * the parent hub (if udev is a normal device) or else the * usb_bus_list_lock (if udev is a root hub). The parent's pointer to @@ -1753,12 +1778,21 @@ 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); /* Determine quirks */ - err = usb_configure_device(udev); /* detect & probe dev/intfs */ + usb_detect_quirks(udev); + err = usb_enumerate_device(udev); /* Read descriptors */ if (err < 0) goto fail; dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n", @@ -1771,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). @@ -1786,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; } @@ -1803,21 +1839,23 @@ fail: */ int usb_deauthorize_device(struct usb_device *usb_dev) { - unsigned cnt; usb_lock_device(usb_dev); if (usb_dev->authorized == 0) goto out_unauthorized; + usb_dev->authorized = 0; usb_set_configuration(usb_dev, -1); + + kfree(usb_dev->product); usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL); + kfree(usb_dev->manufacturer); usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL); + kfree(usb_dev->serial); usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL); - kfree(usb_dev->config); - usb_dev->config = NULL; - for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++) - kfree(usb_dev->rawdescriptors[cnt]); + + usb_destroy_configuration(usb_dev); usb_dev->descriptor.bNumConfigurations = 0; - kfree(usb_dev->rawdescriptors); + out_unauthorized: usb_unlock_device(usb_dev); return 0; @@ -1827,15 +1865,11 @@ out_unauthorized: int usb_authorize_device(struct usb_device *usb_dev) { int result = 0, c; + usb_lock_device(usb_dev); if (usb_dev->authorized == 1) goto out_authorized; - kfree(usb_dev->product); - usb_dev->product = NULL; - kfree(usb_dev->manufacturer); - usb_dev->manufacturer = NULL; - kfree(usb_dev->serial); - usb_dev->serial = NULL; + result = usb_autoresume_device(usb_dev); if (result < 0) { dev_err(&usb_dev->dev, @@ -1848,10 +1882,18 @@ int usb_authorize_device(struct usb_device *usb_dev) "authorization: %d\n", result); goto error_device_descriptor; } + + kfree(usb_dev->product); + usb_dev->product = NULL; + kfree(usb_dev->manufacturer); + usb_dev->manufacturer = NULL; + kfree(usb_dev->serial); + usb_dev->serial = NULL; + usb_dev->authorized = 1; - result = usb_configure_device(usb_dev); + result = usb_enumerate_device(usb_dev); if (result < 0) - goto error_configure; + goto error_enumerate; /* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. */ @@ -1866,8 +1908,10 @@ int usb_authorize_device(struct usb_device *usb_dev) } } dev_info(&usb_dev->dev, "authorized to connect\n"); -error_configure: + +error_enumerate: error_device_descriptor: + usb_autosuspend_device(usb_dev); error_autoresume: out_authorized: usb_unlock_device(usb_dev); // complements locktree @@ -1927,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) @@ -1953,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. */ @@ -1981,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: @@ -2123,9 +2177,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (status) + if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); + /* bail if autosuspend is requested */ + if (msg.event & PM_EVENT_AUTO) + return status; + } } /* see 7.1.7.6 */ @@ -2134,7 +2192,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + if (udev->do_remote_wakeup) + (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, @@ -2321,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; } @@ -2365,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) @@ -2436,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 @@ -2585,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 { @@ -2618,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 */ @@ -2646,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; @@ -2946,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 { @@ -2965,6 +3010,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); + /* We can forget about a "removed" device when there's a physical + * disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce(hub, port1); @@ -2978,8 +3030,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } - /* Return now if debouncing failed or nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + /* Return now if debouncing failed or nothing is connected or + * the device was "removed". + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + test_bit(port1, hub->removed_bits)) { /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 @@ -3122,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; @@ -3189,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 loop; + goto loop_disconnected; /* If the hub has died, clean up after it */ if (hdev->state == USB_STATE_NOTATTACHED) { @@ -3278,8 +3334,11 @@ static void hub_events(void) USB_PORT_FEAT_C_SUSPEND); udev = hdev->children[i-1]; if (udev) { + /* TRSMRCY = 10 msec */ + 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) @@ -3338,11 +3397,15 @@ static void hub_events(void) } } -loop_autopm: - /* Allow autosuspend if we're not going to run again */ - if (list_empty(&hub->event_list)) - usb_autopm_enable(intf); -loop: + loop_autopm: + /* Balance the usb_autopm_get_interface() above */ + usb_autopm_put_interface_no_suspend(intf); + loop: + /* Balance the usb_autopm_get_interface_no_resume() in + * kick_khubd() and allow autosuspend. + */ + usb_autopm_put_interface(intf); + loop_disconnected: usb_unlock_device(hdev); kref_put(&hub->kref, hub_release); @@ -3369,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, @@ -3534,6 +3597,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) { struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_device_descriptor descriptor = udev->descriptor; int i, ret = 0; int port1 = udev->portnum; @@ -3577,6 +3641,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev) /* Restore the device's previous configuration */ if (!udev->actconfig) goto done; + + mutex_lock(&hcd->bandwidth_mutex); + ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); + if (ret < 0) { + dev_warn(&udev->dev, + "Busted HC? Not enough HCD resources for " + "old configuration.\n"); + mutex_unlock(&hcd->bandwidth_mutex); + goto re_enumerate; + } ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_CONFIGURATION, 0, udev->actconfig->desc.bConfigurationValue, 0, @@ -3585,8 +3659,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev) dev_err(&udev->dev, "can't restore configuration #%d (error=%d)\n", udev->actconfig->desc.bConfigurationValue, ret); + mutex_unlock(&hcd->bandwidth_mutex); goto re_enumerate; } + mutex_unlock(&hcd->bandwidth_mutex); usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Put interfaces back into the same altsettings as before. @@ -3596,7 +3672,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev) * endpoint state. */ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { - struct usb_interface *intf = udev->actconfig->interface[i]; + struct usb_host_config *config = udev->actconfig; + struct usb_interface *intf = config->interface[i]; struct usb_interface_descriptor *desc; desc = &intf->cur_altsetting->desc; @@ -3605,8 +3682,14 @@ static int usb_reset_and_verify_device(struct usb_device *udev) usb_enable_interface(udev, intf, true); ret = 0; } else { + /* Let the bandwidth allocation function know that this + * device has been reset, and it will have to use + * alternate setting 0 as the current alternate setting. + */ + intf->resetting_device = 1; ret = usb_set_interface(udev, desc->bInterfaceNumber, desc->bAlternateSetting); + intf->resetting_device = 0; } if (ret < 0) { dev_err(&udev->dev, "failed to restore interface %d " diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index da718e84d58d..cd220277c6c3 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -393,13 +393,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (io->entries <= 0) return io->entries; - /* If we're running on an xHCI host controller, queue the whole scatter - * gather list with one call to urb_enqueue(). This is only for bulk, - * as that endpoint type does not care how the data gets broken up - * across frames. - */ - if (usb_pipebulk(pipe) && - bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) { + if (dev->bus->sg_tablesize > 0) { io->urbs = kmalloc(sizeof *io->urbs, mem_flags); use_sg = true; } else { @@ -409,7 +403,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (!io->urbs) goto nomem; - urb_flags = URB_NO_INTERRUPT; + urb_flags = 0; if (dma) urb_flags |= URB_NO_TRANSFER_DMA_MAP; if (usb_pipein(pipe)) @@ -441,6 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, io->urbs[0]->num_sgs = io->entries; io->entries = 1; } else { + urb_flags |= URB_NO_INTERRUPT; for_each_sg(sg, sg, io->entries, i) { unsigned len; @@ -911,11 +906,11 @@ char *usb_cache_string(struct usb_device *udev, int index) if (index <= 0) return NULL; - buf = kmalloc(MAX_USB_STRING_SIZE, GFP_KERNEL); + buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO); if (buf) { len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); if (len > 0) { - smallbuf = kmalloc(++len, GFP_KERNEL); + smallbuf = kmalloc(++len, GFP_NOIO); if (!smallbuf) return buf; memcpy(smallbuf, buf, len); @@ -1303,6 +1298,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) { struct usb_interface *iface; struct usb_host_interface *alt; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); int ret; int manual = 0; unsigned int epaddr; @@ -1320,11 +1316,23 @@ 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; } + /* Make sure we have enough bandwidth for this alternate interface. + * Remove the current alt setting and add the new alt setting. + */ + mutex_lock(&hcd->bandwidth_mutex); + ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); + if (ret < 0) { + dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", + alternate); + mutex_unlock(&hcd->bandwidth_mutex); + return ret; + } + if (dev->quirks & USB_QUIRK_NO_SET_INTF) ret = -EPIPE; else @@ -1340,8 +1348,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) "manual set_interface for iface %d, alt %d\n", interface, alternate); manual = 1; - } else if (ret < 0) + } else if (ret < 0) { + /* Re-instate the old alt setting */ + usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); + mutex_unlock(&hcd->bandwidth_mutex); return ret; + } + mutex_unlock(&hcd->bandwidth_mutex); /* FIXME drivers shouldn't need to replicate/bugfix the logic here * when they implement async or easily-killable versions of this or @@ -1423,6 +1436,7 @@ int usb_reset_configuration(struct usb_device *dev) { int i, retval; struct usb_host_config *config; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; @@ -1438,12 +1452,46 @@ int usb_reset_configuration(struct usb_device *dev) } config = dev->actconfig; + retval = 0; + mutex_lock(&hcd->bandwidth_mutex); + /* Make sure we have enough bandwidth for each alternate setting 0 */ + for (i = 0; i < config->desc.bNumInterfaces; i++) { + struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + if (!alt) + alt = &intf->altsetting[0]; + if (alt != intf->cur_altsetting) + retval = usb_hcd_alloc_bandwidth(dev, NULL, + intf->cur_altsetting, alt); + if (retval < 0) + break; + } + /* If not, reinstate the old alternate settings */ + if (retval < 0) { +reset_old_alts: + for (i--; i >= 0; i--) { + struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + if (!alt) + alt = &intf->altsetting[0]; + if (alt != intf->cur_altsetting) + usb_hcd_alloc_bandwidth(dev, NULL, + alt, intf->cur_altsetting); + } + mutex_unlock(&hcd->bandwidth_mutex); + return retval; + } retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, config->desc.bConfigurationValue, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval < 0) - return retval; + goto reset_old_alts; + mutex_unlock(&hcd->bandwidth_mutex); /* re-init hc/hcd interface/endpoint state */ for (i = 0; i < config->desc.bNumInterfaces; i++) { @@ -1585,7 +1633,7 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, * * See usb_queue_reset_device() for more details */ -void __usb_queue_reset_device(struct work_struct *ws) +static void __usb_queue_reset_device(struct work_struct *ws) { int rc; struct usb_interface *iface = @@ -1652,6 +1700,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) int i, ret; struct usb_host_config *cp = NULL; struct usb_interface **new_interfaces = NULL; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); int n, nintf; if (dev->authorized == 0 || configuration == -1) @@ -1682,7 +1731,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) if (cp) { nintf = cp->desc.bNumInterfaces; new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), - GFP_KERNEL); + GFP_NOIO); if (!new_interfaces) { dev_err(&dev->dev, "Out of memory\n"); return -ENOMEM; @@ -1691,7 +1740,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) for (; n < nintf; ++n) { new_interfaces[n] = kzalloc( sizeof(struct usb_interface), - GFP_KERNEL); + GFP_NOIO); if (!new_interfaces[n]) { dev_err(&dev->dev, "Out of memory\n"); ret = -ENOMEM; @@ -1721,12 +1770,11 @@ free_interfaces: * host controller will not allow submissions to dropped endpoints. If * this call fails, the device state is unchanged. */ - if (cp) - ret = usb_hcd_check_bandwidth(dev, cp, NULL); - else - ret = usb_hcd_check_bandwidth(dev, NULL, NULL); + mutex_lock(&hcd->bandwidth_mutex); + ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { usb_autosuspend_device(dev); + mutex_unlock(&hcd->bandwidth_mutex); goto free_interfaces; } @@ -1752,10 +1800,12 @@ free_interfaces: dev->actconfig = cp; if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); - usb_hcd_check_bandwidth(dev, NULL, NULL); + usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); usb_autosuspend_device(dev); + mutex_unlock(&hcd->bandwidth_mutex); goto free_interfaces; } + mutex_unlock(&hcd->bandwidth_mutex); usb_set_device_state(dev, USB_STATE_CONFIGURED); /* Initialize the new interface structures and the @@ -1793,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); @@ -1817,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", @@ -1890,7 +1940,7 @@ static void cancel_async_set_config(struct usb_device *udev) * routine gets around the normal restrictions by using a work thread to * submit the change-config request. * - * Returns 0 if the request was succesfully queued, error code otherwise. + * Returns 0 if the request was successfully queued, error code otherwise. * The caller has no way to know whether the queued request will eventually * succeed. */ 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 7ec3041ae79e..43c002e3a9aa 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -82,9 +82,13 @@ static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_device *udev; \ + int retval; \ \ udev = to_usb_device(dev); \ - return sprintf(buf, "%s\n", udev->name); \ + usb_lock_device(udev); \ + retval = sprintf(buf, "%s\n", udev->name); \ + usb_unlock_device(udev); \ + return retval; \ } \ static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); @@ -111,6 +115,12 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf) case USB_SPEED_HIGH: speed = "480"; break; + case USB_SPEED_WIRELESS: + speed = "480"; + break; + case USB_SPEED_SUPER: + speed = "5000"; + break; default: speed = "unknown"; } @@ -139,6 +149,16 @@ show_devnum(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL); static ssize_t +show_devpath(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "%s\n", udev->devpath); +} +static DEVICE_ATTR(devpath, S_IRUGO, show_devpath, NULL); + +static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -171,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; @@ -206,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; } @@ -295,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; } @@ -317,7 +382,6 @@ static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, static const char on_string[] = "on"; static const char auto_string[] = "auto"; -static const char suspend_string[] = "suspend"; static ssize_t show_level(struct device *dev, struct device_attribute *attr, char *buf) @@ -325,13 +389,8 @@ show_level(struct device *dev, struct device_attribute *attr, char *buf) struct usb_device *udev = to_usb_device(dev); const char *p = auto_string; - if (udev->state == USB_STATE_SUSPENDED) { - if (udev->autoresume_disabled) - p = suspend_string; - } else { - if (udev->autosuspend_disabled) - p = on_string; - } + if (udev->state != USB_STATE_SUSPENDED && udev->autosuspend_disabled) + p = on_string; return sprintf(buf, "%s\n", p); } @@ -342,45 +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, old_autoresume_disabled; + int rc; cp = memchr(buf, '\n', count); if (cp) len = cp - buf; usb_lock_device(udev); - old_autosuspend_disabled = udev->autosuspend_disabled; - old_autoresume_disabled = udev->autoresume_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; - udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev, PMSG_USER_RESUME); - - } else if (len == sizeof auto_string - 1 && - strncmp(buf, auto_string, len) == 0) { - udev->autosuspend_disabled = 0; - udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev, PMSG_USER_RESUME); - - } else if (len == sizeof suspend_string - 1 && - strncmp(buf, suspend_string, len) == 0) { - udev->autosuspend_disabled = 0; - udev->autoresume_disabled = 1; - rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND); - - } else + strncmp(buf, on_string, len) == 0) + rc = usb_disable_autosuspend(udev); + + else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) + rc = usb_enable_autosuspend(udev); + + else rc = -EINVAL; - if (rc) { - udev->autosuspend_disabled = old_autosuspend_disabled; - udev->autoresume_disabled = old_autoresume_disabled; - } usb_unlock_device(udev); return (rc < 0 ? rc : count); } @@ -508,6 +547,28 @@ static ssize_t usb_dev_authorized_store(struct device *dev, static DEVICE_ATTR(authorized, 0644, usb_dev_authorized_show, usb_dev_authorized_store); +/* "Safely remove a device" */ +static ssize_t usb_remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int rc = 0; + + usb_lock_device(udev); + if (udev->state != USB_STATE_NOTATTACHED) { + + /* To avoid races, first unconfigure and then remove */ + usb_set_configuration(udev, -1); + rc = usb_remove_device(udev); + } + if (rc == 0) + rc = count; + usb_unlock_device(udev); + return rc; +} +static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store); + static struct attribute *dev_attrs[] = { /* current configuration's attributes */ @@ -516,8 +577,8 @@ static struct attribute *dev_attrs[] = { &dev_attr_bConfigurationValue.attr, &dev_attr_bmAttributes.attr, &dev_attr_bMaxPower.attr, - &dev_attr_urbnum.attr, /* device attributes */ + &dev_attr_urbnum.attr, &dev_attr_idVendor.attr, &dev_attr_idProduct.attr, &dev_attr_bcdDevice.attr, @@ -529,10 +590,13 @@ static struct attribute *dev_attrs[] = { &dev_attr_speed.attr, &dev_attr_busnum.attr, &dev_attr_devnum.attr, + &dev_attr_devpath.attr, &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, }; static struct attribute_group dev_attr_grp = { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 0885d4abdc62..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 | @@ -429,8 +436,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* too small? */ - if (urb->interval <= 0) - return -EINVAL; + switch (dev->speed) { + case USB_SPEED_WIRELESS: + if (urb->interval < 6) + return -EINVAL; + break; + default: + if (urb->interval <= 0) + return -EINVAL; + break; + } /* too big? */ switch (dev->speed) { case USB_SPEED_SUPER: /* units are 125us */ @@ -438,6 +453,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (urb->interval > (1 << 15)) return -EINVAL; max = 1 << 15; + case USB_SPEED_WIRELESS: + if (urb->interval > 16) + return -EINVAL; + break; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) @@ -461,8 +480,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) default: return -EINVAL; } - /* Round down to a power of 2, no more than max */ - urb->interval = min(max, 1 << ilog2(urb->interval)); + 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)); + } } return usb_hcd_submit_urb(urb, mem_flags); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b1b85abb9a2d..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 */ @@ -64,6 +61,43 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); /** + * usb_find_alt_setting() - Given a configuration, find the alternate setting + * for the given interface. + * @config: the configuration to search (not necessarily the current config). + * @iface_num: interface number to search in + * @alt_num: alternate interface setting number to search for. + * + * Search the configuration's interface cache for the given alt setting. + */ +struct usb_host_interface *usb_find_alt_setting( + struct usb_host_config *config, + unsigned int iface_num, + unsigned int alt_num) +{ + struct usb_interface_cache *intf_cache = NULL; + int i; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + if (config->intf_cache[i]->altsetting[0].desc.bInterfaceNumber + == iface_num) { + intf_cache = config->intf_cache[i]; + break; + } + } + if (!intf_cache) + return NULL; + for (i = 0; i < intf_cache->num_altsetting; i++) + if (intf_cache->altsetting[i].desc.bAlternateSetting == alt_num) + return &intf_cache->altsetting[i]; + + printk(KERN_DEBUG "Did not find alt setting %u for intf %u, " + "config %u\n", alt_num, iface_num, + config->desc.bConfigurationValue); + return NULL; +} +EXPORT_SYMBOL_GPL(usb_find_alt_setting); + +/** * usb_ifnum_to_if - get the interface object with a given interface number * @dev: the device whose current configuration is considered * @ifnum: the desired interface @@ -132,7 +166,7 @@ EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting); struct find_interface_arg { int minor; - struct usb_interface *interface; + struct device_driver *drv; }; static int __find_interface(struct device *dev, void *data) @@ -143,12 +177,10 @@ static int __find_interface(struct device *dev, void *data) if (!is_usb_interface(dev)) return 0; + if (dev->driver != arg->drv) + return 0; intf = to_usb_interface(dev); - if (intf->minor != -1 && intf->minor == arg->minor) { - arg->interface = intf; - return 1; - } - return 0; + return intf->minor == arg->minor; } /** @@ -156,21 +188,24 @@ static int __find_interface(struct device *dev, void *data) * @drv: the driver whose current configuration is considered * @minor: the minor number of the desired device * - * This walks the driver device list and returns a pointer to the interface - * with the matching minor. Note, this only works for devices that share the - * USB major number. + * This walks the bus device list and returns a pointer to the interface + * with the matching minor and driver. Note, this only works for devices + * that share the USB major number. */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { struct find_interface_arg argb; - int retval; + struct device *dev; argb.minor = minor; - argb.interface = NULL; - /* eat the error, it will be in argb.interface */ - retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, - __find_interface); - return argb.interface; + argb.drv = &drv->drvwrap.driver; + + dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface); + + /* Drop reference count from bus_find_device */ + put_device(dev); + + return dev ? to_usb_interface(dev) : NULL; } EXPORT_SYMBOL_GPL(usb_find_interface); @@ -190,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); @@ -226,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 @@ -258,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) @@ -291,7 +306,7 @@ static int usb_dev_restore(struct device *dev) return usb_resume(dev, PMSG_RESTORE); } -static struct dev_pm_ops usb_device_pm_ops = { +static const struct dev_pm_ops usb_device_pm_ops = { .prepare = usb_dev_prepare, .complete = usb_dev_complete, .suspend = usb_dev_suspend, @@ -304,9 +319,7 @@ static 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 */ @@ -434,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; @@ -1038,7 +1048,7 @@ static struct notifier_block usb_bus_nb = { struct dentry *usb_debug_root; EXPORT_SYMBOL_GPL(usb_debug_root); -struct dentry *usb_debug_devices; +static struct dentry *usb_debug_devices; static int usb_debugfs_init(void) { @@ -1079,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; @@ -1121,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; } @@ -1143,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 9a8b15e6377a..cd882203ad34 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -24,6 +24,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); extern void usb_detect_quirks(struct usb_device *udev); +extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); @@ -54,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 @@ -85,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 @@ -95,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 @@ -105,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; @@ -137,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; |