Re: [PATCH] fix data toggle mismatch, after driver unbinding without toggle reset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The patch works great.  Thanks!

On Wed, Jan 14, 2009 at 2:55 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
> On Wed, 14 Jan 2009, Dan Streetman wrote:
>
>> The previous patch (as1197), to not reset the data toggle during driver
>> unbinding when an interface is already in altsetting 0, has introduced an
>> intermittent loss of packets due to data toggle mismatch.  Specifically, the
>> ehci and ohci drivers do not use the device toggle field to actually track the
>> current toggle; they use hardware to track the device toggle.  So avoiding the
>> call to usb_settoggle() in usb_endpoint_enable() isn't enough.
>
> Oho!  You are right, although you didn't express the reason very
> clearly.  usb_disable_endpoint's call to usb_hcd_disable_endpoint
> changes the hardware state in EHCI and OHCI controllers without
> changing the corresponding state in the device.
>
>> I think there is an easy way to fix this, and a hard way.  The hard way (and
>> more correct way IMHO) is to make the data toggles HCD-internal, which is what
>> they really should be.  The only interface that drivers or the USB core needs is
>> the ability to reset the data toggle after a set interface or a clear halt.
>> This would be a relatively large change, affecting all the HC drivers, the USB
>> core, and many of the USB device drivers.
>>
>> The easy way is what the patch below does.  Currently, dev->toggle has 2
>> different meanings:
>> -For uhci, it is the actual toggle value
>> -For ehci and ohci, it is a flag to indicate if the toggle should be reset
>> This patch changes it to have 3 meanings:
>> -For uhci, it is the actual toggle value
>> -For ehci and ohci while there is a qh or ed present for an endpoint, it is a
>> flag to indicate if the toggle should be reset
>> -For ehci and ohci when the endpoint is disabled, it stores the last toggle
>> value, which is restored when the endpoint is re-enabled and a new qh or ed is
>> created for the endpoint.  If the toggle really should be reset, it happens
>> normally during the call to usb_enable_endpoint().
>
> No, neither of your suggestions is right.  The real problem is to
> distinguish between enabling/disabling endpoints just in the usbcore
> data structures or in both the core and the hardware.  The reset_toggle
> argument added by as1197 was an attempt to do this, but it targeted
> only the enable-endpoint path.  We need a corresponding change for the
> disable_endpoint path as well.
>
> Does this patch fix everything?
>
> Alan Stern
>
>
>
> Index: usb-2.6/drivers/usb/core/usb.h
> ===================================================================
> --- usb-2.6.orig/drivers/usb/core/usb.h
> +++ usb-2.6/drivers/usb/core/usb.h
> @@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct u
>                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,
> +               bool reset_hardware);
>  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_disable_device(struct usb_device *dev, int skip_ep0);
>  extern int usb_deauthorize_device(struct usb_device *);
> Index: usb-2.6/drivers/usb/core/message.c
> ===================================================================
> --- usb-2.6.orig/drivers/usb/core/message.c
> +++ usb-2.6/drivers/usb/core/message.c
> @@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct u
>  * @dev: the device whose endpoint is being disabled
>  * @epaddr: the endpoint's address.  Endpoint number for output,
>  *     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
> - * pending urbs.
> - *
> - * If the HCD hasn't registered a disable() function, this sets the
> - * endpoint's maxpacket size to 0 to prevent further submissions.
> + * Disables the endpoint for URB submission and nukes all pending URBs.
> + * If @reset_hardware is set then also deallocates hcd/hardware state
> + * for the endpoint.
>  */
> -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;
>        struct usb_host_endpoint *ep;
> @@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_dev
>
>        if (usb_endpoint_out(epaddr)) {
>                ep = dev->ep_out[epnum];
> -               dev->ep_out[epnum] = NULL;
> +               if (reset_hardware)
> +                       dev->ep_out[epnum] = NULL;
>        } else {
>                ep = dev->ep_in[epnum];
> -               dev->ep_in[epnum] = NULL;
> +               if (reset_hardware)
> +                       dev->ep_in[epnum] = NULL;
>        }
>        if (ep) {
>                ep->enabled = 0;
>                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_dev
>  * usb_disable_interface -- Disable all endpoints for an interface
>  * @dev: the device whose interface is being disabled
>  * @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.
>  */
> -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;
>        int i;
>
>        for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
>                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_devic
>        dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__,
>                skip_ep0 ? "non-ep0" : "all");
>        for (i = skip_ep0; i < 16; ++i) {
> -               usb_disable_endpoint(dev, i);
> -               usb_disable_endpoint(dev, i + USB_DIR_IN);
> +               usb_disable_endpoint(dev, i, true);
> +               usb_disable_endpoint(dev, i + USB_DIR_IN, true);
>        }
>        dev->toggle[0] = dev->toggle[1] = 0;
>
> @@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device
>                remove_intf_ep_devs(iface);
>                usb_remove_sysfs_intf_files(iface);
>        }
> -       usb_disable_interface(dev, iface);
> +       usb_disable_interface(dev, iface, true);
>
>        iface->cur_altsetting = alt;
>
> @@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_d
>         */
>
>        for (i = 1; i < 16; ++i) {
> -               usb_disable_endpoint(dev, i);
> -               usb_disable_endpoint(dev, i + USB_DIR_IN);
> +               usb_disable_endpoint(dev, i, true);
> +               usb_disable_endpoint(dev, i + USB_DIR_IN, true);
>        }
>
>        config = dev->actconfig;
> Index: usb-2.6/drivers/usb/core/driver.c
> ===================================================================
> --- usb-2.6.orig/drivers/usb/core/driver.c
> +++ usb-2.6/drivers/usb/core/driver.c
> @@ -284,7 +284,7 @@ static int usb_unbind_interface(struct d
>         * supports "soft" unbinding.
>         */
>        if (!driver->soft_unbind)
> -               usb_disable_interface(udev, intf);
> +               usb_disable_interface(udev, intf, false);
>
>        driver->disconnect(intf);
>        usb_cancel_queued_reset(intf);
> Index: usb-2.6/drivers/usb/core/hub.c
> ===================================================================
> --- usb-2.6.orig/drivers/usb/core/hub.c
> +++ usb-2.6/drivers/usb/core/hub.c
> @@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_
>
>  void usb_ep0_reinit(struct usb_device *udev)
>  {
> -       usb_disable_endpoint(udev, 0 + USB_DIR_IN);
> -       usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
> +       usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
> +       usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
>        usb_enable_endpoint(udev, &udev->ep0, true);
>  }
>  EXPORT_SYMBOL_GPL(usb_ep0_reinit);
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux