summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>2004-07-06 02:51:15 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>2004-07-06 02:51:15 +0000
commit2082b273d6ddce882e7e80b4d3895defb694cd8c (patch)
tree18802ec3726d39cf8f72a0213596201087c65514 /sys
parent1f27c7d93a89e52202c6046c6aa2e11fdc6b28a5 (diff)
sent in by loki@animata.net: This includes all the changes from netbsd
related to working with the data toggle, but also includes some endian fixes. The major changes in the netbsd code were in revisions 1.55 and 1.64. their log messages are: 1.55: Set the data toggle correctly, and use EHCI_QTD_DTC. This fixes problems with my ALi-based drive enclosure (it works now, rather than failing to attach). Also seems to work with a GL811-based enclosure and an ASUS enclosure with a CD-RW, on both Intel and NEC controllers. Note: The ALi enclosure is currently very SLOW, due to some issue with taking too long to notice that the QTD is complete. This requires more investigation. 1.64: Further cleanup of toggle handling. Now that we use EHCI_QH_DTC, we don't need to fiddle with the TOGGLE bit in the overlay descriptor, so minimize how much we fuss with it.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/ehci.c70
1 files changed, 33 insertions, 37 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index 0ad1fb003b8..de7d36d82ea 100644
--- a/sys/dev/usb/ehci.c
+++ b/sys/dev/usb/ehci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ehci.c,v 1.10 2004/07/06 02:49:47 deraadt Exp $ */
+/* $OpenBSD: ehci.c,v 1.11 2004/07/06 02:51:14 deraadt Exp $ */
/* $NetBSD: ehci.c,v 1.54 2004/01/17 13:15:05 jdolecek Exp $ */
/*
@@ -55,18 +55,14 @@
* devices using them don't work.
* Interrupt transfers are not difficult, it's just not done.
*
- * 3) There might also be some issues with the data toggle, it was not
- * completely tested to work properly under all condistions. If wrong
- * toggle would be sent/recvd, bulk data transfers would stop working.
- *
- * 4) The meaty part to implement is the support for USB 2.0 hubs.
+ * 3) The meaty part to implement is the support for USB 2.0 hubs.
* They are quite compolicated since the need to be able to do
* "transaction translation", i.e., converting to/from USB 2 and USB 1.
* So the hub driver needs to handle and schedule these things, to
* assign place in frame where different devices get to go. See chapter
* on hubs in USB 2.0 for details.
*
- * 5) command failures are not recovered correctly
+ * 4) command failures are not recovered correctly
*/
#include <sys/cdefs.h>
@@ -118,6 +114,8 @@ int ehcidebug = 0;
struct ehci_pipe {
struct usbd_pipe pipe;
+ int nexttoggle;
+
ehci_soft_qh_t *sqh;
union {
ehci_soft_qtd_t *qtd;
@@ -700,9 +698,7 @@ void
ehci_idone(struct ehci_xfer *ex)
{
usbd_xfer_handle xfer = &ex->xfer;
-#ifdef EHCI_DEBUG
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
-#endif
ehci_soft_qtd_t *sqtd;
u_int32_t status = 0, nstatus;
int actlen;
@@ -758,9 +754,7 @@ ehci_idone(struct ehci_xfer *ex)
if (sqtd != NULL) {
if (!(xfer->rqflags & URQ_REQUEST))
printf("ehci_idone: need toggle update\n");
-#if 0
- epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token));
-#endif
+ epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(status);
}
status &= EHCI_QTD_STATERRS;
@@ -1070,7 +1064,7 @@ ehci_device_clear_toggle(usbd_pipe_handle pipe)
if (ehcidebug)
usbd_dump_pipe(pipe);
#endif
- epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE);
+ epipe->nexttoggle = 0;
}
Static void
@@ -1229,6 +1223,8 @@ ehci_open(usbd_pipe_handle pipe)
if (sc->sc_dying)
return (USBD_IOERROR);
+ epipe->nexttoggle = 0;
+
if (addr == sc->sc_addr) {
switch (ed->bEndpointAddress) {
case USB_CONTROL_ENDPOINT:
@@ -1258,8 +1254,8 @@ ehci_open(usbd_pipe_handle pipe)
sqh->qh.qh_endp = htole32(
EHCI_QH_SET_ADDR(addr) |
EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
- EHCI_QH_SET_EPS(speed) | /* XXX */
- /* XXX EHCI_QH_DTC ? */
+ EHCI_QH_SET_EPS(speed) |
+ EHCI_QH_DTC |
EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
(speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
EHCI_QH_CTL : 0) |
@@ -1365,8 +1361,8 @@ ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
sqh->qh.qh_curqtd = 0;
sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
sqh->sqtd = sqtd;
- /* Keep toggle, clear the rest, including length. */
- sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE);
+ /* Clear halt */
+ sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED);
}
/*
@@ -2120,8 +2116,8 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
ehci_soft_qtd_t *next, *cur;
ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys;
u_int32_t qtdstatus;
- int len, curlen;
- int i;
+ int len, curlen, mps;
+ int i, tog;
usb_dma_t *dma = &xfer->dmabuf;
DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen));
@@ -2135,8 +2131,10 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
EHCI_QTD_SET_CERR(3)
/* IOC set below */
/* BYTES set below */
- /* XXX Data toggle */
);
+ mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
+ tog = epipe->nexttoggle;
+ qtdstatus |= htole32(EHCI_QTD_SET_TOGGLE(tog));
cur = ehci_alloc_sqtd(sc);
*sp = cur;
@@ -2164,10 +2162,8 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
curlen = len;
}
#endif
-
- /* XXX true for EHCI? */
/* the length must be a multiple of the max size */
- curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
+ curlen -= curlen % mps;
DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, "
"curlen=%d\n", curlen));
#ifdef DIAGNOSTIC
@@ -2212,6 +2208,12 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
cur->len = curlen;
DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n",
dataphys, dataphys + curlen));
+ /* adjust the toggle based on the number of packets in this
+ qtd */
+ if (((curlen + mps - 1) / mps) & 1) {
+ tog ^= 1;
+ qtdstatus ^= htole32(EHCI_QTD_TOGGLE);
+ }
if (len == 0)
break;
DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
@@ -2220,6 +2222,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
}
cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
*ep = cur;
+ epipe->nexttoggle = tog;
DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
*sp, *ep));
@@ -2530,31 +2533,23 @@ ehci_device_request(usbd_xfer_handle xfer)
sqh = epipe->sqh;
epipe->u.ctl.length = len;
- /* XXX
- * Since we're messing with the QH we must know the HC is in sync.
- * This needs to go away since it slows down control transfers.
- * Removing it entails:
- * - fill the QH only once with addr & wMaxPacketSize
- * - put the correct data toggles in the qtds and set DTC
- */
- /* ehci_sync_hc(sc); */
- /* Update device address and length since they may have changed. */
+ /* Update device address and length since they may have changed
+ during the setup of the control pipe in usbd_new_device(). */
/* XXX This only needs to be done once, but it's too early in open. */
/* XXXX Should not touch ED here! */
sqh->qh.qh_endp =
(sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QH_MPLMASK))) |
htole32(
EHCI_QH_SET_ADDR(addr) |
- /* EHCI_QH_DTC | */
EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize))
);
- /* Clear toggle */
- sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE);
/* Set up data transaction */
if (len != 0) {
ehci_soft_qtd_t *end;
+ /* Start toggle at 1. */
+ epipe->nexttoggle = 1;
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
&next, &end);
if (err)
@@ -2562,18 +2557,18 @@ ehci_device_request(usbd_xfer_handle xfer)
end->nextqtd = stat;
end->qtd.qtd_next =
end->qtd.qtd_altnext = htole32(stat->physaddr);
- /* Start toggle at 1. */
- /*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/
} else {
next = stat;
}
memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req);
+ /* Clear toggle */
setup->qtd.qtd_status = htole32(
EHCI_QTD_ACTIVE |
EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(0) |
EHCI_QTD_SET_BYTES(sizeof *req)
);
setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0));
@@ -2587,6 +2582,7 @@ ehci_device_request(usbd_xfer_handle xfer)
EHCI_QTD_ACTIVE |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) |
EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(1) |
EHCI_QTD_IOC
);
stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */