diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2014-03-18 15:47:24 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2014-03-18 15:47:24 +0000 |
commit | e8aa1350bac990b8fde28af5d9e1f55a4677b446 (patch) | |
tree | ac0bef86edbda66ea6785c12b532d470c922ae6b | |
parent | facb6419642369a48559227201f4718a0760c430 (diff) |
Properly clear and free the endpoint associated to a pipe. Do not
forget to update the Endpoint Context with the last valid endpoint
and free the device resources, including its slot, when the default
pipe is closed.
Device addresses can now be reused and I should be done with
descriptor leaks.
-rw-r--r-- | sys/dev/usb/xhci.c | 67 |
1 files changed, 40 insertions, 27 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index 0e80e2eb9f8..f2deac66b4c 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xhci.c,v 1.2 2014/03/12 13:05:02 mpi Exp $ */ +/* $OpenBSD: xhci.c,v 1.3 2014/03/18 15:47:23 mpi Exp $ */ /* * Copyright (c) 2014 Martin Pieuchot @@ -95,8 +95,8 @@ int xhci_command_abort(struct xhci_softc *); void xhci_cmd_reset_endpoint_async(struct xhci_softc *, uint8_t, uint8_t); void xhci_cmd_set_tr_deq_async(struct xhci_softc *, uint8_t, uint8_t, uint64_t); -int xhci_cmd_configure_endpoint(struct xhci_softc *, uint8_t, uint64_t); -int xhci_cmd_stop_endpoint(struct xhci_softc *, uint8_t, uint8_t); +int xhci_cmd_configure_ep(struct xhci_softc *, uint8_t, uint64_t); +int xhci_cmd_stop_ep(struct xhci_softc *, uint8_t, uint8_t); int xhci_cmd_slot_control(struct xhci_softc *, uint8_t *, int); int xhci_cmd_address_device(struct xhci_softc *,uint8_t, uint64_t, int); int xhci_cmd_evaluate_ctx(struct xhci_softc *, uint8_t, uint64_t); @@ -1033,7 +1033,7 @@ xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe, uint32_t port) dev->address = addr; #endif } else { - error = xhci_cmd_configure_endpoint(sc, xp->slot, + error = xhci_cmd_configure_ep(sc, xp->slot, DMAADDR(&sdev->ictx_dma, 0)); if (error) { xhci_ring_free(sc, &xp->ring); @@ -1050,30 +1050,45 @@ void xhci_pipe_close(struct usbd_pipe *pipe) { struct xhci_softc *sc = (struct xhci_softc *)pipe->device->bus; - struct xhci_pipe *xp = (struct xhci_pipe *)pipe; + struct xhci_pipe *lxp, *xp = (struct xhci_pipe *)pipe; + usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; struct xhci_soft_dev *sdev = &sc->sc_sdevs[xp->slot]; + int i; /* Root Hub */ if (pipe->device->depth == 0) return; - /* XXX this is wrong */ - if (!xp->halted || xhci_cmd_stop_endpoint(sc, xp->slot, xp->dci)) { - DPRINTF(("%s: unable to stop pipe=%p dci=%d\n", DEVNAME(sc), - pipe, xp->dci)); - return; - } + if (!xp->halted || xhci_cmd_stop_ep(sc, xp->slot, xp->dci)) + DPRINTF(("%s: error stopping ep (%d)\n", DEVNAME(sc), xp->dci)); /* Mask the endpoint */ - sdev->input_ctx->drop_flags |= htole32(XHCI_INCTX_MASK_DCI(xp->dci)); - sdev->input_ctx->add_flags &= ~htole32(XHCI_INCTX_MASK_DCI(xp->dci)); + sdev->input_ctx->drop_flags = htole32(XHCI_INCTX_MASK_DCI(xp->dci)); + sdev->input_ctx->add_flags = 0; - /* Clear the endpoint context */ - memset(&sdev->ep_ctx[xp->dci-1], 0, sizeof(struct xhci_epctx)); + /* Update last valid Endpoint Context */ + for (i = 30; i >= 0; i--) { + lxp = sdev->pipes[i]; + if (lxp != NULL && lxp != xp) + break; + } + sdev->slot_ctx->info_lo = htole32(XHCI_SCTX_SET_DCI(lxp->dci)); + + /* Clear the Endpoint Context */ + memset(&sdev->ep_ctx[xp->dci - 1], 0, sizeof(struct xhci_epctx)); usb_syncmem(&sdev->ictx_dma, 0, sc->sc_pagesize, BUS_DMASYNC_PREWRITE); + if (xhci_cmd_configure_ep(sc, xp->slot, DMAADDR(&sdev->ictx_dma, 0))) + DPRINTF(("%s: error clearing ep (%d)\n", DEVNAME(sc), xp->dci)); + xhci_ring_free(sc, &xp->ring); + sdev->pipes[xp->dci - 1] = NULL; + + if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_CONTROL) { + xhci_cmd_slot_control(sc, &xp->slot, 0); + xhci_softdev_free(sc, xp->slot); + } } struct usbd_xfer * @@ -1361,7 +1376,7 @@ xhci_command_abort(struct xhci_softc *sc) } int -xhci_cmd_configure_endpoint(struct xhci_softc *sc, uint8_t slot, uint64_t addr) +xhci_cmd_configure_ep(struct xhci_softc *sc, uint8_t slot, uint64_t addr) { struct xhci_trb trb; @@ -1377,7 +1392,7 @@ xhci_cmd_configure_endpoint(struct xhci_softc *sc, uint8_t slot, uint64_t addr) } int -xhci_cmd_stop_endpoint(struct xhci_softc *sc, uint8_t slot, uint8_t dci) +xhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t slot, uint8_t dci) { struct xhci_trb trb; @@ -1434,14 +1449,18 @@ xhci_cmd_slot_control(struct xhci_softc *sc, uint8_t *slotp, int enable) trb.trb_paddr = 0; trb.trb_status = 0; - trb.trb_flags = htole32( - enable ? XHCI_CMD_ENABLE_SLOT : XHCI_CMD_DISABLE_SLOT - ); + if (enable) + trb.trb_flags = htole32(XHCI_CMD_ENABLE_SLOT); + else + trb.trb_flags = htole32( + XHCI_TRB_SET_SLOT(*slotp) | XHCI_CMD_DISABLE_SLOT + ); if (xhci_command_submit(sc, &trb, XHCI_COMMAND_TIMEOUT)) return (EIO); - *slotp = XHCI_TRB_GET_SLOT(letoh32(trb.trb_flags)); + if (enable) + *slotp = XHCI_TRB_GET_SLOT(letoh32(trb.trb_flags)); return (0); } @@ -1546,17 +1565,11 @@ void xhci_softdev_free(struct xhci_softc *sc, uint8_t slot) { struct xhci_soft_dev *sdev = &sc->sc_sdevs[slot]; - int i; sc->sc_dcbaa.segs[slot] = 0; usb_syncmem(&sc->sc_dcbaa.dma, slot * sizeof(uint64_t), sizeof(uint64_t), BUS_DMASYNC_PREWRITE); - for (i = 0; i < 31; i++) { - if (sdev->pipes[i] != NULL) - xhci_ring_free(sc, &sdev->pipes[i]->ring); - } - usb_freemem(&sc->sc_bus, &sdev->octx_dma); usb_freemem(&sc->sc_bus, &sdev->ictx_dma); |