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
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 {
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 */

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;
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 *udev, struct mtx *mtx, uint8_t port)
#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 *udev, struct mtx *mtx, uint8_t port)
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 *udev, struct mtx *mtx, uint8_t port)
/* 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 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
* 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_device *udev, struct mtx *mtx, uint8_t port)
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_device *udev, struct mtx *mtx, uint8_t port)
/* 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 @@ 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. */
err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
if (err) {