aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-03-20 14:59:39 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-04-27 16:28:37 -0400
commit2add5229d77a3de08015feef437653e02372162f (patch)
tree436109572453656747d6e5b3aec14939b1202ec3 /drivers
parent13f6be01db9ada144f28241f939f4f3f8ec8e40b (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')
-rw-r--r--drivers/usb/core/driver.c15
-rw-r--r--drivers/usb/core/quirks.c2
-rw-r--r--drivers/usb/core/sysfs.c81
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
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}