diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-usb | 26 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 81 | ||||
-rw-r--r-- | include/linux/usb.h | 2 |
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 | |||
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. | ||
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 | ||
873 | done: | 873 | done: |
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 | ||
1487 | static int usb_resume(struct device *dev) | 1491 | static 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, | |||
194 | static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, | 194 | static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, |
195 | show_autosuspend, set_autosuspend); | 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 | static char power_group[] = "power"; | 262 | static char power_group[] = "power"; |
198 | 263 | ||
199 | static int add_power_attributes(struct device *dev) | 264 | static 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 | ||
210 | static void remove_power_attributes(struct device *dev) | 280 | static 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) |