usb: core: add support for USB_REQ_SET_ISOCH_DELAY
USB SS and SSP hubs provide wHubDelay values on their hub descriptor which we should inform the USB Device about. The USB Specification 3.0 explains, on section 9.4.11, how to calculate the value and how to issue the request. Note that a USB_REQ_SET_ISOCH_DELAY is valid on all device states (Default, Address, Configured), we just *chose* to issue it from Address state right after successfully fetching the USB Device Descriptor. Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
66222f0d4b
commit
886ee36e72
|
@ -38,6 +38,9 @@
|
|||
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
|
||||
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
|
||||
|
||||
#define USB_TP_TRANSMISSION_DELAY 40 /* ns */
|
||||
#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */
|
||||
|
||||
/* Protect struct usb_device->state and ->children members
|
||||
* Note: Both are also protected by ->dev.sem, except that ->state can
|
||||
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
|
||||
|
@ -1350,6 +1353,20 @@ static int hub_configure(struct usb_hub *hub,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate wHubDelay + 40ns for every hub in the tree of devices.
|
||||
* The resulting value will be used for SetIsochDelay() request.
|
||||
*/
|
||||
if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) {
|
||||
u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay);
|
||||
|
||||
if (hdev->parent)
|
||||
delay += hdev->parent->hub_delay;
|
||||
|
||||
delay += USB_TP_TRANSMISSION_DELAY;
|
||||
hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX);
|
||||
}
|
||||
|
||||
maxchild = hub->descriptor->bNbrPorts;
|
||||
dev_info(hub_dev, "%d port%s detected\n", maxchild,
|
||||
(maxchild == 1) ? "" : "s");
|
||||
|
@ -4597,7 +4614,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (retval >= 0)
|
||||
retval = -EMSGSIZE;
|
||||
} else {
|
||||
u32 delay;
|
||||
|
||||
retval = 0;
|
||||
|
||||
delay = udev->parent->hub_delay;
|
||||
udev->hub_delay = min_t(u32, delay,
|
||||
USB_TP_TRANSMISSION_DELAY_MAX);
|
||||
retval = usb_set_isoch_delay(udev);
|
||||
if (retval) {
|
||||
dev_dbg(&udev->dev,
|
||||
"Failed set isoch delay, error %d\n",
|
||||
retval);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -916,6 +916,30 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_set_isoch_delay - informs the device of the packet transmit delay
|
||||
* @dev: the device whose delay is to be informed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Since this is an optional request, we don't bother if it fails.
|
||||
*/
|
||||
int usb_set_isoch_delay(struct usb_device *dev)
|
||||
{
|
||||
/* skip hub devices */
|
||||
if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
return 0;
|
||||
|
||||
/* skip non-SS/non-SSP devices */
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return 0;
|
||||
|
||||
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
USB_REQ_SET_ISOCH_DELAY,
|
||||
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
|
||||
cpu_to_le16(dev->hub_delay), 0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_get_status - issues a GET_STATUS call
|
||||
* @dev: the device whose status is being checked
|
||||
|
|
|
@ -40,6 +40,7 @@ extern int usb_remove_device(struct usb_device *udev);
|
|||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
extern int usb_set_isoch_delay(struct usb_device *dev);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
|
|
|
@ -609,6 +609,10 @@ struct usb3_lpm_parameters {
|
|||
* to keep track of the number of functions that require USB 3.0 Link Power
|
||||
* Management to be disabled for this usb_device. This count should only
|
||||
* be manipulated by those functions, with the bandwidth_mutex is held.
|
||||
* @hub_delay: cached value consisting of:
|
||||
* parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns)
|
||||
*
|
||||
* Will be used as wValue for SetIsochDelay requests.
|
||||
*
|
||||
* Notes:
|
||||
* Usbcore drivers should not set usbdev->state directly. Instead use
|
||||
|
@ -689,6 +693,8 @@ struct usb_device {
|
|||
struct usb3_lpm_parameters u1_params;
|
||||
struct usb3_lpm_parameters u2_params;
|
||||
unsigned lpm_disable_count;
|
||||
|
||||
u16 hub_delay;
|
||||
};
|
||||
#define to_usb_device(d) container_of(d, struct usb_device, dev)
|
||||
|
||||
|
|
Loading…
Reference in a new issue