diff options
Diffstat (limited to 'hw/usb/hcd-xhci.c')
-rw-r--r-- | hw/usb/hcd-xhci.c | 438 |
1 files changed, 317 insertions, 121 deletions
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 58c88b8a6..ef3177aee 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -346,7 +346,6 @@ typedef struct XHCITransfer { QEMUSGList sgl; bool running_async; bool running_retry; - bool cancelled; bool complete; bool int_req; unsigned int iso_pkts; @@ -355,6 +354,7 @@ typedef struct XHCITransfer { unsigned int streamid; bool in_xfer; bool iso_xfer; + bool timed_xfer; unsigned int trb_count; unsigned int trb_alloced; @@ -373,7 +373,6 @@ struct XHCIStreamContext { dma_addr_t pctx; unsigned int sct; XHCIRing ring; - XHCIStreamContext *sstreams; }; struct XHCIEPContext { @@ -448,7 +447,6 @@ struct XHCIState { /*< public >*/ USBBus bus; - qemu_irq irq; MemoryRegion mem; MemoryRegion mem_cap; MemoryRegion mem_oper; @@ -506,8 +504,11 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid, unsigned int streamid); static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); +static void xhci_xfer_report(XHCITransfer *xfer); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); +static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, + unsigned int slotid, unsigned int epid); static const char *TRBType_names[] = { [TRB_RESERVED] = "TRB_RESERVED", @@ -586,6 +587,14 @@ static const char *TRBCCode_names[] = { [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR", }; +static const char *ep_state_names[] = { + [EP_DISABLED] = "disabled", + [EP_RUNNING] = "running", + [EP_HALTED] = "halted", + [EP_STOPPED] = "stopped", + [EP_ERROR] = "error", +}; + static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) { if (index >= llen || list[index] == NULL) { @@ -606,9 +615,15 @@ static const char *event_name(XHCIEvent *event) ARRAY_SIZE(TRBCCode_names)); } +static const char *ep_state_name(uint32_t state) +{ + return lookup_name(state, ep_state_names, + ARRAY_SIZE(ep_state_names)); +} + static uint64_t xhci_mfindex_get(XHCIState *xhci) { - int64_t now = qemu_get_clock_ns(vm_clock); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); return (now - xhci->mfindex_start) / 125000; } @@ -619,12 +634,12 @@ static void xhci_mfwrap_update(XHCIState *xhci) int64_t now; if ((xhci->usbcmd & bits) == bits) { - now = qemu_get_clock_ns(vm_clock); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff; left = 0x4000 - mfindex; - qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000); + timer_mod(xhci->mfwrap_timer, now + left * 125000); } else { - qemu_del_timer(xhci->mfwrap_timer); + timer_del(xhci->mfwrap_timer); } } @@ -722,7 +737,7 @@ static void xhci_intx_update(XHCIState *xhci) } trace_usb_xhci_irq_intx(level); - qemu_set_irq(xhci->irq, level); + pci_set_irq(pci_dev, level); } static void xhci_msix_update(XHCIState *xhci, int v) @@ -780,7 +795,7 @@ static void xhci_intr_raise(XHCIState *xhci, int v) if (v == 0) { trace_usb_xhci_irq_intx(1); - qemu_set_irq(xhci->irq, 1); + pci_irq_assert(pci_dev); } } @@ -792,7 +807,7 @@ static inline int xhci_running(XHCIState *xhci) static void xhci_die(XHCIState *xhci) { xhci->usbsts |= USBSTS_HCE; - fprintf(stderr, "xhci: asserted controller error\n"); + DPRINTF("xhci: asserted controller error\n"); } static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) @@ -839,8 +854,8 @@ static void xhci_events_update(XHCIState *xhci, int v) erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); if (erdp < intr->er_start || erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", + DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); + DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", v, intr->er_start, intr->er_size); xhci_die(xhci); return; @@ -908,7 +923,7 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) if (intr->er_full) { DPRINTF("xhci_event(): ER full, queueing\n"); if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - fprintf(stderr, "xhci: event queue full, dropping event!\n"); + DPRINTF("xhci: event queue full, dropping event!\n"); return; } intr->ev_buffer[intr->ev_buffer_put++] = *event; @@ -921,8 +936,8 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); if (erdp < intr->er_start || erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", + DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); + DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", v, intr->er_start, intr->er_size); xhci_die(xhci); return; @@ -939,7 +954,7 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) #endif intr->er_full = 1; if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - fprintf(stderr, "xhci: event queue full, dropping event!\n"); + DPRINTF("xhci: event queue full, dropping event!\n"); return; } intr->ev_buffer[intr->ev_buffer_put++] = *event; @@ -1057,7 +1072,7 @@ static void xhci_er_reset(XHCIState *xhci, int v) } /* cache the (sole) event ring segment location */ if (intr->erstsz != 1) { - fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); + DPRINTF("xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); xhci_die(xhci); return; } @@ -1067,7 +1082,7 @@ static void xhci_er_reset(XHCIState *xhci, int v) le32_to_cpus(&seg.addr_high); le32_to_cpus(&seg.size); if (seg.size < 16 || seg.size > 4096) { - fprintf(stderr, "xhci: invalid value for segment size: %d\n", seg.size); + DPRINTF("xhci: invalid value for segment size: %d\n", seg.size); xhci_die(xhci); return; } @@ -1086,7 +1101,7 @@ static void xhci_run(XHCIState *xhci) { trace_usb_xhci_run(); xhci->usbsts &= ~USBSTS_HCH; - xhci->mfindex_start = qemu_get_clock_ns(vm_clock); + xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } static void xhci_stop(XHCIState *xhci) @@ -1116,33 +1131,130 @@ static void xhci_reset_streams(XHCIEPContext *epctx) for (i = 0; i < epctx->nr_pstreams; i++) { epctx->pstreams[i].sct = -1; - g_free(epctx->pstreams[i].sstreams); } } static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) { assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << epctx->max_pstreams; + epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1); epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); } static void xhci_free_streams(XHCIEPContext *epctx) { - int i; - assert(epctx->pstreams != NULL); - if (!epctx->lsa) { - for (i = 0; i < epctx->nr_pstreams; i++) { - g_free(epctx->pstreams[i].sstreams); - } - } g_free(epctx->pstreams); epctx->pstreams = NULL; epctx->nr_pstreams = 0; } +static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, + unsigned int slotid, + uint32_t epmask, + XHCIEPContext **epctxs, + USBEndpoint **eps) +{ + XHCISlot *slot; + XHCIEPContext *epctx; + USBEndpoint *ep; + int i, j; + + assert(slotid >= 1 && slotid <= xhci->numslots); + + slot = &xhci->slots[slotid - 1]; + + for (i = 2, j = 0; i <= 31; i++) { + if (!(epmask & (1 << i))) { + continue; + } + + epctx = slot->eps[i - 1]; + ep = xhci_epid_to_usbep(xhci, slotid, i); + if (!epctx || !epctx->nr_pstreams || !ep) { + continue; + } + + if (epctxs) { + epctxs[j] = epctx; + } + eps[j++] = ep; + } + return j; +} + +static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + USBEndpoint *eps[30]; + int nr_eps; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps); + if (nr_eps) { + usb_device_free_streams(eps[0]->dev, eps, nr_eps); + } +} + +static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + XHCIEPContext *epctxs[30]; + USBEndpoint *eps[30]; + int i, r, nr_eps, req_nr_streams, dev_max_streams; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs, + eps); + if (nr_eps == 0) { + return CC_SUCCESS; + } + + req_nr_streams = epctxs[0]->nr_pstreams; + dev_max_streams = eps[0]->max_streams; + + for (i = 1; i < nr_eps; i++) { + /* + * HdG: I don't expect these to ever trigger, but if they do we need + * to come up with another solution, ie group identical endpoints + * together and make an usb_device_alloc_streams call per group. + */ + if (epctxs[i]->nr_pstreams != req_nr_streams) { + FIXME("guest streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + if (eps[i]->max_streams != dev_max_streams) { + FIXME("device streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + } + + /* + * max-streams in both the device descriptor and in the controller is a + * power of 2. But stream id 0 is reserved, so if a device can do up to 4 + * streams the guest will ask for 5 rounded up to the next power of 2 which + * becomes 8. For emulated devices usb_device_alloc_streams is a nop. + * + * For redirected devices however this is an issue, as there we must ask + * the real xhci controller to alloc streams, and the host driver for the + * real xhci controller will likely disallow allocating more streams then + * the device can handle. + * + * So we limit the requested nr_streams to the maximum number the device + * can handle. + */ + if (req_nr_streams > dev_max_streams) { + req_nr_streams = dev_max_streams; + } + + r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams); + if (r != 0) { + DPRINTF("xhci: alloc streams failed\n"); + return CC_RESOURCE_ERROR; + } + + return CC_SUCCESS; +} + static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, unsigned int streamid, uint32_t *cc_error) @@ -1164,8 +1276,6 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, if (sctx->sct == -1) { xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx)); - fprintf(stderr, "%s: init sctx #%d @ " DMA_ADDR_FMT ": %08x %08x\n", - __func__, streamid, sctx->pctx, ctx[0], ctx[1]); sct = (ctx[0] >> 1) & 0x07; if (epctx->lsa && sct != 1) { *cc_error = CC_INVALID_STREAM_TYPE_ERROR; @@ -1181,6 +1291,7 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, XHCIStreamContext *sctx, uint32_t state) { + XHCIRing *ring = NULL; uint32_t ctx[5]; uint32_t ctx2[2]; @@ -1191,6 +1302,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, /* update ring dequeue ptr */ if (epctx->nr_pstreams) { if (sctx != NULL) { + ring = &sctx->ring; xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); ctx2[0] &= 0xe; ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; @@ -1198,13 +1310,22 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); } } else { - ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; - ctx[3] = (epctx->ring.dequeue >> 16) >> 16; + ring = &epctx->ring; + } + if (ring) { + ctx[2] = ring->dequeue | ring->ccs; + ctx[3] = (ring->dequeue >> 16) >> 16; + DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", epctx->pctx, state, ctx[3], ctx[2]); } xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); + if (epctx->state != state) { + trace_usb_xhci_ep_state(epctx->slotid, epctx->epid, + ep_state_name(epctx->state), + ep_state_name(state)); + } epctx->state = state; } @@ -1227,9 +1348,12 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, epctx->epid = epid; for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { + epctx->transfers[i].xhci = xhci; + epctx->transfers[i].slotid = slotid; + epctx->transfers[i].epid = epid; usb_packet_init(&epctx->transfers[i].packet); } - epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx); + epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); return epctx; } @@ -1257,7 +1381,7 @@ static void xhci_init_epctx(XHCIEPContext *epctx, epctx->ring.ccs = ctx[2] & 1; } - epctx->interval = 1 << (ctx[0] >> 16) & 0xff; + epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, @@ -1289,24 +1413,28 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } -static int xhci_ep_nuke_one_xfer(XHCITransfer *t) +static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) { int killed = 0; + if (report && (t->running_async || t->running_retry)) { + t->status = report; + xhci_xfer_report(t); + } + if (t->running_async) { usb_cancel_packet(&t->packet); t->running_async = 0; - t->cancelled = 1; - DPRINTF("xhci: cancelling transfer, waiting for it to complete\n"); killed = 1; } if (t->running_retry) { XHCIEPContext *epctx = t->xhci->slots[t->slotid-1].eps[t->epid-1]; if (epctx) { epctx->retry = NULL; - qemu_del_timer(epctx->kick_timer); + timer_del(epctx->kick_timer); } t->running_retry = 0; + killed = 1; } if (t->trbs) { g_free(t->trbs); @@ -1319,7 +1447,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t) } static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, - unsigned int epid) + unsigned int epid, TRBCCode report) { XHCISlot *slot; XHCIEPContext *epctx; @@ -1340,13 +1468,15 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { - if (epctx->transfers[xferi].packet.ep) { - ep = epctx->transfers[xferi].packet.ep; + killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); + if (killed) { + report = 0; /* Only report once */ } - killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]); epctx->transfers[xferi].packet.ep = NULL; xferi = (xferi + 1) % TD_QUEUE; } + + ep = xhci_epid_to_usbep(xhci, slotid, epid); if (ep) { usb_device_ep_stopped(ep->dev, ep); } @@ -1358,6 +1488,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, { XHCISlot *slot; XHCIEPContext *epctx; + int i; trace_usb_xhci_ep_disable(slotid, epid); assert(slotid >= 1 && slotid <= xhci->numslots); @@ -1370,7 +1501,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } - xhci_ep_nuke_xfers(xhci, slotid, epid); + xhci_ep_nuke_xfers(xhci, slotid, epid, 0); epctx = slot->eps[epid-1]; @@ -1378,9 +1509,13 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, xhci_free_streams(epctx); } + for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { + usb_packet_cleanup(&epctx->transfers[i].packet); + } + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); - qemu_free_timer(epctx->kick_timer); + timer_free(epctx->kick_timer); g_free(epctx); slot->eps[epid-1] = NULL; @@ -1397,7 +1532,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, assert(slotid >= 1 && slotid <= xhci->numslots); if (epid < 1 || epid > 31) { - fprintf(stderr, "xhci: bad ep %d\n", epid); + DPRINTF("xhci: bad ep %d\n", epid); return CC_TRB_ERROR; } @@ -1408,8 +1543,8 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, return CC_EP_NOT_ENABLED_ERROR; } - if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) { - fprintf(stderr, "xhci: FIXME: endpoint stopped w/ xfers running, " + if (xhci_ep_nuke_xfers(xhci, slotid, epid, CC_STOPPED) > 0) { + DPRINTF("xhci: FIXME: endpoint stopped w/ xfers running, " "data might be lost\n"); } @@ -1434,7 +1569,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, assert(slotid >= 1 && slotid <= xhci->numslots); if (epid < 1 || epid > 31) { - fprintf(stderr, "xhci: bad ep %d\n", epid); + DPRINTF("xhci: bad ep %d\n", epid); return CC_TRB_ERROR; } @@ -1448,13 +1583,13 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; if (epctx->state != EP_HALTED) { - fprintf(stderr, "xhci: reset EP while EP %d not halted (%d)\n", + DPRINTF("xhci: reset EP while EP %d not halted (%d)\n", epid, epctx->state); return CC_CONTEXT_STATE_ERROR; } - if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) { - fprintf(stderr, "xhci: FIXME: endpoint reset w/ xfers running, " + if (xhci_ep_nuke_xfers(xhci, slotid, epid, 0) > 0) { + DPRINTF("xhci: FIXME: endpoint reset w/ xfers running, " "data might be lost\n"); } @@ -1465,7 +1600,8 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, } if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev) { + !xhci->slots[slotid-1].uport->dev || + !xhci->slots[slotid-1].uport->dev->attached) { return CC_USB_TRANSACTION_ERROR; } @@ -1490,7 +1626,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, assert(slotid >= 1 && slotid <= xhci->numslots); if (epid < 1 || epid > 31) { - fprintf(stderr, "xhci: bad ep %d\n", epid); + DPRINTF("xhci: bad ep %d\n", epid); return CC_TRB_ERROR; } @@ -1507,7 +1643,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; if (epctx->state != EP_STOPPED) { - fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid); + DPRINTF("xhci: set EP dequeue pointer while EP %d not stopped\n", epid); return CC_CONTEXT_STATE_ERROR; } @@ -1549,7 +1685,7 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) switch (TRB_TYPE(*trb)) { case TR_DATA: if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n"); + DPRINTF("xhci: data direction mismatch for TR_DATA\n"); goto err; } /* fallthrough */ @@ -1559,7 +1695,7 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) chunk = trb->status & 0x1ffff; if (trb->control & TRB_TR_IDT) { if (chunk > 8 || in_xfer) { - fprintf(stderr, "xhci: invalid immediate data TRB\n"); + DPRINTF("xhci: invalid immediate data TRB\n"); goto err; } qemu_sglist_add(&xfer->sgl, trb->addr, chunk); @@ -1678,7 +1814,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, static int xhci_setup_packet(XHCITransfer *xfer) { XHCIState *xhci = xfer->xhci; - USBDevice *dev; USBEndpoint *ep; int dir; @@ -1686,15 +1821,13 @@ static int xhci_setup_packet(XHCITransfer *xfer) if (xfer->packet.ep) { ep = xfer->packet.ep; - dev = ep->dev; } else { - if (!xhci->slots[xfer->slotid-1].uport) { - fprintf(stderr, "xhci: slot %d has no device\n", + ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid); + if (!ep) { + DPRINTF("xhci: slot %d has no device\n", xfer->slotid); return -1; } - dev = xhci->slots[xfer->slotid-1].uport->dev; - ep = usb_ep_get(dev, dir, xfer->epid >> 1); } xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ @@ -1702,7 +1835,7 @@ static int xhci_setup_packet(XHCITransfer *xfer) xfer->trbs[0].addr, false, xfer->int_req); usb_packet_map(&xfer->packet, &xfer->sgl); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, dev->addr, ep->nr); + xfer->packet.pid, ep->dev->addr, ep->nr); return 0; } @@ -1713,14 +1846,12 @@ static int xhci_complete_packet(XHCITransfer *xfer) xfer->running_async = 1; xfer->running_retry = 0; xfer->complete = 0; - xfer->cancelled = 0; return 0; } else if (xfer->packet.status == USB_RET_NAK) { trace_usb_xhci_xfer_nak(xfer); xfer->running_async = 0; xfer->running_retry = 1; xfer->complete = 0; - xfer->cancelled = 0; return 0; } else { xfer->running_async = 0; @@ -1756,7 +1887,7 @@ static int xhci_complete_packet(XHCITransfer *xfer) xhci_stall_ep(xfer); break; default: - fprintf(stderr, "%s: FIXME: status = %d\n", __func__, + DPRINTF("%s: FIXME: status = %d\n", __func__, xfer->packet.status); FIXME("unhandled USB_RET_*"); } @@ -1780,21 +1911,21 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) /* do some sanity checks */ if (TRB_TYPE(*trb_setup) != TR_SETUP) { - fprintf(stderr, "xhci: ep0 first TD not SETUP: %d\n", + DPRINTF("xhci: ep0 first TD not SETUP: %d\n", TRB_TYPE(*trb_setup)); return -1; } if (TRB_TYPE(*trb_status) != TR_STATUS) { - fprintf(stderr, "xhci: ep0 last TD not STATUS: %d\n", + DPRINTF("xhci: ep0 last TD not STATUS: %d\n", TRB_TYPE(*trb_status)); return -1; } if (!(trb_setup->control & TRB_TR_IDT)) { - fprintf(stderr, "xhci: Setup TRB doesn't have IDT set\n"); + DPRINTF("xhci: Setup TRB doesn't have IDT set\n"); return -1; } if ((trb_setup->status & 0x1ffff) != 8) { - fprintf(stderr, "xhci: Setup TRB has bad length (%d)\n", + DPRINTF("xhci: Setup TRB has bad length (%d)\n", (trb_setup->status & 0x1ffff)); return -1; } @@ -1803,6 +1934,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->in_xfer = bmRequestType & USB_DIR_IN; xfer->iso_xfer = false; + xfer->timed_xfer = false; if (xhci_setup_packet(xfer) < 0) { return -1; @@ -1818,6 +1950,17 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) return 0; } +static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx, uint64_t mfindex) +{ + uint64_t asap = ((mfindex + epctx->interval - 1) & + ~(epctx->interval-1)); + uint64_t kick = epctx->mfindex_last + epctx->interval; + + assert(epctx->interval != 0); + xfer->mfindex_kick = MAX(asap, kick); +} + static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx, uint64_t mfindex) { @@ -1831,25 +1974,25 @@ static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, xfer->mfindex_kick = asap; } } else { - xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) - & TRB_TR_FRAMEID_MASK; + xfer->mfindex_kick = ((xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) + & TRB_TR_FRAMEID_MASK) << 3; xfer->mfindex_kick |= mfindex & ~0x3fff; - if (xfer->mfindex_kick < mfindex) { + if (xfer->mfindex_kick + 0x100 < mfindex) { xfer->mfindex_kick += 0x4000; } } } -static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) +static void xhci_check_intr_iso_kick(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx, uint64_t mfindex) { if (xfer->mfindex_kick > mfindex) { - qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) + + timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (xfer->mfindex_kick - mfindex) * 125000); xfer->running_retry = 1; } else { epctx->mfindex_last = xfer->mfindex_kick; - qemu_del_timer(epctx->kick_timer); + timer_del(epctx->kick_timer); xfer->running_retry = 0; } } @@ -1866,26 +2009,36 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx switch(epctx->type) { case ET_INTR_OUT: case ET_INTR_IN: + xfer->pkts = 0; + xfer->iso_xfer = false; + xfer->timed_xfer = true; + mfindex = xhci_mfindex_get(xhci); + xhci_calc_intr_kick(xhci, xfer, epctx, mfindex); + xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); + if (xfer->running_retry) { + return -1; + } + break; case ET_BULK_OUT: case ET_BULK_IN: xfer->pkts = 0; xfer->iso_xfer = false; + xfer->timed_xfer = false; break; case ET_ISO_OUT: case ET_ISO_IN: xfer->pkts = 1; xfer->iso_xfer = true; + xfer->timed_xfer = true; mfindex = xhci_mfindex_get(xhci); xhci_calc_iso_kick(xhci, xfer, epctx, mfindex); - xhci_check_iso_kick(xhci, xfer, epctx, mfindex); + xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); if (xfer->running_retry) { return -1; } break; default: - fprintf(stderr, "xhci: unknown or unhandled EP " - "(type %d, in %d, ep %02x)\n", - epctx->type, xfer->in_xfer, xfer->epid); + trace_usb_xhci_unimplemented("endpoint type", epctx->type); return -1; } @@ -1923,28 +2076,41 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, assert(epid >= 1 && epid <= 31); if (!xhci->slots[slotid-1].enabled) { - fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid); + DPRINTF("xhci: xhci_kick_ep for disabled slot %d\n", slotid); return; } epctx = xhci->slots[slotid-1].eps[epid-1]; if (!epctx) { - fprintf(stderr, "xhci: xhci_kick_ep for disabled endpoint %d,%d\n", + DPRINTF("xhci: xhci_kick_ep for disabled endpoint %d,%d\n", epid, slotid); return; } + /* If the device has been detached, but the guest has not noticed this + yet the 2 above checks will succeed, but we must NOT continue */ + if (!xhci->slots[slotid - 1].uport || + !xhci->slots[slotid - 1].uport->dev || + !xhci->slots[slotid - 1].uport->dev->attached) { + return; + } + if (epctx->retry) { XHCITransfer *xfer = epctx->retry; trace_usb_xhci_xfer_retry(xfer); assert(xfer->running_retry); - if (xfer->iso_xfer) { - /* retry delayed iso transfer */ + if (xfer->timed_xfer) { + /* time to kick the transfer? */ mfindex = xhci_mfindex_get(xhci); - xhci_check_iso_kick(xhci, xfer, epctx, mfindex); + xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); if (xfer->running_retry) { return; } + xfer->timed_xfer = 0; + xfer->running_retry = 1; + } + if (xfer->iso_xfer) { + /* retry iso transfer */ if (xhci_setup_packet(xfer) < 0) { return; } @@ -2013,9 +2179,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, for (i = 0; i < length; i++) { assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); } - xfer->xhci = xhci; - xfer->epid = epid; - xfer->slotid = slotid; xfer->streamid = streamid; if (epid == 1) { @@ -2023,15 +2186,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; ep = xfer->packet.ep; } else { - fprintf(stderr, "xhci: error firing CTL transfer\n"); + DPRINTF("xhci: error firing CTL transfer\n"); } } else { if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - ep = xfer->packet.ep; } else { - if (!xfer->iso_xfer) { - fprintf(stderr, "xhci: error firing data transfer\n"); + if (!xfer->timed_xfer) { + DPRINTF("xhci: error firing data transfer\n"); } } } @@ -2045,6 +2207,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, break; } } + + ep = xhci_epid_to_usbep(xhci, slotid, epid); if (ep) { usb_device_flush_ep_queue(ep->dev, ep); } @@ -2076,6 +2240,7 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) xhci->slots[slotid-1].enabled = 0; xhci->slots[slotid-1].addressed = 0; + xhci->slots[slotid-1].uport = NULL; return CC_SUCCESS; } @@ -2118,7 +2283,6 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, int i; TRBCCode res; - trace_usb_xhci_slot_address(slotid); assert(slotid >= 1 && slotid <= xhci->numslots); dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); @@ -2132,7 +2296,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) { - fprintf(stderr, "xhci: invalid input context control %08x %08x\n", + DPRINTF("xhci: invalid input context control %08x %08x\n", ictl_ctx[0], ictl_ctx[1]); return CC_TRB_ERROR; } @@ -2148,13 +2312,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, uport = xhci_lookup_uport(xhci, slot_ctx); if (uport == NULL) { - fprintf(stderr, "xhci: port not found\n"); + DPRINTF("xhci: port not found\n"); return CC_TRB_ERROR; } + trace_usb_xhci_slot_address(slotid, uport->path); dev = uport->dev; - if (!dev) { - fprintf(stderr, "xhci: port %s not connected\n", uport->path); + if (!dev || !dev->attached) { + DPRINTF("xhci: port %s not connected\n", uport->path); return CC_USB_TRANSACTION_ERROR; } @@ -2163,7 +2328,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, continue; } if (xhci->slots[i].uport == uport) { - fprintf(stderr, "xhci: port %s already assigned to slot %d\n", + DPRINTF("xhci: port %s already assigned to slot %d\n", uport->path, i+1); return CC_TRB_ERROR; } @@ -2247,7 +2412,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) { - fprintf(stderr, "xhci: invalid input context control %08x %08x\n", + DPRINTF("xhci: invalid input context control %08x %08x\n", ictl_ctx[0], ictl_ctx[1]); return CC_TRB_ERROR; } @@ -2256,10 +2421,12 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) { - fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]); + DPRINTF("xhci: invalid slot state %08x\n", slot_ctx[3]); return CC_CONTEXT_STATE_ERROR; } + xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]); + for (i = 2; i <= 31; i++) { if (ictl_ctx[0] & (1<<i)) { xhci_disable_ep(xhci, slotid, i); @@ -2281,6 +2448,16 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, } } + res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); + if (res != CC_SUCCESS) { + for (i = 2; i <= 31; i++) { + if (ictl_ctx[1] & (1 << i)) { + xhci_disable_ep(xhci, slotid, i); + } + } + return res; + } + slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); @@ -2317,7 +2494,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid, xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) { - fprintf(stderr, "xhci: invalid input context control %08x %08x\n", + DPRINTF("xhci: invalid input context control %08x %08x\n", ictl_ctx[0], ictl_ctx[1]); return CC_TRB_ERROR; } @@ -2396,11 +2573,11 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr unsigned int slotid; slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK; if (slotid < 1 || slotid > xhci->numslots) { - fprintf(stderr, "xhci: bad slot id %d\n", slotid); + DPRINTF("xhci: bad slot id %d\n", slotid); event->ccode = CC_TRB_ERROR; return 0; } else if (!xhci->slots[slotid-1].enabled) { - fprintf(stderr, "xhci: slot id %d not enabled\n", slotid); + DPRINTF("xhci: slot id %d not enabled\n", slotid); event->ccode = CC_SLOT_NOT_ENABLED_ERROR; return 0; } @@ -2423,7 +2600,7 @@ static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) for (ep = 0; ep < 31; ep++) { if (xhci->slots[slot].eps[ep]) { - xhci_ep_nuke_xfers(xhci, slot+1, ep+1); + xhci_ep_nuke_xfers(xhci, slot + 1, ep + 1, 0); } } xhci->slots[slot].uport = NULL; @@ -2516,7 +2693,7 @@ static void xhci_process_commands(XHCIState *xhci) } } if (i >= xhci->numslots) { - fprintf(stderr, "xhci: no device slots available\n"); + DPRINTF("xhci: no device slots available\n"); event.ccode = CC_NO_SLOTS_ERROR; } else { slotid = i+1; @@ -2708,7 +2885,7 @@ static void xhci_reset(DeviceState *dev) trace_usb_xhci_reset(); if (!(xhci->usbsts & USBSTS_HCH)) { - fprintf(stderr, "xhci: reset while running!\n"); + DPRINTF("xhci: reset while running!\n"); } xhci->usbcmd = 0; @@ -2745,7 +2922,7 @@ static void xhci_reset(DeviceState *dev) xhci->intr[i].ev_buffer_get = 0; } - xhci->mfindex_start = qemu_get_clock_ns(vm_clock); + xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); xhci_mfwrap_update(xhci); } @@ -2886,7 +3063,7 @@ static void xhci_port_write(void *ptr, hwaddr reg, /* windows does this for some reason, don't spam stderr */ break; default: - fprintf(stderr, "%s: ignore pls write (old %d, new %d)\n", + DPRINTF("%s: ignore pls write (old %d, new %d)\n", __func__, old_pls, new_pls); break; } @@ -2963,6 +3140,14 @@ static void xhci_oper_write(void *ptr, hwaddr reg, } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) { xhci_stop(xhci); } + if (val & USBCMD_CSS) { + /* save state */ + xhci->usbsts &= ~USBSTS_SRE; + } + if (val & USBCMD_CRS) { + /* restore state */ + xhci->usbsts |= USBSTS_SRE; + } xhci->usbcmd = val & 0xc0f; xhci_mfwrap_update(xhci); if (val & USBCMD_HCRST) { @@ -3129,7 +3314,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, trace_usb_xhci_doorbell_write(reg, val); if (!xhci_running(xhci)) { - fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n"); + DPRINTF("xhci: wrote doorbell while xHC stopped or paused\n"); return; } @@ -3139,16 +3324,16 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, if (val == 0) { xhci_process_commands(xhci); } else { - fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", + DPRINTF("xhci: bad doorbell 0 write: 0x%x\n", (uint32_t)val); } } else { epid = val & 0xff; streamid = (val >> 16) & 0xffff; if (reg > xhci->numslots) { - fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg); + DPRINTF("xhci: bad doorbell %d\n", (int)reg); } else if (epid > 31) { - fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", + DPRINTF("xhci: bad doorbell %d write: 0x%x\n", (int)reg, (uint32_t)val); } else { xhci_kick_ep(xhci, reg, epid, streamid); @@ -3238,7 +3423,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet) XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - xhci_ep_nuke_one_xfer(xfer); + xhci_ep_nuke_one_xfer(xfer, 0); return; } xhci_complete_packet(xfer); @@ -3273,6 +3458,19 @@ static int xhci_find_epid(USBEndpoint *ep) } } +static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, + unsigned int slotid, unsigned int epid) +{ + assert(slotid >= 1 && slotid <= xhci->numslots); + + if (!xhci->slots[slotid - 1].uport) { + return NULL; + } + + return usb_ep_get(xhci->slots[slotid - 1].uport->dev, + (epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT, epid >> 1); +} + static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, unsigned int stream) { @@ -3309,7 +3507,7 @@ static void usb_xhci_init(XHCIState *xhci) usbports = MAX(xhci->numports_2, xhci->numports_3); xhci->numports = xhci->numports_2 + xhci->numports_3; - usb_bus_new(&xhci->bus, &xhci_bus_ops, dev); + usb_bus_new(&xhci->bus, sizeof(xhci->bus), &xhci_bus_ops, dev); for (i = 0; i < usbports; i++) { speedmask = 0; @@ -3366,9 +3564,7 @@ static int usb_xhci_initfn(struct PCIDevice *dev) xhci->numslots = 1; } - xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci); - - xhci->irq = dev->irq[0]; + xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS); memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci, @@ -3438,7 +3634,7 @@ static int usb_xhci_post_load(void *opaque, int version_id) slot->uport = xhci_lookup_uport(xhci, slot_ctx); assert(slot->uport && slot->uport->dev); - for (epid = 1; epid <= 32; epid++) { + for (epid = 1; epid <= 31; epid++) { pctx = slot->ctx + 32 * epid; xhci_dma_read_u32s(xhci, pctx, ep_ctx, sizeof(ep_ctx)); state = ep_ctx[0] & EP_STATE_MASK; @@ -3451,7 +3647,7 @@ static int usb_xhci_post_load(void *opaque, int version_id) epctx->state = state; if (state == EP_RUNNING) { /* kick endpoint after vmload is finished */ - qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock)); + timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } } } @@ -3600,6 +3796,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xhci; dc->props = xhci_properties; dc->reset = xhci_reset; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->init = usb_xhci_initfn; k->vendor_id = PCI_VENDOR_ID_NEC; @@ -3607,7 +3804,6 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; k->revision = 0x03; k->is_express = 1; - k->no_hotplug = 1; } static const TypeInfo xhci_info = { |