#else
#define DPRINTF(...) do {} while (0)
#endif
-#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
- __func__, __LINE__); abort(); } while (0)
+#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \
+ __func__, __LINE__, _msg); abort(); } while (0)
#define MAXPORTS_2 15
#define MAXPORTS_3 15
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
typedef struct XHCIState XHCIState;
+typedef struct XHCIStreamContext XHCIStreamContext;
+typedef struct XHCIEPContext XHCIEPContext;
#define get_field(data, field) \
(((data) >> field##_SHIFT) & field##_MASK)
} EPType;
typedef struct XHCIRing {
- dma_addr_t base;
dma_addr_t dequeue;
bool ccs;
} XHCIRing;
unsigned int iso_pkts;
unsigned int slotid;
unsigned int epid;
+ unsigned int streamid;
bool in_xfer;
bool iso_xfer;
uint64_t mfindex_kick;
} XHCITransfer;
-typedef struct XHCIEPContext {
+struct XHCIStreamContext {
+ dma_addr_t pctx;
+ unsigned int sct;
+ XHCIRing ring;
+ XHCIStreamContext *sstreams;
+};
+
+struct XHCIEPContext {
XHCIState *xhci;
unsigned int slotid;
unsigned int epid;
unsigned int max_psize;
uint32_t state;
+ /* streams */
+ unsigned int max_pstreams;
+ bool lsa;
+ unsigned int nr_pstreams;
+ XHCIStreamContext *pstreams;
+
/* iso xfer scheduling */
unsigned int interval;
int64_t mfindex_last;
QEMUTimer *kick_timer;
-} XHCIEPContext;
+};
typedef struct XHCISlot {
bool enabled;
+ bool addressed;
dma_addr_t ctx;
USBPort *uport;
- unsigned int devaddr;
XHCIEPContext * eps[31];
} XHCISlot;
} XHCIInterrupter;
struct XHCIState {
- PCIDevice pci_dev;
+ /*< private >*/
+ PCIDevice parent_obj;
+ /*< public >*/
+
USBBus bus;
qemu_irq irq;
MemoryRegion mem;
MemoryRegion mem_oper;
MemoryRegion mem_runtime;
MemoryRegion mem_doorbell;
- const char *name;
- unsigned int devaddr;
/* properties */
uint32_t numports_2;
XHCIRing cmd_ring;
};
+#define TYPE_XHCI "nec-usb-xhci"
+
+#define XHCI(obj) \
+ OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI)
+
typedef struct XHCIEvRingSeg {
uint32_t addr_low;
uint32_t addr_high;
};
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
- unsigned int epid);
+ unsigned int epid, unsigned int streamid);
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid);
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
[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) {
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;
}
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);
}
}
assert((len % sizeof(uint32_t)) == 0);
- pci_dma_read(&xhci->pci_dev, addr, buf, len);
+ pci_dma_read(PCI_DEVICE(xhci), addr, buf, len);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
buf[i] = le32_to_cpu(buf[i]);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
tmp[i] = cpu_to_le32(buf[i]);
}
- pci_dma_write(&xhci->pci_dev, addr, tmp, len);
+ pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len);
}
static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
static void xhci_intx_update(XHCIState *xhci)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
int level = 0;
- if (msix_enabled(&xhci->pci_dev) ||
- msi_enabled(&xhci->pci_dev)) {
+ if (msix_enabled(pci_dev) ||
+ msi_enabled(pci_dev)) {
return;
}
static void xhci_msix_update(XHCIState *xhci, int v)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
bool enabled;
- if (!msix_enabled(&xhci->pci_dev)) {
+ if (!msix_enabled(pci_dev)) {
return;
}
if (enabled) {
trace_usb_xhci_irq_msix_use(v);
- msix_vector_use(&xhci->pci_dev, v);
+ msix_vector_use(pci_dev, v);
xhci->intr[v].msix_used = true;
} else {
trace_usb_xhci_irq_msix_unuse(v);
- msix_vector_unuse(&xhci->pci_dev, v);
+ msix_vector_unuse(pci_dev, v);
xhci->intr[v].msix_used = false;
}
}
static void xhci_intr_raise(XHCIState *xhci, int v)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
+
xhci->intr[v].erdp_low |= ERDP_EHB;
xhci->intr[v].iman |= IMAN_IP;
xhci->usbsts |= USBSTS_EINT;
return;
}
- if (msix_enabled(&xhci->pci_dev)) {
+ if (msix_enabled(pci_dev)) {
trace_usb_xhci_irq_msix(v);
- msix_notify(&xhci->pci_dev, v);
+ msix_notify(pci_dev, v);
return;
}
- if (msi_enabled(&xhci->pci_dev)) {
+ if (msi_enabled(pci_dev)) {
trace_usb_xhci_irq_msi(v);
- msi_notify(&xhci->pci_dev, v);
+ msi_notify(pci_dev, v);
return;
}
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
XHCIInterrupter *intr = &xhci->intr[v];
XHCITRB ev_trb;
dma_addr_t addr;
ev_trb.status, ev_trb.control);
addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
- pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
+ pci_dma_write(pci_dev, addr, &ev_trb, TRB_SIZE);
intr->er_ep_idx++;
if (intr->er_ep_idx >= intr->er_size) {
static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
dma_addr_t base)
{
- ring->base = base;
ring->dequeue = base;
ring->ccs = 1;
}
static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
dma_addr_t *addr)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
+
while (1) {
TRBType type;
- pci_dma_read(&xhci->pci_dev, ring->dequeue, trb, TRB_SIZE);
+ pci_dma_read(pci_dev, ring->dequeue, trb, TRB_SIZE);
trb->addr = ring->dequeue;
trb->ccs = ring->ccs;
le64_to_cpus(&trb->parameter);
static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
XHCITRB trb;
int length = 0;
dma_addr_t dequeue = ring->dequeue;
while (1) {
TRBType type;
- pci_dma_read(&xhci->pci_dev, dequeue, &trb, TRB_SIZE);
+ pci_dma_read(pci_dev, dequeue, &trb, TRB_SIZE);
le64_to_cpus(&trb.parameter);
le32_to_cpus(&trb.status);
le32_to_cpus(&trb.control);
return;
}
dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high);
- pci_dma_read(&xhci->pci_dev, erstba, &seg, sizeof(seg));
+ pci_dma_read(PCI_DEVICE(xhci), erstba, &seg, sizeof(seg));
le32_to_cpus(&seg.addr_low);
le32_to_cpus(&seg.addr_high);
le32_to_cpus(&seg.size);
{
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)
xhci->crcr_low &= ~CRCR_CRR;
}
+static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count,
+ dma_addr_t base)
+{
+ XHCIStreamContext *stctx;
+ unsigned int i;
+
+ stctx = g_new0(XHCIStreamContext, count);
+ for (i = 0; i < count; i++) {
+ stctx[i].pctx = base + i * 16;
+ stctx[i].sct = -1;
+ }
+ return stctx;
+}
+
+static void xhci_reset_streams(XHCIEPContext *epctx)
+{
+ unsigned int i;
+
+ 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->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 XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx,
+ unsigned int streamid,
+ uint32_t *cc_error)
+{
+ XHCIStreamContext *sctx;
+ dma_addr_t base;
+ uint32_t ctx[2], sct;
+
+ assert(streamid != 0);
+ if (epctx->lsa) {
+ if (streamid >= epctx->nr_pstreams) {
+ *cc_error = CC_INVALID_STREAM_ID_ERROR;
+ return NULL;
+ }
+ sctx = epctx->pstreams + streamid;
+ } else {
+ FIXME("secondary streams not implemented yet");
+ }
+
+ if (sctx->sct == -1) {
+ xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx));
+ sct = (ctx[0] >> 1) & 0x07;
+ if (epctx->lsa && sct != 1) {
+ *cc_error = CC_INVALID_STREAM_TYPE_ERROR;
+ return NULL;
+ }
+ sctx->sct = sct;
+ base = xhci_addr64(ctx[0] & ~0xf, ctx[1]);
+ xhci_ring_init(epctx->xhci, &sctx->ring, base);
+ }
+ return sctx;
+}
+
static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
- uint32_t state)
+ XHCIStreamContext *sctx, uint32_t state)
{
uint32_t ctx[5];
+ uint32_t ctx2[2];
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
ctx[0] &= ~EP_STATE_MASK;
ctx[0] |= state;
- ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
- ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
- DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
- epctx->pctx, state, ctx[3], ctx[2]);
+
+ /* update ring dequeue ptr */
+ if (epctx->nr_pstreams) {
+ if (sctx != NULL) {
+ xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2));
+ ctx2[0] &= 0xe;
+ ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs;
+ ctx2[1] = (sctx->ring.dequeue >> 16) >> 16;
+ 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;
+ 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;
}
static void xhci_ep_kick_timer(void *opaque)
{
XHCIEPContext *epctx = opaque;
- xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
+ xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
}
-static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
- unsigned int epid, dma_addr_t pctx,
- uint32_t *ctx)
+static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
+ unsigned int slotid,
+ unsigned int epid)
{
- XHCISlot *slot;
XHCIEPContext *epctx;
- dma_addr_t dequeue;
int i;
- trace_usb_xhci_ep_enable(slotid, epid);
- assert(slotid >= 1 && slotid <= xhci->numslots);
- assert(epid >= 1 && epid <= 31);
-
- slot = &xhci->slots[slotid-1];
- if (slot->eps[epid-1]) {
- xhci_disable_ep(xhci, slotid, epid);
- }
-
- epctx = g_malloc(sizeof(XHCIEPContext));
- memset(epctx, 0, sizeof(XHCIEPContext));
+ epctx = g_new0(XHCIEPContext, 1);
epctx->xhci = xhci;
epctx->slotid = slotid;
epctx->epid = epid;
- slot->eps[epid-1] = epctx;
+ for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+ usb_packet_init(&epctx->transfers[i].packet);
+ }
+ epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx);
+
+ return epctx;
+}
+
+static void xhci_init_epctx(XHCIEPContext *epctx,
+ dma_addr_t pctx, uint32_t *ctx)
+{
+ dma_addr_t dequeue;
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
- xhci_ring_init(xhci, &epctx->ring, dequeue);
- epctx->ring.ccs = ctx[2] & 1;
epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
epctx->pctx = pctx;
epctx->max_psize = ctx[1]>>16;
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
+ epctx->max_pstreams = (ctx[0] >> 10) & 0xf;
+ epctx->lsa = (ctx[0] >> 15) & 1;
DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
epid/2, epid%2, epctx->max_psize);
- for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
- usb_packet_init(&epctx->transfers[i].packet);
+ if (epctx->max_pstreams) {
+ xhci_alloc_streams(epctx, dequeue);
+ } else {
+ xhci_ring_init(epctx->xhci, &epctx->ring, dequeue);
+ epctx->ring.ccs = ctx[2] & 1;
}
epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
+}
+
+static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+ unsigned int epid, dma_addr_t pctx,
+ uint32_t *ctx)
+{
+ XHCISlot *slot;
+ XHCIEPContext *epctx;
+
+ trace_usb_xhci_ep_enable(slotid, epid);
+ assert(slotid >= 1 && slotid <= xhci->numslots);
+ assert(epid >= 1 && epid <= 31);
+
+ slot = &xhci->slots[slotid-1];
+ if (slot->eps[epid-1]) {
+ xhci_disable_ep(xhci, slotid, epid);
+ }
+
+ epctx = xhci_alloc_epctx(xhci, slotid, epid);
+ slot->eps[epid-1] = epctx;
+ xhci_init_epctx(epctx, pctx, ctx);
+
epctx->mfindex_last = 0;
- epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx);
epctx->state = EP_RUNNING;
ctx[0] &= ~EP_STATE_MASK;
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;
}
ep = epctx->transfers[xferi].packet.ep;
}
killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
+ epctx->transfers[xferi].packet.ep = NULL;
xferi = (xferi + 1) % TD_QUEUE;
}
if (ep) {
epctx = slot->eps[epid-1];
- xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+ if (epctx->nr_pstreams) {
+ xhci_free_streams(epctx);
+ }
- qemu_free_timer(epctx->kick_timer);
+ xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
+
+ timer_free(epctx->kick_timer);
g_free(epctx);
slot->eps[epid-1] = NULL;
epctx = slot->eps[epid-1];
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
+
+ if (epctx->nr_pstreams) {
+ xhci_reset_streams(epctx);
+ }
return CC_SUCCESS;
}
{
XHCISlot *slot;
XHCIEPContext *epctx;
- USBDevice *dev;
trace_usb_xhci_ep_reset(slotid, epid);
assert(slotid >= 1 && slotid <= xhci->numslots);
ep |= 0x80;
}
- dev = xhci->slots[slotid-1].uport->dev;
- if (!dev) {
+ if (!xhci->slots[slotid-1].uport ||
+ !xhci->slots[slotid-1].uport->dev) {
return CC_USB_TRANSACTION_ERROR;
}
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
+
+ if (epctx->nr_pstreams) {
+ xhci_reset_streams(epctx);
+ }
return CC_SUCCESS;
}
static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
- unsigned int epid, uint64_t pdequeue)
+ unsigned int epid, unsigned int streamid,
+ uint64_t pdequeue)
{
XHCISlot *slot;
XHCIEPContext *epctx;
+ XHCIStreamContext *sctx;
dma_addr_t dequeue;
assert(slotid >= 1 && slotid <= xhci->numslots);
return CC_TRB_ERROR;
}
- trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
+ trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue);
dequeue = xhci_mask64(pdequeue);
slot = &xhci->slots[slotid-1];
epctx = slot->eps[epid-1];
-
if (epctx->state != EP_STOPPED) {
fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
return CC_CONTEXT_STATE_ERROR;
}
- xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
- epctx->ring.ccs = dequeue & 1;
+ if (epctx->nr_pstreams) {
+ uint32_t err;
+ sctx = xhci_find_stream(epctx, streamid, &err);
+ if (sctx == NULL) {
+ return err;
+ }
+ xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf);
+ sctx->ring.ccs = dequeue & 1;
+ } else {
+ sctx = NULL;
+ xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
+ epctx->ring.ccs = dequeue & 1;
+ }
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED);
return CC_SUCCESS;
}
int i;
xfer->int_req = false;
- pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
+ pci_dma_sglist_init(&xfer->sgl, PCI_DEVICE(xhci), xfer->trb_count);
for (i = 0; i < xfer->trb_count; i++) {
XHCITRB *trb = &xfer->trbs[i];
dma_addr_t addr;
XHCIState *xhci = xfer->xhci;
XHCISlot *slot = &xhci->slots[xfer->slotid-1];
XHCIEPContext *epctx = slot->eps[xfer->epid-1];
+ uint32_t err;
+ XHCIStreamContext *sctx;
- epctx->ring.dequeue = xfer->trbs[0].addr;
- epctx->ring.ccs = xfer->trbs[0].ccs;
- xhci_set_ep_state(xhci, epctx, EP_HALTED);
- DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
- DPRINTF("xhci: will continue at "DMA_ADDR_FMT"\n", epctx->ring.dequeue);
+ if (epctx->nr_pstreams) {
+ sctx = xhci_find_stream(epctx, xfer->streamid, &err);
+ if (sctx == NULL) {
+ return;
+ }
+ sctx->ring.dequeue = xfer->trbs[0].addr;
+ sctx->ring.ccs = xfer->trbs[0].ccs;
+ xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED);
+ } else {
+ epctx->ring.dequeue = xfer->trbs[0].addr;
+ epctx->ring.ccs = xfer->trbs[0].ccs;
+ xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED);
+ }
}
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
}
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
- usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false,
- xfer->int_req);
+ usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid,
+ 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);
trace_usb_xhci_xfer_error(xfer, xfer->packet.status);
switch (xfer->packet.status) {
case USB_RET_NODEV:
+ case USB_RET_IOERROR:
xfer->status = CC_USB_TRANSACTION_ERROR;
xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
+ case USB_RET_BABBLE:
+ xfer->status = CC_BABBLE_DETECTED;
+ xhci_xfer_report(xfer);
+ xhci_stall_ep(xfer);
+ break;
default:
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
xfer->packet.status);
- FIXME();
+ FIXME("unhandled USB_RET_*");
}
return 0;
}
trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1];
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
/* at most one Event Data TRB allowed after STATUS */
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0);
}
return 0;
}
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;
}
}
xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid);
}
return 0;
}
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
{
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
return xhci_submit(xhci, xfer, epctx);
}
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+ unsigned int epid, unsigned int streamid)
{
+ XHCIStreamContext *stctx;
XHCIEPContext *epctx;
+ XHCIRing *ring;
USBEndpoint *ep = NULL;
uint64_t mfindex;
int length;
int i;
- trace_usb_xhci_ep_kick(slotid, epid);
+ trace_usb_xhci_ep_kick(slotid, epid, streamid);
assert(slotid >= 1 && slotid <= xhci->numslots);
assert(epid >= 1 && epid <= 31);
return;
}
- xhci_set_ep_state(xhci, epctx, EP_RUNNING);
+
+ if (epctx->nr_pstreams) {
+ uint32_t err;
+ stctx = xhci_find_stream(epctx, streamid, &err);
+ if (stctx == NULL) {
+ return;
+ }
+ ring = &stctx->ring;
+ xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING);
+ } else {
+ ring = &epctx->ring;
+ streamid = 0;
+ xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING);
+ }
+ assert(ring->dequeue != 0);
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
if (xfer->running_async || xfer->running_retry) {
break;
}
- length = xhci_ring_chain_length(xhci, &epctx->ring);
+ length = xhci_ring_chain_length(xhci, ring);
if (length < 0) {
break;
} else if (length == 0) {
xfer->trb_count = length;
for (i = 0; i < length; i++) {
- assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
+ 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) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
}
xhci->slots[slotid-1].enabled = 0;
+ xhci->slots[slotid-1].addressed = 0;
return CC_SUCCESS;
}
assert(slotid >= 1 && slotid <= xhci->numslots);
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
- poctx = ldq_le_pci_dma(&xhci->pci_dev, dcbaap + 8*slotid);
+ poctx = ldq_le_pci_dma(PCI_DEVICE(xhci), dcbaap + 8 * slotid);
ictx = xhci_mask64(pictx);
octx = xhci_mask64(poctx);
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
} else {
USBPacket p;
- slot->devaddr = xhci->devaddr++;
- slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
- DPRINTF("xhci: device address is %d\n", slot->devaddr);
+ uint8_t buf[1];
+
+ slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
usb_device_reset(dev);
+ memset(&p, 0, sizeof(p));
+ usb_packet_addbuf(&p, buf, sizeof(buf));
usb_packet_setup(&p, USB_TOKEN_OUT,
- usb_ep_get(dev, USB_TOKEN_OUT, 0),
+ usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
0, false, false);
usb_device_handle_control(dev, &p,
DeviceOutRequest | USB_REQ_SET_ADDRESS,
- slot->devaddr, 0, 0, NULL);
+ slotid, 0, 0, NULL);
assert(p.status != USB_RET_ASYNC);
}
xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
+ xhci->slots[slotid-1].addressed = 1;
return res;
}
return slotid;
}
+/* cleanup slot state on usb device detach */
+static void xhci_detach_slot(XHCIState *xhci, USBPort *uport)
+{
+ int slot, ep;
+
+ for (slot = 0; slot < xhci->numslots; slot++) {
+ if (xhci->slots[slot].uport == uport) {
+ break;
+ }
+ }
+ if (slot == xhci->numslots) {
+ return;
+ }
+
+ for (ep = 0; ep < 31; ep++) {
+ if (xhci->slots[slot].eps[ep]) {
+ xhci_ep_nuke_xfers(xhci, slot+1, ep+1);
+ }
+ }
+ xhci->slots[slot].uport = NULL;
+}
+
static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
{
dma_addr_t ctx;
/* TODO: actually implement real values here */
bw_ctx[0] = 0;
memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
- pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx));
+ pci_dma_write(PCI_DEVICE(xhci), ctx, bw_ctx, sizeof(bw_ctx));
return CC_SUCCESS;
}
static void xhci_via_challenge(XHCIState *xhci, uint64_t addr)
{
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
uint32_t buf[8];
uint32_t obuf[8];
dma_addr_t paddr = xhci_mask64(addr);
- pci_dma_read(&xhci->pci_dev, paddr, &buf, 32);
+ pci_dma_read(pci_dev, paddr, &buf, 32);
memcpy(obuf, buf, sizeof(obuf));
obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
}
- pci_dma_write(&xhci->pci_dev, paddr, &obuf, 32);
+ pci_dma_write(pci_dev, paddr, &obuf, 32);
}
static void xhci_process_commands(XHCIState *xhci)
if (slotid) {
unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
& TRB_CR_EPID_MASK;
- event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
+ unsigned int streamid = (trb.status >> 16) & 0xffff;
+ event.ccode = xhci_set_ep_dequeue(xhci, slotid,
+ epid, streamid,
trb.parameter);
}
break;
}
break;
default:
- fprintf(stderr, "xhci: unimplemented command %d\n", type);
+ trace_usb_xhci_unimplemented("command", type);
event.ccode = CC_TRB_ERROR;
break;
}
if ((port->portsc & bits) == bits) {
return;
}
+ trace_usb_xhci_port_notify(port->portnr, bits);
port->portsc |= bits;
if (!xhci_running(port->xhci)) {
return;
xhci_port_notify(port, PORTSC_CSC);
}
-static void xhci_port_reset(XHCIPort *port)
+static void xhci_port_reset(XHCIPort *port, bool warm_reset)
{
trace_usb_xhci_port_reset(port->portnr);
usb_device_reset(port->uport->dev);
switch (port->uport->dev->speed) {
+ case USB_SPEED_SUPER:
+ if (warm_reset) {
+ port->portsc |= PORTSC_WRC;
+ }
+ /* fall through */
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
static void xhci_reset(DeviceState *dev)
{
- XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev);
+ XHCIState *xhci = XHCI(dev);
int i;
trace_usb_xhci_reset();
xhci->dcbaap_low = 0;
xhci->dcbaap_high = 0;
xhci->config = 0;
- xhci->devaddr = 2;
for (i = 0; i < xhci->numslots; i++) {
xhci_disable_slot(xhci, i+1);
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);
}
break;
case 0x10: /* HCCPARAMS */
if (sizeof(dma_addr_t) == 4) {
- ret = 0x00081000;
+ ret = 0x00087000;
} else {
- ret = 0x00081001;
+ ret = 0x00087001;
}
break;
case 0x14: /* DBOFF */
ret = 0x00000000; /* reserved */
break;
default:
- fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg);
+ trace_usb_xhci_unimplemented("cap read", reg);
ret = 0;
}
break;
case 0x0c: /* reserved */
default:
- fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
- port->portnr, (uint32_t)reg);
+ trace_usb_xhci_unimplemented("port read", reg);
ret = 0;
}
uint64_t val, unsigned size)
{
XHCIPort *port = ptr;
- uint32_t portsc;
+ uint32_t portsc, notify;
trace_usb_xhci_port_write(port->portnr, reg, val);
switch (reg) {
case 0x00: /* PORTSC */
+ /* write-1-to-start bits */
+ if (val & PORTSC_WPR) {
+ xhci_port_reset(port, true);
+ break;
+ }
+ if (val & PORTSC_PR) {
+ xhci_port_reset(port, false);
+ break;
+ }
+
portsc = port->portsc;
+ notify = 0;
/* write-1-to-clear bits*/
portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
if (val & PORTSC_LWS) {
/* overwrite PLS only when LWS=1 */
- uint32_t pls = get_field(val, PORTSC_PLS);
- set_field(&portsc, pls, PORTSC_PLS);
- trace_usb_xhci_port_link(port->portnr, pls);
+ uint32_t old_pls = get_field(port->portsc, PORTSC_PLS);
+ uint32_t new_pls = get_field(val, PORTSC_PLS);
+ switch (new_pls) {
+ case PLS_U0:
+ if (old_pls != PLS_U0) {
+ set_field(&portsc, new_pls, PORTSC_PLS);
+ trace_usb_xhci_port_link(port->portnr, new_pls);
+ notify = PORTSC_PLC;
+ }
+ break;
+ case PLS_U3:
+ if (old_pls < PLS_U3) {
+ set_field(&portsc, new_pls, PORTSC_PLS);
+ trace_usb_xhci_port_link(port->portnr, new_pls);
+ }
+ break;
+ case PLS_RESUME:
+ /* windows does this for some reason, don't spam stderr */
+ break;
+ default:
+ fprintf(stderr, "%s: ignore pls write (old %d, new %d)\n",
+ __func__, old_pls, new_pls);
+ break;
+ }
}
/* read/write bits */
portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
port->portsc = portsc;
- /* write-1-to-start bits */
- if (val & PORTSC_PR) {
- xhci_port_reset(port);
+ if (notify) {
+ xhci_port_notify(port, notify);
}
break;
case 0x04: /* PORTPMSC */
case 0x08: /* PORTLI */
default:
- fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
- port->portnr, (uint32_t)reg);
+ trace_usb_xhci_unimplemented("port write", reg);
}
}
ret = xhci->config;
break;
default:
- fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg);
+ trace_usb_xhci_unimplemented("oper read", reg);
ret = 0;
}
uint64_t val, unsigned size)
{
XHCIState *xhci = ptr;
+ DeviceState *d = DEVICE(ptr);
trace_usb_xhci_oper_write(reg, val);
xhci->usbcmd = val & 0xc0f;
xhci_mfwrap_update(xhci);
if (val & USBCMD_HCRST) {
- xhci_reset(&xhci->pci_dev.qdev);
+ xhci_reset(d);
}
xhci_intx_update(xhci);
break;
xhci->config = val & 0xff;
break;
default:
- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
+ trace_usb_xhci_unimplemented("oper write", reg);
}
}
ret = xhci_mfindex_get(xhci) & 0x3fff;
break;
default:
- fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n",
- (int)reg);
+ trace_usb_xhci_unimplemented("runtime read", reg);
break;
}
} else {
trace_usb_xhci_runtime_write(reg, val);
if (reg < 0x20) {
- fprintf(stderr, "%s: reg 0x%x unimplemented\n", __func__, (int)reg);
+ trace_usb_xhci_unimplemented("runtime write", reg);
return;
}
xhci_events_update(xhci, v);
break;
default:
- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n",
- (int)reg);
+ trace_usb_xhci_unimplemented("oper write", reg);
}
}
uint64_t val, unsigned size)
{
XHCIState *xhci = ptr;
+ unsigned int epid, streamid;
trace_usb_xhci_doorbell_write(reg, val);
(uint32_t)val);
}
} else {
+ epid = val & 0xff;
+ streamid = (val >> 16) & 0xffff;
if (reg > xhci->numslots) {
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
- } else if (val > 31) {
+ } else if (epid > 31) {
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
(int)reg, (uint32_t)val);
} else {
- xhci_kick_ep(xhci, reg, val);
+ xhci_kick_ep(xhci, reg, epid, streamid);
}
}
}
+static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ /* nothing */
+}
+
static const MemoryRegionOps xhci_cap_ops = {
.read = xhci_cap_read,
+ .write = xhci_cap_write,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.impl.min_access_size = 4,
XHCIState *xhci = usbport->opaque;
XHCIPort *port = xhci_lookup_port(xhci, usbport);
+ xhci_detach_slot(xhci, usbport);
xhci_port_update(port, 1);
}
return;
}
xhci_complete_packet(xfer);
- xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid);
}
static void xhci_child_detach(USBPort *uport, USBDevice *child)
{
USBBus *bus = usb_bus_from_device(child);
XHCIState *xhci = container_of(bus, XHCIState, bus);
- int i;
- for (i = 0; i < xhci->numslots; i++) {
- if (xhci->slots[i].uport == uport) {
- xhci->slots[i].uport = NULL;
- }
- }
+ xhci_detach_slot(xhci, uport);
}
static USBPortOps xhci_uport_ops = {
.child_detach = xhci_child_detach,
};
-static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
-{
- XHCISlot *slot;
- int slotid;
-
- for (slotid = 1; slotid <= xhci->numslots; slotid++) {
- slot = &xhci->slots[slotid-1];
- if (slot->devaddr == dev->addr) {
- return slotid;
- }
- }
- return 0;
-}
-
static int xhci_find_epid(USBEndpoint *ep)
{
if (ep->nr == 0) {
}
}
-static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
+ unsigned int stream)
{
XHCIState *xhci = container_of(bus, XHCIState, bus);
int slotid;
DPRINTF("%s\n", __func__);
- slotid = xhci_find_slotid(xhci, ep->dev);
+ slotid = ep->dev->addr;
if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
return;
}
- xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
+ xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream);
}
static USBBusOps xhci_bus_ops = {
.wakeup_endpoint = xhci_wakeup_endpoint,
};
-static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
+static void usb_xhci_init(XHCIState *xhci)
{
+ DeviceState *dev = DEVICE(xhci);
XHCIPort *port;
int i, usbports, speedmask;
usbports = MAX(xhci->numports_2, xhci->numports_3);
xhci->numports = xhci->numports_2 + xhci->numports_3;
- usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
+ usb_bus_new(&xhci->bus, &xhci_bus_ops, dev);
for (i = 0; i < usbports; i++) {
speedmask = 0;
{
int i, ret;
- XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+ XHCIState *xhci = XHCI(dev);
- xhci->pci_dev.config[PCI_CLASS_PROG] = 0x30; /* xHCI */
- xhci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
- xhci->pci_dev.config[PCI_CACHE_LINE_SIZE] = 0x10;
- xhci->pci_dev.config[0x60] = 0x30; /* release number */
+ dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */
+ dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
+ dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
+ dev->config[0x60] = 0x30; /* release number */
- usb_xhci_init(xhci, &dev->qdev);
+ usb_xhci_init(xhci);
if (xhci->numintrs > MAXINTRS) {
xhci->numintrs = MAXINTRS;
}
+ while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */
+ xhci->numintrs++;
+ }
if (xhci->numintrs < 1) {
xhci->numintrs = 1;
}
xhci->numslots = 1;
}
- xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
+ xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci);
- xhci->irq = xhci->pci_dev.irq[0];
+ xhci->irq = dev->irq[0];
- memory_region_init(&xhci->mem, "xhci", LEN_REGS);
- memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci,
+ memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS);
+ memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci,
"capabilities", LEN_CAP);
- memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci,
+ memory_region_init_io(&xhci->mem_oper, OBJECT(xhci), &xhci_oper_ops, xhci,
"operational", 0x400);
- memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci,
+ memory_region_init_io(&xhci->mem_runtime, OBJECT(xhci), &xhci_runtime_ops, xhci,
"runtime", LEN_RUNTIME);
- memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci,
+ memory_region_init_io(&xhci->mem_doorbell, OBJECT(xhci), &xhci_doorbell_ops, xhci,
"doorbell", LEN_DOORBELL);
memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap);
XHCIPort *port = &xhci->ports[i];
uint32_t offset = OFF_OPER + 0x400 + 0x10 * i;
port->xhci = xhci;
- memory_region_init_io(&port->mem, &xhci_port_ops, port,
+ memory_region_init_io(&port->mem, OBJECT(xhci), &xhci_port_ops, port,
port->name, 0x10);
memory_region_add_subregion(&xhci->mem, offset, &port->mem);
}
- pci_register_bar(&xhci->pci_dev, 0,
+ pci_register_bar(dev, 0,
PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
&xhci->mem);
- ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
+ ret = pcie_endpoint_cap_init(dev, 0xa0);
assert(ret >= 0);
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
- msi_init(&xhci->pci_dev, 0x70, xhci->numintrs, true, false);
+ msi_init(dev, 0x70, xhci->numintrs, true, false);
}
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
- msix_init(&xhci->pci_dev, xhci->numintrs,
+ msix_init(dev, xhci->numintrs,
&xhci->mem, 0, OFF_MSIX_TABLE,
&xhci->mem, 0, OFF_MSIX_PBA,
0x90);
return 0;
}
+static int usb_xhci_post_load(void *opaque, int version_id)
+{
+ XHCIState *xhci = opaque;
+ PCIDevice *pci_dev = PCI_DEVICE(xhci);
+ XHCISlot *slot;
+ XHCIEPContext *epctx;
+ dma_addr_t dcbaap, pctx;
+ uint32_t slot_ctx[4];
+ uint32_t ep_ctx[5];
+ int slotid, epid, state, intr;
+
+ dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
+
+ for (slotid = 1; slotid <= xhci->numslots; slotid++) {
+ slot = &xhci->slots[slotid-1];
+ if (!slot->addressed) {
+ continue;
+ }
+ slot->ctx =
+ xhci_mask64(ldq_le_pci_dma(pci_dev, dcbaap + 8 * slotid));
+ xhci_dma_read_u32s(xhci, slot->ctx, slot_ctx, sizeof(slot_ctx));
+ slot->uport = xhci_lookup_uport(xhci, slot_ctx);
+ assert(slot->uport && slot->uport->dev);
+
+ for (epid = 1; epid <= 32; epid++) {
+ pctx = slot->ctx + 32 * epid;
+ xhci_dma_read_u32s(xhci, pctx, ep_ctx, sizeof(ep_ctx));
+ state = ep_ctx[0] & EP_STATE_MASK;
+ if (state == EP_DISABLED) {
+ continue;
+ }
+ epctx = xhci_alloc_epctx(xhci, slotid, epid);
+ slot->eps[epid-1] = epctx;
+ xhci_init_epctx(epctx, pctx, ep_ctx);
+ epctx->state = state;
+ if (state == EP_RUNNING) {
+ /* kick endpoint after vmload is finished */
+ timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ }
+ }
+ }
+
+ for (intr = 0; intr < xhci->numintrs; intr++) {
+ if (xhci->intr[intr].msix_used) {
+ msix_vector_use(pci_dev, intr);
+ } else {
+ msix_vector_unuse(pci_dev, intr);
+ }
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_xhci_ring = {
+ .name = "xhci-ring",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(dequeue, XHCIRing),
+ VMSTATE_BOOL(ccs, XHCIRing),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_xhci_port = {
+ .name = "xhci-port",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(portsc, XHCIPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_xhci_slot = {
+ .name = "xhci-slot",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(enabled, XHCISlot),
+ VMSTATE_BOOL(addressed, XHCISlot),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_xhci_event = {
+ .name = "xhci-event",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(type, XHCIEvent),
+ VMSTATE_UINT32(ccode, XHCIEvent),
+ VMSTATE_UINT64(ptr, XHCIEvent),
+ VMSTATE_UINT32(length, XHCIEvent),
+ VMSTATE_UINT32(flags, XHCIEvent),
+ VMSTATE_UINT8(slotid, XHCIEvent),
+ VMSTATE_UINT8(epid, XHCIEvent),
+ }
+};
+
+static bool xhci_er_full(void *opaque, int version_id)
+{
+ struct XHCIInterrupter *intr = opaque;
+ return intr->er_full;
+}
+
+static const VMStateDescription vmstate_xhci_intr = {
+ .name = "xhci-intr",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ /* registers */
+ VMSTATE_UINT32(iman, XHCIInterrupter),
+ VMSTATE_UINT32(imod, XHCIInterrupter),
+ VMSTATE_UINT32(erstsz, XHCIInterrupter),
+ VMSTATE_UINT32(erstba_low, XHCIInterrupter),
+ VMSTATE_UINT32(erstba_high, XHCIInterrupter),
+ VMSTATE_UINT32(erdp_low, XHCIInterrupter),
+ VMSTATE_UINT32(erdp_high, XHCIInterrupter),
+
+ /* state */
+ VMSTATE_BOOL(msix_used, XHCIInterrupter),
+ VMSTATE_BOOL(er_pcs, XHCIInterrupter),
+ VMSTATE_UINT64(er_start, XHCIInterrupter),
+ VMSTATE_UINT32(er_size, XHCIInterrupter),
+ VMSTATE_UINT32(er_ep_idx, XHCIInterrupter),
+
+ /* event queue (used if ring is full) */
+ VMSTATE_BOOL(er_full, XHCIInterrupter),
+ VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full),
+ VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full),
+ VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE,
+ xhci_er_full, 1,
+ vmstate_xhci_event, XHCIEvent),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_xhci = {
.name = "xhci",
- .unmigratable = 1,
+ .version_id = 1,
+ .post_load = usb_xhci_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(parent_obj, XHCIState),
+ VMSTATE_MSIX(parent_obj, XHCIState),
+
+ VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1,
+ vmstate_xhci_port, XHCIPort),
+ VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1,
+ vmstate_xhci_slot, XHCISlot),
+ VMSTATE_STRUCT_VARRAY_UINT32(intr, XHCIState, numintrs, 1,
+ vmstate_xhci_intr, XHCIInterrupter),
+
+ /* Operational Registers */
+ VMSTATE_UINT32(usbcmd, XHCIState),
+ VMSTATE_UINT32(usbsts, XHCIState),
+ VMSTATE_UINT32(dnctrl, XHCIState),
+ VMSTATE_UINT32(crcr_low, XHCIState),
+ VMSTATE_UINT32(crcr_high, XHCIState),
+ VMSTATE_UINT32(dcbaap_low, XHCIState),
+ VMSTATE_UINT32(dcbaap_high, XHCIState),
+ VMSTATE_UINT32(config, XHCIState),
+
+ /* Runtime Registers & state */
+ VMSTATE_INT64(mfindex_start, XHCIState),
+ VMSTATE_TIMER(mfwrap_timer, XHCIState),
+ VMSTATE_STRUCT(cmd_ring, XHCIState, 1, vmstate_xhci_ring, XHCIRing),
+
+ VMSTATE_END_OF_LIST()
+ }
};
static Property xhci_properties[] = {
dc->vmsd = &vmstate_xhci;
dc->props = xhci_properties;
dc->reset = xhci_reset;
+ set_bit(DEVICE_CATEGORY_USB, dc->categories);
k->init = usb_xhci_initfn;
k->vendor_id = PCI_VENDOR_ID_NEC;
k->device_id = PCI_DEVICE_ID_NEC_UPD720200;
k->no_hotplug = 1;
}
-static TypeInfo xhci_info = {
- .name = "nec-usb-xhci",
+static const TypeInfo xhci_info = {
+ .name = TYPE_XHCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XHCIState),
.class_init = xhci_class_init,