aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/message.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2009-01-15 17:03:33 -0500
committerGreg Kroah-Hartman <gregkh@kvm.kroah.org>2009-01-27 19:15:32 -0500
commitddeac4e75f2527a340f9dc655bde49bb2429b39b (patch)
tree2bde5ee81d30cb6010de9262f97ba07b9cb8969b /drivers/usb/core/message.c
parent95bec45d2051227ef037f1080d7cef003b88d852 (diff)
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: Alan Stern <stern@rowland.harvard.edu> Reported-by: Dan Streetman <ddstreet@ieee.org> Tested-by: Dan Streetman <ddstreet@ieee.org> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core/message.c')
-rw-r--r--drivers/usb/core/message.c40
1 files changed, 24 insertions, 16 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index de51667dd64..31fb204f44c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
1039 * @dev: the device whose endpoint is being disabled 1039 * @dev: the device whose endpoint is being disabled
1040 * @epaddr: the endpoint's address. Endpoint number for output, 1040 * @epaddr: the endpoint's address. Endpoint number for output,
1041 * endpoint number + USB_DIR_IN for input 1041 * endpoint number + USB_DIR_IN for input
1042 * @reset_hardware: flag to erase any endpoint state stored in the
1043 * controller hardware
1042 * 1044 *
1043 * Deallocates hcd/hardware state for this endpoint ... and nukes all 1045 * Disables the endpoint for URB submission and nukes all pending URBs.
1044 * pending urbs. 1046 * If @reset_hardware is set then also deallocates hcd/hardware state
1045 * 1047 * for the endpoint.
1046 * If the HCD hasn't registered a disable() function, this sets the
1047 * endpoint's maxpacket size to 0 to prevent further submissions.
1048 */ 1048 */
1049void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) 1049void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
1050 bool reset_hardware)
1050{ 1051{
1051 unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; 1052 unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
1052 struct usb_host_endpoint *ep; 1053 struct usb_host_endpoint *ep;
@@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
1056 1057
1057 if (usb_endpoint_out(epaddr)) { 1058 if (usb_endpoint_out(epaddr)) {
1058 ep = dev->ep_out[epnum]; 1059 ep = dev->ep_out[epnum];
1059 dev->ep_out[epnum] = NULL; 1060 if (reset_hardware)
1061 dev->ep_out[epnum] = NULL;
1060 } else { 1062 } else {
1061 ep = dev->ep_in[epnum]; 1063 ep = dev->ep_in[epnum];
1062 dev->ep_in[epnum] = NULL; 1064 if (reset_hardware)
1065 dev->ep_in[epnum] = NULL;
1063 } 1066 }
1064 if (ep) { 1067 if (ep) {
1065 ep->enabled = 0; 1068 ep->enabled = 0;
1066 usb_hcd_flush_endpoint(dev, ep); 1069 usb_hcd_flush_endpoint(dev, ep);
1067 usb_hcd_disable_endpoint(dev, ep); 1070 if (reset_hardware)
1071 usb_hcd_disable_endpoint(dev, ep);
1068 } 1072 }
1069} 1073}
1070 1074
@@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
1072 * usb_disable_interface -- Disable all endpoints for an interface 1076 * usb_disable_interface -- Disable all endpoints for an interface
1073 * @dev: the device whose interface is being disabled 1077 * @dev: the device whose interface is being disabled
1074 * @intf: pointer to the interface descriptor 1078 * @intf: pointer to the interface descriptor
1079 * @reset_hardware: flag to erase any endpoint state stored in the
1080 * controller hardware
1075 * 1081 *
1076 * Disables all the endpoints for the interface's current altsetting. 1082 * Disables all the endpoints for the interface's current altsetting.
1077 */ 1083 */
1078void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) 1084void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf,
1085 bool reset_hardware)
1079{ 1086{
1080 struct usb_host_interface *alt = intf->cur_altsetting; 1087 struct usb_host_interface *alt = intf->cur_altsetting;
1081 int i; 1088 int i;
1082 1089
1083 for (i = 0; i < alt->desc.bNumEndpoints; ++i) { 1090 for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
1084 usb_disable_endpoint(dev, 1091 usb_disable_endpoint(dev,
1085 alt->endpoint[i].desc.bEndpointAddress); 1092 alt->endpoint[i].desc.bEndpointAddress,
1093 reset_hardware);
1086 } 1094 }
1087} 1095}
1088 1096
@@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
1103 dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, 1111 dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__,
1104 skip_ep0 ? "non-ep0" : "all"); 1112 skip_ep0 ? "non-ep0" : "all");
1105 for (i = skip_ep0; i < 16; ++i) { 1113 for (i = skip_ep0; i < 16; ++i) {
1106 usb_disable_endpoint(dev, i); 1114 usb_disable_endpoint(dev, i, true);
1107 usb_disable_endpoint(dev, i + USB_DIR_IN); 1115 usb_disable_endpoint(dev, i + USB_DIR_IN, true);
1108 } 1116 }
1109 dev->toggle[0] = dev->toggle[1] = 0; 1117 dev->toggle[0] = dev->toggle[1] = 0;
1110 1118
@@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
1274 remove_intf_ep_devs(iface); 1282 remove_intf_ep_devs(iface);
1275 usb_remove_sysfs_intf_files(iface); 1283 usb_remove_sysfs_intf_files(iface);
1276 } 1284 }
1277 usb_disable_interface(dev, iface); 1285 usb_disable_interface(dev, iface, true);
1278 1286
1279 iface->cur_altsetting = alt; 1287 iface->cur_altsetting = alt;
1280 1288
@@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev)
1353 */ 1361 */
1354 1362
1355 for (i = 1; i < 16; ++i) { 1363 for (i = 1; i < 16; ++i) {
1356 usb_disable_endpoint(dev, i); 1364 usb_disable_endpoint(dev, i, true);
1357 usb_disable_endpoint(dev, i + USB_DIR_IN); 1365 usb_disable_endpoint(dev, i + USB_DIR_IN, true);
1358 } 1366 }
1359 1367
1360 config = dev->actconfig; 1368 config = dev->actconfig;