diff options
Diffstat (limited to 'drivers/usb/core')
| -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); |
