diff options
-rw-r--r-- | drivers/usb/core/hub.c | 42 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 9 |
2 files changed, 37 insertions, 14 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 28f5bbae35e0..6346fb2acbd7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -2039,6 +2039,18 @@ static void hub_free_dev(struct usb_device *udev) | |||
2039 | hcd->driver->free_dev(hcd, udev); | 2039 | hcd->driver->free_dev(hcd, udev); |
2040 | } | 2040 | } |
2041 | 2041 | ||
2042 | static void hub_disconnect_children(struct usb_device *udev) | ||
2043 | { | ||
2044 | struct usb_hub *hub = usb_hub_to_struct_hub(udev); | ||
2045 | int i; | ||
2046 | |||
2047 | /* Free up all the children before we remove this device */ | ||
2048 | for (i = 0; i < udev->maxchild; i++) { | ||
2049 | if (hub->ports[i]->child) | ||
2050 | usb_disconnect(&hub->ports[i]->child); | ||
2051 | } | ||
2052 | } | ||
2053 | |||
2042 | /** | 2054 | /** |
2043 | * usb_disconnect - disconnect a device (usbcore-internal) | 2055 | * usb_disconnect - disconnect a device (usbcore-internal) |
2044 | * @pdev: pointer to device being disconnected | 2056 | * @pdev: pointer to device being disconnected |
@@ -2057,9 +2069,10 @@ static void hub_free_dev(struct usb_device *udev) | |||
2057 | */ | 2069 | */ |
2058 | void usb_disconnect(struct usb_device **pdev) | 2070 | void usb_disconnect(struct usb_device **pdev) |
2059 | { | 2071 | { |
2060 | struct usb_device *udev = *pdev; | 2072 | struct usb_port *port_dev = NULL; |
2061 | struct usb_hub *hub = usb_hub_to_struct_hub(udev); | 2073 | struct usb_device *udev = *pdev; |
2062 | int i; | 2074 | struct usb_hub *hub; |
2075 | int port1; | ||
2063 | 2076 | ||
2064 | /* mark the device as inactive, so any further urb submissions for | 2077 | /* mark the device as inactive, so any further urb submissions for |
2065 | * this device (and any of its children) will fail immediately. | 2078 | * this device (and any of its children) will fail immediately. |
@@ -2071,11 +2084,7 @@ void usb_disconnect(struct usb_device **pdev) | |||
2071 | 2084 | ||
2072 | usb_lock_device(udev); | 2085 | usb_lock_device(udev); |
2073 | 2086 | ||
2074 | /* Free up all the children before we remove this device */ | 2087 | hub_disconnect_children(udev); |
2075 | for (i = 0; i < udev->maxchild; i++) { | ||
2076 | if (hub->ports[i]->child) | ||
2077 | usb_disconnect(&hub->ports[i]->child); | ||
2078 | } | ||
2079 | 2088 | ||
2080 | /* deallocate hcd/hardware state ... nuking all pending urbs and | 2089 | /* deallocate hcd/hardware state ... nuking all pending urbs and |
2081 | * cleaning up all state associated with the current configuration | 2090 | * cleaning up all state associated with the current configuration |
@@ -2086,15 +2095,19 @@ void usb_disconnect(struct usb_device **pdev) | |||
2086 | usb_hcd_synchronize_unlinks(udev); | 2095 | usb_hcd_synchronize_unlinks(udev); |
2087 | 2096 | ||
2088 | if (udev->parent) { | 2097 | if (udev->parent) { |
2089 | int port1 = udev->portnum; | 2098 | port1 = udev->portnum; |
2090 | struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); | 2099 | hub = usb_hub_to_struct_hub(udev->parent); |
2091 | struct usb_port *port_dev = hub->ports[port1 - 1]; | 2100 | port_dev = hub->ports[port1 - 1]; |
2092 | 2101 | ||
2093 | sysfs_remove_link(&udev->dev.kobj, "port"); | 2102 | sysfs_remove_link(&udev->dev.kobj, "port"); |
2094 | sysfs_remove_link(&port_dev->dev.kobj, "device"); | 2103 | sysfs_remove_link(&port_dev->dev.kobj, "device"); |
2095 | 2104 | ||
2096 | if (test_and_clear_bit(port1, hub->child_usage_bits)) | 2105 | /* |
2097 | pm_runtime_put(&port_dev->dev); | 2106 | * As usb_port_runtime_resume() de-references udev, make |
2107 | * sure no resumes occur during removal | ||
2108 | */ | ||
2109 | if (!test_and_set_bit(port1, hub->child_usage_bits)) | ||
2110 | pm_runtime_get_sync(&port_dev->dev); | ||
2098 | } | 2111 | } |
2099 | 2112 | ||
2100 | usb_remove_ep_devs(&udev->ep0); | 2113 | usb_remove_ep_devs(&udev->ep0); |
@@ -2116,6 +2129,9 @@ void usb_disconnect(struct usb_device **pdev) | |||
2116 | *pdev = NULL; | 2129 | *pdev = NULL; |
2117 | spin_unlock_irq(&device_state_lock); | 2130 | spin_unlock_irq(&device_state_lock); |
2118 | 2131 | ||
2132 | if (port_dev && test_and_clear_bit(port1, hub->child_usage_bits)) | ||
2133 | pm_runtime_put(&port_dev->dev); | ||
2134 | |||
2119 | hub_free_dev(udev); | 2135 | hub_free_dev(udev); |
2120 | 2136 | ||
2121 | put_device(&udev->dev); | 2137 | put_device(&udev->dev); |
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 8b1655700104..62036faf56c0 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c | |||
@@ -76,6 +76,7 @@ static int usb_port_runtime_resume(struct device *dev) | |||
76 | struct usb_device *hdev = to_usb_device(dev->parent->parent); | 76 | struct usb_device *hdev = to_usb_device(dev->parent->parent); |
77 | struct usb_interface *intf = to_usb_interface(dev->parent); | 77 | struct usb_interface *intf = to_usb_interface(dev->parent); |
78 | struct usb_hub *hub = usb_hub_to_struct_hub(hdev); | 78 | struct usb_hub *hub = usb_hub_to_struct_hub(hdev); |
79 | struct usb_device *udev = port_dev->child; | ||
79 | struct usb_port *peer = port_dev->peer; | 80 | struct usb_port *peer = port_dev->peer; |
80 | int port1 = port_dev->portnum; | 81 | int port1 = port_dev->portnum; |
81 | int retval; | 82 | int retval; |
@@ -97,7 +98,7 @@ static int usb_port_runtime_resume(struct device *dev) | |||
97 | usb_autopm_get_interface(intf); | 98 | usb_autopm_get_interface(intf); |
98 | retval = usb_hub_set_port_power(hdev, hub, port1, true); | 99 | retval = usb_hub_set_port_power(hdev, hub, port1, true); |
99 | msleep(hub_power_on_good_delay(hub)); | 100 | msleep(hub_power_on_good_delay(hub)); |
100 | if (port_dev->child && !retval) { | 101 | if (udev && !retval) { |
101 | /* | 102 | /* |
102 | * Attempt to wait for usb hub port to be reconnected in order | 103 | * Attempt to wait for usb hub port to be reconnected in order |
103 | * to make the resume procedure successful. The device may have | 104 | * to make the resume procedure successful. The device may have |
@@ -109,6 +110,12 @@ static int usb_port_runtime_resume(struct device *dev) | |||
109 | dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", | 110 | dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", |
110 | retval); | 111 | retval); |
111 | retval = 0; | 112 | retval = 0; |
113 | |||
114 | /* Force the child awake to revalidate after the power loss. */ | ||
115 | if (!test_and_set_bit(port1, hub->child_usage_bits)) { | ||
116 | pm_runtime_get_noresume(&port_dev->dev); | ||
117 | pm_request_resume(&udev->dev); | ||
118 | } | ||
112 | } | 119 | } |
113 | 120 | ||
114 | usb_autopm_put_interface(intf); | 121 | usb_autopm_put_interface(intf); |