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>
This commit is contained in:
parent
df718962bf
commit
2caf7fcdb8
|
@ -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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
* 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)
|
||||||
* 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)
|
||||||
* 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)
|
||||||
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:
|
||||||
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,
|
||||||
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,
|
||||||
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);
|
||||||
|
|
Reference in New Issue