Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: re-enable interface after driver unbinds

This patch (as1197) fixes an error introduced recently. Since a
significant number of devices can't handle Set-Interface requests, we
no longer call usb_set_interface() when a driver unbinds from an
interface, provided the interface is already in altsetting 0. However
the interface still does get disabled, and the call to
usb_set_interface() was the only thing re-enabling it. Since the
interface doesn't get re-enabled, further attempts to use it fail.

So the patch adds a call to usb_enable_interface() when a driver
unbinds and the interface is in altsetting 0. For this to work
right, the interface's endpoints have to be re-enabled but their
toggles have to be left alone. Therefore an additional argument is
added to usb_enable_endpoint() and usb_enable_interface(), a flag
indicating whether or not the endpoint toggles should be reset.

This is a forward-ported version of a patch which fixes Bugzilla
#12301.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: David Roka <roka@dawid.hu>
Reported-by: Erik Ekman <erik@kryo.se>
Tested-by: Erik Ekman <erik@kryo.se>
Tested-by: Alon Bar-Lev <alon.barlev@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
2caf7fcd df718962

+26 -16
+6 -3
drivers/usb/core/driver.c
··· 295 295 * altsetting means creating new endpoint device entries). 296 296 * When either of these happens, defer the Set-Interface. 297 297 */ 298 - if (intf->cur_altsetting->desc.bAlternateSetting == 0) 299 - ; /* Already in altsetting 0 so skip Set-Interface */ 300 - else if (!error && intf->dev.power.status == DPM_ON) 298 + if (intf->cur_altsetting->desc.bAlternateSetting == 0) { 299 + /* Already in altsetting 0 so skip Set-Interface. 300 + * Just re-enable it without affecting the endpoint toggles. 301 + */ 302 + usb_enable_interface(udev, intf, false); 303 + } else if (!error && intf->dev.power.status == DPM_ON) 301 304 usb_set_interface(udev, intf->altsetting[0]. 302 305 desc.bInterfaceNumber, 0); 303 306 else
+1 -1
drivers/usb/core/hub.c
··· 2384 2384 { 2385 2385 usb_disable_endpoint(udev, 0 + USB_DIR_IN); 2386 2386 usb_disable_endpoint(udev, 0 + USB_DIR_OUT); 2387 - usb_enable_endpoint(udev, &udev->ep0); 2387 + usb_enable_endpoint(udev, &udev->ep0, true); 2388 2388 } 2389 2389 EXPORT_SYMBOL_GPL(usb_ep0_reinit); 2390 2390
+15 -10
drivers/usb/core/message.c
··· 1143 1143 * usb_enable_endpoint - Enable an endpoint for USB communications 1144 1144 * @dev: the device whose interface is being enabled 1145 1145 * @ep: the endpoint 1146 + * @reset_toggle: flag to set the endpoint's toggle back to 0 1146 1147 * 1147 - * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. 1148 + * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers. 1148 1149 * For control endpoints, both the input and output sides are handled. 1149 1150 */ 1150 - void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) 1151 + void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, 1152 + bool reset_toggle) 1151 1153 { 1152 1154 int epnum = usb_endpoint_num(&ep->desc); 1153 1155 int is_out = usb_endpoint_dir_out(&ep->desc); 1154 1156 int is_control = usb_endpoint_xfer_control(&ep->desc); 1155 1157 1156 1158 if (is_out || is_control) { 1157 - usb_settoggle(dev, epnum, 1, 0); 1159 + if (reset_toggle) 1160 + usb_settoggle(dev, epnum, 1, 0); 1158 1161 dev->ep_out[epnum] = ep; 1159 1162 } 1160 1163 if (!is_out || is_control) { 1161 - usb_settoggle(dev, epnum, 0, 0); 1164 + if (reset_toggle) 1165 + usb_settoggle(dev, epnum, 0, 0); 1162 1166 dev->ep_in[epnum] = ep; 1163 1167 } 1164 1168 ep->enabled = 1; ··· 1172 1168 * usb_enable_interface - Enable all the endpoints for an interface 1173 1169 * @dev: the device whose interface is being enabled 1174 1170 * @intf: pointer to the interface descriptor 1171 + * @reset_toggles: flag to set the endpoints' toggles back to 0 1175 1172 * 1176 1173 * Enables all the endpoints for the interface's current altsetting. 1177 1174 */ 1178 - static void usb_enable_interface(struct usb_device *dev, 1179 - struct usb_interface *intf) 1175 + void usb_enable_interface(struct usb_device *dev, 1176 + struct usb_interface *intf, bool reset_toggles) 1180 1177 { 1181 1178 struct usb_host_interface *alt = intf->cur_altsetting; 1182 1179 int i; 1183 1180 1184 1181 for (i = 0; i < alt->desc.bNumEndpoints; ++i) 1185 - usb_enable_endpoint(dev, &alt->endpoint[i]); 1182 + usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles); 1186 1183 } 1187 1184 1188 1185 /** ··· 1308 1303 * during the SETUP stage - hence EP0 toggles are "don't care" here. 1309 1304 * (Likewise, EP0 never "halts" on well designed devices.) 1310 1305 */ 1311 - usb_enable_interface(dev, iface); 1306 + usb_enable_interface(dev, iface, true); 1312 1307 if (device_is_registered(&iface->dev)) { 1313 1308 usb_create_sysfs_intf_files(iface); 1314 1309 create_intf_ep_devs(iface); ··· 1387 1382 usb_remove_sysfs_intf_files(intf); 1388 1383 } 1389 1384 intf->cur_altsetting = alt; 1390 - usb_enable_interface(dev, intf); 1385 + usb_enable_interface(dev, intf, true); 1391 1386 if (device_is_registered(&intf->dev)) { 1392 1387 usb_create_sysfs_intf_files(intf); 1393 1388 create_intf_ep_devs(intf); ··· 1690 1685 alt = &intf->altsetting[0]; 1691 1686 1692 1687 intf->cur_altsetting = alt; 1693 - usb_enable_interface(dev, intf); 1688 + usb_enable_interface(dev, intf, true); 1694 1689 intf->dev.parent = &dev->dev; 1695 1690 intf->dev.driver = NULL; 1696 1691 intf->dev.bus = &usb_bus_type;
+1 -1
drivers/usb/core/usb.c
··· 362 362 dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; 363 363 dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; 364 364 /* ep0 maxpacket comes later, from device descriptor */ 365 - usb_enable_endpoint(dev, &dev->ep0); 365 + usb_enable_endpoint(dev, &dev->ep0, true); 366 366 dev->can_submit = 1; 367 367 368 368 /* Save readable and stable topology id, distinguishing devices
+3 -1
drivers/usb/core/usb.h
··· 12 12 extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint); 13 13 14 14 extern void usb_enable_endpoint(struct usb_device *dev, 15 - struct usb_host_endpoint *ep); 15 + struct usb_host_endpoint *ep, bool reset_toggle); 16 + extern void usb_enable_interface(struct usb_device *dev, 17 + struct usb_interface *intf, bool reset_toggles); 16 18 extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); 17 19 extern void usb_disable_interface(struct usb_device *dev, 18 20 struct usb_interface *intf);