/*
* USB 2.0 spec Section 11.24.2.7
+ * USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6
*/
static int get_port_status(struct usb_device *hdev, int port1,
- struct usb_port_status *data)
+ void *data, u16 value, u16 length)
{
int i, status = -ETIMEDOUT;
for (i = 0; i < USB_STS_RETRIES &&
(status == -ETIMEDOUT || status == -EPIPE); i++) {
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
- USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
- data, sizeof(*data), USB_STS_TIMEOUT);
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
+ port1, data, length, USB_STS_TIMEOUT);
}
return status;
}
-static int hub_port_status(struct usb_hub *hub, int port1,
- u16 *status, u16 *change)
+static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
+ u16 *status, u16 *change, u32 *ext_status)
{
int ret;
+ int len = 4;
+
+ if (type != HUB_PORT_STATUS)
+ len = 8;
mutex_lock(&hub->status_mutex);
- ret = get_port_status(hub->hdev, port1, &hub->status->port);
- if (ret < 4) {
+ ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
+ if (ret < len) {
if (ret != -ENODEV)
dev_err(hub->intfdev,
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->port.wPortStatus);
*change = le16_to_cpu(hub->status->port.wPortChange);
-
+ if (type != HUB_PORT_STATUS && ext_status)
+ *ext_status = le32_to_cpu(
+ hub->status->port.dwExtPortStatus);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
return ret;
}
+static int hub_port_status(struct usb_hub *hub, int port1,
+ u16 *status, u16 *change)
+{
+ return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
+ status, change, NULL);
+}
+
static void kick_hub_wq(struct usb_hub *hub)
{
struct usb_interface *intf;
return result;
}
+/*
+ * Return 1 if port speed is SuperSpeedPlus, 0 otherwise
+ * check it from the link protocol field of the current speed ID attribute.
+ * current speed ID is got from ext port status request. Sublink speed attribute
+ * table is returned with the hub BOS SSP device capability descriptor
+ */
+static int port_speed_is_ssp(struct usb_device *hdev, int speed_id)
+{
+ int ssa_count;
+ u32 ss_attr;
+ int i;
+ struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap;
+
+ if (!ssp_cap)
+ return 0;
+
+ ssa_count = le32_to_cpu(ssp_cap->bmAttributes) &
+ USB_SSP_SUBLINK_SPEED_ATTRIBS;
+
+ for (i = 0; i <= ssa_count; i++) {
+ ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]);
+ if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID))
+ return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP);
+ }
+ return 0;
+}
/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
static unsigned hub_is_wusb(struct usb_hub *hub)
int delay_time, ret;
u16 portstatus;
u16 portchange;
+ u32 ext_portstatus = 0;
for (delay_time = 0;
delay_time < HUB_RESET_TIMEOUT;
msleep(delay);
/* read and decode port status */
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (hub_is_superspeedplus(hub->hdev))
+ ret = hub_ext_port_status(hub, port1,
+ HUB_EXT_PORT_STATUS,
+ &portstatus, &portchange,
+ &ext_portstatus);
+ else
+ ret = hub_port_status(hub, port1, &portstatus,
+ &portchange);
if (ret < 0)
return ret;
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
+ else if (hub_is_superspeedplus(hub->hdev) &&
+ port_speed_is_ssp(hub->hdev, ext_portstatus &
+ USB_EXT_PORT_STAT_RX_SPEED_ID))
+ udev->speed = USB_SPEED_SUPER_PLUS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+/*
+ * Port status type for GetPortStatus requests added in USB 3.1
+ * See USB 3.1 spec Table 10-12
+ */
+#define HUB_PORT_STATUS 0
+#define HUB_PORT_PD_STATUS 1
+#define HUB_EXT_PORT_STATUS 2
+
/*
* Hub class requests
* See USB 2.0 spec Table 11-16
/*
* Hub Status and Hub Change results
* See USB 2.0 spec Table 11-19 and Table 11-20
+ * USB 3.1 extends the port status request and may return 4 additional bytes.
+ * See USB 3.1 spec section 10.16.2.6 Table 10-12 and 10-15
*/
struct usb_port_status {
__le16 wPortStatus;
__le16 wPortChange;
+ __le32 dwExtPortStatus;
} __attribute__ ((packed));
/*
#define USB_PORT_STAT_C_LINK_STATE 0x0040
#define USB_PORT_STAT_C_CONFIG_ERROR 0x0080
+/*
+ * USB 3.1 dwExtPortStatus field masks
+ * See USB 3.1 spec 10.16.2.6.3 Table 10-15
+ */
+
+#define USB_EXT_PORT_STAT_RX_SPEED_ID 0x0000000f
+#define USB_EXT_PORT_STAT_TX_SPEED_ID 0x000000f0
+#define USB_EXT_PORT_STAT_RX_LANES 0x00000f00
+#define USB_EXT_PORT_STAT_TX_LANES 0x0000f000
+
/*
* wHubCharacteristics (masks)
* See USB 2.0 spec Table 11-13, offset 3