1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00

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
This commit is contained in:
Hans Petter Selasky 2012-01-13 22:26:13 +00:00
parent e02f894bbe
commit 93ee6e858b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=230091
2 changed files with 64 additions and 24 deletions

View File

@ -627,14 +627,15 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
} }
} else { } else {
switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) { switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
case UPS_PORT_LS_U0: case UPS_PORT_LS_U3:
case UPS_PORT_LS_U1: is_suspend = 1;
case UPS_PORT_LS_U2: break;
case UPS_PORT_LS_RESUME: case UPS_PORT_LS_SS_INA:
usbd_req_warm_reset_port(udev, NULL, portno);
is_suspend = 0; is_suspend = 0;
break; break;
default: default:
is_suspend = 1; is_suspend = 0;
break; break;
} }
} }
@ -793,7 +794,8 @@ uhub_explore(struct usb_device *udev)
break; 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); err = uhub_suspend_resume_port(sc, portno);
if (err) { if (err) {
/* most likely the HUB is gone */ /* most likely the HUB is gone */

View File

@ -785,12 +785,17 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
struct usb_port_status ps; struct usb_port_status ps;
usb_error_t err; usb_error_t err;
uint16_t n; uint16_t n;
uint16_t status;
uint16_t change;
#ifdef USB_DEBUG #ifdef USB_DEBUG
uint16_t pr_poll_delay; uint16_t pr_poll_delay;
uint16_t pr_recovery_delay; uint16_t pr_recovery_delay;
#endif #endif
DPRINTF("\n");
/* clear any leftover port reset changes first */ /* clear any leftover port reset changes first */
usbd_req_clear_port_feature( usbd_req_clear_port_feature(
udev, mtx, port, UHF_C_PORT_RESET); udev, mtx, port, UHF_C_PORT_RESET);
@ -817,9 +822,6 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
#endif #endif
n = 0; n = 0;
while (1) { while (1) {
uint16_t status;
uint16_t change;
#ifdef USB_DEBUG #ifdef USB_DEBUG
/* wait for the device to recover from reset */ /* wait for the device to recover from reset */
usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
@ -830,9 +832,9 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
n += USB_PORT_RESET_DELAY; n += USB_PORT_RESET_DELAY;
#endif #endif
err = usbd_req_get_port_status(udev, mtx, &ps, port); err = usbd_req_get_port_status(udev, mtx, &ps, port);
if (err) { if (err)
goto done; goto done;
}
status = UGETW(ps.wPortStatus); status = UGETW(ps.wPortStatus);
change = UGETW(ps.wPortChange); change = UGETW(ps.wPortChange);
@ -862,9 +864,9 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
/* clear port reset first */ /* clear port reset first */
err = usbd_req_clear_port_feature( err = usbd_req_clear_port_feature(
udev, mtx, port, UHF_C_PORT_RESET); udev, mtx, port, UHF_C_PORT_RESET);
if (err) { if (err)
goto done; goto done;
}
/* check for timeout */ /* check for timeout */
if (n == 0) { if (n == 0) {
err = USB_ERR_TIMEOUT; err = USB_ERR_TIMEOUT;
@ -898,21 +900,50 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
* disabled. * disabled.
*------------------------------------------------------------------------*/ *------------------------------------------------------------------------*/
usb_error_t 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; struct usb_port_status ps;
usb_error_t err; usb_error_t err;
uint16_t n; uint16_t n;
uint16_t status;
uint16_t change;
#ifdef USB_DEBUG #ifdef USB_DEBUG
uint16_t pr_poll_delay; uint16_t pr_poll_delay;
uint16_t pr_recovery_delay; uint16_t pr_recovery_delay;
#endif #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; 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 #ifdef USB_DEBUG
/* range check input parameters */ /* range check input parameters */
pr_poll_delay = usb_pr_poll_delay; pr_poll_delay = usb_pr_poll_delay;
@ -938,17 +969,20 @@ usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
n += USB_PORT_RESET_DELAY; n += USB_PORT_RESET_DELAY;
#endif #endif
err = usbd_req_get_port_status(udev, mtx, &ps, port); err = usbd_req_get_port_status(udev, mtx, &ps, port);
if (err) { if (err)
goto done; goto done;
}
status = UGETW(ps.wPortStatus);
change = UGETW(ps.wPortChange);
/* if the device disappeared, just give up */ /* if the device disappeared, just give up */
if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { if (!(status & UPS_CURRENT_CONNECT_STATUS))
goto done; goto done;
}
/* check if reset is complete */ /* check if reset is complete */
if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) { if (change & UPS_C_BH_PORT_RESET)
break; break;
}
/* check for timeout */ /* check for timeout */
if (n > 1000) { if (n > 1000) {
n = 0; n = 0;
@ -959,9 +993,9 @@ usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
/* clear port reset first */ /* clear port reset first */
err = usbd_req_clear_port_feature( err = usbd_req_clear_port_feature(
udev, mtx, port, UHF_C_BH_PORT_RESET); udev, mtx, port, UHF_C_BH_PORT_RESET);
if (err) { if (err)
goto done; goto done;
}
/* check for timeout */ /* check for timeout */
if (n == 0) { if (n == 0) {
err = USB_ERR_TIMEOUT; err = USB_ERR_TIMEOUT;
@ -2004,6 +2038,10 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
} }
} }
/* 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. */ /* Try to reset the parent HUB port. */
err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
if (err) { if (err) {