diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index a420d72a02541a3af1ef5234faa4cca4a6bbc617..9e3e943f313c55dc3479be6b96f4ffbc58c988d2 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1224,6 +1224,26 @@ void usb_autosuspend_device(struct usb_device *udev) // __FUNCTION__, udev->pm_usage_cnt); } +/** + * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces + * @udev: the usb_device to autosuspend + * + * This routine should be called when a core subsystem thinks @udev may + * be ready to autosuspend. + * + * @udev's usage counter left unchanged. If it 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. + * + * This routine can run only in process context. + */ +void usb_try_autosuspend_device(struct usb_device *udev) +{ + usb_autopm_do_device(udev, 0); + // dev_dbg(&udev->dev, "%s: cnt %d\n", + // __FUNCTION__, udev->pm_usage_cnt); +} + /** * usb_autoresume_device - immediately autoresume a USB device and its interfaces * @udev: the usb_device to autoresume diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cad4fb323f6cab5d260649e2375dc026b4127d18..311d5df80386c91174e6614e79ea5e337c09d9a6 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -158,6 +158,65 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); +#ifdef CONFIG_USB_SUSPEND + +static ssize_t +show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + + return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ); +} + +static ssize_t +set_autosuspend(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + unsigned value, old; + + if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ) + return -EINVAL; + value *= HZ; + + old = udev->autosuspend_delay; + udev->autosuspend_delay = value; + if (value > 0 && old == 0) + usb_try_autosuspend_device(udev); + + return count; +} + +static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, + show_autosuspend, set_autosuspend); + +static char power_group[] = "power"; + +static int add_power_attributes(struct device *dev) +{ + int rc = 0; + + if (is_usb_device(dev)) + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_autosuspend.attr, + power_group); + return rc; +} + +static void remove_power_attributes(struct device *dev) +{ + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_autosuspend.attr, + power_group); +} + +#else + +#define add_power_attributes(dev) 0 +#define remove_power_attributes(dev) do {} while (0) + +#endif /* CONFIG_USB_SUSPEND */ + /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ @@ -230,6 +289,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) if (retval) return retval; + retval = add_power_attributes(dev); + if (retval) + goto error; + if (udev->manufacturer) { retval = device_create_file(dev, &dev_attr_manufacturer); if (retval) @@ -262,6 +325,7 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) device_remove_file(dev, &dev_attr_manufacturer); device_remove_file(dev, &dev_attr_product); device_remove_file(dev, &dev_attr_serial); + remove_power_attributes(dev); sysfs_remove_group(&dev->kobj, &dev_attr_grp); } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index b0a35f45b099ddcfe53bee573a10e82c162c6561..08b5a04e3755883652036d5f4f12f56240de9b62 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -66,11 +66,13 @@ static inline void usb_pm_unlock(struct usb_device *udev) {} #ifdef CONFIG_USB_SUSPEND extern void usb_autosuspend_device(struct usb_device *udev); +extern void usb_try_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); #else -#define usb_autosuspend_device(udev) do {} while (0) +#define usb_autosuspend_device(udev) do {} while (0) +#define usb_try_autosuspend_device(udev) do {} while (0) static inline int usb_autoresume_device(struct usb_device *udev) { return 0;