diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c479d30eeaa..03eb5ed503f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1758,6 +1758,13 @@ and is between 256 and 4096 characters. It is defined in the file Note that genuine overcurrent events won't be reported either. + usbcore.autosuspend= + [USB] The autosuspend time delay (in seconds) used + for newly-detected USB devices (default 2). This + is the time required before an idle device will be + autosuspended. Devices for which the delay is set + to 0 won't be autosuspended at all. + usbhid.mousepoll= [USBHID] The interval which mice are to be polled at. diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f9196a0a941..a420d72a025 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -963,12 +963,16 @@ static int autosuspend_check(struct usb_device *udev) int i; struct usb_interface *intf; - /* For autosuspend, fail fast if anything is in use. - * Also fail if any interfaces require remote wakeup but it - * isn't available. */ + /* 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. + */ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; + if (!udev->autosuspend_delay) + return -EPERM; + if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -991,7 +995,7 @@ static int autosuspend_check(struct usb_device *udev) #define autosuspend_check(udev) 0 -#endif +#endif /* CONFIG_USB_SUSPEND */ /** * usb_suspend_both - suspend a USB device and its interfaces @@ -1186,7 +1190,7 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) udev->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); usb_pm_unlock(udev); return status; } @@ -1270,7 +1274,7 @@ static int usb_autopm_do_interface(struct usb_interface *intf, intf->pm_usage_cnt -= inc_usage_cnt; } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + udev->autosuspend_delay); } usb_pm_unlock(udev); return status; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index ea0e48e9f61..0e5c646cb4f 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -39,8 +39,10 @@ static const struct usb_device_id usb_quirk_list[] = { static void usb_autosuspend_quirk(struct usb_device *udev) { - /* unbalanced resume to prevent autosuspends */ - usb_autoresume_device(udev); +#ifdef CONFIG_USB_SUSPEND + /* disable autosuspend, but allow the user to re-enable it via sysfs */ + udev->autosuspend_delay = 0; +#endif } static const struct usb_device_id *find_id(struct usb_device *udev) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 3db721cd557..54b42ce311c 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -50,6 +51,16 @@ static int nousb; /* Disable USB when built into kernel image */ struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ +#ifdef CONFIG_USB_SUSPEND +static int usb_autosuspend_delay = 2; /* Default delay value, + * in seconds */ +module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644); +MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); + +#else +#define usb_autosuspend_delay 0 +#endif + /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -306,6 +317,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); + dev->autosuspend_delay = usb_autosuspend_delay * HZ; #endif return dev; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 045cbd11188..b0a35f45b09 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -65,8 +65,6 @@ static inline void usb_pm_unlock(struct usb_device *udev) {} #ifdef CONFIG_USB_SUSPEND -#define USB_AUTOSUSPEND_DELAY (HZ*2) - extern void usb_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); diff --git a/include/linux/usb.h b/include/linux/usb.h index 37e522eba47..87dc75a6cee 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -389,10 +389,13 @@ struct usb_device { int pm_usage_cnt; /* usage counter for autosuspend */ u32 quirks; /* quirks of the whole device */ + #ifdef CONFIG_PM struct delayed_work autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ + unsigned autosuspend_delay; /* in jiffies */ + unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ #endif