diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-03-20 14:59:39 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 16:28:37 -0400 |
commit | 2add5229d77a3de08015feef437653e02372162f (patch) | |
tree | 436109572453656747d6e5b3aec14939b1202ec3 /drivers/usb | |
parent | 13f6be01db9ada144f28241f939f4f3f8ec8e40b (diff) |
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>
Diffstat (limited to 'drivers/usb')
-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 |
3 files changed, 90 insertions, 8 deletions
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 | } |