Commit 2caf7fcd authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarDavid Roka <roka@dawid.hu>
Reported-by: default avatarErik Ekman <erik@kryo.se>
Tested-by: default avatarErik Ekman <erik@kryo.se>
Tested-by: default avatarAlon Bar-Lev <alon.barlev@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent df718962
...@@ -295,9 +295,12 @@ static int usb_unbind_interface(struct device *dev) ...@@ -295,9 +295,12 @@ static int usb_unbind_interface(struct device *dev)
* altsetting means creating new endpoint device entries). * altsetting means creating new endpoint device entries).
* When either of these happens, defer the Set-Interface. * When either of these happens, defer the Set-Interface.
*/ */
if (intf->cur_altsetting->desc.bAlternateSetting == 0) if (intf->cur_altsetting->desc.bAlternateSetting == 0) {
; /* Already in altsetting 0 so skip Set-Interface */ /* Already in altsetting 0 so skip Set-Interface.
else if (!error && intf->dev.power.status == DPM_ON) * Just re-enable it without affecting the endpoint toggles.
*/
usb_enable_interface(udev, intf, false);
} else if (!error && intf->dev.power.status == DPM_ON)
usb_set_interface(udev, intf->altsetting[0]. usb_set_interface(udev, intf->altsetting[0].
desc.bInterfaceNumber, 0); desc.bInterfaceNumber, 0);
else else
......
...@@ -2384,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev) ...@@ -2384,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev)
{ {
usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_IN);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT); usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
usb_enable_endpoint(udev, &udev->ep0); usb_enable_endpoint(udev, &udev->ep0, true);
} }
EXPORT_SYMBOL_GPL(usb_ep0_reinit); EXPORT_SYMBOL_GPL(usb_ep0_reinit);
......
...@@ -1143,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) ...@@ -1143,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
* usb_enable_endpoint - Enable an endpoint for USB communications * usb_enable_endpoint - Enable an endpoint for USB communications
* @dev: the device whose interface is being enabled * @dev: the device whose interface is being enabled
* @ep: the endpoint * @ep: the endpoint
* @reset_toggle: flag to set the endpoint's toggle back to 0
* *
* Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers.
* For control endpoints, both the input and output sides are handled. * For control endpoints, both the input and output sides are handled.
*/ */
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
bool reset_toggle)
{ {
int epnum = usb_endpoint_num(&ep->desc); int epnum = usb_endpoint_num(&ep->desc);
int is_out = usb_endpoint_dir_out(&ep->desc); int is_out = usb_endpoint_dir_out(&ep->desc);
int is_control = usb_endpoint_xfer_control(&ep->desc); int is_control = usb_endpoint_xfer_control(&ep->desc);
if (is_out || is_control) { if (is_out || is_control) {
usb_settoggle(dev, epnum, 1, 0); if (reset_toggle)
usb_settoggle(dev, epnum, 1, 0);
dev->ep_out[epnum] = ep; dev->ep_out[epnum] = ep;
} }
if (!is_out || is_control) { if (!is_out || is_control) {
usb_settoggle(dev, epnum, 0, 0); if (reset_toggle)
usb_settoggle(dev, epnum, 0, 0);
dev->ep_in[epnum] = ep; dev->ep_in[epnum] = ep;
} }
ep->enabled = 1; ep->enabled = 1;
...@@ -1168,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) ...@@ -1168,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
* usb_enable_interface - Enable all the endpoints for an interface * usb_enable_interface - Enable all the endpoints for an interface
* @dev: the device whose interface is being enabled * @dev: the device whose interface is being enabled
* @intf: pointer to the interface descriptor * @intf: pointer to the interface descriptor
* @reset_toggles: flag to set the endpoints' toggles back to 0
* *
* Enables all the endpoints for the interface's current altsetting. * Enables all the endpoints for the interface's current altsetting.
*/ */
static void usb_enable_interface(struct usb_device *dev, void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf) struct usb_interface *intf, bool reset_toggles)
{ {
struct usb_host_interface *alt = intf->cur_altsetting; struct usb_host_interface *alt = intf->cur_altsetting;
int i; int i;
for (i = 0; i < alt->desc.bNumEndpoints; ++i) for (i = 0; i < alt->desc.bNumEndpoints; ++i)
usb_enable_endpoint(dev, &alt->endpoint[i]); usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles);
} }
/** /**
...@@ -1303,7 +1308,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) ...@@ -1303,7 +1308,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* during the SETUP stage - hence EP0 toggles are "don't care" here. * during the SETUP stage - hence EP0 toggles are "don't care" here.
* (Likewise, EP0 never "halts" on well designed devices.) * (Likewise, EP0 never "halts" on well designed devices.)
*/ */
usb_enable_interface(dev, iface); usb_enable_interface(dev, iface, true);
if (device_is_registered(&iface->dev)) { if (device_is_registered(&iface->dev)) {
usb_create_sysfs_intf_files(iface); usb_create_sysfs_intf_files(iface);
create_intf_ep_devs(iface); create_intf_ep_devs(iface);
...@@ -1382,7 +1387,7 @@ int usb_reset_configuration(struct usb_device *dev) ...@@ -1382,7 +1387,7 @@ int usb_reset_configuration(struct usb_device *dev)
usb_remove_sysfs_intf_files(intf); usb_remove_sysfs_intf_files(intf);
} }
intf->cur_altsetting = alt; intf->cur_altsetting = alt;
usb_enable_interface(dev, intf); usb_enable_interface(dev, intf, true);
if (device_is_registered(&intf->dev)) { if (device_is_registered(&intf->dev)) {
usb_create_sysfs_intf_files(intf); usb_create_sysfs_intf_files(intf);
create_intf_ep_devs(intf); create_intf_ep_devs(intf);
...@@ -1685,7 +1690,7 @@ free_interfaces: ...@@ -1685,7 +1690,7 @@ free_interfaces:
alt = &intf->altsetting[0]; alt = &intf->altsetting[0];
intf->cur_altsetting = alt; intf->cur_altsetting = alt;
usb_enable_interface(dev, intf); usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev; intf->dev.parent = &dev->dev;
intf->dev.driver = NULL; intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type; intf->dev.bus = &usb_bus_type;
......
...@@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, ...@@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
/* ep0 maxpacket comes later, from device descriptor */ /* ep0 maxpacket comes later, from device descriptor */
usb_enable_endpoint(dev, &dev->ep0); usb_enable_endpoint(dev, &dev->ep0, true);
dev->can_submit = 1; dev->can_submit = 1;
/* Save readable and stable topology id, distinguishing devices /* Save readable and stable topology id, distinguishing devices
......
...@@ -12,7 +12,9 @@ extern int usb_create_ep_devs(struct device *parent, ...@@ -12,7 +12,9 @@ extern int usb_create_ep_devs(struct device *parent,
extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint); extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint);
extern void usb_enable_endpoint(struct usb_device *dev, extern void usb_enable_endpoint(struct usb_device *dev,
struct usb_host_endpoint *ep); struct usb_host_endpoint *ep, bool reset_toggle);
extern void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf, bool reset_toggles);
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_interface(struct usb_device *dev, extern void usb_disable_interface(struct usb_device *dev,
struct usb_interface *intf); struct usb_interface *intf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment