diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-07-06 02:51:15 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-07-06 02:51:15 +0000 |
commit | 2082b273d6ddce882e7e80b4d3895defb694cd8c (patch) | |
tree | 18802ec3726d39cf8f72a0213596201087c65514 /sys | |
parent | 1f27c7d93a89e52202c6046c6aa2e11fdc6b28a5 (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.c | 70 |
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? */ |