Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F158159746
D57204.id178461.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D57204.id178461.diff
View Options
diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h
--- a/sys/dev/usb/usb_hub.h
+++ b/sys/dev/usb/usb_hub.h
@@ -41,6 +41,25 @@
#endif
};
+#define UHUB_SS_SUBLINKS_MAX_IDS 16
+
+/*
+ * The following structure define the usb_sublink_information
+ */
+struct usb_hub_ss_sublinks {
+ uint8_t num_ids;
+
+ struct {
+ uint8_t exp;
+ uint8_t lp;
+ uint16_t val;
+ /*
+ * Max 4 bit. For each we have 1 rx and 1 tx
+ * ID starts from 1.
+ */
+ } hub_status[(UHUB_SS_SUBLINKS_MAX_IDS) * 2];
+};
+
/*
* The following structure defines an USB HUB.
*/
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -588,6 +588,80 @@
return (err);
}
+static enum usb_dev_speed
+usb_hub_sublink_status_to_speed(struct usb_hub_ss_sublinks *links, int id)
+{
+ /* rx + tx per id */
+ if (id > (links->num_ids * 2))
+ return (USB_SPEED_VARIABLE);
+
+ DPRINTFN(4, "exp: %d, val: %d\n", links->hub_status[id].exp,
+ links->hub_status[id].val);
+
+ switch (links->hub_status[id].exp) {
+ case 0:
+ case 1:
+ return (USB_SPEED_LOW);
+ case 2:
+ return (links->hub_status[id].val >= 480 ? USB_SPEED_HIGH :
+ USB_SPEED_FULL);
+ case 3:
+ if (links->hub_status[id].val <= 5 ||
+ links->hub_status[id].lp == 0)
+ return (USB_SPEED_SUPER);
+ if (links->hub_status[id].val <= 10)
+ return (USB_SPEED_SUPER_PLUS);
+ return (USB_SPEED_SUPER_PLUS_X2);
+ }
+
+ return (USB_SPEED_VARIABLE);
+}
+static usb_error_t
+uhub_read_ext_port_status(struct uhub_softc *sc, uint8_t portno)
+{
+ struct usb_ext_status ps;
+ usb_error_t err;
+ uint32_t rx_id, tx_id;
+
+ if (sc->sc_usb_port_errors >= UHUB_USB_PORT_ERRORS_MAX) {
+ DPRINTFN(4, "port %d, HUB looks dead, too many errors\n",
+ portno);
+ sc->sc_st.ext_speed = USB_SPEED_VARIABLE;
+ return (USB_ERR_TIMEOUT);
+ }
+
+ err = usbd_req_get_ext_port_status(sc->sc_udev, NULL, &ps, portno);
+
+ if (err == 0) {
+ rx_id = UDS_RX_SUBLINK_SPEED_ID_BITS(UGETW(ps.wStatus));
+ tx_id = UDS_TX_SUBLINK_SPEED_ID_BITS(UGETW(ps.wStatus));
+ DPRINTFN(4, "rx_id: %d tx_id: %d\n", rx_id, tx_id);
+ sc->sc_st.ext_speed = MIN(
+ usb_hub_sublink_status_to_speed(&sc->sc_slinks, rx_id * 2),
+ usb_hub_sublink_status_to_speed(&sc->sc_slinks,
+ (tx_id * 2) + 1));
+
+ if (UDS_RX_SUBLINK_LANE_COUNT_BITS(UGETW(ps.wStatus)) ||
+ UDS_TX_SUBLINK_LANE_COUNT_BITS(UGETW(ps.wStatus))) {
+ /*
+ * Multi-lane link: use LP of the Rx sublink to
+ * distinguish Gen1x2 (LP=0, 10G) from Gen2x2 (LP=1, 20G).
+ */
+ if (sc->sc_slinks.hub_status[rx_id * 2].lp == 0)
+ sc->sc_st.ext_speed = USB_SPEED_SUPER_PLUS_X2;
+ else
+ sc->sc_st.ext_speed = USB_SPEED_SUPER_PLUS_GEN2_X2;
+ }
+ } else {
+ sc->sc_st.ext_speed = 0;
+ sc->sc_usb_port_errors++;
+ }
+
+ /* debugging print */
+ DPRINTFN(4, "port %d, ext_speed=0x%04x\n", portno, sc->sc_st.ext_speed);
+ return (err);
+}
+
/*------------------------------------------------------------------------*
* uhub_reattach_port
*
@@ -660,6 +734,9 @@
power_mask = UPS_PORT_POWER;
break;
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
+ case USB_SPEED_SUPER_PLUS_GEN2_X2:
if (udev->parent_hub == NULL)
power_mask = 0; /* XXX undefined */
else
@@ -753,9 +830,24 @@
case USB_SPEED_LOW:
speed = USB_SPEED_LOW;
break;
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
+ case USB_SPEED_SUPER_PLUS_GEN2_X2:
+ err = uhub_read_ext_port_status(sc, portno);
+ if (!err) {
+ speed = sc->sc_st.ext_speed;
+ } else {
+ speed = USB_SPEED_SUPER;
+ }
+ break;
case USB_SPEED_SUPER:
+ /*
+ * Port Speed bits in SuperSpeed Hub is only valid when the hub
+ * is run uder Enhanced SS speed, which has been handle in the
+ * USB_SPEED_SUPER_PLUS case. Therefore, a SuperSpeed Hub can
+ * only attach to a SuperSpeed Device
+ */
if (udev->parent_hub == NULL) {
- /* Root HUB - special case */
switch (sc->sc_st.port_status & UPS_OTHER_SPEED) {
case 0:
speed = USB_SPEED_FULL;
@@ -779,16 +871,16 @@
speed = udev->speed;
break;
}
- if (speed == USB_SPEED_SUPER) {
- err = usbd_req_set_hub_u1_timeout(udev, NULL,
- portno, 128 - (2 * udev->depth));
+ if (speed >= USB_SPEED_SUPER) {
+ err = usbd_req_set_hub_u1_timeout(udev, NULL, portno,
+ 127 - (2 * udev->depth));
if (err) {
DPRINTFN(0, "port %d U1 timeout "
"failed, error=%s\n",
portno, usbd_errstr(err));
}
- err = usbd_req_set_hub_u2_timeout(udev, NULL,
- portno, 128 - (2 * udev->depth));
+ err = usbd_req_set_hub_u2_timeout(udev, NULL, portno,
+ 127 - (2 * udev->depth));
if (err) {
DPRINTFN(0, "port %d U2 timeout "
"failed, error=%s\n",
@@ -973,6 +1065,8 @@
return (1);
break;
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
if (udev->depth > USB_SS_HUB_DEPTH_MAX)
return (1);
break;
@@ -1088,7 +1182,7 @@
if (err != USB_ERR_NORMAL_COMPLETION)
retval = err;
}
- if (udev->speed == USB_SPEED_SUPER &&
+ if (udev->speed >= USB_SPEED_SUPER &&
(sc->sc_st.port_change & UPS_C_BH_PORT_RESET) != 0) {
DPRINTF("Warm reset finished on port %u.\n", portno);
err = usbd_req_clear_port_feature(
@@ -1138,6 +1232,75 @@
return (ENXIO);
}
+static usb_error_t
+uhub_ss_caps_get(struct usb_bos_descriptor *bdesc,
+ struct usb_devcap_ss_plus_descriptor **desc)
+{
+ struct usb_bos_cap_descriptor *cap = NULL;
+
+ *desc = NULL;
+ if (bdesc == NULL)
+ return (USB_ERR_INVAL);
+
+ while ((cap = usbd_bos_foreach(bdesc, cap))) {
+ if (cap->bDescriptorType == UDESC_DEVICE_CAPABILITY &&
+ cap->bDevCapabilityType == USB_DEVCAP_SUPERSPEED_PLUS) {
+ *desc = (struct usb_devcap_ss_plus_descriptor *)cap;
+ return (0);
+ }
+ }
+ return (USB_ERR_INVAL);
+}
+
+static usb_error_t
+uhub_ss_caps_to_hub_sslink(struct usb_devcap_ss_plus_descriptor *desc,
+ struct usb_hub_ss_sublinks *links)
+{
+ uint32_t attrs = UGETW(desc->bmAttributes);
+ uint32_t n = USB_DEVCAP_SSPLUS_SSAC_GET(attrs) + 1;
+ uint32_t link_val;
+ uint8_t id, st;
+ uint32_t i;
+
+ links->num_ids = USB_DEVCAP_SSPLUS_SSIC_GET(attrs) + 1;
+ if (links->num_ids >= UHUB_SS_SUBLINKS_MAX_IDS) {
+ return (USB_ERR_INVAL);
+ }
+
+ for (i = 0; i < n; ++i) {
+ link_val = UGETDW(desc->bmSublinkSpeedAttr[i]);
+ id = USB_DEVCAP_SSPLUS_SSID_GET(link_val);
+ st = USB_DEVCAP_SSPLUS_ST_GET(link_val);
+
+ /*
+ * asymmetric link
+ */
+ if (st & 0x01) {
+ links->hub_status[id * 2 + (st >> 1)].exp =
+ USB_DEVCAP_SSPLUS_LSE_GET(link_val);
+ links->hub_status[id * 2 + (st >> 1)].lp =
+ USB_DEVCAP_SSPLUS_LP_GET(link_val);
+ links->hub_status[id * 2 + (st >> 1)].val =
+ USB_DEVCAP_SSPLUS_LSM_GET(link_val);
+ } else {
+ links->hub_status[id * 2].exp =
+ USB_DEVCAP_SSPLUS_LSE_GET(link_val);
+ links->hub_status[id * 2].lp = USB_DEVCAP_SSPLUS_LP_GET(
+ link_val);
+ links->hub_status[id * 2].val =
+ USB_DEVCAP_SSPLUS_LSM_GET(link_val);
+ links->hub_status[id * 2 + 1].exp =
+ USB_DEVCAP_SSPLUS_LSE_GET(link_val);
+ links->hub_status[id * 2 + 1].lp =
+ USB_DEVCAP_SSPLUS_LP_GET(link_val);
+ links->hub_status[id * 2 + 1].val =
+ USB_DEVCAP_SSPLUS_LSM_GET(link_val);
+ }
+ }
+
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
/* NOTE: The information returned by this function can be wrong. */
usb_error_t
uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt)
@@ -1172,7 +1335,9 @@
if (udev->speed == USB_SPEED_HIGH)
tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3;
break;
-
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
+ case USB_SPEED_SUPER_PLUS_GEN2_X2:
case USB_SPEED_SUPER:
err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
if (err) {
@@ -1209,6 +1374,9 @@
struct usb_hub *hub;
struct usb_hub_descriptor hubdesc20;
struct usb_hub_ss_descriptor hubdesc30;
+ struct usb_bos_descriptor *bdesc;
+ struct usb_devcap_ss_plus_descriptor *dev_desc;
+ uint32_t bdesc_len;
#if USB_HAVE_DISABLE_ENUM
struct sysctl_ctx_list *sysctl_ctx;
struct sysctl_oid *sysctl_tree;
@@ -1302,6 +1470,37 @@
}
}
break;
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
+ case USB_SPEED_SUPER_PLUS_GEN2_X2:
+ err = usbd_req_get_bos_descriptor_full(udev, NULL, &bdesc,
+ &bdesc_len);
+ if (err) {
+ DPRINTFN(0,
+ "Getting USB BOS descriptor failed,"
+ "error=%s\n",
+ usbd_errstr(err));
+ goto error;
+ }
+ err = uhub_ss_caps_get(bdesc, &dev_desc);
+ if (err) {
+ DPRINTFN(0,
+ "Getting USB SS Plus descriptor failed,"
+ "error=%s\n",
+ usbd_errstr(err));
+ free(bdesc, M_USBDEV);
+ goto error;
+ }
+ err = uhub_ss_caps_to_hub_sslink(dev_desc, &sc->sc_slinks);
+ if (err) {
+ DPRINTFN(0, "Failed to parse SS Link Descriptor\n");
+ free(bdesc, M_USBDEV);
+ goto error;
+ }
+ free(bdesc, M_USBDEV);
+ /*
+ * fallthrough
+ */
case USB_SPEED_SUPER:
if (udev->parent_hub != NULL) {
err = usbd_req_set_hub_depth(udev, NULL,
@@ -1474,6 +1673,9 @@
removable++;
break;
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER_PLUS_X2:
+ case USB_SPEED_SUPER_PLUS_GEN2_X2:
if (!UHD_NOT_REMOV(&hubdesc30, portno))
removable++;
break;
diff --git a/sys/dev/usb/usb_hub_private.h b/sys/dev/usb/usb_hub_private.h
--- a/sys/dev/usb/usb_hub_private.h
+++ b/sys/dev/usb/usb_hub_private.h
@@ -46,6 +46,7 @@
struct uhub_current_state {
uint16_t port_change;
uint16_t port_status;
+ uint16_t ext_speed;
};
struct uhub_softc {
@@ -65,6 +66,7 @@
#define UHUB_USB_PORT_ERRORS_MAX 4
uint8_t sc_flags;
#define UHUB_FLAG_DID_EXPLORE 0x01
+ struct usb_hub_ss_sublinks sc_slinks;
};
struct hub_result {
struct usb_device *udev;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, May 30, 4:23 AM (6 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33595491
Default Alt Text
D57204.id178461.diff (9 KB)
Attached To
Mode
D57204: usb: Add support for SSP and SSPx2 hubs
Attached
Detach File
Event Timeline
Log In to Comment