diff options
author | 2014-11-10 14:16:13 +0000 | |
---|---|---|
committer | 2014-11-10 14:16:13 +0000 | |
commit | ffe08da5aa4cc25fd48a1ca6d44f4e23c512eed3 (patch) | |
tree | 4dd27125f54bc3be7af1830aba8e0c9d25271fc8 | |
parent | tweak previous; ok mikeb (diff) | |
download | wireguard-openbsd-ffe08da5aa4cc25fd48a1ca6d44f4e23c512eed3.tar.xz wireguard-openbsd-ffe08da5aa4cc25fd48a1ca6d44f4e23c512eed3.zip |
Apparently xhci(4) also needs a hook to set the address of a device.
Some Low/Full speed devices do not like to get a SET_ADDRESS command
before we have read (some bits of) their device descriptor. So change
the attach logic to issue two "Device Address" command with a BSR dance.
This should fix the "device problem, disabling port" error seen on root
hubs with some Low/Full speed devices, reported by miod@.
-rw-r--r-- | sys/dev/usb/xhci.c | 126 |
1 files changed, 85 insertions, 41 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index ca9785620e1..cc7bb108c41 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xhci.c,v 1.37 2014/11/09 14:03:04 mpi Exp $ */ +/* $OpenBSD: xhci.c,v 1.38 2014/11/10 14:16:13 mpi Exp $ */ /* * Copyright (c) 2014 Martin Pieuchot @@ -79,6 +79,7 @@ void xhci_event_xfer(struct xhci_softc *, uint64_t, uint32_t, uint32_t); void xhci_event_command(struct xhci_softc *, uint64_t); void xhci_event_port_change(struct xhci_softc *, uint64_t, uint32_t); int xhci_pipe_init(struct xhci_softc *, struct usbd_pipe *); +void xhci_context_setup(struct xhci_softc *, struct usbd_pipe *); int xhci_scratchpad_alloc(struct xhci_softc *, int); void xhci_scratchpad_free(struct xhci_softc *); int xhci_softdev_alloc(struct xhci_softc *, uint8_t); @@ -101,7 +102,7 @@ void xhci_cmd_set_tr_deq_async(struct xhci_softc *, uint8_t, uint8_t, uint64_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 xhci_cmd_set_address(struct xhci_softc *, uint8_t, uint64_t, uint32_t); int xhci_cmd_evaluate_ctx(struct xhci_softc *, uint8_t, uint64_t); #ifdef XHCI_DEBUG int xhci_cmd_noop(struct xhci_softc *); @@ -116,6 +117,7 @@ void xhci_timeout(void *); /* USBD Bus Interface. */ usbd_status xhci_pipe_open(struct usbd_pipe *); +int xhci_setaddr(struct usbd_device *, int); void xhci_softintr(void *); void xhci_poll(struct usbd_bus *); struct usbd_xfer *xhci_allocx(struct usbd_bus *); @@ -142,6 +144,7 @@ void xhci_device_generic_done(struct usbd_xfer *); struct usbd_bus_methods xhci_bus_methods = { .open_pipe = xhci_pipe_open, + .dev_setaddr = xhci_setaddr, .soft_intr = xhci_softintr, .do_poll = xhci_poll, .allocx = xhci_allocx, @@ -1023,8 +1026,8 @@ xhci_get_txinfo(struct xhci_softc *sc, struct usbd_pipe *pipe) return (XHCI_EPCTX_MAX_ESIT_PAYLOAD(mep) | XHCI_EPCTX_AVG_TRB_LEN(atl)); } -int -xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe) +void +xhci_context_setup(struct xhci_softc *sc, struct usbd_pipe *pipe) { struct xhci_pipe *xp = (struct xhci_pipe *)pipe; struct xhci_soft_dev *sdev = &sc->sc_sdevs[xp->slot]; @@ -1033,18 +1036,6 @@ xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe) uint8_t ival, speed, cerr = 0; uint32_t mps, route = 0, rhport = 0; struct usbd_device *hub; - int error; - - DPRINTF(("%s: dev %d dci %u (epAddr=0x%x)\n", DEVNAME(sc), xp->slot, - xp->dci, pipe->endpoint->edesc->bEndpointAddress)); - - if (xhci_ring_alloc(sc, &xp->ring, XHCI_MAX_TRANSFERS)) - return (ENOMEM); - - xp->free_trbs = xp->ring.ntrb; - xp->halted = 0; - - sdev->pipes[xp->dci - 1] = xp; /* * Calculate the Route String. Assume that there is no hub with @@ -1083,8 +1074,7 @@ xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe) mps = 512; break; default: - xhci_ring_free(sc, &xp->ring); - return (EINVAL); + return; } /* XXX Until we fix wMaxPacketSize for ctrl ep depending on the speed */ @@ -1144,37 +1134,45 @@ xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe) } usb_syncmem(&sdev->ictx_dma, 0, sc->sc_pagesize, BUS_DMASYNC_PREWRITE); +} + +int +xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe) +{ + struct xhci_pipe *xp = (struct xhci_pipe *)pipe; + struct xhci_soft_dev *sdev = &sc->sc_sdevs[xp->slot]; + int error; + + DPRINTF(("%s: dev %d dci %u (epAddr=0x%x)\n", DEVNAME(sc), xp->slot, + xp->dci, pipe->endpoint->edesc->bEndpointAddress)); + + if (xhci_ring_alloc(sc, &xp->ring, XHCI_MAX_TRANSFERS)) + return (ENOMEM); + + xp->free_trbs = xp->ring.ntrb; + xp->halted = 0; + + sdev->pipes[xp->dci - 1] = xp; + + xhci_context_setup(sc, pipe); if (xp->dci == 1) { /* * If we are opening the default pipe, the Slot should * be in the ENABLED state. Issue an "Address Device" - * with BSR=0 and jump directly to the ADDRESSED state. - * This way we don't need a callback to assign an addr. + * with BSR=1 to put the device in the DEFAULT state. + * We cannot jump directly to the ADDRESSED state with + * BSR=0 because some Low/Full speed devices wont accept + * a SET_ADDRESS command before we've read their device + * descriptor. */ - error = xhci_cmd_address_device(sc, xp->slot, - DMAADDR(&sdev->ictx_dma, 0)); + error = xhci_cmd_set_address(sc, xp->slot, + DMAADDR(&sdev->ictx_dma, 0), XHCI_TRB_BSR); } else { error = xhci_cmd_configure_ep(sc, xp->slot, DMAADDR(&sdev->ictx_dma, 0)); } - usb_syncmem(&sdev->octx_dma, 0, sc->sc_pagesize, BUS_DMASYNC_POSTREAD); - -#ifdef XHCI_DEBUG - if (xp->dci == 1 && !error) { - struct xhci_sctx *sctx; - uint8_t addr; - - /* Get output slot context. */ - sctx = KERNADDR(&sdev->octx_dma, 0); - addr = XHCI_SCTX_DEV_ADDR(letoh32(sctx->state)); - error = (addr == 0); - - printf("%s: dev %d addr %d\n", DEVNAME(sc), xp->slot, addr); - } -#endif - if (error) { xhci_ring_free(sc, &xp->ring); return (EIO); @@ -1231,6 +1229,51 @@ xhci_pipe_close(struct usbd_pipe *pipe) } } +/* + * Transition a device from DEFAULT to ADDRESSED Slot state, this hook + * is needed for Low/Full speed devices. + * + * See section 4.5.3 of USB 3.1 Specification for more details. + */ +int +xhci_setaddr(struct usbd_device *dev, int addr) +{ + struct xhci_softc *sc = (struct xhci_softc *)dev->bus; + struct xhci_pipe *xp = (struct xhci_pipe *)dev->default_pipe; + struct xhci_soft_dev *sdev = &sc->sc_sdevs[xp->slot]; + int error; + + /* Root Hub */ + if (dev->depth == 0) + return (0); + + KASSERT(xp->dci == 1); + + xhci_context_setup(sc, dev->default_pipe); + + error = xhci_cmd_set_address(sc, xp->slot, + DMAADDR(&sdev->ictx_dma, 0), 0); + +#ifdef XHCI_DEBUG + if (error == 0) { + struct xhci_sctx *sctx; + uint8_t addr; + + usb_syncmem(&sdev->octx_dma, 0, sc->sc_pagesize, + BUS_DMASYNC_POSTREAD); + + /* Get output slot context. */ + sctx = KERNADDR(&sdev->octx_dma, 0); + addr = XHCI_SCTX_DEV_ADDR(letoh32(sctx->state)); + error = (addr == 0); + + printf("%s: dev %d addr %d\n", DEVNAME(sc), xp->slot, addr); + } +#endif + + return (error); +} + struct usbd_xfer * xhci_allocx(struct usbd_bus *bus) { @@ -1580,16 +1623,17 @@ xhci_cmd_slot_control(struct xhci_softc *sc, uint8_t *slotp, int enable) } int -xhci_cmd_address_device(struct xhci_softc *sc, uint8_t slot, uint64_t addr) +xhci_cmd_set_address(struct xhci_softc *sc, uint8_t slot, uint64_t addr, + uint32_t bsr) { struct xhci_trb trb; - DPRINTF(("%s: %s\n", DEVNAME(sc), __func__)); + DPRINTF(("%s: %s BSR=%u\n", DEVNAME(sc), __func__, bsr ? 1 : 0)); trb.trb_paddr = htole64(addr); trb.trb_status = 0; trb.trb_flags = htole32( - XHCI_TRB_SET_SLOT(slot) | XHCI_CMD_ADDRESS_DEVICE + XHCI_TRB_SET_SLOT(slot) | XHCI_CMD_ADDRESS_DEVICE | bsr ); return (xhci_command_submit(sc, &trb, XHCI_COMMAND_TIMEOUT)); |