Re: [RFC PATCH 7/8] usb: xhci: Rewrite xhci_create_usb3_bos_desc()

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

 



Hi

On 2.2.2021 5.42, Thinh Nguyen wrote:
> The current xhci_create_usb3_bos_desc() uses a static bos structure and
> various magic numbers and offset making it difficult to extend support
> for USB 3.2. Let's rewrite this entire function to support dual-lane in
> USB 3.2.

Agree, it's time to get rid of the static u8 array used for bos.
 
> 
> The hub driver matches the port speed ID from the extended port status
> to the SSID of the sublink speed attributes to detect if the device
> supports SuperSpeed Plus. Currently we don't provide the default gen1x2
> and gen2x2 sublink speed capability descriptor for USB 3.2 roothub. The
> USB stack depends on this to detect and match the correct speed.
> In addition, if the xHCI host provides Protocol Speed ID (PSI)
> capability, then make sure to convert Protocol Speed ID Mantissa and
> Exponent (PSIM & PSIE) to lane speed for gen1x2 and gen2x2.
> 
> Signed-off-by: Thinh Nguyen <Thinh.Nguyen@xxxxxxxxxxxx>
> ---
>  drivers/usb/host/xhci-hub.c | 279 +++++++++++++++++++++++++++++++++++-
>  1 file changed, 277 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 74c497fd3476..c095c30212e5 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -11,6 +11,7 @@
>  
>  #include <linux/slab.h>
>  #include <asm/unaligned.h>
> +#include <linux/bitfield.h>
>  
>  #include "xhci.ch"
>  #include "xhci-trace.h"
> @@ -52,7 +53,281 @@ static u8 usb_bos_descriptor [] = {
>  	0xb5, 0x40, 0x0a, 0x00,		/* 10Gbps, SSP, symmetric, tx, ID = 5 */
>  };
>  
> -static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf,
> +static int xhci_fill_default_ssp_attr(struct xhci_hcd *xhci,
> +		struct usb_ssp_cap_descriptor *ssp_cap)
> +{
> +	u32 attr;
> +	u8 ssac;
> +	int i;
> +
> +	attr = le32_to_cpu(ssp_cap->bmAttributes);
> +	ssac = FIELD_GET(USB_SSP_SUBLINK_SPEED_ATTRIBS, attr);
> +
> +	/* Currently we only support USB 3.1 and 3.2 */
> +	if (ssac != 3 && ssac != 7)
> +		return -EINVAL;
> +
> +	/*
> +	 * Map default xHCI port speed ID to SSID:
> +	 *  SSID 4 = Symmetric SSP Gen1x1
> +	 *  SSID 5 = Symmetric SSP Gen2x1
> +	 *  SSID 6 = Symmetric SSP Gen1x2
> +	 *  SSID 7 = Symmetric SSP Gen2x2
> +	 */
> +	for (i = 0; i < ssac + 1; i++) {
> +		u8 ssid;
> +		u8 type;
> +		u8 lp;
> +		u16 mantissa;
> +
> +		ssid = (i >> 1) + 4;
> +
> +		if (ssid > 4)
> +			lp = USB_SSP_SUBLINK_SPEED_LP_SSP;
> +		else
> +			lp = USB_SSP_SUBLINK_SPEED_LP_SS;
> +
> +		if (ssid == 5 || ssid == 7)
> +			mantissa = 10;
> +		else
> +			mantissa = 5;
> +
> +		if (i % 2)
> +			type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX;
> +		else
> +			type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX;
> +
> +		ssp_cap->bmSublinkSpeedAttr[i] =
> +			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID,
> +					       ssid) |
> +				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE,
> +					       USB_SSP_SUBLINK_SPEED_LSE_GBPS) |
> +				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) |
> +				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, lp) |
> +				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM,
> +					       mantissa));
> +	}
> +
> +	return 0;
> +}

I don't think it makes sense to generates the default sublinkSpeedAttr[] entries like
the function above does.
The content is static and defined in xhci spec.
For these I'd just make a static u32 array. (minding endianness)

static u32 ssp_cap_default_ssa[] = {
	0xaaaaaaaa, /* USB 3.0 SS 5Gbps Id:4, rx */ 
	0xbbbbbbbb, /* USB 3.0 SS 5Gbps Id:4, tx */ 
	0xcccccccc, /* USB 3.1 SSP 10Gbps Id:5  rx */ 
	0xdddddddd, /* USB 3.1 SSP 10Gbps Id:5, tx */ 
	0xeeeeeeee, /* USB 3.2 Gen1x2 10Gbps Id:6, rx */ 
	...
}

Then when creating the ssp cap descriptor set the attribute count (ssac) based on
bcdUSB as you do, and assign the default values if custom ones aren't given (!psi_count).

for (i = 0; i < ssac + 1; i++)
	ssp_cap->mbSublinkSpeedAttr[i] = ssp_capdefault_ssa[i]; 
> +
> +static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
> +				      u16 wLength)
> +{
> +	struct usb_bos_descriptor	*bos;
> +	struct usb_ss_cap_descriptor	*ss_cap;
> +	struct usb_ssp_cap_descriptor	*ssp_cap;
> +	struct xhci_port_cap		*port_cap = NULL;
> +	u16				bcdUSB;
> +	u32				reg;
> +	u32				min_rate = 0;
> +	u8				min_ssid;
> +	u8				ssac;
> +	u8				ssic;
> +	int				offset;
> +	int				i;
> +
> +	/* BOS descriptor */
> +	bos = (struct usb_bos_descriptor *)buf;
> +	bos->bLength = USB_DT_BOS_SIZE;
> +	bos->bDescriptorType = USB_DT_BOS;
> +	bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE +
> +					USB_DT_USB_SS_CAP_SIZE);
> +	bos->bNumDeviceCaps = 1;

This is much better than the current way we fill the descriptor, much more readable.

> +
> +	/* Create the descriptor for port with the highest revision */
> +	for (i = 0; i < xhci->num_port_caps; i++) {
> +		u8 major = xhci->port_caps[i].maj_rev;
> +		u8 minor = xhci->port_caps[i].min_rev;
> +		u16 rev = (major << 8) | minor;
> +
> +		if (i == 0 || bcdUSB < rev) {
> +			bcdUSB = rev;
> +			port_cap = &xhci->port_caps[i];
> +		}
> +	}
> +
> +	if (bcdUSB >= 0x0310) {
> +		/* Two SSA entries for each unique PSI ID, RX and TX */
> +		if (port_cap->psi_count)
> +			ssac = port_cap->psi_uid_count * 2 - 1;
> +		else if (bcdUSB == 0x0320)

maybe (bcdUSB >= 0x320), allows old driver to handle future hardware.

> +			ssac = 7;
> +		else
> +			ssac = 3;
> +
> +		if (port_cap->psi_count)
> +			ssic = port_cap->psi_count - 1;
> +		else
> +			ssic = (ssac + 1) / 2 - 1;
> +
> +		bos->bNumDeviceCaps++;
> +		bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE +
> +						USB_DT_USB_SS_CAP_SIZE +
> +						USB_DT_USB_SSP_CAP_SIZE(ssac));
> +	}
> +
> +	if (wLength < USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE)
> +		return wLength;
> +
> +	/* SuperSpeed USB Device Capability */
> +	ss_cap = (struct usb_ss_cap_descriptor *)&buf[USB_DT_BOS_SIZE];
> +	ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
> +	ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
> +	ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
> +	ss_cap->bmAttributes = 0; /* set later */
> +	ss_cap->wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION);
> +	ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
> +	ss_cap->bU1devExitLat = 0; /* set later */
> +	ss_cap->bU2DevExitLat = 0; /* set later */
> +
> +	reg = readl(&xhci->cap_regs->hcc_params);
> +	if (HCC_LTC(reg))
> +		ss_cap->bmAttributes |= USB_LTM_SUPPORT;
> +
> +	if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
> +		reg = readl(&xhci->cap_regs->hcs_params3);
> +		ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg);
> +		ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg));
> +	}
> +
> +	if (wLength < le16_to_cpu(bos->wTotalLength))
> +		return wLength;
> +
> +	if (bcdUSB < 0x0310)
> +		return le16_to_cpu(bos->wTotalLength);
> +
> +	ssp_cap = (struct usb_ssp_cap_descriptor *)&buf[USB_DT_BOS_SIZE +
> +		USB_DT_USB_SS_CAP_SIZE];
> +	ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac);
> +	ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
> +	ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
> +	ssp_cap->bReserved = 0;
> +	ssp_cap->wReserved = 0;
> +	ssp_cap->bmAttributes =
> +		cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) |
> +			    FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic));
> +
> +	if (!port_cap->psi_count) {

for (i = 0; i < ssac + 1; i++)
	ssp_cap->mbSublinkSpeedAttr[i] = ssp_cap_default_ssa[i];  //mind endianness

> +		int ret;
> +
> +		ret = xhci_fill_default_ssp_attr(xhci, ssp_cap);
> +		if (ret)
> +			return ret;
> +
> +		min_ssid = 4;
> +		goto out;
> +	}
> +
> +	offset = 0;
> +	for (i = 0; i < port_cap->psi_count; i++) {

I need to take a better look at this sublinkSpeedAttr[] creation from custom PSIC table a bit later.

Thanks
-Mathias



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

  Powered by Linux