Index: sys/dev/usb/ohci.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ohci.c,v retrieving revision 1.310 diff -u -p -r1.310 ohci.c --- sys/dev/usb/ohci.c 3 Jun 2020 15:38:02 -0000 1.310 +++ sys/dev/usb/ohci.c 1 Dec 2020 17:39:13 -0000 @@ -492,6 +492,7 @@ ohci_alloc_std(ohci_softc_t *sc) memset(&std->td, 0, sizeof(ohci_td_t)); std->nexttd = NULL; std->xfer = NULL; + std->held = NULL; return std; } @@ -539,14 +540,17 @@ ohci_alloc_std_chain(ohci_softc_t *sc, s DPRINTFN(8, "xfer %#jx nstd %jd", (uintptr_t)xfer, nstd, 0, 0); - for (size_t j = 0; j < ox->ox_nstd;) { + for (size_t j = 0; j < ox->ox_nstd; j++) { ohci_soft_td_t *cur = ohci_alloc_std(sc); if (cur == NULL) goto nomem; - ox->ox_stds[j++] = cur; + ox->ox_stds[j] = cur; + cur->held = &ox->ox_stds[j]; cur->xfer = xfer; cur->flags = 0; + DPRINTFN(10, "xfer=%#jx new std=%#jx held at %#jx", (uintptr_t)ox, + (uintptr_t)cur, (uintptr_t)cur->held, 0); } return 0; @@ -802,6 +806,8 @@ ohci_init(ohci_softc_t *sc) for (i = 0; i < OHCI_HASH_SIZE; i++) LIST_INIT(&sc->sc_hash_itds[i]); + TAILQ_INIT(&sc->sc_abortingxfers); + sc->sc_xferpool = pool_cache_init(sizeof(struct ohci_xfer), 0, 0, 0, "ohcixfer", NULL, IPL_USB, NULL, NULL, NULL); @@ -1357,6 +1363,20 @@ ohci_intr1(ohci_softc_t *sc) */ softint_schedule(sc->sc_rhsc_si); } + if (eintrs & OHCI_SF) { + struct ohci_xfer *ox, *tmp; + TAILQ_FOREACH_SAFE(ox, &sc->sc_abortingxfers, ox_abnext, tmp) { + DPRINTFN(10, "SF %#jx xfer %#jx", (uintptr_t)sc, (uintptr_t)ox, 0, 0); + ox->ox_abintrs &= ~OHCI_SF; + KASSERT(ox->ox_abintrs == 0); + TAILQ_REMOVE(&sc->sc_abortingxfers, ox, ox_abnext); + } + cv_broadcast(&sc->sc_softwake_cv); + + KASSERT(TAILQ_EMPTY(&sc->sc_abortingxfers)); + DPRINTFN(10, "end SOF %#jx", (uintptr_t)sc, 0, 0, 0); + /* Don't remove OHIC_SF from eintrs so it is blocked below */ + } if (eintrs != 0) { /* Block unprocessed interrupts. */ @@ -1414,12 +1434,22 @@ ohci_softintr(void *v) struct ohci_pipe *opipe; int len, cc; int i, j, actlen, iframes, uedir; - ohci_physaddr_t done; + ohci_physaddr_t done = 0; KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); OHCIHIST_FUNC(); OHCIHIST_CALLED(); + /* + * Only read hccadone if WDH is set - we might get here from places + * other than an interrupt + */ + if (!(OREAD4(sc, OHCI_INTERRUPT_STATUS) & OHCI_WDH)) { + DPRINTFN(10, "no WDH %#jx", (uintptr_t)sc, 0, 0, 0); + return; + } + + DPRINTFN(10, "WDH %#jx", (uintptr_t)sc, 0, 0, 0); usb_syncmem(&sc->sc_hccadma, offsetof(struct ohci_hcca, hcca_done_head), sizeof(sc->sc_hcca->hcca_done_head), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); @@ -1474,9 +1504,9 @@ ohci_softintr(void *v) for (std = sdone; std; std = stdnext) { xfer = std->xfer; stdnext = std->dnext; - DPRINTFN(10, "std=%#jx xfer=%#jx hcpriv=%#jx", (uintptr_t)std, - (uintptr_t)xfer, (uintptr_t)(xfer ? xfer->ux_hcpriv : 0), - 0); + DPRINTFN(10, "std=%#jx xfer=%#jx hcpriv=%#jx dnext=%'jx", + (uintptr_t)std, (uintptr_t)xfer, + (uintptr_t)(xfer ? xfer->ux_hcpriv : 0), (uintptr_t)stdnext); if (xfer == NULL) { /* * xfer == NULL: There seems to be no xfer associated @@ -1486,6 +1516,12 @@ ohci_softintr(void *v) */ continue; } + if (std->held == NULL) { + DPRINTFN(10, "std=%#jx held is null", (uintptr_t)std, 0, 0, 0); + ohci_hash_rem_td(sc, std); + ohci_free_std_locked(sc, std); + continue; + } /* * Try to claim this xfer for completion. If it has @@ -2069,6 +2105,7 @@ ohci_open(struct usbd_pipe *pipe) goto bad; opipe->tail.itd = sitd; + sitd->held = &opipe->tail.itd; tdphys = sitd->physaddr; fmt = OHCI_ED_SET_FORMAT(OHCI_ED_FORMAT_ISO); if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) @@ -2081,6 +2118,7 @@ ohci_open(struct usbd_pipe *pipe) goto bad; opipe->tail.td = std; + std->held = &opipe->tail.td; tdphys = std->physaddr; fmt = OHCI_ED_SET_FORMAT(OHCI_ED_FORMAT_GEN) | @@ -2241,17 +2279,35 @@ ohci_abortx(struct usbd_xfer *xfer) usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags), sizeof(sed->ed.ed_flags), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - sed->ed.ed_flags |= HTOO32(OHCI_ED_SKIP); /* force hardware skip */ - usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags), - sizeof(sed->ed.ed_flags), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + if (!(sed->ed.ed_flags & OHCI_HALTED)) { + /* force hardware skip */ + DPRINTFN(1, "pausing ed=%#jx", (uintptr_t)sed, 0, 0, 0); + sed->ed.ed_flags |= HTOO32(OHCI_ED_SKIP); + usb_syncmem(&sed->dma, + sed->offs + offsetof(ohci_ed_t, ed_flags), + sizeof(sed->ed.ed_flags), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - /* - * HC Step 2: Wait until we know hardware has finished any possible - * use of the xfer. - */ - /* Hardware finishes in 1ms */ - usb_delay_ms_locked(opipe->pipe.up_dev->ud_bus, 20, &sc->sc_lock); + DPRINTFN(10, "WDH %#jx xfer %#jx", (uintptr_t)sc, (uintptr_t)xfer, 0, 0); + struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer); + ox->ox_abintrs = OHCI_SF; + TAILQ_INSERT_TAIL(&sc->sc_abortingxfers, ox, ox_abnext); + + OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_SF); + sc->sc_eintrs |= OHCI_SF; + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_SF); + /* + * Step 2: Wait until we know hardware has finished any possible + * use of the xfer. + */ + while (ox->ox_abintrs != 0) { + DPRINTFN(10, "WDH %#jx xfer %#jx intrs %#x", (uintptr_t)sc, + (uintptr_t)xfer, (uintptr_t)ox->ox_abintrs, 0); + cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); + } + } else { + DPRINTFN(1, "halted ed=%#jx", (uintptr_t)sed, 0, 0, 0); + } /* * HC Step 3: Remove any vestiges of the xfer from the hardware. @@ -2273,12 +2329,38 @@ ohci_abortx(struct usbd_xfer *xfer) } DPRINTF("--- dump end ---", 0, 0, 0, 0); #endif + +#define OHCI_CC_ACCESSED_P(x) \ + (((x) & OHCI_CC_NOT_ACCESSED_MASK) != OHCI_CC_NOT_ACCESSED) + headp = O32TOH(sed->ed.ed_headp) & OHCI_HEADMASK; hit = 0; for (; p->xfer == xfer; p = n) { hit |= headp == p->physaddr; n = p->nexttd; - ohci_hash_rem_td(sc, p); + + int cc = OHCI_TD_GET_CC(O32TOH(p->td.td_flags)); + if (!OHCI_CC_ACCESSED_P(cc)) { + ohci_hash_rem_td(sc, p); + continue; + } + DPRINTFN(10, "xfer=%#jx has been touched by HC", (uintptr_t)p, + 0, 0, 0); + + mutex_exit(&sc->sc_lock); + ohci_soft_td_t *std = ohci_alloc_std(sc); + if (std == NULL) { + /* XXX What to do??? */ + panic("hmm"); + } + mutex_enter(&sc->sc_lock); + + DPRINTFN(10, "new std=%#jx now held at %#jx", (uintptr_t)std, + (uintptr_t)p->held, 0, 0); + *(p->held) = std; + std->held = p->held; + std->xfer = xfer; + p->held = NULL; } /* Zap headp register if hardware pointed inside the xfer. */ if (hit) { @@ -2613,6 +2695,13 @@ ohci_device_ctrl_init(struct usbd_xfer * ox->ox_setup = setup; ox->ox_stat = stat; ox->ox_nstd = 0; + setup->held = &ox->ox_setup; + stat->held = &ox->ox_stat; + + DPRINTFN(10, "xfer=%#jx setup=%#jx held at %#jx", (uintptr_t)ox, + (uintptr_t)setup, (uintptr_t)setup->held, 0); + DPRINTFN(10, "xfer=%#jx stat= %#jx held at %#jx", (uintptr_t)ox, + (uintptr_t)stat, (uintptr_t)stat->held, 0); /* Set up data transaction */ if (len != 0) { @@ -2718,13 +2807,21 @@ ohci_device_ctrl_start(struct usbd_xfer setup = opipe->tail.td; opipe->tail.td = ox->ox_setup; ox->ox_setup = setup; + setup->held = &ox->ox_setup; + + DPRINTFN(10, "xfer=%#jx new setup=%#jx held at %#jx", (uintptr_t)ox, + (uintptr_t)setup, (uintptr_t)setup->held, 0); stat = ox->ox_stat; /* point at sentinel */ tail = opipe->tail.td; + tail->held = &opipe->tail.td; sed = opipe->sed; + DPRINTFN(10, "xfer=%#jx new tail=%#jx held at %#jx", (uintptr_t)ox, + (uintptr_t)tail, (uintptr_t)tail->held, 0); + KASSERTMSG(OHCI_ED_GET_FA(O32TOH(sed->ed.ed_flags)) == dev->ud_addr, "address ED %" __PRIuBITS " pipe %d\n", OHCI_ED_GET_FA(O32TOH(sed->ed.ed_flags)), dev->ud_addr); @@ -2995,13 +3092,19 @@ ohci_device_bulk_start(struct usbd_xfer data = opipe->tail.td; opipe->tail.td = ox->ox_stds[0]; ox->ox_stds[0] = data; + data->held = &ox->ox_stds[0]; ohci_reset_std_chain(sc, xfer, len, isread, data, &last); + DPRINTFN(10, "xfer=%#jx new data=%#jx held at %#jx", + (uintptr_t)ox, (uintptr_t)data, (uintptr_t)data->held, 0); /* point at sentinel */ tail = opipe->tail.td; memset(&tail->td, 0, sizeof(tail->td)); + tail->held = &opipe->tail.td; tail->nexttd = NULL; tail->xfer = NULL; + DPRINTFN(10, "xfer=%#jx new tail=%#jx held at %#ux", + (uintptr_t)ox, (uintptr_t)tail, (uintptr_t)tail->held, 0); usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); xfer->ux_hcpriv = data; @@ -3194,13 +3297,19 @@ ohci_device_intr_start(struct usbd_xfer data = opipe->tail.td; opipe->tail.td = ox->ox_stds[0]; ox->ox_stds[0] = data; + data->held = &ox->ox_stds[0]; ohci_reset_std_chain(sc, xfer, len, isread, data, &last); + DPRINTFN(10, "xfer=%#jx new data=%#jx held at %#jx", + (uintptr_t)ox, (uintptr_t)data, (uintptr_t)data->held, 0); /* point at sentinel */ tail = opipe->tail.td; memset(&tail->td, 0, sizeof(tail->td)); + tail->held = &opipe->tail.td; tail->nexttd = NULL; tail->xfer = NULL; + DPRINTFN(10, "xfer=%#jx new tail=%#jx held at %#jx", + (uintptr_t)ox, (uintptr_t)tail, (uintptr_t)tail->held, 0); usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); xfer->ux_hcpriv = data; @@ -3402,8 +3511,11 @@ ohci_device_isoc_init(struct usbd_xfer * goto fail; } ox->ox_sitds[i] = sitd; + sitd->held = &ox->ox_sitds[i]; sitd->xfer = xfer; sitd->flags = 0; +// DPRINTFN(10, "xfer=%#jx new tail=%#jx held at %#jx", +// (uintptr_t)ox, (uintptr_t)tail, (uintptr_t)tail->held, 0); } return 0; @@ -3509,6 +3621,7 @@ ohci_device_isoc_enter(struct usbd_xfer sitd = opipe->tail.itd; opipe->tail.itd = ox->ox_sitds[0]; ox->ox_sitds[0] = sitd; + sitd->held = &ox->ox_sitds[0]; boff = 0; buf = DMAADDR(&xfer->ux_dmabuf, 0); @@ -3587,6 +3700,7 @@ ohci_device_isoc_enter(struct usbd_xfer /* point at sentinel */ tail = opipe->tail.itd; memset(&tail->itd, 0, sizeof(tail->itd)); + tail->held = &opipe->tail.itd; tail->nextitd = NULL; tail->xfer = NULL; usb_syncmem(&tail->dma, tail->offs, sizeof(tail->itd), Index: sys/dev/usb/ohcivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.61 diff -u -p -r1.61 ohcivar.h --- sys/dev/usb/ohcivar.h 15 Mar 2020 07:56:19 -0000 1.61 +++ sys/dev/usb/ohcivar.h 1 Dec 2020 17:39:13 -0000 @@ -50,6 +50,7 @@ typedef struct ohci_soft_td { ohci_td_t td; struct ohci_soft_td *nexttd; /* mirrors nexttd in TD */ struct ohci_soft_td *dnext; /* next in done list */ + struct ohci_soft_td **held; /* where the ref to this std is held */ ohci_physaddr_t physaddr; usb_dma_t dma; int offs; @@ -68,6 +69,7 @@ typedef struct ohci_soft_itd { ohci_itd_t itd; struct ohci_soft_itd *nextitd; /* mirrors nexttd in ITD */ struct ohci_soft_itd *dnext; /* next in done list */ + struct ohci_soft_itd **held; /* where the ref to this sitd is held */ ohci_physaddr_t physaddr; usb_dma_t dma; int offs; @@ -108,6 +110,8 @@ typedef struct ohci_softc { LIST_HEAD(, ohci_soft_td) sc_hash_tds[OHCI_HASH_SIZE]; LIST_HEAD(, ohci_soft_itd) sc_hash_itds[OHCI_HASH_SIZE]; + TAILQ_HEAD(, ohci_xfer) sc_abortingxfers; + int sc_noport; int sc_endian; @@ -118,6 +122,8 @@ typedef struct ohci_softc { int sc_flags; #define OHCIF_SUPERIO 0x0001 + kcondvar_t sc_softwake_cv; + ohci_soft_ed_t *sc_freeeds; ohci_soft_td_t *sc_freetds; ohci_soft_itd_t *sc_freeitds; @@ -139,6 +145,8 @@ typedef struct ohci_softc { struct ohci_xfer { struct usbd_xfer xfer; + uint32_t ox_abintrs; + TAILQ_ENTRY(ohci_xfer) ox_abnext; /* ctrl */ ohci_soft_td_t *ox_setup; ohci_soft_td_t *ox_stat;