diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/config.c | 10 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 53 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 10 |
7 files changed, 69 insertions, 46 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index bfb3731d42db..2d4fd530e5e4 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -185,10 +185,12 @@ static int usb_parse_interface(struct device *ddev, int cfgno, num_ep = USB_MAXENDPOINTS; } - len = sizeof(struct usb_host_endpoint) * num_ep; - alt->endpoint = kzalloc(len, GFP_KERNEL); - if (!alt->endpoint) - return -ENOMEM; + if (num_ep > 0) { /* Can't allocate 0 bytes */ + len = sizeof(struct usb_host_endpoint) * num_ep; + alt->endpoint = kzalloc(len, GFP_KERNEL); + if (!alt->endpoint) + return -ENOMEM; + } /* Parse all the endpoint descriptors */ n = 0; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b9f7f90aef82..2619986e5300 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -983,7 +983,10 @@ static int autosuspend_check(struct usb_device *udev) #else -#define autosuspend_check(udev) 0 +static inline int autosuspend_check(struct usb_device *udev) +{ + return 0; +} #endif /* CONFIG_USB_SUSPEND */ @@ -1041,7 +1044,6 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) if (status < 0) goto done; } - cancel_delayed_work(&udev->autosuspend); /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { @@ -1062,9 +1064,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) usb_resume_interface(intf); } + /* Try another autosuspend when the interfaces aren't busy */ + if (udev->auto_pm) + autosuspend_check(udev); + /* If the suspend succeeded, propagate it up the tree */ - } else if (parent) - usb_autosuspend_device(parent); + } else { + cancel_delayed_work(&udev->autosuspend); + if (parent) + usb_autosuspend_device(parent); + } done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); @@ -1475,6 +1484,7 @@ int usb_external_resume_device(struct usb_device *udev) usb_pm_lock(udev); udev->auto_pm = 0; status = usb_resume_both(udev); + udev->last_busy = jiffies; usb_pm_unlock(udev); /* Now that the device is awake, we can start trying to autosuspend diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 40cf882293e6..8969e42434b9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1018,8 +1018,8 @@ done: atomic_dec (&urb->use_count); if (urb->reject) wake_up (&usb_kill_urb_queue); - usb_put_urb (urb); usbmon_urb_submit_error(&hcd->self, urb, status); + usb_put_urb (urb); } return status; } @@ -1175,10 +1175,6 @@ void usb_hcd_endpoint_disable (struct usb_device *udev, struct urb *urb; hcd = bus_to_hcd(udev->bus); - - WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && - udev->state != USB_STATE_NOTATTACHED); - local_irq_disable (); /* ep is already gone from udev->ep_{in,out}[]; no more submits */ @@ -1685,7 +1681,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); #ifdef CONFIG_PM - flush_workqueue(ksuspend_usb_wq); + cancel_work_sync(&hcd->wakeup_work); #endif mutex_lock(&usb_bus_list_lock); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f6b74a678de5..24f10a19dbdb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1158,6 +1158,30 @@ static void release_address(struct usb_device *udev) } } +#ifdef CONFIG_USB_SUSPEND + +static void usb_stop_pm(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); + + /* Stop any autosuspend requests already submitted */ + cancel_rearming_delayed_work(&udev->autosuspend); +} + +#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 @@ -1224,13 +1248,7 @@ void usb_disconnect(struct usb_device **pdev) *pdev = NULL; spin_unlock_irq(&device_state_lock); - /* Decrement the parent's count of unsuspended children */ - if (udev->parent) { - usb_pm_lock(udev); - if (!udev->discon_suspended) - usb_autosuspend_device(udev->parent); - usb_pm_unlock(udev); - } + usb_stop_pm(udev); put_device(&udev->dev); } @@ -2201,14 +2219,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, continue; } - /* Use a short timeout the first time through, - * so that recalcitrant full-speed devices with - * 8- or 16-byte ep0-maxpackets won't slow things - * down tremendously by NAKing the unexpectedly - * early status stage. Also, retry on all errors; - * some devices are flakey. - * 255 is for WUSB devices, we actually need to use 512. - * WUSB1.0[4.8.1]. + /* Retry on all errors; some devices are flakey. + * 255 is for WUSB devices, we actually need to use + * 512 (WUSB1.0[4.8.1]). */ for (j = 0; j < 3; ++j) { buf->bMaxPacketSize0 = 0; @@ -2216,7 +2229,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, - (i ? USB_CTRL_GET_TIMEOUT : 1000)); + USB_CTRL_GET_TIMEOUT); switch (buf->bMaxPacketSize0) { case 8: case 16: case 32: case 64: case 255: if (buf->bDescriptorType == @@ -2426,10 +2439,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (portchange & USB_PORT_STAT_C_CONNECTION) { status = hub_port_debounce(hub, port1); - if (status < 0 && printk_ratelimit()) { - dev_err (hub_dev, - "connect-debounce failed, port %d disabled\n", - port1); + if (status < 0) { + if (printk_ratelimit()) + dev_err (hub_dev, "connect-debounce failed, " + "port %d disabled\n", port1); goto done; } portstatus = status; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b7434787db5f..f9fed34bf7d8 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -221,15 +221,10 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { - int interval; - - if (usb_dev->speed == USB_SPEED_HIGH) - interval = 1 << min(15, ep->desc.bInterval - 1); - else - interval = ep->desc.bInterval; pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); usb_fill_int_urb(urb, usb_dev, pipe, data, len, - usb_api_blocking_completion, NULL, interval); + usb_api_blocking_completion, NULL, + ep->desc.bInterval); } else usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, usb_api_blocking_completion, NULL); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index e7c982377488..be37c863fdfb 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -232,12 +232,15 @@ set_level(struct device *dev, struct device_attribute *attr, int len = count; char *cp; int rc = 0; + int old_autosuspend_disabled, old_autoresume_disabled; 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... @@ -263,6 +266,10 @@ set_level(struct device *dev, struct device_attribute *attr, } 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); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 18ddc5e67e39..4a6299bd0047 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -184,10 +184,6 @@ static void usb_release_dev(struct device *dev) udev = to_usb_device(dev); -#ifdef CONFIG_USB_SUSPEND - cancel_delayed_work(&udev->autosuspend); - flush_workqueue(ksuspend_usb_wq); -#endif usb_destroy_configuration(udev); usb_put_hcd(bus_to_hcd(udev->bus)); kfree(udev->product); @@ -205,7 +201,11 @@ struct device_type usb_device_type = { static int ksuspend_usb_init(void) { - ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd"); + /* 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; |