mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
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:
parent
8a7fca58aa
commit
9eb0d7025d
7 changed files with 133 additions and 8 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue