# 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)))