# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.296   -> 1.297  
#	 include/linux/usb.h	1.19    -> 1.20   
#	   drivers/usb/usb.c	1.30    -> 1.31   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/02/12	mdiehl@mdiehl.de	1.297
# [PATCH] usb_set_interface: correct toggle reset
#   
# this is a patch to prevent usb_set_interface() from erroneously resetting
# the toggles for all endpoints instead of only the affected ones from the
# requested interface/altsetting. I've also added some missing parentheses
# to related macros in usb.h as I prefered not to take special care for
# nasty side-effects ;-)
#   
# Patch below was created against 2.4.18-pre9 (with some lines of offset it
# applies to 2.5.4-pre5 as well).
#   
# Tested in multi-interface configuration to provide evidence it:
# * correctly identifies the affected endpoints and resets the toggles
# * doesn't touch endpoints from other interfaces
# * provides correct handling of shared EP0
# * solves an issue I had with 2.4.18-pre9 where setting one interface
#   occasionally caused transfers on other interface to hang due to lost
#   toggle synchronisation
# 
# Despite being a pure bugfix, well localized and (IMHO) pretty obviously
# correct wrt. USB-spec, I'd like to suggest including this in early
# 2.4.19-pre. Just in case some existing driver would somehow workaround
# the currently wrong behavior and might break with this fix. And it's
# not very urgent right now, as we are probably close to 2.4.18-rc1.
#   
# Regards,
# Martin
# --------------------------------------------
#
diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c
--- a/drivers/usb/usb.c	Tue Feb 12 22:58:06 2002
+++ b/drivers/usb/usb.c	Tue Feb 12 22:58:06 2002
@@ -2322,7 +2322,8 @@
 int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 {
 	struct usb_interface *iface;
-	int ret;
+	struct usb_interface_descriptor *iface_as;
+	int i, ret;
 
 	iface = usb_ifnum_to_if(dev, interface);
 	if (!iface) {
@@ -2344,8 +2345,30 @@
 		return ret;
 
 	iface->act_altsetting = alternate;
-	dev->toggle[0] = 0;	/* 9.1.1.5 says to do this */
-	dev->toggle[1] = 0;
+
+	/* 9.1.1.5: reset toggles for all endpoints affected by this iface-as
+	 *
+	 * Note:
+	 * Despite EP0 is always present in all interfaces/AS, the list of
+	 * endpoints from the descriptor does not contain EP0. Due to its
+	 * omnipresence one might expect EP0 being considered "affected" by
+	 * any SetInterface request and hence assume toggles need to be reset.
+	 * However, EP0 toggles are re-synced for every individual transfer
+	 * during the SETUP stage - hence EP0 toggles are "don't care" here.
+	 */
+
+	iface_as = &iface->altsetting[alternate];
+	for (i = 0; i < iface_as->bNumEndpoints; i++) {
+		u8	ep = iface_as->endpoint[i].bEndpointAddress;
+
+		usb_settoggle(dev, ep&USB_ENDPOINT_NUMBER_MASK, usb_endpoint_out(ep), 0);
+	}
+
+	/* usb_set_maxpacket() sets the maxpacket size for all EP in all
+	 * interfaces but it shouldn't do any harm here: we have changed
+	 * the AS for the requested interface only, hence for unaffected
+	 * interfaces it's just re-application of still-valid values.
+	 */
 	usb_set_maxpacket(dev);
 	return 0;
 }
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Tue Feb 12 22:58:06 2002
+++ b/include/linux/usb.h	Tue Feb 12 22:58:06 2002
@@ -1156,12 +1156,12 @@
 #define PIPE_DEVEP_MASK		0x0007ff00
 
 /* The D0/D1 toggle bits */
-#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1)
-#define	usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << ep))
-#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep))
+#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
+#define	usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << (ep)))
+#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))
 
 /* Endpoint halt control/status */
-#define usb_endpoint_out(ep_dir)	(((ep_dir >> 7) & 1) ^ 1)
+#define usb_endpoint_out(ep_dir)	((((ep_dir) >> 7) & 1) ^ 1)
 #define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
 #define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))
 #define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))
