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

USB: fix toggle mismatch in disable_endpoint paths

This patch (as1200) finishes some fixes that were left incomplete by
an earlier patch.

Although nobody has addressed this issue in the past, it turns out
that we need to distinguish between two different modes of disabling
and enabling endpoints.  In one mode only the data structures in
usbcore are affected, and in the other mode the host controller and
device hardware states are affected as well.

The earlier patch added an extra argument to the routines in the
enable_endpoint pathways to reflect this difference.  This patch adds
corresponding arguments to the disable_endpoint pathways.  Without
this change, the endpoint toggle state can get out of sync between
the host and the device.  The exact mechanism depends on the details
of the host controller (whether or not it stores its own copy of the
toggle values).
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarDan Streetman <ddstreet@ieee.org>
Tested-by: default avatarDan Streetman <ddstreet@ieee.org>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 95bec45d
...@@ -284,7 +284,7 @@ static int usb_unbind_interface(struct device *dev) ...@@ -284,7 +284,7 @@ static int usb_unbind_interface(struct device *dev)
* supports "soft" unbinding. * supports "soft" unbinding.
*/ */
if (!driver->soft_unbind) if (!driver->soft_unbind)
usb_disable_interface(udev, intf); usb_disable_interface(udev, intf, false);
driver->disconnect(intf); driver->disconnect(intf);
usb_cancel_queued_reset(intf); usb_cancel_queued_reset(intf);
......
...@@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1) ...@@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1)
void usb_ep0_reinit(struct usb_device *udev) void usb_ep0_reinit(struct usb_device *udev)
{ {
usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT); usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
usb_enable_endpoint(udev, &udev->ep0, true); usb_enable_endpoint(udev, &udev->ep0, true);
} }
EXPORT_SYMBOL_GPL(usb_ep0_reinit); EXPORT_SYMBOL_GPL(usb_ep0_reinit);
......
...@@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf) ...@@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
* @dev: the device whose endpoint is being disabled * @dev: the device whose endpoint is being disabled
* @epaddr: the endpoint's address. Endpoint number for output, * @epaddr: the endpoint's address. Endpoint number for output,
* endpoint number + USB_DIR_IN for input * endpoint number + USB_DIR_IN for input
* @reset_hardware: flag to erase any endpoint state stored in the
* controller hardware
* *
* Deallocates hcd/hardware state for this endpoint ... and nukes all * Disables the endpoint for URB submission and nukes all pending URBs.
* pending urbs. * If @reset_hardware is set then also deallocates hcd/hardware state
* * for the endpoint.
* If the HCD hasn't registered a disable() function, this sets the
* endpoint's maxpacket size to 0 to prevent further submissions.
*/ */
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
bool reset_hardware)
{ {
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
struct usb_host_endpoint *ep; struct usb_host_endpoint *ep;
...@@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) ...@@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
if (usb_endpoint_out(epaddr)) { if (usb_endpoint_out(epaddr)) {
ep = dev->ep_out[epnum]; ep = dev->ep_out[epnum];
dev->ep_out[epnum] = NULL; if (reset_hardware)
dev->ep_out[epnum] = NULL;
} else { } else {
ep = dev->ep_in[epnum]; ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL; if (reset_hardware)
dev->ep_in[epnum] = NULL;
} }
if (ep) { if (ep) {
ep->enabled = 0; ep->enabled = 0;
usb_hcd_flush_endpoint(dev, ep); usb_hcd_flush_endpoint(dev, ep);
usb_hcd_disable_endpoint(dev, ep); if (reset_hardware)
usb_hcd_disable_endpoint(dev, ep);
} }
} }
...@@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) ...@@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
* usb_disable_interface -- Disable all endpoints for an interface * usb_disable_interface -- Disable all endpoints for an interface
* @dev: the device whose interface is being disabled * @dev: the device whose interface is being disabled
* @intf: pointer to the interface descriptor * @intf: pointer to the interface descriptor
* @reset_hardware: flag to erase any endpoint state stored in the
* controller hardware
* *
* Disables all the endpoints for the interface's current altsetting. * Disables all the endpoints for the interface's current altsetting.
*/ */
void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf,
bool reset_hardware)
{ {
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_disable_endpoint(dev, usb_disable_endpoint(dev,
alt->endpoint[i].desc.bEndpointAddress); alt->endpoint[i].desc.bEndpointAddress,
reset_hardware);
} }
} }
...@@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) ...@@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__,
skip_ep0 ? "non-ep0" : "all"); skip_ep0 ? "non-ep0" : "all");
for (i = skip_ep0; i < 16; ++i) { for (i = skip_ep0; i < 16; ++i) {
usb_disable_endpoint(dev, i); usb_disable_endpoint(dev, i, true);
usb_disable_endpoint(dev, i + USB_DIR_IN); usb_disable_endpoint(dev, i + USB_DIR_IN, true);
} }
dev->toggle[0] = dev->toggle[1] = 0; dev->toggle[0] = dev->toggle[1] = 0;
...@@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) ...@@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
remove_intf_ep_devs(iface); remove_intf_ep_devs(iface);
usb_remove_sysfs_intf_files(iface); usb_remove_sysfs_intf_files(iface);
} }
usb_disable_interface(dev, iface); usb_disable_interface(dev, iface, true);
iface->cur_altsetting = alt; iface->cur_altsetting = alt;
...@@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev) ...@@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev)
*/ */
for (i = 1; i < 16; ++i) { for (i = 1; i < 16; ++i) {
usb_disable_endpoint(dev, i); usb_disable_endpoint(dev, i, true);
usb_disable_endpoint(dev, i + USB_DIR_IN); usb_disable_endpoint(dev, i + USB_DIR_IN, true);
} }
config = dev->actconfig; config = dev->actconfig;
......
...@@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct usb_device *dev, ...@@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct usb_device *dev,
struct usb_host_endpoint *ep, bool reset_toggle); struct usb_host_endpoint *ep, bool reset_toggle);
extern void usb_enable_interface(struct usb_device *dev, extern void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf, bool reset_toggles); 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,
bool reset_hardware);
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, bool reset_hardware);
extern void usb_release_interface_cache(struct kref *ref); extern void usb_release_interface_cache(struct kref *ref);
extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *); extern int usb_deauthorize_device(struct usb_device *);
......
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