PERFORCE change 181685 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sun Aug 1 15:46:23 UTC 2010
http://p4web.freebsd.org/@@181685?ac=10
Change 181685 by hselasky at hselasky_laptop001 on 2010/08/01 15:45:30
USB CORE:
- add full USB 3.0 (SuperSpeed) support to HUB class driver.
Affected files ...
.. //depot/projects/usb/src/sys/dev/usb/usb.h#53 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_hub.c#50 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_hub.h#17 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_request.c#35 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_request.h#12 edit
Differences ...
==== //depot/projects/usb/src/sys/dev/usb/usb.h#53 (text+ko) ====
@@ -226,6 +226,7 @@
#define UR_GET_TT_STATE 0x0a
#define UR_STOP_TT 0x0b
#define UR_SET_HUB_DEPTH 0x0c
+#define USB_SS_HUB_DEPTH_MAX 4 /* exclusive */
#define UR_GET_PORT_ERR_COUNT 0x0d
/* Feature numbers */
==== //depot/projects/usb/src/sys/dev/usb/usb_hub.c#50 (text+ko) ====
@@ -109,6 +109,7 @@
#define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
+#define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB)
/* prototypes for type checking: */
@@ -267,14 +268,14 @@
}
/* start control transfer, if device mode */
- if (child->flags.usb_mode == USB_MODE_DEVICE) {
+ if (child->flags.usb_mode == USB_MODE_DEVICE)
usbd_ctrl_transfer_setup(child);
- }
+
/* if a HUB becomes present, do a recursive HUB explore */
- if (child->hub) {
+ if (child->hub)
err = (child->hub->explore) (child);
- }
+
done:
return (err);
}
@@ -373,11 +374,17 @@
DPRINTF("Port %d is in Host Mode\n", portno);
if (sc->sc_st.port_status & UPS_SUSPEND) {
+ /*
+ * NOTE: Should not get here in SuperSpeed
+ * mode, because the HUB should report this
+ * bit as zero.
+ */
DPRINTF("Port %d was still "
"suspended, clearing.\n", portno);
- err = usbd_req_clear_port_feature(sc->sc_udev,
+ err = usbd_req_clear_port_feature(udev,
NULL, portno, UHF_PORT_SUSPEND);
}
+
/* USB Host Mode */
/* wait for maximum device power up time */
@@ -438,6 +445,27 @@
case USB_SPEED_LOW:
speed = USB_SPEED_LOW;
break;
+ case USB_SPEED_SUPER:
+ if (udev->parent_hub == NULL) {
+ /* Root HUB - special case */
+ switch (sc->sc_st.port_status & UPS_OTHER_SPEED) {
+ case 0:
+ speed = USB_SPEED_FULL;
+ break;
+ case UPS_LOW_SPEED:
+ speed = USB_SPEED_LOW;
+ break;
+ case UPS_HIGH_SPEED:
+ speed = USB_SPEED_HIGH;
+ break;
+ default:
+ speed = USB_SPEED_SUPER;
+ break;
+ }
+ } else {
+ speed = USB_SPEED_SUPER;
+ }
+ break;
default:
/* same speed like parent */
speed = udev->speed;
@@ -485,6 +513,28 @@
}
/*------------------------------------------------------------------------*
+ * usb_device_20_compatible
+ *
+ * Returns:
+ * 0: HUB does not support suspend and resume
+ * Else: HUB supports suspend and resume
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb_device_20_compatible(struct usb_device *udev)
+{
+ if (udev == NULL)
+ return (0);
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*------------------------------------------------------------------------*
* uhub_suspend_resume_port
*
* Returns:
@@ -507,8 +557,14 @@
/* first clear the port suspend change bit */
- err = usbd_req_clear_port_feature(udev, NULL,
- portno, UHF_C_PORT_SUSPEND);
+ if (usb_device_20_compatible(udev)) {
+ err = usbd_req_clear_port_feature(udev, NULL,
+ portno, UHF_C_PORT_SUSPEND);
+ } else {
+ err = usbd_req_clear_port_feature(udev, NULL,
+ portno, UHF_C_PORT_LINK_STATE);
+ }
+
if (err) {
DPRINTF("clearing suspend failed.\n");
goto done;
@@ -520,12 +576,24 @@
DPRINTF("reading port status failed.\n");
goto done;
}
- /* get current state */
+ /* convert current state */
- if (sc->sc_st.port_status & UPS_SUSPEND) {
- is_suspend = 1;
+ if (usb_device_20_compatible(udev)) {
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ is_suspend = 1;
+ } else {
+ is_suspend = 0;
+ }
} else {
- is_suspend = 0;
+ switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
+ case UPS_PORT_LS_U0:
+ case UPS_PORT_LS_U1:
+ is_suspend = 0;
+ break;
+ default:
+ is_suspend = 1;
+ break;
+ }
}
DPRINTF("suspended=%u\n", is_suspend);
@@ -540,7 +608,8 @@
*/
if (is_suspend == 0)
usb_dev_resume_peer(child);
- else if (child->flags.usb_mode == USB_MODE_DEVICE)
+ else if ((child->flags.usb_mode == USB_MODE_DEVICE) ||
+ (usb_device_20_compatible(child) == 0))
usb_dev_suspend_peer(child);
}
done:
@@ -562,6 +631,26 @@
usb_needs_explore(bus, 0);
}
+static uint8_t
+uhub_is_too_deep(struct usb_device *udev)
+{
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ case USB_SPEED_HIGH:
+ if (udev->depth > USB_HUB_MAX_DEPTH)
+ return (1);
+ break;
+ case USB_SPEED_SUPER:
+ if (udev->depth >= USB_SS_HUB_DEPTH_MAX)
+ return (1);
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
/*------------------------------------------------------------------------*
* uhub_explore
*
@@ -584,11 +673,11 @@
DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address);
- /* ignore hubs that are too deep */
- if (udev->depth > USB_HUB_MAX_DEPTH) {
+ /* ignore devices that are too deep */
+ if (uhub_is_too_deep(udev))
return (USB_ERR_TOO_DEEP);
- }
+ /* check if device is suspended */
if (udev->flags.self_suspended) {
/* need to wait until the child signals resume */
DPRINTF("Device is suspended!\n");
@@ -655,7 +744,7 @@
break;
}
}
- if (sc->sc_st.port_change & UPS_C_SUSPEND) {
+ if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) {
err = uhub_suspend_resume_port(sc, portno);
if (err) {
/* most likely the HUB is gone */
@@ -683,17 +772,17 @@
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
- if (uaa->usb_mode != USB_MODE_HOST) {
+ if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
- }
+
/*
- * The subclass for USB HUBs is ignored because it is 0 for
- * some and 1 for others.
+ * The subclass for USB HUBs is currently ignored because it
+ * is 0 for some and 1 for others.
*/
if ((uaa->info.bConfigIndex == 0) &&
- (uaa->info.bDeviceClass == UDCLASS_HUB)) {
+ (uaa->info.bDeviceClass == UDCLASS_HUB))
return (0);
- }
+
return (ENXIO);
}
@@ -705,7 +794,8 @@
struct usb_device *udev = uaa->device;
struct usb_device *parent_hub = udev->parent_hub;
struct usb_hub *hub;
- struct usb_hub_descriptor hubdesc;
+ struct usb_hub_descriptor hubdesc20;
+ struct usb_hub_ss_descriptor hubdesc30;
uint16_t pwrdly;
uint8_t x;
uint8_t nports;
@@ -732,38 +822,96 @@
parent_hub ?
parent_hub->flags.self_powered : 0);
- if (udev->depth > USB_HUB_MAX_DEPTH) {
- DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored\n",
- USB_HUB_MAX_DEPTH);
+ if (uhub_is_too_deep(udev)) {
+ DPRINTFN(0, "HUB at depth %d, "
+ "exceeds maximum. HUB ignored\n", (int)udev->depth);
goto error;
}
+
if (!udev->flags.self_powered && parent_hub &&
(!parent_hub->flags.self_powered)) {
- DPRINTFN(0, "bus powered HUB connected to "
+ DPRINTFN(0, "Bus powered HUB connected to "
"bus powered HUB. HUB ignored\n");
goto error;
}
/* get HUB descriptor */
- DPRINTFN(2, "getting HUB descriptor\n");
+ DPRINTFN(2, "Getting HUB descriptor\n");
- /* assuming that there is one port */
- err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, 1);
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ /* assuming that there is one port */
+ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
+ if (err) {
+ DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get number of ports */
+ nports = hubdesc20.bNbrPorts;
- nports = hubdesc.bNbrPorts;
+ /* get power delay */
+ pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
+ USB_EXTRA_POWER_UP_TIME);
- if (!err && (nports >= 8)) {
/* get complete HUB descriptor */
- err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, nports);
- }
- if (err) {
- DPRINTFN(0, "getting hub descriptor failed,"
- "error=%s\n", usbd_errstr(err));
- goto error;
- }
- if (hubdesc.bNbrPorts != nports) {
- DPRINTFN(0, "number of ports changed\n");
- goto error;
+ if (nports >= 8) {
+ /* check number of ports */
+ if (nports > 127) {
+ DPRINTFN(0, "Invalid number of USB 2.0 ports,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get complete HUB descriptor */
+ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports);
+
+ if (err) {
+ DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ if (hubdesc20.bNbrPorts != nports) {
+ DPRINTFN(0, "Number of ports changed\n");
+ goto error;
+ }
+ }
+ break;
+ case USB_SPEED_SUPER:
+ err = usbd_req_set_hub_depth(udev, NULL, udev->depth);
+ if (err) {
+ DPRINTFN(0, "Setting USB 3.0 HUB depth failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30);
+ if (err) {
+ DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get number of ports */
+ nports = hubdesc30.bNbrPorts;
+
+ /* get power delay */
+ pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
+ USB_EXTRA_POWER_UP_TIME);
+
+ /* check number of ports */
+ if (nports > 15) {
+ DPRINTFN(0, "Invalid number of USB 3.0 ports,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ break;
+ default:
+ DPRINTF("Assuming HUB has only one port\n");
+ /* default number of ports */
+ nports = 1;
+ /* default power delay */
+ pwrdly = ((10 * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME);
+ break;
}
if (nports == 0) {
DPRINTFN(0, "portless HUB\n");
@@ -784,7 +932,7 @@
/* initialize HUB structure */
hub->hubsoftc = sc;
hub->explore = &uhub_explore;
- hub->nports = hubdesc.bNbrPorts;
+ hub->nports = nports;
hub->hubudev = udev;
/* if self powered hub, give ports maximum current */
@@ -840,8 +988,6 @@
/* XXX should check for none, individual, or ganged power? */
removable = 0;
- pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
- USB_EXTRA_POWER_UP_TIME);
for (x = 0; x != nports; x++) {
/* set up data structures */
@@ -852,8 +998,29 @@
portno = x + 1;
/* check if port is removable */
- if (!UHD_NOT_REMOV(&hubdesc, portno)) {
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ if (!UHD_NOT_REMOV(&hubdesc20, portno))
+ removable++;
+ break;
+ case USB_SPEED_SUPER:
+ if (!err) {
+ err = usbd_req_set_hub_u1_timeout(udev, NULL,
+ portno, 128 - (2 * udev->depth));
+ }
+ if (!err) {
+ err = usbd_req_set_hub_u2_timeout(udev, NULL,
+ portno, 128 - (2 * udev->depth));
+ }
+ if (!UHD_NOT_REMOV(&hubdesc30, portno))
+ removable++;
+ break;
+ default:
+ DPRINTF("Assuming removable port\n");
removable++;
+ break;
}
if (!err) {
/* turn the power on */
@@ -914,9 +1081,8 @@
struct usb_device *child;
uint8_t x;
- if (hub == NULL) { /* must be partially working */
+ if (hub == NULL) /* must be partially working */
return (0);
- }
/* Make sure interrupt transfer is gone. */
usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
@@ -1788,6 +1954,7 @@
(udev->pwr_save.write_refs != 0) ||
((udev->pwr_save.read_refs != 0) &&
(udev->flags.usb_mode == USB_MODE_HOST) &&
+ (usb_device_20_compatible(udev) != 0) &&
(usb_peer_can_wakeup(udev) == 0)));
}
@@ -1959,13 +2126,16 @@
/* reduce chance of instant resume failure by waiting a little bit */
usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
- /* resume current port (Valid in Host and Device Mode) */
- err = usbd_req_clear_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_SUSPEND);
- if (err) {
- DPRINTFN(0, "Resuming port failed\n");
- return;
+ if (usb_device_20_compatible(udev)) {
+ /* resume current port (Valid in Host and Device Mode) */
+ err = usbd_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Resuming port failed\n");
+ return;
+ }
}
+
/* resume settle time */
usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY));
@@ -2005,7 +2175,8 @@
usbd_sr_unlock(udev);
/* check if peer has wakeup capability */
- if (usb_peer_can_wakeup(udev)) {
+ if (usb_peer_can_wakeup(udev) &&
+ usb_device_20_compatible(udev)) {
/* clear remote wakeup */
err = usbd_req_clear_device_feature(udev,
NULL, UF_DEVICE_REMOTE_WAKEUP);
@@ -2015,7 +2186,6 @@
usbd_errstr(err));
}
}
- return;
}
/*------------------------------------------------------------------------*
@@ -2110,7 +2280,8 @@
usbd_sr_unlock(udev);
- if (usb_peer_can_wakeup(udev)) {
+ if (usb_peer_can_wakeup(udev) &&
+ usb_device_20_compatible(udev)) {
/* allow device to do remote wakeup */
err = usbd_req_set_device_feature(udev,
NULL, UF_DEVICE_REMOTE_WAKEUP);
@@ -2132,12 +2303,15 @@
usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp));
}
- /* suspend current port */
- err = usbd_req_set_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_SUSPEND);
- if (err) {
- DPRINTFN(0, "Suspending port failed\n");
- return;
+
+ if (usb_device_20_compatible(udev)) {
+ /* suspend current port */
+ err = usbd_req_set_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Suspending port failed\n");
+ return;
+ }
}
udev = udev->parent_hub;
==== //depot/projects/usb/src/sys/dev/usb/usb_hub.h#17 (text+ko) ====
==== //depot/projects/usb/src/sys/dev/usb/usb_request.c#35 (text+ko) ====
@@ -1293,6 +1293,27 @@
}
/*------------------------------------------------------------------------*
+ * usbd_req_get_ss_hub_descriptor
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
+ struct usb_hub_ss_descriptor *hd)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_SS_HUB, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(*hd));
+ return (usbd_do_request(udev, mtx, &req, hd));
+}
+
+/*------------------------------------------------------------------------*
* usbd_req_get_hub_status
*
* Returns:
@@ -1406,6 +1427,71 @@
}
/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_u1_timeout
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx,
+ uint8_t port, uint8_t timeout)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UHF_PORT_U1_TIMEOUT);
+ req.wIndex[0] = port;
+ req.wIndex[1] = timeout;
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_u2_timeout
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx,
+ uint8_t port, uint8_t timeout)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UHF_PORT_U2_TIMEOUT);
+ req.wIndex[0] = port;
+ req.wIndex[1] = timeout;
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_depth
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx,
+ uint16_t depth)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_HUB_DEPTH;
+ USETW(req.wValue, depth);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
* usbd_req_clear_port_feature
*
* Returns:
==== //depot/projects/usb/src/sys/dev/usb/usb_request.h#12 (text+ko) ====
@@ -56,6 +56,8 @@
usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev,
struct mtx *mtx, struct usb_hub_descriptor *hd,
uint8_t nports);
+usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev,
+ struct mtx *mtx, struct usb_hub_ss_descriptor *hd);
usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx,
struct usb_hub_status *st);
usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx,
@@ -69,7 +71,15 @@
usb_error_t usbd_req_set_port_feature(struct usb_device *udev,
struct mtx *mtx, uint8_t port, uint16_t sel);
usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx);
-usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel);
-usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_clear_device_feature(struct usb_device *udev,
+ struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_set_device_feature(struct usb_device *udev,
+ struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev,
+ struct mtx *mtx, uint8_t port, uint8_t timeout);
+usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev,
+ struct mtx *mtx, uint8_t port, uint8_t timeout);
+usb_error_t usbd_req_set_hub_depth(struct usb_device *udev,
+ struct mtx *mtx, uint16_t depth);
#endif /* _USB_REQUEST_H_ */
More information about the p4-projects
mailing list