Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: add power/level sysfs attribute

This patch (as874) adds another piece to the user-visible part of the
USB autosuspend interface. The new power/level sysfs attribute allows
users to force the device on (with autosuspend off), force the device
to sleep (with autoresume off), or return to normal automatic operation.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
2add5229 13f6be01

+118 -8
+26
Documentation/ABI/testing/sysfs-bus-usb
··· 13 13 14 14 The autosuspend delay for newly-created devices is set to 15 15 the value of the usbcore.autosuspend module parameter. 16 + 17 + What: /sys/bus/usb/devices/.../power/level 18 + Date: March 2007 19 + KernelVersion: 2.6.21 20 + Contact: Alan Stern <stern@rowland.harvard.edu> 21 + Description: 22 + Each USB device directory will contain a file named 23 + power/level. This file holds a power-level setting for 24 + the device, one of "on", "auto", or "suspend". 25 + 26 + "on" means that the device is not allowed to autosuspend, 27 + although normal suspends for system sleep will still 28 + be honored. "auto" means the device will autosuspend 29 + and autoresume in the usual manner, according to the 30 + capabilities of its driver. "suspend" means the device 31 + is forced into a suspended state and it will not autoresume 32 + in response to I/O requests. However remote-wakeup requests 33 + from the device may still be enabled (the remote-wakeup 34 + setting is controlled separately by the power/wakeup 35 + attribute). 36 + 37 + During normal use, devices should be left in the "auto" 38 + level. The other levels are meant for administrative uses. 39 + If you want to suspend a device immediately but leave it 40 + free to wake up in response to I/O requests, you should 41 + write "0" to power/autosuspend.
+12 -3
drivers/usb/core/driver.c
··· 872 872 873 873 done: 874 874 // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); 875 - if (status == 0) 875 + if (status == 0) { 876 + udev->autoresume_disabled = 0; 876 877 udev->dev.power.power_state.event = PM_EVENT_ON; 878 + } 877 879 return status; 878 880 } 879 881 ··· 972 970 udev->do_remote_wakeup = device_may_wakeup(&udev->dev); 973 971 if (udev->pm_usage_cnt > 0) 974 972 return -EBUSY; 975 - if (udev->autosuspend_delay < 0) 973 + if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) 976 974 return -EPERM; 977 975 978 976 if (udev->actconfig) { ··· 1118 1116 struct usb_interface *intf; 1119 1117 struct usb_device *parent = udev->parent; 1120 1118 1119 + if (udev->auto_pm && udev->autoresume_disabled) 1120 + return -EPERM; 1121 1121 cancel_delayed_work(&udev->autosuspend); 1122 1122 if (udev->state == USB_STATE_NOTATTACHED) 1123 1123 return -ENODEV; ··· 1490 1486 1491 1487 static int usb_resume(struct device *dev) 1492 1488 { 1489 + struct usb_device *udev; 1490 + 1493 1491 if (!is_usb_device(dev)) /* Ignore PM for interfaces */ 1494 1492 return 0; 1495 - return usb_external_resume_device(to_usb_device(dev)); 1493 + udev = to_usb_device(dev); 1494 + if (udev->autoresume_disabled) 1495 + return -EPERM; 1496 + return usb_external_resume_device(udev); 1496 1497 } 1497 1498 1498 1499 #else
+1 -1
drivers/usb/core/quirks.c
··· 42 42 { 43 43 #ifdef CONFIG_USB_SUSPEND 44 44 /* disable autosuspend, but allow the user to re-enable it via sysfs */ 45 - udev->autosuspend_delay = 0; 45 + udev->autosuspend_disabled = 1; 46 46 #endif 47 47 } 48 48
+77 -4
drivers/usb/core/sysfs.c
··· 11 11 12 12 13 13 #include <linux/kernel.h> 14 + #include <linux/string.h> 14 15 #include <linux/usb.h> 15 16 #include "usb.h" 16 17 ··· 185 184 if (value >= 0) 186 185 usb_try_autosuspend_device(udev); 187 186 else { 188 - usb_lock_device(udev); 189 - usb_external_resume_device(udev); 190 - usb_unlock_device(udev); 187 + if (usb_autoresume_device(udev) == 0) 188 + usb_autosuspend_device(udev); 191 189 } 192 190 return count; 193 191 } ··· 194 194 static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, 195 195 show_autosuspend, set_autosuspend); 196 196 197 + static const char on_string[] = "on"; 198 + static const char auto_string[] = "auto"; 199 + static const char suspend_string[] = "suspend"; 200 + 201 + static ssize_t 202 + show_level(struct device *dev, struct device_attribute *attr, char *buf) 203 + { 204 + struct usb_device *udev = to_usb_device(dev); 205 + const char *p = auto_string; 206 + 207 + if (udev->state == USB_STATE_SUSPENDED) { 208 + if (udev->autoresume_disabled) 209 + p = suspend_string; 210 + } else { 211 + if (udev->autosuspend_disabled) 212 + p = on_string; 213 + } 214 + return sprintf(buf, "%s\n", p); 215 + } 216 + 217 + static ssize_t 218 + set_level(struct device *dev, struct device_attribute *attr, 219 + const char *buf, size_t count) 220 + { 221 + struct usb_device *udev = to_usb_device(dev); 222 + int len = count; 223 + char *cp; 224 + int rc = 0; 225 + 226 + cp = memchr(buf, '\n', count); 227 + if (cp) 228 + len = cp - buf; 229 + 230 + usb_lock_device(udev); 231 + 232 + /* Setting the flags without calling usb_pm_lock is a subject to 233 + * races, but who cares... 234 + */ 235 + if (len == sizeof on_string - 1 && 236 + strncmp(buf, on_string, len) == 0) { 237 + udev->autosuspend_disabled = 1; 238 + udev->autoresume_disabled = 0; 239 + rc = usb_external_resume_device(udev); 240 + 241 + } else if (len == sizeof auto_string - 1 && 242 + strncmp(buf, auto_string, len) == 0) { 243 + udev->autosuspend_disabled = 0; 244 + udev->autoresume_disabled = 0; 245 + rc = usb_external_resume_device(udev); 246 + 247 + } else if (len == sizeof suspend_string - 1 && 248 + strncmp(buf, suspend_string, len) == 0) { 249 + udev->autosuspend_disabled = 0; 250 + udev->autoresume_disabled = 1; 251 + rc = usb_external_suspend_device(udev, PMSG_SUSPEND); 252 + 253 + } else 254 + rc = -EINVAL; 255 + 256 + usb_unlock_device(udev); 257 + return (rc < 0 ? rc : count); 258 + } 259 + 260 + static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); 261 + 197 262 static char power_group[] = "power"; 198 263 199 264 static int add_power_attributes(struct device *dev) 200 265 { 201 266 int rc = 0; 202 267 203 - if (is_usb_device(dev)) 268 + if (is_usb_device(dev)) { 204 269 rc = sysfs_add_file_to_group(&dev->kobj, 205 270 &dev_attr_autosuspend.attr, 206 271 power_group); 272 + if (rc == 0) 273 + rc = sysfs_add_file_to_group(&dev->kobj, 274 + &dev_attr_level.attr, 275 + power_group); 276 + } 207 277 return rc; 208 278 } 209 279 210 280 static void remove_power_attributes(struct device *dev) 211 281 { 282 + sysfs_remove_file_from_group(&dev->kobj, 283 + &dev_attr_level.attr, 284 + power_group); 212 285 sysfs_remove_file_from_group(&dev->kobj, 213 286 &dev_attr_autosuspend.attr, 214 287 power_group);
+2
include/linux/usb.h
··· 398 398 399 399 unsigned auto_pm:1; /* autosuspend/resume in progress */ 400 400 unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ 401 + unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ 402 + unsigned autoresume_disabled:1; /* disabled by the user */ 401 403 #endif 402 404 }; 403 405 #define to_usb_device(d) container_of(d, struct usb_device, dev)