usb: add plumbing for updating interrupt endpoint interval state
authorJonathan Bell <jonathan@raspberrypi.org>
Tue, 11 Jun 2019 09:55:00 +0000 (10:55 +0100)
committerDom Cobley <popcornmix@gmail.com>
Mon, 19 Feb 2024 11:32:53 +0000 (11:32 +0000)
xHCI caches device and endpoint data after the interface is configured,
so an explicit command needs to be issued for any device driver wanting
to alter the polling interval of an endpoint.

Add usb_fixup_endpoint() to allow drivers to do this. The fixup must be
called after calculating endpoint bandwidth requirements but before any
URBs are submitted.

If polling intervals are shortened, any bandwidth reservations are no
longer valid but in practice polling intervals are only ever relaxed.

Limit the scope to interrupt transfers for now.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
drivers/usb/core/hcd.c
drivers/usb/core/message.c
include/linux/usb.h
include/linux/usb/hcd.h

index 12b6dfeaf658c909aeef88699c42a40a7d15c38e..0de6ac7681888e4365c3f0b1c6b595f7063bd6b0 100644 (file)
@@ -1952,6 +1952,16 @@ reset:
        return ret;
 }
 
+void usb_hcd_fixup_endpoint(struct usb_device *udev,
+                           struct usb_host_endpoint *ep, int interval)
+{
+       struct usb_hcd *hcd;
+
+       hcd = bus_to_hcd(udev->bus);
+       if (hcd->driver->fixup_endpoint)
+               hcd->driver->fixup_endpoint(hcd, udev, ep, interval);
+}
+
 /* Disables the endpoint: synchronizes with the hcd to make sure all
  * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
  * have been called previously.  Use for set_configuration, set_interface,
index 1ee070e98fc82e1409e9ff9b9be2b0065eab5b67..89c7339386a64c7db65d40032a611f23916025c2 100644 (file)
@@ -1265,6 +1265,21 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
        intf->ep_devs_created = 0;
 }
 
+void usb_fixup_endpoint(struct usb_device *dev, int epaddr, int interval)
+{
+       unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+       struct usb_host_endpoint *ep;
+
+       if (usb_endpoint_out(epaddr))
+               ep = dev->ep_out[epnum];
+       else
+               ep = dev->ep_in[epnum];
+
+       if (ep && usb_endpoint_xfer_int(&ep->desc))
+               usb_hcd_fixup_endpoint(dev, ep, interval);
+}
+EXPORT_SYMBOL_GPL(usb_fixup_endpoint);
+
 /**
  * usb_disable_endpoint -- Disable an endpoint by address
  * @dev: the device whose endpoint is being disabled
index a21074861f91fe351027a9ce325868d7cd5ccae1..4a521591a357b587e8305749af6334751d2f03ae 100644 (file)
@@ -1888,6 +1888,8 @@ extern int usb_clear_halt(struct usb_device *dev, int pipe);
 extern int usb_reset_configuration(struct usb_device *dev);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
 extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr);
+extern void usb_fixup_endpoint(struct usb_device *dev, int epaddr,
+                              int interval);
 
 /* this request isn't really synchronous, but it belongs with the others */
 extern int usb_driver_set_configuration(struct usb_device *udev, int config);
index 61d4f0b793dcdc7300b2c991e53b862381edcb35..d3c789ac5a0554042ad949b13a78ca9932878a2c 100644 (file)
@@ -372,6 +372,11 @@ struct hc_driver {
                 * or bandwidth constraints.
                 */
        void    (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+               /* Override the endpoint-derived interval
+                * (if there is any cached hardware state).
+                */
+       void    (*fixup_endpoint)(struct usb_hcd *hcd, struct usb_device *udev,
+                                 struct usb_host_endpoint *ep, int interval);
                /* Returns the hardware-chosen device address */
        int     (*address_device)(struct usb_hcd *, struct usb_device *udev);
                /* prepares the hardware to send commands to the device */
@@ -436,6 +441,8 @@ extern void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *);
 extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *);
 extern void usb_hcd_flush_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep);
+extern void usb_hcd_fixup_endpoint(struct usb_device *udev,
+               struct usb_host_endpoint *ep, int interval);
 extern void usb_hcd_disable_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep);
 extern void usb_hcd_reset_endpoint(struct usb_device *udev,