Module Name: src
Committed By: riz
Date: Thu Sep 26 01:51:47 UTC 2013
Modified Files:
src/sys/dev/usb [netbsd-6]: ehci.c uhci.c
Log Message:
Pull up following revision(s) (requested by skrll in ticket #950):
sys/dev/usb/uhci.c: revision 1.255
sys/dev/usb/uhci.c: revision 1.256
sys/dev/usb/ehci.c: revision 1.205
sys/dev/usb/uhci.c: revision 1.258
Add missed byteswap ops for BE machines in block added in rev 1.223.
Call usb_syncmem() against descriptors more strictly.
(not sure if the previous ones were fatal though)
Deal with control transfers better by
- removing the UHCI_PTR_VF flag for the setup and status stages
which means they are scheduled less aggressively. Some devices
appear to require this (blymn@ has one). The flag was
introduced as a performance improvement for bulk transfers.
- Checking for short reads and making sure the status stage runs
if they're encountered.
PR/47522 Enumeration of LUFA/Atmel devices on UHCI fails
Thanks to jak@ and blymn@ for testing and mlelstv@ for comments.
To generate a diff of this commit:
cvs rdiff -u -r1.183 -r1.183.2.1 src/sys/dev/usb/ehci.c
cvs rdiff -u -r1.242 -r1.242.2.1 src/sys/dev/usb/uhci.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/ehci.c
diff -u src/sys/dev/usb/ehci.c:1.183 src/sys/dev/usb/ehci.c:1.183.2.1
--- src/sys/dev/usb/ehci.c:1.183 Fri Dec 23 00:51:43 2011
+++ src/sys/dev/usb/ehci.c Thu Sep 26 01:51:47 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: ehci.c,v 1.183 2011/12/23 00:51:43 jakllsch Exp $ */
+/* $NetBSD: ehci.c,v 1.183.2.1 2013/09/26 01:51:47 riz Exp $ */
/*
* Copyright (c) 2004-2008 The NetBSD Foundation, Inc.
@@ -52,7 +52,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.183 2011/12/23 00:51:43 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.183.2.1 2013/09/26 01:51:47 riz Exp $");
#include "ohci.h"
#include "uhci.h"
@@ -763,7 +763,11 @@ ehci_check_qh_intr(ehci_softc_t *sc, str
lsqtd->offs + offsetof(ehci_qtd_t, qtd_status),
sizeof(lsqtd->qtd.qtd_status),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) {
+ status = le32toh(lsqtd->qtd.qtd_status);
+ usb_syncmem(&lsqtd->dma,
+ lsqtd->offs + offsetof(ehci_qtd_t, qtd_status),
+ sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD);
+ if (status & EHCI_QTD_ACTIVE) {
DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex));
for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) {
usb_syncmem(&sqtd->dma,
@@ -786,9 +790,6 @@ ehci_check_qh_intr(ehci_softc_t *sc, str
}
DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n",
ex, ex->sqtdstart));
- usb_syncmem(&lsqtd->dma,
- lsqtd->offs + offsetof(ehci_qtd_t, qtd_status),
- sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD);
return;
}
done:
Index: src/sys/dev/usb/uhci.c
diff -u src/sys/dev/usb/uhci.c:1.242 src/sys/dev/usb/uhci.c:1.242.2.1
--- src/sys/dev/usb/uhci.c:1.242 Fri Dec 23 00:51:46 2011
+++ src/sys/dev/usb/uhci.c Thu Sep 26 01:51:47 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: uhci.c,v 1.242 2011/12/23 00:51:46 jakllsch Exp $ */
+/* $NetBSD: uhci.c,v 1.242.2.1 2013/09/26 01:51:47 riz Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */
/*
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.242 2011/12/23 00:51:46 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.242.2.1 2013/09/26 01:51:47 riz Exp $");
#include "opt_usb.h"
@@ -1102,6 +1102,7 @@ void
uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *pqh;
+ uint32_t elink;
SPLUSBCHECK;
@@ -1125,7 +1126,10 @@ uhci_remove_hs_ctrl(uhci_softc_t *sc, uh
usb_syncmem(&sqh->dma, sqh->offs + offsetof(uhci_qh_t, qh_elink),
sizeof(sqh->qh.qh_elink),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ elink = le32toh(sqh->qh.qh_elink);
+ usb_syncmem(&sqh->dma, sqh->offs + offsetof(uhci_qh_t, qh_elink),
+ sizeof(sqh->qh.qh_elink), BUS_DMASYNC_PREREAD);
+ if (!(elink & UHCI_PTR_T)) {
sqh->qh.qh_elink = htole32(UHCI_PTR_T);
usb_syncmem(&sqh->dma,
sqh->offs + offsetof(uhci_qh_t, qh_elink),
@@ -1175,6 +1179,7 @@ void
uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *pqh;
+ uint32_t elink;
SPLUSBCHECK;
@@ -1183,7 +1188,10 @@ uhci_remove_ls_ctrl(uhci_softc_t *sc, uh
usb_syncmem(&sqh->dma, sqh->offs + offsetof(uhci_qh_t, qh_elink),
sizeof(sqh->qh.qh_elink),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ elink = le32toh(sqh->qh.qh_elink);
+ usb_syncmem(&sqh->dma, sqh->offs + offsetof(uhci_qh_t, qh_elink),
+ sizeof(sqh->qh.qh_elink), BUS_DMASYNC_PREREAD);
+ if (!(elink & UHCI_PTR_T)) {
sqh->qh.qh_elink = htole32(UHCI_PTR_T);
usb_syncmem(&sqh->dma,
sqh->offs + offsetof(uhci_qh_t, qh_elink),
@@ -1421,54 +1429,89 @@ uhci_check_intr(uhci_softc_t *sc, uhci_i
return;
}
#endif
- /*
- * If the last TD is still active we need to check whether there
- * is an error somewhere in the middle, or whether there was a
- * short packet (SPD and not ACTIVE).
- */
usb_syncmem(&lstd->dma,
lstd->offs + offsetof(uhci_td_t, td_status),
sizeof(lstd->td.td_status),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if (le32toh(lstd->td.td_status) & UHCI_TD_ACTIVE) {
- DPRINTFN(12, ("uhci_check_intr: active ii=%p\n", ii));
- for (std = ii->stdstart; std != lstd; std = std->link.std) {
- usb_syncmem(&std->dma,
- std->offs + offsetof(uhci_td_t, td_status),
- sizeof(std->td.td_status),
- BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- status = le32toh(std->td.td_status);
- usb_syncmem(&std->dma,
- std->offs + offsetof(uhci_td_t, td_status),
- sizeof(std->td.td_status), BUS_DMASYNC_PREREAD);
- /* If there's an active TD the xfer isn't done. */
- if (status & UHCI_TD_ACTIVE)
- break;
- /* Any kind of error makes the xfer done. */
- if (status & UHCI_TD_STALLED)
- goto done;
- /* We want short packets, and it is short: it's done */
- usb_syncmem(&std->dma,
- std->offs + offsetof(uhci_td_t, td_token),
- sizeof(std->td.td_token),
- BUS_DMASYNC_POSTWRITE);
- if ((status & UHCI_TD_SPD) &&
- UHCI_TD_GET_ACTLEN(status) <
- UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token)))
- goto done;
- }
- DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n",
- ii, ii->stdstart));
- usb_syncmem(&lstd->dma,
- lstd->offs + offsetof(uhci_td_t, td_status),
- sizeof(lstd->td.td_status),
- BUS_DMASYNC_PREREAD);
+ status = le32toh(lstd->td.td_status);
+ usb_syncmem(&lstd->dma,
+ lstd->offs + offsetof(uhci_td_t, td_status),
+ sizeof(lstd->td.td_status),
+ BUS_DMASYNC_PREREAD);
+
+ /* If the last TD is not marked active we can complete */
+ if (!(status & UHCI_TD_ACTIVE)) {
+ done:
+ DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
+ callout_stop(&ii->xfer->timeout_handle);
+ uhci_idone(ii);
return;
}
- done:
- DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
- callout_stop(&ii->xfer->timeout_handle);
- uhci_idone(ii);
+
+ /*
+ * If the last TD is still active we need to check whether there
+ * is an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ DPRINTFN(12, ("uhci_check_intr: active ii=%p\n", ii));
+ for (std = ii->stdstart; std != lstd; std = std->link.std) {
+ usb_syncmem(&std->dma,
+ std->offs + offsetof(uhci_td_t, td_status),
+ sizeof(std->td.td_status),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ status = le32toh(std->td.td_status);
+ usb_syncmem(&std->dma,
+ std->offs + offsetof(uhci_td_t, td_status),
+ sizeof(std->td.td_status), BUS_DMASYNC_PREREAD);
+
+ /* If there's an active TD the xfer isn't done. */
+ if (status & UHCI_TD_ACTIVE) {
+ DPRINTFN(12, ("%s: ii=%p std=%p still active\n",
+ __func__, ii, std));
+ return;
+ }
+
+ /* Any kind of error makes the xfer done. */
+ if (status & UHCI_TD_STALLED)
+ goto done;
+
+ /*
+ * If the data phase of a control transfer is short, we need
+ * to complete the status stage
+ */
+ usbd_xfer_handle xfer = ii->xfer;
+ usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
+ uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+
+ if ((status & UHCI_TD_SPD) && xfertype == UE_CONTROL) {
+ struct uhci_pipe *upipe =
+ (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_qh_t *sqh = upipe->u.ctl.sqh;
+ uhci_soft_td_t *stat = upipe->u.ctl.stat;
+
+ DPRINTFN(12, ("%s: ii=%p std=%p control status"
+ "phase needs completion\n", __func__, ii,
+ ii->stdstart));
+
+ sqh->qh.qh_elink =
+ htole32(stat->physaddr | UHCI_PTR_TD);
+ usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh),
+ BUS_DMASYNC_PREWRITE);
+ break;
+ }
+
+ /* We want short packets, and it is short: it's done */
+ usb_syncmem(&std->dma,
+ std->offs + offsetof(uhci_td_t, td_token),
+ sizeof(std->td.td_token),
+ BUS_DMASYNC_POSTWRITE);
+
+ if ((status & UHCI_TD_SPD) &&
+ UHCI_TD_GET_ACTLEN(status) <
+ UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token))) {
+ goto done;
+ }
+ }
}
/* Called at splusb() */
@@ -1830,6 +1873,7 @@ uhci_free_std_chain(uhci_softc_t *sc, uh
uhci_soft_td_t *stdend)
{
uhci_soft_td_t *p;
+ uint32_t td_link;
/*
* to avoid race condition with the controller which may be looking
@@ -1841,8 +1885,13 @@ uhci_free_std_chain(uhci_softc_t *sc, uh
p->offs + offsetof(uhci_td_t, td_link),
sizeof(p->td.td_link),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if ((p->td.td_link & UHCI_PTR_T) == 0) {
- p->td.td_link = UHCI_PTR_T;
+ td_link = le32toh(p->td.td_link);
+ usb_syncmem(&p->dma,
+ p->offs + offsetof(uhci_td_t, td_link),
+ sizeof(p->td.td_link),
+ BUS_DMASYNC_PREREAD);
+ if ((td_link & UHCI_PTR_T) == 0) {
+ p->td.td_link = htole32(UHCI_PTR_T);
usb_syncmem(&p->dma,
p->offs + offsetof(uhci_td_t, td_link),
sizeof(p->td.td_link),
@@ -2420,7 +2469,7 @@ uhci_device_request(usbd_xfer_handle xfe
return (err);
next = data;
dataend->link.std = stat;
- dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_TD);
usb_syncmem(&dataend->dma,
dataend->offs + offsetof(uhci_td_t, td_link),
sizeof(dataend->td.td_link),
@@ -2434,7 +2483,7 @@ uhci_device_request(usbd_xfer_handle xfe
usb_syncmem(&upipe->u.ctl.reqdma, 0, sizeof *req, BUS_DMASYNC_PREWRITE);
setup->link.std = next;
- setup->td.td_link = htole32(next->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ setup->td.td_link = htole32(next->physaddr | UHCI_PTR_TD);
setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
UHCI_TD_ACTIVE);
setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof *req, endpt, addr));