diff options
-rw-r--r-- | drivers/usb/core/hub.c | 10 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 49 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 2 | ||||
-rw-r--r-- | include/linux/usb.h | 3 |
4 files changed, 63 insertions, 1 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index cc93aa9336fe..53fe049b40f5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1034,8 +1034,10 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) | |||
1034 | if (udev->children[i]) | 1034 | if (udev->children[i]) |
1035 | recursively_mark_NOTATTACHED(udev->children[i]); | 1035 | recursively_mark_NOTATTACHED(udev->children[i]); |
1036 | } | 1036 | } |
1037 | if (udev->state == USB_STATE_SUSPENDED) | 1037 | if (udev->state == USB_STATE_SUSPENDED) { |
1038 | udev->discon_suspended = 1; | 1038 | udev->discon_suspended = 1; |
1039 | udev->active_duration -= jiffies; | ||
1040 | } | ||
1039 | udev->state = USB_STATE_NOTATTACHED; | 1041 | udev->state = USB_STATE_NOTATTACHED; |
1040 | } | 1042 | } |
1041 | 1043 | ||
@@ -1084,6 +1086,12 @@ void usb_set_device_state(struct usb_device *udev, | |||
1084 | else | 1086 | else |
1085 | device_init_wakeup(&udev->dev, 0); | 1087 | device_init_wakeup(&udev->dev, 0); |
1086 | } | 1088 | } |
1089 | if (udev->state == USB_STATE_SUSPENDED && | ||
1090 | new_state != USB_STATE_SUSPENDED) | ||
1091 | udev->active_duration -= jiffies; | ||
1092 | else if (new_state == USB_STATE_SUSPENDED && | ||
1093 | udev->state != USB_STATE_SUSPENDED) | ||
1094 | udev->active_duration += jiffies; | ||
1087 | udev->state = new_state; | 1095 | udev->state = new_state; |
1088 | } else | 1096 | } else |
1089 | recursively_mark_NOTATTACHED(udev); | 1097 | recursively_mark_NOTATTACHED(udev); |
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 32bd130b1eed..021b1d37462c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -249,6 +249,41 @@ static void remove_persist_attributes(struct device *dev) | |||
249 | #ifdef CONFIG_USB_SUSPEND | 249 | #ifdef CONFIG_USB_SUSPEND |
250 | 250 | ||
251 | static ssize_t | 251 | static ssize_t |
252 | show_connected_duration(struct device *dev, struct device_attribute *attr, | ||
253 | char *buf) | ||
254 | { | ||
255 | struct usb_device *udev = to_usb_device(dev); | ||
256 | |||
257 | return sprintf(buf, "%u\n", | ||
258 | jiffies_to_msecs(jiffies - udev->connect_time)); | ||
259 | } | ||
260 | |||
261 | static DEVICE_ATTR(connected_duration, S_IRUGO, show_connected_duration, NULL); | ||
262 | |||
263 | /* | ||
264 | * If the device is resumed, the last time the device was suspended has | ||
265 | * been pre-subtracted from active_duration. We add the current time to | ||
266 | * get the duration that the device was actually active. | ||
267 | * | ||
268 | * If the device is suspended, the active_duration is up-to-date. | ||
269 | */ | ||
270 | static ssize_t | ||
271 | show_active_duration(struct device *dev, struct device_attribute *attr, | ||
272 | char *buf) | ||
273 | { | ||
274 | struct usb_device *udev = to_usb_device(dev); | ||
275 | int duration; | ||
276 | |||
277 | if (udev->state != USB_STATE_SUSPENDED) | ||
278 | duration = jiffies_to_msecs(jiffies + udev->active_duration); | ||
279 | else | ||
280 | duration = jiffies_to_msecs(udev->active_duration); | ||
281 | return sprintf(buf, "%u\n", duration); | ||
282 | } | ||
283 | |||
284 | static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL); | ||
285 | |||
286 | static ssize_t | ||
252 | show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) | 287 | show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) |
253 | { | 288 | { |
254 | struct usb_device *udev = to_usb_device(dev); | 289 | struct usb_device *udev = to_usb_device(dev); |
@@ -365,6 +400,14 @@ static int add_power_attributes(struct device *dev) | |||
365 | rc = sysfs_add_file_to_group(&dev->kobj, | 400 | rc = sysfs_add_file_to_group(&dev->kobj, |
366 | &dev_attr_level.attr, | 401 | &dev_attr_level.attr, |
367 | power_group); | 402 | power_group); |
403 | if (rc == 0) | ||
404 | rc = sysfs_add_file_to_group(&dev->kobj, | ||
405 | &dev_attr_connected_duration.attr, | ||
406 | power_group); | ||
407 | if (rc == 0) | ||
408 | rc = sysfs_add_file_to_group(&dev->kobj, | ||
409 | &dev_attr_active_duration.attr, | ||
410 | power_group); | ||
368 | } | 411 | } |
369 | return rc; | 412 | return rc; |
370 | } | 413 | } |
@@ -372,6 +415,12 @@ static int add_power_attributes(struct device *dev) | |||
372 | static void remove_power_attributes(struct device *dev) | 415 | static void remove_power_attributes(struct device *dev) |
373 | { | 416 | { |
374 | sysfs_remove_file_from_group(&dev->kobj, | 417 | sysfs_remove_file_from_group(&dev->kobj, |
418 | &dev_attr_active_duration.attr, | ||
419 | power_group); | ||
420 | sysfs_remove_file_from_group(&dev->kobj, | ||
421 | &dev_attr_connected_duration.attr, | ||
422 | power_group); | ||
423 | sysfs_remove_file_from_group(&dev->kobj, | ||
375 | &dev_attr_level.attr, | 424 | &dev_attr_level.attr, |
376 | power_group); | 425 | power_group); |
377 | sysfs_remove_file_from_group(&dev->kobj, | 426 | sysfs_remove_file_from_group(&dev->kobj, |
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index bc5edacb0c34..fdb444d81271 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
@@ -339,6 +339,8 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) | |||
339 | mutex_init(&dev->pm_mutex); | 339 | mutex_init(&dev->pm_mutex); |
340 | INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); | 340 | INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); |
341 | dev->autosuspend_delay = usb_autosuspend_delay * HZ; | 341 | dev->autosuspend_delay = usb_autosuspend_delay * HZ; |
342 | dev->connect_time = jiffies; | ||
343 | dev->active_duration = -jiffies; | ||
342 | #endif | 344 | #endif |
343 | if (root_hub) /* Root hub always ok [and always wired] */ | 345 | if (root_hub) /* Root hub always ok [and always wired] */ |
344 | dev->authorized = 1; | 346 | dev->authorized = 1; |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 8aae045e9a2e..f8a60756fe2a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -419,12 +419,15 @@ struct usb_device { | |||
419 | u32 quirks; /* quirks of the whole device */ | 419 | u32 quirks; /* quirks of the whole device */ |
420 | atomic_t urbnum; /* number of URBs submitted for the whole device */ | 420 | atomic_t urbnum; /* number of URBs submitted for the whole device */ |
421 | 421 | ||
422 | unsigned long active_duration; /* total time device is not suspended */ | ||
423 | |||
422 | #ifdef CONFIG_PM | 424 | #ifdef CONFIG_PM |
423 | struct delayed_work autosuspend; /* for delayed autosuspends */ | 425 | struct delayed_work autosuspend; /* for delayed autosuspends */ |
424 | struct mutex pm_mutex; /* protects PM operations */ | 426 | struct mutex pm_mutex; /* protects PM operations */ |
425 | 427 | ||
426 | unsigned long last_busy; /* time of last use */ | 428 | unsigned long last_busy; /* time of last use */ |
427 | int autosuspend_delay; /* in jiffies */ | 429 | int autosuspend_delay; /* in jiffies */ |
430 | unsigned long connect_time; /* time device was first connected */ | ||
428 | 431 | ||
429 | unsigned auto_pm:1; /* autosuspend/resume in progress */ | 432 | unsigned auto_pm:1; /* autosuspend/resume in progress */ |
430 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ | 433 | unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ |