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>