diff options
Diffstat (limited to 'drivers/usb/core/message.c')
-rw-r--r-- | drivers/usb/core/message.c | 26 |
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5701e857392b..0b5ec234c787 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -1135,15 +1135,26 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf, | |||
1135 | * Deallocates hcd/hardware state for the endpoints (nuking all or most | 1135 | * Deallocates hcd/hardware state for the endpoints (nuking all or most |
1136 | * pending urbs) and usbcore state for the interfaces, so that usbcore | 1136 | * pending urbs) and usbcore state for the interfaces, so that usbcore |
1137 | * must usb_set_configuration() before any interfaces could be used. | 1137 | * must usb_set_configuration() before any interfaces could be used. |
1138 | * | ||
1139 | * Must be called with hcd->bandwidth_mutex held. | ||
1138 | */ | 1140 | */ |
1139 | void usb_disable_device(struct usb_device *dev, int skip_ep0) | 1141 | void usb_disable_device(struct usb_device *dev, int skip_ep0) |
1140 | { | 1142 | { |
1141 | int i; | 1143 | int i; |
1144 | struct usb_hcd *hcd = bus_to_hcd(dev->bus); | ||
1142 | 1145 | ||
1143 | /* getting rid of interfaces will disconnect | 1146 | /* getting rid of interfaces will disconnect |
1144 | * any drivers bound to them (a key side effect) | 1147 | * any drivers bound to them (a key side effect) |
1145 | */ | 1148 | */ |
1146 | if (dev->actconfig) { | 1149 | if (dev->actconfig) { |
1150 | /* | ||
1151 | * FIXME: In order to avoid self-deadlock involving the | ||
1152 | * bandwidth_mutex, we have to mark all the interfaces | ||
1153 | * before unregistering any of them. | ||
1154 | */ | ||
1155 | for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) | ||
1156 | dev->actconfig->interface[i]->unregistering = 1; | ||
1157 | |||
1147 | for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { | 1158 | for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { |
1148 | struct usb_interface *interface; | 1159 | struct usb_interface *interface; |
1149 | 1160 | ||
@@ -1153,7 +1164,6 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) | |||
1153 | continue; | 1164 | continue; |
1154 | dev_dbg(&dev->dev, "unregistering interface %s\n", | 1165 | dev_dbg(&dev->dev, "unregistering interface %s\n", |
1155 | dev_name(&interface->dev)); | 1166 | dev_name(&interface->dev)); |
1156 | interface->unregistering = 1; | ||
1157 | remove_intf_ep_devs(interface); | 1167 | remove_intf_ep_devs(interface); |
1158 | device_del(&interface->dev); | 1168 | device_del(&interface->dev); |
1159 | } | 1169 | } |
@@ -1172,6 +1182,16 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) | |||
1172 | 1182 | ||
1173 | dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, | 1183 | dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, |
1174 | skip_ep0 ? "non-ep0" : "all"); | 1184 | skip_ep0 ? "non-ep0" : "all"); |
1185 | if (hcd->driver->check_bandwidth) { | ||
1186 | /* First pass: Cancel URBs, leave endpoint pointers intact. */ | ||
1187 | for (i = skip_ep0; i < 16; ++i) { | ||
1188 | usb_disable_endpoint(dev, i, false); | ||
1189 | usb_disable_endpoint(dev, i + USB_DIR_IN, false); | ||
1190 | } | ||
1191 | /* Remove endpoints from the host controller internal state */ | ||
1192 | usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); | ||
1193 | /* Second pass: remove endpoint pointers */ | ||
1194 | } | ||
1175 | for (i = skip_ep0; i < 16; ++i) { | 1195 | for (i = skip_ep0; i < 16; ++i) { |
1176 | usb_disable_endpoint(dev, i, true); | 1196 | usb_disable_endpoint(dev, i, true); |
1177 | usb_disable_endpoint(dev, i + USB_DIR_IN, true); | 1197 | usb_disable_endpoint(dev, i + USB_DIR_IN, true); |
@@ -1273,6 +1293,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) | |||
1273 | interface); | 1293 | interface); |
1274 | return -EINVAL; | 1294 | return -EINVAL; |
1275 | } | 1295 | } |
1296 | if (iface->unregistering) | ||
1297 | return -ENODEV; | ||
1276 | 1298 | ||
1277 | alt = usb_altnum_to_altsetting(iface, alternate); | 1299 | alt = usb_altnum_to_altsetting(iface, alternate); |
1278 | if (!alt) { | 1300 | if (!alt) { |
@@ -1727,6 +1749,7 @@ free_interfaces: | |||
1727 | /* if it's already configured, clear out old state first. | 1749 | /* if it's already configured, clear out old state first. |
1728 | * getting rid of old interfaces means unbinding their drivers. | 1750 | * getting rid of old interfaces means unbinding their drivers. |
1729 | */ | 1751 | */ |
1752 | mutex_lock(hcd->bandwidth_mutex); | ||
1730 | if (dev->state != USB_STATE_ADDRESS) | 1753 | if (dev->state != USB_STATE_ADDRESS) |
1731 | usb_disable_device(dev, 1); /* Skip ep0 */ | 1754 | usb_disable_device(dev, 1); /* Skip ep0 */ |
1732 | 1755 | ||
@@ -1739,7 +1762,6 @@ free_interfaces: | |||
1739 | * host controller will not allow submissions to dropped endpoints. If | 1762 | * host controller will not allow submissions to dropped endpoints. If |
1740 | * this call fails, the device state is unchanged. | 1763 | * this call fails, the device state is unchanged. |
1741 | */ | 1764 | */ |
1742 | mutex_lock(hcd->bandwidth_mutex); | ||
1743 | ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); | 1765 | ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); |
1744 | if (ret < 0) { | 1766 | if (ret < 0) { |
1745 | mutex_unlock(hcd->bandwidth_mutex); | 1767 | mutex_unlock(hcd->bandwidth_mutex); |