svn commit: r230091 - head/sys/dev/usb

Hans Petter Selasky hselasky at FreeBSD.org
Fri Jan 13 22:26:14 UTC 2012


Author: hselasky
Date: Fri Jan 13 22:26:13 2012
New Revision: 230091
URL: http://svn.freebsd.org/changeset/base/230091

Log:
  Improve support for USB 3.0 HUBs. In certain states we
  should do a warm reset instead of the default reset.
  
  MFC after:	5 days

Modified:
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_request.c

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Fri Jan 13 22:19:14 2012	(r230090)
+++ head/sys/dev/usb/usb_hub.c	Fri Jan 13 22:26:13 2012	(r230091)
@@ -627,14 +627,15 @@ uhub_suspend_resume_port(struct uhub_sof
 		}
 	} else {
 		switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
-		case UPS_PORT_LS_U0:
-		case UPS_PORT_LS_U1:
-		case UPS_PORT_LS_U2:
-		case UPS_PORT_LS_RESUME:
+		case UPS_PORT_LS_U3:
+			is_suspend = 1;
+			break;
+		case UPS_PORT_LS_SS_INA:
+			usbd_req_warm_reset_port(udev, NULL, portno);
 			is_suspend = 0;
 			break;
 		default:
-			is_suspend = 1;
+			is_suspend = 0;
 			break;
 		}
 	}
@@ -793,7 +794,8 @@ uhub_explore(struct usb_device *udev)
 				break;
 			}
 		}
-		if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) {
+		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 */

Modified: head/sys/dev/usb/usb_request.c
==============================================================================
--- head/sys/dev/usb/usb_request.c	Fri Jan 13 22:19:14 2012	(r230090)
+++ head/sys/dev/usb/usb_request.c	Fri Jan 13 22:26:13 2012	(r230091)
@@ -785,12 +785,17 @@ usbd_req_reset_port(struct usb_device *u
 	struct usb_port_status ps;
 	usb_error_t err;
 	uint16_t n;
+	uint16_t status;
+	uint16_t change;
 
 #ifdef USB_DEBUG
 	uint16_t pr_poll_delay;
 	uint16_t pr_recovery_delay;
 
 #endif
+
+	DPRINTF("\n");
+
 	/* clear any leftover port reset changes first */
 	usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_PORT_RESET);
@@ -817,9 +822,6 @@ usbd_req_reset_port(struct usb_device *u
 #endif
 	n = 0;
 	while (1) {
-		uint16_t status;
-		uint16_t change;
-
 #ifdef USB_DEBUG
 		/* wait for the device to recover from reset */
 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
@@ -830,9 +832,9 @@ usbd_req_reset_port(struct usb_device *u
 		n += USB_PORT_RESET_DELAY;
 #endif
 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
-		if (err) {
+		if (err)
 			goto done;
-		}
+
 		status = UGETW(ps.wPortStatus);
 		change = UGETW(ps.wPortChange);
 
@@ -862,9 +864,9 @@ usbd_req_reset_port(struct usb_device *u
 	/* clear port reset first */
 	err = usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_PORT_RESET);
-	if (err) {
+	if (err)
 		goto done;
-	}
+
 	/* check for timeout */
 	if (n == 0) {
 		err = USB_ERR_TIMEOUT;
@@ -898,21 +900,50 @@ done:
  *       disabled.
  *------------------------------------------------------------------------*/
 usb_error_t
-usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
+usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx,
+    uint8_t port)
 {
 	struct usb_port_status ps;
 	usb_error_t err;
 	uint16_t n;
+	uint16_t status;
+	uint16_t change;
 
 #ifdef USB_DEBUG
 	uint16_t pr_poll_delay;
 	uint16_t pr_recovery_delay;
 
 #endif
-	err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET);
-	if (err) {
+
+	DPRINTF("\n");
+
+	err = usbd_req_get_port_status(udev, mtx, &ps, port);
+	if (err)
 		goto done;
+
+	status = UGETW(ps.wPortStatus);
+
+	switch (UPS_PORT_LINK_STATE_GET(status)) {
+	case UPS_PORT_LS_U3:
+	case UPS_PORT_LS_COMP_MODE:
+	case UPS_PORT_LS_LOOPBACK:
+	case UPS_PORT_LS_SS_INA:
+		break;
+	default:
+		DPRINTF("Wrong state for warm reset\n");
+		return (0);
 	}
+
+	/* clear any leftover warm port reset changes first */
+	usbd_req_clear_port_feature(udev, mtx,
+	    port, UHF_C_BH_PORT_RESET);
+
+	/* set warm port reset */
+	err = usbd_req_set_port_feature(udev, mtx,
+	    port, UHF_BH_PORT_RESET);
+	if (err)
+		goto done;
+
 #ifdef USB_DEBUG
 	/* range check input parameters */
 	pr_poll_delay = usb_pr_poll_delay;
@@ -938,17 +969,20 @@ usbd_req_warm_reset_port(struct usb_devi
 		n += USB_PORT_RESET_DELAY;
 #endif
 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
-		if (err) {
+		if (err)
 			goto done;
-		}
+
+		status = UGETW(ps.wPortStatus);
+		change = UGETW(ps.wPortChange);
+
 		/* if the device disappeared, just give up */
-		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
+		if (!(status & UPS_CURRENT_CONNECT_STATUS))
 			goto done;
-		}
+
 		/* check if reset is complete */
-		if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) {
+		if (change & UPS_C_BH_PORT_RESET)
 			break;
-		}
+
 		/* check for timeout */
 		if (n > 1000) {
 			n = 0;
@@ -959,9 +993,9 @@ usbd_req_warm_reset_port(struct usb_devi
 	/* clear port reset first */
 	err = usbd_req_clear_port_feature(
 	    udev, mtx, port, UHF_C_BH_PORT_RESET);
-	if (err) {
+	if (err)
 		goto done;
-	}
+
 	/* check for timeout */
 	if (n == 0) {
 		err = USB_ERR_TIMEOUT;
@@ -2004,6 +2038,10 @@ retry:
 		}
 	}
 
+	/* Try to warm reset first */
+	if (parent_hub->speed == USB_SPEED_SUPER)
+		usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no);
+
 	/* Try to reset the parent HUB port. */
 	err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
 	if (err) {


More information about the svn-src-all mailing list