diff options
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r-- | drivers/usb/core/driver.c | 49 |
1 files changed, 42 insertions, 7 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6a3b5cae3a6e..2f3dc4cdf79b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -301,7 +301,7 @@ static int usb_probe_interface(struct device *dev) | |||
301 | 301 | ||
302 | intf->condition = USB_INTERFACE_BINDING; | 302 | intf->condition = USB_INTERFACE_BINDING; |
303 | 303 | ||
304 | /* Bound interfaces are initially active. They are | 304 | /* Probed interfaces are initially active. They are |
305 | * runtime-PM-enabled only if the driver has autosuspend support. | 305 | * runtime-PM-enabled only if the driver has autosuspend support. |
306 | * They are sensitive to their children's power states. | 306 | * They are sensitive to their children's power states. |
307 | */ | 307 | */ |
@@ -437,11 +437,11 @@ int usb_driver_claim_interface(struct usb_driver *driver, | |||
437 | 437 | ||
438 | iface->condition = USB_INTERFACE_BOUND; | 438 | iface->condition = USB_INTERFACE_BOUND; |
439 | 439 | ||
440 | /* Bound interfaces are initially active. They are | 440 | /* Claimed interfaces are initially inactive (suspended). They are |
441 | * runtime-PM-enabled only if the driver has autosuspend support. | 441 | * runtime-PM-enabled only if the driver has autosuspend support. |
442 | * They are sensitive to their children's power states. | 442 | * They are sensitive to their children's power states. |
443 | */ | 443 | */ |
444 | pm_runtime_set_active(dev); | 444 | pm_runtime_set_suspended(dev); |
445 | pm_suspend_ignore_children(dev, false); | 445 | pm_suspend_ignore_children(dev, false); |
446 | if (driver->supports_autosuspend) | 446 | if (driver->supports_autosuspend) |
447 | pm_runtime_enable(dev); | 447 | pm_runtime_enable(dev); |
@@ -1170,7 +1170,7 @@ done: | |||
1170 | static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | 1170 | static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) |
1171 | { | 1171 | { |
1172 | int status = 0; | 1172 | int status = 0; |
1173 | int i = 0; | 1173 | int i = 0, n = 0; |
1174 | struct usb_interface *intf; | 1174 | struct usb_interface *intf; |
1175 | 1175 | ||
1176 | if (udev->state == USB_STATE_NOTATTACHED || | 1176 | if (udev->state == USB_STATE_NOTATTACHED || |
@@ -1179,7 +1179,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1179 | 1179 | ||
1180 | /* Suspend all the interfaces and then udev itself */ | 1180 | /* Suspend all the interfaces and then udev itself */ |
1181 | if (udev->actconfig) { | 1181 | if (udev->actconfig) { |
1182 | for (; i < udev->actconfig->desc.bNumInterfaces; i++) { | 1182 | n = udev->actconfig->desc.bNumInterfaces; |
1183 | for (i = n - 1; i >= 0; --i) { | ||
1183 | intf = udev->actconfig->interface[i]; | 1184 | intf = udev->actconfig->interface[i]; |
1184 | status = usb_suspend_interface(udev, intf, msg); | 1185 | status = usb_suspend_interface(udev, intf, msg); |
1185 | if (status != 0) | 1186 | if (status != 0) |
@@ -1192,7 +1193,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1192 | /* If the suspend failed, resume interfaces that did get suspended */ | 1193 | /* If the suspend failed, resume interfaces that did get suspended */ |
1193 | if (status != 0) { | 1194 | if (status != 0) { |
1194 | msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); | 1195 | msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); |
1195 | while (--i >= 0) { | 1196 | while (++i < n) { |
1196 | intf = udev->actconfig->interface[i]; | 1197 | intf = udev->actconfig->interface[i]; |
1197 | usb_resume_interface(udev, intf, msg, 0); | 1198 | usb_resume_interface(udev, intf, msg, 0); |
1198 | } | 1199 | } |
@@ -1263,13 +1264,47 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) | |||
1263 | return status; | 1264 | return status; |
1264 | } | 1265 | } |
1265 | 1266 | ||
1267 | static void choose_wakeup(struct usb_device *udev, pm_message_t msg) | ||
1268 | { | ||
1269 | int w, i; | ||
1270 | struct usb_interface *intf; | ||
1271 | |||
1272 | /* Remote wakeup is needed only when we actually go to sleep. | ||
1273 | * For things like FREEZE and QUIESCE, if the device is already | ||
1274 | * autosuspended then its current wakeup setting is okay. | ||
1275 | */ | ||
1276 | if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) { | ||
1277 | if (udev->state != USB_STATE_SUSPENDED) | ||
1278 | udev->do_remote_wakeup = 0; | ||
1279 | return; | ||
1280 | } | ||
1281 | |||
1282 | /* If remote wakeup is permitted, see whether any interface drivers | ||
1283 | * actually want it. | ||
1284 | */ | ||
1285 | w = 0; | ||
1286 | if (device_may_wakeup(&udev->dev) && udev->actconfig) { | ||
1287 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
1288 | intf = udev->actconfig->interface[i]; | ||
1289 | w |= intf->needs_remote_wakeup; | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | /* If the device is autosuspended with the wrong wakeup setting, | ||
1294 | * autoresume now so the setting can be changed. | ||
1295 | */ | ||
1296 | if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup) | ||
1297 | pm_runtime_resume(&udev->dev); | ||
1298 | udev->do_remote_wakeup = w; | ||
1299 | } | ||
1300 | |||
1266 | /* The device lock is held by the PM core */ | 1301 | /* The device lock is held by the PM core */ |
1267 | int usb_suspend(struct device *dev, pm_message_t msg) | 1302 | int usb_suspend(struct device *dev, pm_message_t msg) |
1268 | { | 1303 | { |
1269 | struct usb_device *udev = to_usb_device(dev); | 1304 | struct usb_device *udev = to_usb_device(dev); |
1270 | 1305 | ||
1271 | do_unbind_rebind(udev, DO_UNBIND); | 1306 | do_unbind_rebind(udev, DO_UNBIND); |
1272 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); | 1307 | choose_wakeup(udev, msg); |
1273 | return usb_suspend_both(udev, msg); | 1308 | return usb_suspend_both(udev, msg); |
1274 | } | 1309 | } |
1275 | 1310 | ||