Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 6 Jun 2011 21:45:09 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r222786 - head/sys/dev/usb
Message-ID:  <201106062145.p56Lj9C2050184@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Mon Jun  6 21:45:09 2011
New Revision: 222786
URL: http://svn.freebsd.org/changeset/base/222786

Log:
  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

Modified:
  head/sys/dev/usb/usb_device.h
  head/sys/dev/usb/usb_freebsd.h
  head/sys/dev/usb/usb_generic.c
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_request.c
  head/sys/dev/usb/usb_request.h
  head/sys/dev/usb/usbdi.h

Modified: head/sys/dev/usb/usb_device.h
==============================================================================
--- head/sys/dev/usb/usb_device.h	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_device.h	Mon Jun  6 21:45:09 2011	(r222786)
@@ -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 */

Modified: head/sys/dev/usb/usb_freebsd.h
==============================================================================
--- head/sys/dev/usb/usb_freebsd.h	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_freebsd.h	Mon Jun  6 21:45:09 2011	(r222786)
@@ -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 */

Modified: head/sys/dev/usb/usb_generic.c
==============================================================================
--- head/sys/dev/usb/usb_generic.c	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_generic.c	Mon Jun  6 21:45:09 2011	(r222786)
@@ -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);
 }
 

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_hub.c	Mon Jun  6 21:45:09 2011	(r222786)
@@ -242,9 +242,14 @@ uhub_explore_sub(struct uhub_softc *sc, 
 	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
 	/* 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);
+	}
+}

Modified: head/sys/dev/usb/usb_request.c
==============================================================================
--- head/sys/dev/usb/usb_request.c	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_request.c	Mon Jun  6 21:45:09 2011	(r222786)
@@ -238,6 +238,10 @@ usb_do_clear_stall_callback(struct usb_x
 
 	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 
 		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_d
 	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));
+}

Modified: head/sys/dev/usb/usb_request.h
==============================================================================
--- head/sys/dev/usb/usb_request.h	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usb_request.h	Mon Jun  6 21:45:09 2011	(r222786)
@@ -85,5 +85,9 @@ usb_error_t usbd_req_set_hub_u2_timeout(
 		    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_ */

Modified: head/sys/dev/usb/usbdi.h
==============================================================================
--- head/sys/dev/usb/usbdi.h	Mon Jun  6 21:41:10 2011	(r222785)
+++ head/sys/dev/usb/usbdi.h	Mon Jun  6 21:45:09 2011	(r222786)
@@ -542,6 +542,7 @@ void	usbd_m_copy_in(struct usb_page_cach
 	    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,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201106062145.p56Lj9C2050184>