Improve enumeration of Low- and Full-speed devices connected through a

High-speed USB HUB by resetting the transaction translator (TT)
before trying re-enumeration. Also when clear-stall fails multiple times
try a re-enumeration.

Suggested by:	Trevor Blackwell
MFC after:	14 days
This commit is contained in:
Hans Petter Selasky 2011-06-06 21:45:09 +00:00
parent 8a7fca58aa
commit 9eb0d7025d
7 changed files with 133 additions and 8 deletions

View file

@ -187,6 +187,8 @@ struct usb_device {
struct usb_host_endpoint *linux_endpoint_end;
uint16_t devnum;
#endif
uint32_t clear_stall_errors; /* number of clear-stall failures */
};
/* globals */

View file

@ -66,6 +66,7 @@
#define USB_HUB_MAX_DEPTH 5
#define USB_EP0_BUFSIZE 1024 /* bytes */
#define USB_CS_RESET_LIMIT 20 /* failures = 20 * 50 ms = 1sec */
typedef uint32_t usb_timeout_t; /* milliseconds */
typedef uint32_t usb_frlength_t; /* bytes */

View file

@ -966,10 +966,8 @@ ugen_re_enumerate(struct usb_fifo *f)
/* ignore any errors */
DPRINTFN(6, "no FIFOs\n");
}
if (udev->re_enumerate_wait == 0) {
udev->re_enumerate_wait = 1;
usb_needs_explore(udev->bus, 0);
}
/* start re-enumeration of device */
usbd_start_re_enumerate(udev);
return (0);
}

View file

@ -242,9 +242,14 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
if (child->flags.usb_mode == USB_MODE_HOST) {
usbd_enum_lock(child);
if (child->re_enumerate_wait) {
err = usbd_set_config_index(child, USB_UNCONFIG_INDEX);
if (err == 0)
err = usbd_req_re_enumerate(child, NULL);
err = usbd_set_config_index(child,
USB_UNCONFIG_INDEX);
if (err != 0) {
DPRINTF("Unconfigure failed: "
"%s: Ignored.\n",
usbd_errstr(err));
}
err = usbd_req_re_enumerate(child, NULL);
if (err == 0)
err = usbd_set_config_index(child, 0);
if (err == 0) {
@ -2471,3 +2476,19 @@ usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode)
/* use fixed power mode given by hardware driver */
return (temp);
}
/*------------------------------------------------------------------------*
* usbd_start_re_enumerate
*
* This function starts re-enumeration of the given USB device. This
* function does not need to be called BUS-locked. This function does
* not wait until the re-enumeration is completed.
*------------------------------------------------------------------------*/
void
usbd_start_re_enumerate(struct usb_device *udev)
{
if (udev->re_enumerate_wait == 0) {
udev->re_enumerate_wait = 1;
usb_needs_explore(udev->bus, 0);
}
}

View file

@ -238,6 +238,10 @@ usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
/* reset error counter */
udev->clear_stall_errors = 0;
if (ep == NULL)
goto tr_setup; /* device was unconfigured */
if (ep->edesc &&
@ -289,8 +293,23 @@ tr_setup:
goto tr_setup;
default:
if (xfer->error == USB_ERR_CANCELLED) {
if (error == USB_ERR_CANCELLED)
break;
DPRINTF("Clear stall failed.\n");
if (udev->clear_stall_errors == USB_CS_RESET_LIMIT)
goto tr_setup;
if (error == USB_ERR_TIMEOUT) {
udev->clear_stall_errors = USB_CS_RESET_LIMIT;
DPRINTF("Trying to re-enumerate.\n");
usbd_start_re_enumerate(udev);
} else {
udev->clear_stall_errors++;
if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) {
DPRINTF("Trying to re-enumerate.\n");
usbd_start_re_enumerate(udev);
}
}
goto tr_setup;
}
@ -1936,6 +1955,23 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
return (USB_ERR_INVAL);
}
retry:
/*
* Try to reset the High Speed parent HUB of a LOW- or FULL-
* speed device, if any.
*/
if (udev->parent_hs_hub != NULL &&
udev->speed != USB_SPEED_HIGH) {
DPRINTF("Trying to reset parent High Speed TT.\n");
err = usbd_req_reset_tt(udev->parent_hs_hub, NULL,
udev->hs_port_no);
if (err) {
DPRINTF("Resetting parent High "
"Speed TT failed (%s).\n",
usbd_errstr(err));
}
}
/* Try to reset the parent HUB port. */
err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
if (err) {
DPRINTFN(0, "addr=%d, port reset failed, %s\n",
@ -2033,3 +2069,65 @@ usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx,
USETW(req.wLength, 0);
return (usbd_do_request(udev, mtx, &req, 0));
}
/*------------------------------------------------------------------------*
* usbd_req_reset_tt
*
* Returns:
* 0: Success
* Else: Failure
*------------------------------------------------------------------------*/
usb_error_t
usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx,
uint8_t port)
{
struct usb_device_request req;
/* For single TT HUBs the port should be 1 */
if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
port = 1;
req.bmRequestType = UT_WRITE_CLASS_OTHER;
req.bRequest = UR_RESET_TT;
USETW(req.wValue, 0);
req.wIndex[0] = port;
req.wIndex[1] = 0;
USETW(req.wLength, 0);
return (usbd_do_request(udev, mtx, &req, 0));
}
/*------------------------------------------------------------------------*
* usbd_req_clear_tt_buffer
*
* For single TT HUBs the port should be 1.
*
* Returns:
* 0: Success
* Else: Failure
*------------------------------------------------------------------------*/
usb_error_t
usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx,
uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint)
{
struct usb_device_request req;
uint16_t wValue;
/* For single TT HUBs the port should be 1 */
if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
port = 1;
wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) |
((endpoint & 0x80) << 8) | ((type & 3) << 12);
req.bmRequestType = UT_WRITE_CLASS_OTHER;
req.bRequest = UR_CLEAR_TT_BUFFER;
USETW(req.wValue, wValue);
req.wIndex[0] = port;
req.wIndex[1] = 0;
USETW(req.wLength, 0);
return (usbd_do_request(udev, mtx, &req, 0));
}

View file

@ -85,5 +85,9 @@ usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev,
struct mtx *mtx, uint8_t port, uint8_t timeout);
usb_error_t usbd_req_set_hub_depth(struct usb_device *udev,
struct mtx *mtx, uint16_t depth);
usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx,
uint8_t port);
usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx,
uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint);
#endif /* _USB_REQUEST_H_ */

View file

@ -542,6 +542,7 @@ void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset,
struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len);
void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
usb_frlength_t len);
void usbd_start_re_enumerate(struct usb_device *udev);
int usb_fifo_attach(struct usb_device *udev, void *priv_sc,
struct mtx *priv_mtx, struct usb_fifo_methods *pm,