diff options
author | le <le@FreeBSD.org> | 2004-06-26 00:52:37 +0000 |
---|---|---|
committer | le <le@FreeBSD.org> | 2004-06-26 00:52:37 +0000 |
commit | cd721672a01a90040c2f1e5af0a17840d6d8c529 (patch) | |
tree | 371c27da3dbfc2cb1279a6d0de6cf5293988a52d /sys/dev/usb/ehci.c | |
parent | d1f0e75537b0d494a6823fbcb82d16b517383233 (diff) | |
download | FreeBSD-src-cd721672a01a90040c2f1e5af0a17840d6d8c529.zip FreeBSD-src-cd721672a01a90040c2f1e5af0a17840d6d8c529.tar.gz |
MFNetBSD ehci.c and ehcireg.h
ehci.c (1.55), ehcireg.h (1.16); author: mycroft
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.
ehci.c (1.56); author: mycroft
Failure to properly mask off UE_DIR_IN from the endpoint address
was causing OHCI_ED_FORMAT_ISO and EHCI_QH_HRECL to get set
spuriously, causing rather interesting lossage.
Suddenly I get MUCH better performance with ehci...
ehci.c (1.58); author: mycroft
Fix a stupid bug in ehci_check_intr() that caused use to try to
complete a transaction that was still running. Now ehci can
handle multiple devices being active at once.
ehci.c (1.59); author: enami
As the ehci_idone() now uses the variable `epipe'
unconditionally, always declare it (in other words, make this
file compile w/o EHCI_DEBUG).
ehci.c (1.60); author: mycroft
Remove comment about the data toggle being borked.
ehci.c (1.61); author: mycroft
Update comment.
ehci.c (1.62); author: mycroft
Adjust a couple of comments to make it clear WTF is going on.
ehci.c (1.63); author: mycroft
Fix an error in a debug printf().
ehci.c (1.64), ehcireg.h (1.17); author: mycroft
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.
Obtained from: NetBSD
Diffstat (limited to 'sys/dev/usb/ehci.c')
-rw-r--r-- | sys/dev/usb/ehci.c | 92 |
1 files changed, 48 insertions, 44 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index 7055172..11dc97a 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -3,6 +3,8 @@ /* Also ported from NetBSD: * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $ * $NetBSD: ehci.c,v 1.54 2004/01/17 13:15:05 jdolecek Exp $ + * up to + * $NetBSD: ehci.c,v 1.64 2004/06/23 06:45:56 mycroft Exp $ */ /* @@ -14,11 +16,11 @@ __FBSDID("$FreeBSD$"); /* - * Copyright (c) 2001 The NetBSD Foundation, Inc. + * Copyright (c) 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (lennart@augustsson.net). + * by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -67,18 +69,14 @@ __FBSDID("$FreeBSD$"); * 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/param.h> @@ -139,6 +137,8 @@ SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, struct ehci_pipe { struct usbd_pipe pipe; + int nexttoggle; + ehci_soft_qh_t *sqh; union { ehci_soft_qtd_t *qtd; @@ -705,7 +705,7 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) if (status & EHCI_QTD_HALTED) goto done; /* We want short packets, and it is short: it's done */ - if (EHCI_QTD_SET_BYTES(status) != 0) + if (EHCI_QTD_GET_BYTES(status) != 0) goto done; } DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", @@ -722,9 +722,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; @@ -778,11 +776,12 @@ ehci_idone(struct ehci_xfer *ex) /* If there are left over TDs we need to update the toggle. */ if (sqtd != NULL) { - if (!(xfer->rqflags & URQ_REQUEST)) - printf("ehci_idone: need toggle update\n"); + printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus); #if 0 - epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(ex->sqtdstart); #endif + epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); } status &= EHCI_QTD_STATERRS; @@ -794,9 +793,8 @@ ehci_idone(struct ehci_xfer *ex) char sbuf[128]; bitmask_snprintf((u_int32_t)status, - "\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" - "\7HALTED", - sbuf, sizeof(sbuf)); + "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR" + "\3MISSED", sbuf, sizeof(sbuf)); DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, ("ehci_idone: error, addr=%d, endpt=0x%02x, " @@ -1094,7 +1092,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 @@ -1253,6 +1251,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: @@ -1281,9 +1281,9 @@ ehci_open(usbd_pipe_handle pipe) /* qh_link filled when the QH is added */ sqh->qh.qh_endp = htole32( EHCI_QH_SET_ADDR(addr) | - EHCI_QH_SET_ENDPT(ed->bEndpointAddress) | - EHCI_QH_SET_EPS(speed) | /* XXX */ - /* XXX EHCI_QH_DTC ? */ + EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) | + 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) | @@ -1389,8 +1389,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); } /* @@ -2138,8 +2138,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, offset; - int i; + int len, curlen, mps, offset; + int i, tog; usb_dma_t *dma = &xfer->dmabuf; DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); @@ -2148,14 +2148,20 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, len = alen; dataphys = DMAADDR(dma, 0); dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1)); +#if 0 +printf("status=%08x toggle=%d\n", epipe->sqh->qh.qh_qtd.qtd_status, + epipe->nexttoggle); +#endif qtdstatus = htole32( EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | 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 |= EHCI_QTD_SET_TOGGLE(tog); cur = ehci_alloc_sqtd(sc); *sp = cur; @@ -2200,10 +2206,8 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, curlen = EHCI_PAGE_SIZE - EHCI_PAGE_MASK(dataphys); #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 @@ -2248,6 +2252,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 ^= EHCI_QTD_TOGGLE_MASK; + } if (len == 0) break; DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); @@ -2257,6 +2267,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)); @@ -2571,31 +2582,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_QG_MPLMASK))) | + (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) @@ -2603,18 +2606,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)); @@ -2628,6 +2631,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? */ |