/* The SuperSpeedPlus USB Device Capability descriptor */
if (gadget_is_superspeed_plus(cdev->gadget)) {
struct usb_ssp_cap_descriptor *ssp_cap;
+ u8 ssac = 1;
+ u8 ssic;
+ int i;
- ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
+ if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2)
+ ssac = 3;
/*
- * Report typical values.
+ * Paired RX and TX sublink speed attributes share
+ * the same SSID.
*/
+ ssic = (ssac + 1) / 2 - 1;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
- ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
+ ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac));
+ 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, 1) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, 0));
+ cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic));
ssp_cap->wFunctionalitySupport =
cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) |
FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) |
FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1));
- ssp_cap->bmSublinkSpeedAttr[0] =
- cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE,
- USB_SSP_SUBLINK_SPEED_LSE_GBPS) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
- USB_SSP_SUBLINK_SPEED_ST_SYM_RX) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP,
- USB_SSP_SUBLINK_SPEED_LP_SSP) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
-
- ssp_cap->bmSublinkSpeedAttr[1] =
- cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE,
- USB_SSP_SUBLINK_SPEED_LSE_GBPS) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
- USB_SSP_SUBLINK_SPEED_ST_SYM_TX) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP,
- USB_SSP_SUBLINK_SPEED_LP_SSP) |
- FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
+ /*
+ * Use 1 SSID if the gadget supports up to gen2x1 or not
+ * specified:
+ * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps.
+ *
+ * Use 1 SSID if the gadget supports up to gen1x2:
+ * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+ *
+ * Use 2 SSIDs if the gadget supports up to gen2x2:
+ * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+ * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps.
+ */
+ for (i = 0; i < ssac + 1; i++) {
+ u8 ssid;
+ u8 mantissa;
+ u8 type;
+
+ ssid = i >> 1;
+
+ if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 ||
+ cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN)
+ mantissa = 10;
+ else
+ mantissa = 5 << ssid;
+
+ 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,
+ USB_SSP_SUBLINK_SPEED_LP_SSP) |
+ FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa));
+ }
}
return le16_to_cpu(bos->wTotalLength);