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]

 



Mathias Nyman wrote:
> 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]; 

That looks good. I'll revise it.

>> +
>> +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.

Ok.

>
>> +			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

Thanks for the review!

Thinh




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

  Powered by Linux