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:
parent
e02f894bbe
commit
93ee6e858b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=230091
@ -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 */
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user