aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hub.c42
-rw-r--r--drivers/usb/core/port.c9
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
2042static 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 */
2058void usb_disconnect(struct usb_device **pdev) 2070void 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);