diff options
-rw-r--r-- | drivers/usb/core/driver.c | 34 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 14 | ||||
-rw-r--r-- | include/linux/usb.h | 1 |
3 files changed, 38 insertions, 11 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 40c1bf09b2b7..0fa15bd62c48 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -1048,7 +1048,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1048 | 1048 | ||
1049 | /* If the suspend succeeded, propagate it up the tree */ | 1049 | /* If the suspend succeeded, propagate it up the tree */ |
1050 | } else if (parent) | 1050 | } else if (parent) |
1051 | usb_autosuspend_device(parent, 0); | 1051 | usb_autosuspend_device(parent, 1); |
1052 | 1052 | ||
1053 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); | 1053 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); |
1054 | return status; | 1054 | return status; |
@@ -1096,9 +1096,25 @@ int usb_resume_both(struct usb_device *udev) | |||
1096 | /* Propagate the resume up the tree, if necessary */ | 1096 | /* Propagate the resume up the tree, if necessary */ |
1097 | if (udev->state == USB_STATE_SUSPENDED) { | 1097 | if (udev->state == USB_STATE_SUSPENDED) { |
1098 | if (parent) { | 1098 | if (parent) { |
1099 | usb_pm_lock(parent); | 1099 | status = usb_autoresume_device(parent, 1); |
1100 | parent->auto_pm = 1; | 1100 | if (status == 0) { |
1101 | status = usb_resume_both(parent); | 1101 | status = usb_resume_device(udev); |
1102 | if (status) { | ||
1103 | usb_autosuspend_device(parent, 1); | ||
1104 | |||
1105 | /* It's possible usb_resume_device() | ||
1106 | * failed after the port was | ||
1107 | * unsuspended, causing udev to be | ||
1108 | * logically disconnected. We don't | ||
1109 | * want usb_disconnect() to autosuspend | ||
1110 | * the parent again, so tell it that | ||
1111 | * udev disconnected while still | ||
1112 | * suspended. */ | ||
1113 | if (udev->state == | ||
1114 | USB_STATE_NOTATTACHED) | ||
1115 | udev->discon_suspended = 1; | ||
1116 | } | ||
1117 | } | ||
1102 | } else { | 1118 | } else { |
1103 | 1119 | ||
1104 | /* We can't progagate beyond the USB subsystem, | 1120 | /* We can't progagate beyond the USB subsystem, |
@@ -1107,11 +1123,9 @@ int usb_resume_both(struct usb_device *udev) | |||
1107 | if (udev->dev.parent->power.power_state.event != | 1123 | if (udev->dev.parent->power.power_state.event != |
1108 | PM_EVENT_ON) | 1124 | PM_EVENT_ON) |
1109 | status = -EHOSTUNREACH; | 1125 | status = -EHOSTUNREACH; |
1110 | } | 1126 | else |
1111 | if (status == 0) | 1127 | status = usb_resume_device(udev); |
1112 | status = usb_resume_device(udev); | 1128 | } |
1113 | if (parent) | ||
1114 | usb_pm_unlock(parent); | ||
1115 | } else { | 1129 | } else { |
1116 | 1130 | ||
1117 | /* Needed only for setting udev->dev.power.power_state.event | 1131 | /* Needed only for setting udev->dev.power.power_state.event |
@@ -1119,8 +1133,6 @@ int usb_resume_both(struct usb_device *udev) | |||
1119 | status = usb_resume_device(udev); | 1133 | status = usb_resume_device(udev); |
1120 | } | 1134 | } |
1121 | 1135 | ||
1122 | /* Now the parent won't suspend until we are finished */ | ||
1123 | |||
1124 | if (status == 0 && udev->actconfig) { | 1136 | if (status == 0 && udev->actconfig) { |
1125 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | 1137 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { |
1126 | intf = udev->actconfig->interface[i]; | 1138 | intf = udev->actconfig->interface[i]; |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 46df5e60764b..e46d38b18249 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) | |||
1039 | if (udev->children[i]) | 1039 | if (udev->children[i]) |
1040 | recursively_mark_NOTATTACHED(udev->children[i]); | 1040 | recursively_mark_NOTATTACHED(udev->children[i]); |
1041 | } | 1041 | } |
1042 | if (udev->state == USB_STATE_SUSPENDED) | ||
1043 | udev->discon_suspended = 1; | ||
1042 | udev->state = USB_STATE_NOTATTACHED; | 1044 | udev->state = USB_STATE_NOTATTACHED; |
1043 | } | 1045 | } |
1044 | 1046 | ||
@@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **pdev) | |||
1228 | *pdev = NULL; | 1230 | *pdev = NULL; |
1229 | spin_unlock_irq(&device_state_lock); | 1231 | spin_unlock_irq(&device_state_lock); |
1230 | 1232 | ||
1233 | /* Decrement the parent's count of unsuspended children */ | ||
1234 | if (udev->parent) { | ||
1235 | usb_pm_lock(udev); | ||
1236 | if (!udev->discon_suspended) | ||
1237 | usb_autosuspend_device(udev->parent, 1); | ||
1238 | usb_pm_unlock(udev); | ||
1239 | } | ||
1240 | |||
1231 | put_device(&udev->dev); | 1241 | put_device(&udev->dev); |
1232 | } | 1242 | } |
1233 | 1243 | ||
@@ -1356,6 +1366,10 @@ static int __usb_new_device(void *void_data) | |||
1356 | goto fail; | 1366 | goto fail; |
1357 | } | 1367 | } |
1358 | 1368 | ||
1369 | /* Increment the parent's count of unsuspended children */ | ||
1370 | if (udev->parent) | ||
1371 | usb_autoresume_device(udev->parent, 1); | ||
1372 | |||
1359 | exit: | 1373 | exit: |
1360 | module_put(THIS_MODULE); | 1374 | module_put(THIS_MODULE); |
1361 | return err; | 1375 | return err; |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 5634a2d91ec0..0cd73edeef13 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -362,6 +362,7 @@ struct usb_device { | |||
362 | u8 portnum; /* Parent port number (origin 1) */ | 362 | u8 portnum; /* Parent port number (origin 1) */ |
363 | u8 level; /* Number of USB hub ancestors */ | 363 | u8 level; /* Number of USB hub ancestors */ |
364 | 364 | ||
365 | unsigned discon_suspended:1; /* Disconnected while suspended */ | ||
365 | unsigned have_langid:1; /* whether string_langid is valid */ | 366 | unsigned have_langid:1; /* whether string_langid is valid */ |
366 | int string_langid; /* language ID for strings */ | 367 | int string_langid; /* language ID for strings */ |
367 | 368 | ||