Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Mar 2011 21:16:25 +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: r219845 - head/sys/dev/usb/controller
Message-ID:  <201103212116.p2LLGPGF021033@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Mon Mar 21 21:16:25 2011
New Revision: 219845
URL: http://svn.freebsd.org/changeset/base/219845

Log:
  - Bugfix: Fix a EHCI hardware race, where the hardware computed data toggle
  value is updated after that we read it in the queue-head. This patch can
  fix problems with BULK timeouts. The issue was found on a Nvidia chipset.
  
  MFC after:	14 days
  Approved by:	thompsa (mentor)

Modified:
  head/sys/dev/usb/controller/ehci.c

Modified: head/sys/dev/usb/controller/ehci.c
==============================================================================
--- head/sys/dev/usb/controller/ehci.c	Mon Mar 21 21:16:12 2011	(r219844)
+++ head/sys/dev/usb/controller/ehci.c	Mon Mar 21 21:16:25 2011	(r219845)
@@ -1180,6 +1180,26 @@ _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_
 	return (last);
 }
 
+static void
+ehci_data_toggle_update(struct usb_xfer *xfer, uint16_t actlen, uint16_t xlen)
+{
+	uint8_t full = (actlen == xlen);
+	uint8_t dt;
+
+	/* count number of full packets */
+	dt = (actlen / xfer->max_packet_size) & 1;
+
+	/* cumpute remainder */
+	actlen = actlen % xfer->max_packet_size;
+
+	if (actlen > 0)
+		dt ^= 1;	/* short packet at the end */
+	else if (!full)
+		dt ^= 1;	/* zero length packet at the end */
+
+	xfer->endpoint->toggle_next ^= dt;
+}
+
 static usb_error_t
 ehci_non_isoc_done_sub(struct usb_xfer *xfer)
 {
@@ -1213,7 +1233,10 @@ ehci_non_isoc_done_sub(struct usb_xfer *
 			status |= EHCI_QTD_HALTED;
 		} else if (xfer->aframes != xfer->nframes) {
 			xfer->frlengths[xfer->aframes] += td->len - len;
+			/* manually update data toggle */
+			ehci_data_toggle_update(xfer, td->len - len, td->len);
 		}
+
 		/* Check for last transfer */
 		if (((void *)td) == xfer->td_transfer_last) {
 			td = NULL;
@@ -1295,9 +1318,6 @@ ehci_non_isoc_done(struct usb_xfer *xfer
 
 	status = hc32toh(sc, qh->qh_qtd.qtd_status);
 
-	xfer->endpoint->toggle_next =
-	    (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0;
-
 	/* reset scanner */
 
 	xfer->td_transfer_cache = xfer->td_transfer_first;
@@ -1876,6 +1896,8 @@ ehci_setup_standard_chain(struct usb_xfe
 	if (xfer->flags_int.control_xfr) {
 		if (xfer->flags_int.control_hdr) {
 
+			xfer->endpoint->toggle_next = 0;
+
 			temp.qtd_status &=
 			    htohc32(temp.sc, EHCI_QTD_SET_CERR(3));
 			temp.qtd_status |= htohc32(temp.sc,



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