Implement what in Intel-speech is known as "bandwidth reclamation".

It means that we continously poll USB devices that have a pending transfer
instead of polling just once every ms.  This speeds up some transfers
at the expense of using more PCI bandwidth.
This commit is contained in:
augustss 2000-08-13 16:18:09 +00:00
parent d2e1f953ef
commit 4f325f2674
3 changed files with 143 additions and 37 deletions

View File

@ -6,9 +6,6 @@ High priority:
On a short control transfer the status phase needs to be executed anyway.
Do bandwidth reclamation while we have outstanding bulk transfers.
(Use an inactive TD on last QH to avoid PIIX bug.)
Allow interrupt out endpoints. (USB 1.1)
Fix flow control in ucom (copy from com driver).

View File

@ -1,4 +1,4 @@
/* $NetBSD: uhci.c,v 1.122 2000/08/08 19:51:47 tv Exp $ */
/* $NetBSD: uhci.c,v 1.123 2000/08/13 16:18:09 augustss Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */
/*
@ -158,9 +158,9 @@ Static void uhci_busreset(uhci_softc_t *);
Static void uhci_shutdown(void *v);
Static void uhci_power(int, void *);
Static usbd_status uhci_run(uhci_softc_t *, int run);
Static uhci_soft_td_t *uhci_alloc_std(uhci_softc_t *);
Static uhci_soft_td_t *uhci_alloc_std(uhci_softc_t *);
Static void uhci_free_std(uhci_softc_t *, uhci_soft_td_t *);
Static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *);
Static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *);
Static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *);
#if 0
Static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *,
@ -181,11 +181,15 @@ Static void uhci_idone(uhci_intr_info_t *);
Static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status);
Static void uhci_timeout(void *);
Static void uhci_add_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
Static void uhci_add_ls_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
Static void uhci_add_hs_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
Static void uhci_add_bulk(uhci_softc_t *, uhci_soft_qh_t *);
Static void uhci_remove_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
Static void uhci_remove_ls_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
Static void uhci_remove_hs_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
Static void uhci_remove_bulk(uhci_softc_t *,uhci_soft_qh_t *);
Static int uhci_str(usb_string_descriptor_t *, int, char *);
Static void uhci_add_loop(uhci_softc_t *sc);
Static void uhci_rem_loop(uhci_softc_t *sc);
Static usbd_status uhci_setup_isoc(usbd_pipe_handle pipe);
Static void uhci_device_isoc_enter(usbd_xfer_handle);
@ -379,7 +383,7 @@ uhci_init(uhci_softc_t *sc)
{
usbd_status err;
int i, j;
uhci_soft_qh_t *csqh, *bsqh, *sqh;
uhci_soft_qh_t *clsqh, *chsqh, *bsqh, *sqh, *lsqh;
uhci_soft_td_t *std;
DPRINTFN(1,("uhci_init: start\n"));
@ -406,25 +410,59 @@ uhci_init(uhci_softc_t *sc)
UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */
UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma)); /* set frame list*/
/*
* Allocate a TD, inactive, that hangs from the last QH.
* This is to avoid a bug in the PIIX that makes it run berserk
* otherwise.
*/
std = uhci_alloc_std(sc);
if (std == NULL)
return (USBD_NOMEM);
std->link.std = NULL;
std->td.td_link = htole32(UHCI_PTR_T);
std->td.td_status = htole32(0); /* inactive */
std->td.td_token = htole32(0);
std->td.td_buffer = htole32(0);
/* Allocate the dummy QH marking the end and used for looping the QHs.*/
lsqh = uhci_alloc_sqh(sc);
if (lsqh == NULL)
return (USBD_NOMEM);
lsqh->hlink = NULL;
lsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */
lsqh->elink = std;
lsqh->qh.qh_elink = htole32(std->physaddr | UHCI_PTR_TD);
sc->sc_last_qh = lsqh;
/* Allocate the dummy QH where bulk traffic will be queued. */
bsqh = uhci_alloc_sqh(sc);
if (bsqh == NULL)
return (USBD_NOMEM);
bsqh->hlink = NULL;
bsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */
bsqh->hlink = lsqh;
bsqh->qh.qh_hlink = htole32(lsqh->physaddr | UHCI_PTR_QH);
bsqh->elink = NULL;
bsqh->qh.qh_elink = htole32(UHCI_PTR_T);
sc->sc_bulk_start = sc->sc_bulk_end = bsqh;
/* Allocate the dummy QH where control traffic will be queued. */
csqh = uhci_alloc_sqh(sc);
if (csqh == NULL)
/* Allocate dummy QH where high speed control traffic will be queued. */
chsqh = uhci_alloc_sqh(sc);
if (chsqh == NULL)
return (USBD_NOMEM);
csqh->hlink = bsqh;
csqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
csqh->elink = NULL;
csqh->qh.qh_elink = htole32(UHCI_PTR_T);
sc->sc_ctl_start = sc->sc_ctl_end = csqh;
chsqh->hlink = bsqh;
chsqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
chsqh->elink = NULL;
chsqh->qh.qh_elink = htole32(UHCI_PTR_T);
sc->sc_hctl_start = sc->sc_hctl_end = chsqh;
/* Allocate dummy QH where control traffic will be queued. */
clsqh = uhci_alloc_sqh(sc);
if (clsqh == NULL)
return (USBD_NOMEM);
clsqh->hlink = bsqh;
clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH);
clsqh->elink = NULL;
clsqh->qh.qh_elink = htole32(UHCI_PTR_T);
sc->sc_lctl_start = sc->sc_lctl_end = clsqh;
/*
* Make all (virtual) frame list pointers point to the interrupt
@ -441,8 +479,8 @@ uhci_init(uhci_softc_t *sc)
std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */
std->td.td_token = htole32(0);
std->td.td_buffer = htole32(0);
sqh->hlink = csqh;
sqh->qh.qh_hlink = htole32(csqh->physaddr | UHCI_PTR_QH);
sqh->hlink = clsqh;
sqh->qh.qh_hlink = htole32(clsqh->physaddr | UHCI_PTR_QH);
sqh->elink = NULL;
sqh->qh.qh_elink = htole32(UHCI_PTR_T);
sc->sc_vframes[i].htd = std;
@ -777,7 +815,7 @@ uhci_dump_all(uhci_softc_t *sc)
uhci_dumpregs(sc);
printf("intrs=%d\n", sc->sc_bus.no_intrs);
/*printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);*/
uhci_dump_qh(sc->sc_ctl_start);
uhci_dump_qh(sc->sc_lctl_start);
}
@ -931,37 +969,96 @@ uhci_root_ctrl_done(usbd_xfer_handle xfer)
{
}
/* Add control QH, called at splusb(). */
/*
* Let the last QH loop back to the high speed control transfer QH.
* This is what intel calls "bandwidth reclamation" and improves
* USB performance a lot for some devices.
* If we are already looping, just count it.
*/
void
uhci_add_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
uhci_add_loop(uhci_softc_t *sc) {
if (++sc->sc_loops == 1) {
DPRINTFN(10,("uhci_start_loop: add\n"));
/* Note, we don't loop back the soft pointer. */
sc->sc_last_qh->qh.qh_hlink =
htole32(sc->sc_hctl_start->physaddr | UHCI_PTR_QH);
}
}
void
uhci_rem_loop(uhci_softc_t *sc) {
if (--sc->sc_loops == 0) {
DPRINTFN(5,("uhci_end_loop: remove\n"));
sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T);
}
}
/* Add high speed control QH, called at splusb(). */
void
uhci_add_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *eqh;
SPLUSBCHECK;
DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh));
eqh = sc->sc_ctl_end;
eqh = sc->sc_hctl_end;
sqh->hlink = eqh->hlink;
sqh->qh.qh_hlink = eqh->qh.qh_hlink;
eqh->hlink = sqh;
eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
sc->sc_ctl_end = sqh;
sc->sc_hctl_end = sqh;
uhci_add_loop(sc);
}
/* Remove control QH, called at splusb(). */
/* Remove high speed control QH, called at splusb(). */
void
uhci_remove_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *pqh;
SPLUSBCHECK;
DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh));
pqh = uhci_find_prev_qh(sc->sc_ctl_start, sqh);
DPRINTFN(10, ("uhci_remove_hs_ctrl: sqh=%p\n", sqh));
uhci_rem_loop(sc);
pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh);
pqh->hlink = sqh->hlink;
pqh->qh.qh_hlink = sqh->qh.qh_hlink;
if (sc->sc_ctl_end == sqh)
sc->sc_ctl_end = pqh;
if (sc->sc_hctl_end == sqh)
sc->sc_hctl_end = pqh;
}
/* Add low speed control QH, called at splusb(). */
void
uhci_add_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *eqh;
SPLUSBCHECK;
DPRINTFN(10, ("uhci_add_ls_ctrl: sqh=%p\n", sqh));
eqh = sc->sc_lctl_end;
sqh->hlink = eqh->hlink;
sqh->qh.qh_hlink = eqh->qh.qh_hlink;
eqh->hlink = sqh;
eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
sc->sc_lctl_end = sqh;
}
/* Remove low speed control QH, called at splusb(). */
void
uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
uhci_soft_qh_t *pqh;
SPLUSBCHECK;
DPRINTFN(10, ("uhci_remove_ls_ctrl: sqh=%p\n", sqh));
pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh);
pqh->hlink = sqh->hlink;
pqh->qh.qh_hlink = sqh->qh.qh_hlink;
if (sc->sc_lctl_end == sqh)
sc->sc_lctl_end = pqh;
}
/* Add bulk QH, called at splusb(). */
@ -979,6 +1076,7 @@ uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
eqh->hlink = sqh;
eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
sc->sc_bulk_end = sqh;
uhci_add_loop(sc);
}
/* Remove bulk QH, called at splusb(). */
@ -990,6 +1088,7 @@ uhci_remove_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
SPLUSBCHECK;
DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh));
uhci_rem_loop(sc);
pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh);
pqh->hlink = sqh->hlink;
pqh->qh.qh_hlink = sqh->qh.qh_hlink;
@ -2004,7 +2103,10 @@ uhci_device_request(usbd_xfer_handle xfer)
sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD);
s = splusb();
uhci_add_ctrl(sc, sqh);
if (dev->lowspeed)
uhci_add_ls_ctrl(sc, sqh);
else
uhci_add_hs_ctrl(sc, sqh);
uhci_add_intr_info(sc, ii);
#ifdef UHCI_DEBUG
if (uhcidebug > 12) {
@ -2441,7 +2543,10 @@ uhci_device_ctrl_done(usbd_xfer_handle xfer)
uhci_del_intr_info(ii); /* remove from active list */
uhci_remove_ctrl(sc, upipe->u.ctl.sqh);
if (upipe->pipe.device->lowspeed)
uhci_remove_ls_ctrl(sc, upipe->u.ctl.sqh);
else
uhci_remove_hs_ctrl(sc, upipe->u.ctl.sqh);
if (upipe->u.ctl.length != 0)
uhci_free_std_chain(sc, ii->stdstart->link.std, ii->stdend);

View File

@ -1,4 +1,4 @@
/* $NetBSD: uhcivar.h,v 1.31 2000/06/01 14:28:59 augustss Exp $ */
/* $NetBSD: uhcivar.h,v 1.32 2000/08/13 16:18:09 augustss Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhcivar.h,v 1.14 1999/11/17 22:33:42 n_hibma Exp $ */
/*
@ -140,10 +140,14 @@ typedef struct uhci_softc {
usb_dma_t sc_dma;
struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT];
uhci_soft_qh_t *sc_ctl_start; /* dummy QH for control */
uhci_soft_qh_t *sc_ctl_end; /* last control QH */
uhci_soft_qh_t *sc_lctl_start; /* dummy QH for low speed control */
uhci_soft_qh_t *sc_lctl_end; /* last control QH */
uhci_soft_qh_t *sc_hctl_start; /* dummy QH for high speed control */
uhci_soft_qh_t *sc_hctl_end; /* last control QH */
uhci_soft_qh_t *sc_bulk_start; /* dummy QH for bulk */
uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */
uhci_soft_qh_t *sc_last_qh; /* dummy QH at the end */
u_int32_t sc_loops; /* number of QHs that wants looping */
uhci_soft_td_t *sc_freetds; /* TD free list */
uhci_soft_qh_t *sc_freeqhs; /* QH free list */