|
@@ -537,29 +537,34 @@ static int get_hub_status(struct usb_device *hdev,
|
|
|
|
|
|
/*
|
|
|
* 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);
|
|
@@ -568,13 +573,22 @@ static int hub_port_status(struct usb_hub *hub, int port1,
|
|
|
} 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;
|
|
@@ -2612,6 +2626,32 @@ out_authorized:
|
|
|
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)
|
|
@@ -2676,6 +2716,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|
|
int delay_time, ret;
|
|
|
u16 portstatus;
|
|
|
u16 portchange;
|
|
|
+ u32 ext_portstatus = 0;
|
|
|
|
|
|
for (delay_time = 0;
|
|
|
delay_time < HUB_RESET_TIMEOUT;
|
|
@@ -2684,7 +2725,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|
|
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;
|
|
|
|
|
@@ -2727,6 +2775,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|
|
|
|
|
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)
|