aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-11-07 10:31:30 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-11-11 21:06:48 -0500
commit36caff5d795429c572443894e8789c2150dd796b (patch)
treea2079deddd3e5066f22dfc166f297a2da9636432 /drivers/usb/core
parent7fd94beecaff19b346efbf6b77288ab4b0b42dbd (diff)
USB: fix endpoint-disabling for failed config changes
This patch (as1631) fixes a bug that shows up when a config change fails for a device under an xHCI controller. The controller needs to be told to disable the endpoints that have been enabled for the new config. The existing code does this, but before storing the information about which endpoints were enabled! As a result, any second attempt to install the new config is doomed to fail because xhci-hcd will refuse to enable an endpoint that is already enabled. The patch optimistically initializes the new endpoints' device structures before asking the device to switch to the new config. If the request fails then the endpoint information is already stored, so we can use usb_hcd_alloc_bandwidth() to disable the endpoints with no trouble. The rest of the error path is slightly more complex now; we have to disable the new interfaces and call put_device() rather than simply deallocating them. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: Matthias Schniedermeyer <ms@citd.de> CC: Sarah Sharp <sarah.a.sharp@linux.intel.com> CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/message.c54
1 files changed, 31 insertions, 23 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 1ed5afd91e6d..a557658f3223 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1806,29 +1806,8 @@ free_interfaces:
1806 goto free_interfaces; 1806 goto free_interfaces;
1807 } 1807 }
1808 1808
1809 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 1809 /*
1810 USB_REQ_SET_CONFIGURATION, 0, configuration, 0, 1810 * Initialize the new interface structures and the
1811 NULL, 0, USB_CTRL_SET_TIMEOUT);
1812 if (ret < 0) {
1813 /* All the old state is gone, so what else can we do?
1814 * The device is probably useless now anyway.
1815 */
1816 cp = NULL;
1817 }
1818
1819 dev->actconfig = cp;
1820 if (!cp) {
1821 usb_set_device_state(dev, USB_STATE_ADDRESS);
1822 usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
1823 /* Leave LPM disabled while the device is unconfigured. */
1824 mutex_unlock(hcd->bandwidth_mutex);
1825 usb_autosuspend_device(dev);
1826 goto free_interfaces;
1827 }
1828 mutex_unlock(hcd->bandwidth_mutex);
1829 usb_set_device_state(dev, USB_STATE_CONFIGURED);
1830
1831 /* Initialize the new interface structures and the
1832 * hc/hcd/usbcore interface/endpoint state. 1811 * hc/hcd/usbcore interface/endpoint state.
1833 */ 1812 */
1834 for (i = 0; i < nintf; ++i) { 1813 for (i = 0; i < nintf; ++i) {
@@ -1872,6 +1851,35 @@ free_interfaces:
1872 } 1851 }
1873 kfree(new_interfaces); 1852 kfree(new_interfaces);
1874 1853
1854 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
1855 USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
1856 NULL, 0, USB_CTRL_SET_TIMEOUT);
1857 if (ret < 0 && cp) {
1858 /*
1859 * All the old state is gone, so what else can we do?
1860 * The device is probably useless now anyway.
1861 */
1862 usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
1863 for (i = 0; i < nintf; ++i) {
1864 usb_disable_interface(dev, cp->interface[i], true);
1865 put_device(&cp->interface[i]->dev);
1866 cp->interface[i] = NULL;
1867 }
1868 cp = NULL;
1869 }
1870
1871 dev->actconfig = cp;
1872 mutex_unlock(hcd->bandwidth_mutex);
1873
1874 if (!cp) {
1875 usb_set_device_state(dev, USB_STATE_ADDRESS);
1876
1877 /* Leave LPM disabled while the device is unconfigured. */
1878 usb_autosuspend_device(dev);
1879 return ret;
1880 }
1881 usb_set_device_state(dev, USB_STATE_CONFIGURED);
1882
1875 if (cp->string == NULL && 1883 if (cp->string == NULL &&
1876 !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) 1884 !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
1877 cp->string = usb_cache_string(dev, cp->desc.iConfiguration); 1885 cp->string = usb_cache_string(dev, cp->desc.iConfiguration);