aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb26
-rw-r--r--drivers/usb/core/driver.c15
-rw-r--r--drivers/usb/core/quirks.c2
-rw-r--r--drivers/usb/core/sysfs.c81
-rw-r--r--include/linux/usb.h2
5 files changed, 118 insertions, 8 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 00a84326325f..f9937add033d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -13,3 +13,29 @@ Description:
13 13
14 The autosuspend delay for newly-created devices is set to 14 The autosuspend delay for newly-created devices is set to
15 the value of the usbcore.autosuspend module parameter. 15 the value of the usbcore.autosuspend module parameter.
16
17What: /sys/bus/usb/devices/.../power/level
18Date: March 2007
19KernelVersion: 2.6.21
20Contact: Alan Stern <stern@rowland.harvard.edu>
21Description:
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.
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 884179f1e163..9b6a60fafddb 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -872,8 +872,10 @@ static int usb_resume_device(struct usb_device *udev)
872 872
873done: 873done:
874 // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); 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 udev->dev.power.power_state.event = PM_EVENT_ON; 877 udev->dev.power.power_state.event = PM_EVENT_ON;
878 }
877 return status; 879 return status;
878} 880}
879 881
@@ -970,7 +972,7 @@ static int autosuspend_check(struct usb_device *udev)
970 udev->do_remote_wakeup = device_may_wakeup(&udev->dev); 972 udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
971 if (udev->pm_usage_cnt > 0) 973 if (udev->pm_usage_cnt > 0)
972 return -EBUSY; 974 return -EBUSY;
973 if (udev->autosuspend_delay < 0) 975 if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled)
974 return -EPERM; 976 return -EPERM;
975 977
976 if (udev->actconfig) { 978 if (udev->actconfig) {
@@ -1116,6 +1118,8 @@ static int usb_resume_both(struct usb_device *udev)
1116 struct usb_interface *intf; 1118 struct usb_interface *intf;
1117 struct usb_device *parent = udev->parent; 1119 struct usb_device *parent = udev->parent;
1118 1120
1121 if (udev->auto_pm && udev->autoresume_disabled)
1122 return -EPERM;
1119 cancel_delayed_work(&udev->autosuspend); 1123 cancel_delayed_work(&udev->autosuspend);
1120 if (udev->state == USB_STATE_NOTATTACHED) 1124 if (udev->state == USB_STATE_NOTATTACHED)
1121 return -ENODEV; 1125 return -ENODEV;
@@ -1486,9 +1490,14 @@ static int usb_suspend(struct device *dev, pm_message_t message)
1486 1490
1487static int usb_resume(struct device *dev) 1491static int usb_resume(struct device *dev)
1488{ 1492{
1493 struct usb_device *udev;
1494
1489 if (!is_usb_device(dev)) /* Ignore PM for interfaces */ 1495 if (!is_usb_device(dev)) /* Ignore PM for interfaces */
1490 return 0; 1496 return 0;
1491 return usb_external_resume_device(to_usb_device(dev)); 1497 udev = to_usb_device(dev);
1498 if (udev->autoresume_disabled)
1499 return -EPERM;
1500 return usb_external_resume_device(udev);
1492} 1501}
1493 1502
1494#else 1503#else
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f08ec85a6d64..739f520908aa 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -42,7 +42,7 @@ static void usb_autosuspend_quirk(struct usb_device *udev)
42{ 42{
43#ifdef CONFIG_USB_SUSPEND 43#ifdef CONFIG_USB_SUSPEND
44 /* disable autosuspend, but allow the user to re-enable it via sysfs */ 44 /* disable autosuspend, but allow the user to re-enable it via sysfs */
45 udev->autosuspend_delay = 0; 45 udev->autosuspend_disabled = 1;
46#endif 46#endif
47} 47}
48 48
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 731001f7d2c1..2ea47a38aefa 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -11,6 +11,7 @@
11 11
12 12
13#include <linux/kernel.h> 13#include <linux/kernel.h>
14#include <linux/string.h>
14#include <linux/usb.h> 15#include <linux/usb.h>
15#include "usb.h" 16#include "usb.h"
16 17
@@ -184,9 +185,8 @@ set_autosuspend(struct device *dev, struct device_attribute *attr,
184 if (value >= 0) 185 if (value >= 0)
185 usb_try_autosuspend_device(udev); 186 usb_try_autosuspend_device(udev);
186 else { 187 else {
187 usb_lock_device(udev); 188 if (usb_autoresume_device(udev) == 0)
188 usb_external_resume_device(udev); 189 usb_autosuspend_device(udev);
189 usb_unlock_device(udev);
190 } 190 }
191 return count; 191 return count;
192} 192}
@@ -194,22 +194,95 @@ set_autosuspend(struct device *dev, struct device_attribute *attr,
194static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, 194static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
195 show_autosuspend, set_autosuspend); 195 show_autosuspend, set_autosuspend);
196 196
197static const char on_string[] = "on";
198static const char auto_string[] = "auto";
199static const char suspend_string[] = "suspend";
200
201static ssize_t
202show_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
217static ssize_t
218set_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
260static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
261
197static char power_group[] = "power"; 262static char power_group[] = "power";
198 263
199static int add_power_attributes(struct device *dev) 264static int add_power_attributes(struct device *dev)
200{ 265{
201 int rc = 0; 266 int rc = 0;
202 267
203 if (is_usb_device(dev)) 268 if (is_usb_device(dev)) {
204 rc = sysfs_add_file_to_group(&dev->kobj, 269 rc = sysfs_add_file_to_group(&dev->kobj,
205 &dev_attr_autosuspend.attr, 270 &dev_attr_autosuspend.attr,
206 power_group); 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 return rc; 277 return rc;
208} 278}
209 279
210static void remove_power_attributes(struct device *dev) 280static void remove_power_attributes(struct device *dev)
211{ 281{
212 sysfs_remove_file_from_group(&dev->kobj, 282 sysfs_remove_file_from_group(&dev->kobj,
283 &dev_attr_level.attr,
284 power_group);
285 sysfs_remove_file_from_group(&dev->kobj,
213 &dev_attr_autosuspend.attr, 286 &dev_attr_autosuspend.attr,
214 power_group); 287 power_group);
215} 288}
diff --git a/include/linux/usb.h b/include/linux/usb.h
index cc24d089faa0..5e8e144afbae 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -398,6 +398,8 @@ struct usb_device {
398 398
399 unsigned auto_pm:1; /* autosuspend/resume in progress */ 399 unsigned auto_pm:1; /* autosuspend/resume in progress */
400 unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ 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#endif 403#endif
402}; 404};
403#define to_usb_device(d) container_of(d, struct usb_device, dev) 405#define to_usb_device(d) container_of(d, struct usb_device, dev)