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) {
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to