#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
__func__, __LINE__); abort(); } while (0)
-#define MAXSLOTS 8
-#define MAXINTRS 1
+#define MAXPORTS_2 8
+#define MAXPORTS_3 8
-#define USB2_PORTS 4
-#define USB3_PORTS 4
-
-#define MAXPORTS (USB2_PORTS+USB3_PORTS)
+#define MAXPORTS (MAXPORTS_2+MAXPORTS_3)
+#define MAXSLOTS MAXPORTS
+#define MAXINTRS 1 /* MAXPORTS */
#define TD_QUEUE 24
-#define BG_XFERS 8
-#define BG_PKTS 8
/* Very pessimistic, let's hope it's enough for all cases */
#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
#define ER_FULL_HACK
#define LEN_CAP 0x40
-#define OFF_OPER LEN_CAP
#define LEN_OPER (0x400 + 0x10 * MAXPORTS)
-#define OFF_RUNTIME ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
-#define LEN_RUNTIME (0x20 + MAXINTRS * 0x20)
-#define OFF_DOORBELL (OFF_RUNTIME + LEN_RUNTIME)
+#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20)
#define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20)
+#define OFF_OPER LEN_CAP
+#define OFF_RUNTIME 0x1000
+#define OFF_DOORBELL 0x2000
/* must be power of 2 */
-#define LEN_REGS 0x2000
+#define LEN_REGS 0x4000
+#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME
+#error Increase OFF_RUNTIME
+#endif
+#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL
+#error Increase OFF_DOORBELL
+#endif
#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
# error Increase LEN_REGS
#endif
} XHCIRing;
typedef struct XHCIPort {
- USBPort port;
uint32_t portsc;
+ uint32_t portnr;
+ USBPort *uport;
+ uint32_t speedmask;
} XHCIPort;
struct XHCIState;
typedef struct XHCITransfer {
XHCIState *xhci;
USBPacket packet;
+ QEMUSGList sgl;
bool running_async;
bool running_retry;
bool cancelled;
bool complete;
- bool backgrounded;
unsigned int iso_pkts;
unsigned int slotid;
unsigned int epid;
bool in_xfer;
bool iso_xfer;
- bool bg_xfer;
unsigned int trb_count;
unsigned int trb_alloced;
XHCITRB *trbs;
- unsigned int data_length;
- unsigned int data_alloced;
- uint8_t *data;
-
TRBCCode status;
unsigned int pkts;
unsigned int pktsize;
unsigned int cur_pkt;
+
+ uint64_t mfindex_kick;
} XHCITransfer;
typedef struct XHCIEPContext {
+ XHCIState *xhci;
+ unsigned int slotid;
+ unsigned int epid;
+
XHCIRing ring;
unsigned int next_xfer;
unsigned int comp_xfer;
XHCITransfer transfers[TD_QUEUE];
XHCITransfer *retry;
- bool bg_running;
- bool bg_updating;
- unsigned int next_bg;
- XHCITransfer bg_transfers[BG_XFERS];
EPType type;
dma_addr_t pctx;
unsigned int max_psize;
- bool has_bg;
uint32_t state;
+
+ /* iso xfer scheduling */
+ unsigned int interval;
+ int64_t mfindex_last;
+ QEMUTimer *kick_timer;
} XHCIEPContext;
typedef struct XHCISlot {
qemu_irq irq;
MemoryRegion mem;
const char *name;
- uint32_t msi;
unsigned int devaddr;
+ /* properties */
+ uint32_t numports_2;
+ uint32_t numports_3;
+ uint32_t msi;
+
/* Operational Registers */
uint32_t usbcmd;
uint32_t usbsts;
uint32_t dcbaap_high;
uint32_t config;
+ USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)];
XHCIPort ports[MAXPORTS];
XHCISlot slots[MAXSLOTS];
+ uint32_t numports;
/* Runtime Registers */
- uint32_t mfindex;
- /* note: we only support one interrupter */
uint32_t iman;
uint32_t imod;
uint32_t erstsz;
uint32_t erdp_low;
uint32_t erdp_high;
+ int64_t mfindex_start;
+ QEMUTimer *mfwrap_timer;
+
dma_addr_t er_start;
uint32_t er_size;
bool er_pcs;
uint32_t rsvd;
} XHCIEvRingSeg;
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+ unsigned int epid);
+static void xhci_event(XHCIState *xhci, XHCIEvent *event);
+static void xhci_write_event(XHCIState *xhci, XHCIEvent *event);
+
static const char *TRBType_names[] = {
[TRB_RESERVED] = "TRB_RESERVED",
[TR_NORMAL] = "TR_NORMAL",
[CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
};
+static const char *TRBCCode_names[] = {
+ [CC_INVALID] = "CC_INVALID",
+ [CC_SUCCESS] = "CC_SUCCESS",
+ [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR",
+ [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED",
+ [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR",
+ [CC_TRB_ERROR] = "CC_TRB_ERROR",
+ [CC_STALL_ERROR] = "CC_STALL_ERROR",
+ [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR",
+ [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR",
+ [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR",
+ [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR",
+ [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR",
+ [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR",
+ [CC_SHORT_PACKET] = "CC_SHORT_PACKET",
+ [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN",
+ [CC_RING_OVERRUN] = "CC_RING_OVERRUN",
+ [CC_VF_ER_FULL] = "CC_VF_ER_FULL",
+ [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR",
+ [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN",
+ [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR",
+ [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR",
+ [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR",
+ [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR",
+ [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR",
+ [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED",
+ [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED",
+ [CC_STOPPED] = "CC_STOPPED",
+ [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID",
+ [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR]
+ = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR",
+ [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN",
+ [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR",
+ [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR",
+ [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR",
+ [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR",
+ [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR",
+};
+
static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
{
if (index >= llen || list[index] == NULL) {
ARRAY_SIZE(TRBType_names));
}
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
- unsigned int epid);
+static const char *event_name(XHCIEvent *event)
+{
+ return lookup_name(event->ccode, TRBCCode_names,
+ ARRAY_SIZE(TRBCCode_names));
+}
+
+static uint64_t xhci_mfindex_get(XHCIState *xhci)
+{
+ int64_t now = qemu_get_clock_ns(vm_clock);
+ return (now - xhci->mfindex_start) / 125000;
+}
+
+static void xhci_mfwrap_update(XHCIState *xhci)
+{
+ const uint32_t bits = USBCMD_RS | USBCMD_EWE;
+ uint32_t mfindex, left;
+ int64_t now;
+
+ if ((xhci->usbcmd & bits) == bits) {
+ now = qemu_get_clock_ns(vm_clock);
+ mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff;
+ left = 0x4000 - mfindex;
+ qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000);
+ } else {
+ qemu_del_timer(xhci->mfwrap_timer);
+ }
+}
+
+static void xhci_mfwrap_timer(void *opaque)
+{
+ XHCIState *xhci = opaque;
+ XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
+
+ xhci_event(xhci, &wrap);
+ xhci_mfwrap_update(xhci);
+}
static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high)
{
}
}
+static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
+{
+ int index;
+
+ if (!uport->dev) {
+ return NULL;
+ }
+ switch (uport->dev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ index = uport->index;
+ break;
+ case USB_SPEED_SUPER:
+ index = uport->index + xhci->numports_2;
+ break;
+ default:
+ return NULL;
+ }
+ return &xhci->ports[index];
+}
+
static void xhci_irq_update(XHCIState *xhci)
{
int level = 0;
ev_trb.control = cpu_to_le32(ev_trb.control);
trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
- ev_trb.parameter, ev_trb.status, ev_trb.control);
+ event_name(event), ev_trb.parameter,
+ ev_trb.status, ev_trb.control);
addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
{
trace_usb_xhci_run();
xhci->usbsts &= ~USBSTS_HCH;
+ xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
}
static void xhci_stop(XHCIState *xhci)
epctx->state = state;
}
+static void xhci_ep_kick_timer(void *opaque)
+{
+ XHCIEPContext *epctx = opaque;
+ xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
+}
+
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid, dma_addr_t pctx,
uint32_t *ctx)
epctx = g_malloc(sizeof(XHCIEPContext));
memset(epctx, 0, sizeof(XHCIEPContext));
+ epctx->xhci = xhci;
+ epctx->slotid = slotid;
+ epctx->epid = epid;
slot->eps[epid-1] = epctx;
epctx->pctx = pctx;
epctx->max_psize = ctx[1]>>16;
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
- epctx->has_bg = false;
- if (epctx->type == ET_ISO_IN) {
- epctx->has_bg = true;
- }
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);
}
+ epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
+ 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;
ctx[0] |= EP_RUNNING;
if (t->running_retry) {
t->running_retry = 0;
epctx->retry = NULL;
- }
- if (t->backgrounded) {
- t->backgrounded = 0;
+ qemu_del_timer(epctx->kick_timer);
}
if (t->trbs) {
g_free(t->trbs);
}
- if (t->data) {
- g_free(t->data);
- }
t->trbs = NULL;
- t->data = NULL;
t->trb_count = t->trb_alloced = 0;
- t->data_length = t->data_alloced = 0;
xferi = (xferi + 1) % TD_QUEUE;
}
- if (epctx->has_bg) {
- xferi = epctx->next_bg;
- for (i = 0; i < BG_XFERS; i++) {
- XHCITransfer *t = &epctx->bg_transfers[xferi];
- if (t->running_async) {
- usb_cancel_packet(&t->packet);
- t->running_async = 0;
- t->cancelled = 1;
- DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
- killed++;
- }
- if (t->data) {
- g_free(t->data);
- }
-
- t->data = NULL;
- xferi = (xferi + 1) % BG_XFERS;
- }
- }
return killed;
}
xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+ qemu_free_timer(epctx->kick_timer);
g_free(epctx);
slot->eps[epid-1] = NULL;
ep |= 0x80;
}
- dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
+ dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev;
if (!dev) {
return CC_USB_TRANSACTION_ERROR;
}
return CC_TRB_ERROR;
}
- DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
+ trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
dequeue = xhci_mask64(pdequeue);
slot = &xhci->slots[slotid-1];
return CC_SUCCESS;
}
-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
- unsigned int length, bool in_xfer, bool out_xfer,
- bool report)
+static int xhci_xfer_map(XHCITransfer *xfer)
{
- int i;
- uint32_t edtla = 0;
- unsigned int transferred = 0;
- unsigned int left = length;
- bool reported = 0;
- bool shortpkt = 0;
- XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+ int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
XHCIState *xhci = xfer->xhci;
+ int i;
- DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
- length, in_xfer, out_xfer, report);
-
- assert(!(in_xfer && out_xfer));
-
+ pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
for (i = 0; i < xfer->trb_count; i++) {
XHCITRB *trb = &xfer->trbs[i];
dma_addr_t addr;
case TR_DATA:
if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
- xhci_die(xhci);
- return transferred;
+ goto err;
}
/* fallthrough */
case TR_NORMAL:
case TR_ISOCH:
addr = xhci_mask64(trb->parameter);
+ chunk = trb->status & 0x1ffff;
+ if (trb->control & TRB_TR_IDT) {
+ if (chunk > 8 || in_xfer) {
+ fprintf(stderr, "xhci: invalid immediate data TRB\n");
+ goto err;
+ }
+ qemu_sglist_add(&xfer->sgl, trb->addr, chunk);
+ } else {
+ qemu_sglist_add(&xfer->sgl, addr, chunk);
+ }
+ break;
+ }
+ }
+
+ usb_packet_map(&xfer->packet, &xfer->sgl);
+ return 0;
+
+err:
+ qemu_sglist_destroy(&xfer->sgl);
+ xhci_die(xhci);
+ return -1;
+}
+
+static void xhci_xfer_unmap(XHCITransfer *xfer)
+{
+ usb_packet_unmap(&xfer->packet, &xfer->sgl);
+ qemu_sglist_destroy(&xfer->sgl);
+}
+
+static void xhci_xfer_report(XHCITransfer *xfer)
+{
+ uint32_t edtla = 0;
+ unsigned int left;
+ bool reported = 0;
+ bool shortpkt = 0;
+ XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+ XHCIState *xhci = xfer->xhci;
+ int i;
+
+ left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
+
+ for (i = 0; i < xfer->trb_count; i++) {
+ XHCITRB *trb = &xfer->trbs[i];
+ unsigned int chunk = 0;
+
+ switch (TRB_TYPE(*trb)) {
+ case TR_DATA:
+ case TR_NORMAL:
+ case TR_ISOCH:
chunk = trb->status & 0x1ffff;
if (chunk > left) {
chunk = left;
- shortpkt = 1;
- }
- if (in_xfer || out_xfer) {
- if (trb->control & TRB_TR_IDT) {
- uint64_t idata;
- if (chunk > 8 || in_xfer) {
- fprintf(stderr, "xhci: invalid immediate data TRB\n");
- xhci_die(xhci);
- return transferred;
- }
- idata = le64_to_cpu(trb->parameter);
- memcpy(data, &idata, chunk);
- } else {
- DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
- DMA_ADDR_FMT "\n", in_xfer, chunk, addr);
- if (in_xfer) {
- pci_dma_write(&xhci->pci_dev, addr, data, chunk);
- } else {
- pci_dma_read(&xhci->pci_dev, addr, data, chunk);
- }
-#ifdef DEBUG_DATA
- unsigned int count = chunk;
- int i;
- if (count > 16) {
- count = 16;
- }
- DPRINTF(" ::");
- for (i = 0; i < count; i++) {
- DPRINTF(" %02x", data[i]);
- }
- DPRINTF("\n");
-#endif
+ if (xfer->status == CC_SUCCESS) {
+ shortpkt = 1;
}
}
left -= chunk;
- data += chunk;
edtla += chunk;
- transferred += chunk;
break;
case TR_STATUS:
reported = 0;
break;
}
- if (report && !reported && (trb->control & TRB_TR_IOC ||
- (shortpkt && (trb->control & TRB_TR_ISP)))) {
+ if (!reported && ((trb->control & TRB_TR_IOC) ||
+ (shortpkt && (trb->control & TRB_TR_ISP)) ||
+ (xfer->status != CC_SUCCESS))) {
event.slotid = xfer->slotid;
event.epid = xfer->epid;
event.length = (trb->status & 0x1ffff) - chunk;
}
xhci_event(xhci, &event);
reported = 1;
+ if (xfer->status != CC_SUCCESS) {
+ return;
+ }
}
}
- return transferred;
}
static void xhci_stall_ep(XHCITransfer *xfer)
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
XHCIEPContext *epctx);
-static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
+static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
{
- if (epctx->bg_updating) {
- return;
- }
- DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
- assert(epctx->has_bg);
- DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
- epctx->bg_updating = 1;
- while (epctx->transfers[epctx->comp_xfer].backgrounded &&
- epctx->bg_transfers[epctx->next_bg].complete) {
- XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
- XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
-#if 0
- DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
- epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
- bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
- );
-#endif
- assert(epctx->type == ET_ISO_IN);
- assert(bg->iso_xfer);
- assert(bg->in_xfer);
- uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
-#if 0
- int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
- fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
-#else
- int len = 0;
- FIXME();
-#endif
- fg->complete = 1;
- fg->backgrounded = 0;
-
- if (fg->status == CC_STALL_ERROR) {
- xhci_stall_ep(fg);
- }
-
- xhci_xfer_data(fg, p, len, 1, 0, 1);
-
- epctx->comp_xfer++;
- if (epctx->comp_xfer == TD_QUEUE) {
- epctx->comp_xfer = 0;
- }
- DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
- bg->cur_pkt++;
- if (bg->cur_pkt == bg->pkts) {
- bg->complete = 0;
- if (xhci_submit(xhci, bg, epctx) < 0) {
- fprintf(stderr, "xhci: bg resubmit failed\n");
- }
- epctx->next_bg++;
- if (epctx->next_bg == BG_XFERS) {
- epctx->next_bg = 0;
- }
- DPRINTF("next bg xfer: %d\n", epctx->next_bg);
-
- xhci_kick_ep(xhci, fg->slotid, fg->epid);
- }
+ if (!(port->portsc & PORTSC_PED)) {
+ return NULL;
}
- epctx->bg_updating = 0;
+ return usb_find_device(port->uport, addr);
}
-#if 0
-static void xhci_xfer_cb(struct libusb_transfer *transfer)
+static int xhci_setup_packet(XHCITransfer *xfer)
{
- XHCIState *xhci;
- XHCITransfer *xfer;
-
- xfer = (XHCITransfer *)transfer->user_data;
- xhci = xfer->xhci;
-
- DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
- xfer->epid, transfer->status);
-
- assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
- assert(xfer->epid >= 1 && xfer->epid <= 31);
-
- if (xfer->cancelled) {
- DPRINTF("xhci: transfer cancelled, not reporting anything\n");
- xfer->running = 0;
- return;
- }
-
- XHCIEPContext *epctx;
- XHCISlot *slot;
- slot = &xhci->slots[xfer->slotid-1];
- assert(slot->eps[xfer->epid-1]);
- epctx = slot->eps[xfer->epid-1];
-
- if (xfer->bg_xfer) {
- DPRINTF("xhci: background transfer, updating\n");
- xfer->complete = 1;
- xfer->running = 0;
- xhci_bg_update(xhci, epctx);
- return;
- }
-
- if (xfer->iso_xfer) {
- transfer->status = transfer->iso_packet_desc[0].status;
- transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
- }
-
- xfer->status = libusb_to_ccode(transfer->status);
-
- xfer->complete = 1;
- xfer->running = 0;
-
- if (transfer->status == LIBUSB_TRANSFER_STALL)
- xhci_stall_ep(xhci, epctx, xfer);
+ XHCIState *xhci = xfer->xhci;
+ XHCIPort *port;
+ USBDevice *dev;
+ USBEndpoint *ep;
+ int dir;
- DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
+ dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
- if (xfer->in_xfer) {
- if (xfer->epid == 1) {
- xhci_xfer_data(xhci, xfer, xfer->data + 8,
- transfer->actual_length, 1, 0, 1);
- } else {
- xhci_xfer_data(xhci, xfer, xfer->data,
- transfer->actual_length, 1, 0, 1);
- }
+ if (xfer->packet.ep) {
+ ep = xfer->packet.ep;
+ dev = ep->dev;
} else {
- xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
- }
-
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
-}
-
-static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
- uint8_t bmRequestType, uint8_t bRequest,
- uint16_t wValue, uint16_t wIndex, uint16_t wLength)
-{
- uint16_t type_req = (bmRequestType << 8) | bRequest;
-
- switch (type_req) {
- case 0x0000 | USB_REQ_SET_CONFIGURATION:
- DPRINTF("xhci: HLE switch configuration\n");
- return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
- case 0x0100 | USB_REQ_SET_INTERFACE:
- DPRINTF("xhci: HLE set interface altsetting\n");
- return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
- case 0x0200 | USB_REQ_CLEAR_FEATURE:
- if (wValue == 0) { // endpoint halt
- DPRINTF("xhci: HLE clear halt\n");
- return xhci_clear_halt(xhci, xfer->slotid, wIndex);
- }
- case 0x0000 | USB_REQ_SET_ADDRESS:
- fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
- return 0;
- default:
- return 0;
+ port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+ dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+ if (!dev) {
+ fprintf(stderr, "xhci: slot %d port %d has no device\n",
+ xfer->slotid, xhci->slots[xfer->slotid-1].port);
+ return -1;
+ }
+ ep = usb_ep_get(dev, dir, xfer->epid >> 1);
}
-}
-#endif
-
-static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
-{
- USBEndpoint *ep;
- int dir;
- dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
- ep = usb_ep_get(dev, dir, xfer->epid >> 1);
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
- usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
+ xhci_xfer_map(xfer);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
return 0;
xfer->running_async = 0;
xfer->running_retry = 0;
xfer->complete = 1;
+ xhci_xfer_unmap(xfer);
}
if (ret >= 0) {
- xfer->status = CC_SUCCESS;
- xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
trace_usb_xhci_xfer_success(xfer, ret);
+ xfer->status = CC_SUCCESS;
+ xhci_xfer_report(xfer);
return 0;
}
switch (ret) {
case USB_RET_NODEV:
xfer->status = CC_USB_TRANSACTION_ERROR;
- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+ xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
case USB_RET_STALL:
xfer->status = CC_STALL_ERROR;
- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+ xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
default:
return 0;
}
-static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
-{
- if (!(port->portsc & PORTSC_PED)) {
- return NULL;
- }
- return usb_find_device(&port->port, addr);
-}
-
static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
{
XHCITRB *trb_setup, *trb_status;
uint8_t bmRequestType;
- uint16_t wLength;
- XHCIPort *port;
- USBDevice *dev;
int ret;
trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1];
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
- trb_setup->parameter >> 48);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
/* at most one Event Data TRB allowed after STATUS */
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
}
bmRequestType = trb_setup->parameter;
- wLength = trb_setup->parameter >> 48;
-
- if (xfer->data && xfer->data_alloced < wLength) {
- xfer->data_alloced = 0;
- g_free(xfer->data);
- xfer->data = NULL;
- }
- if (!xfer->data) {
- DPRINTF("xhci: alloc %d bytes data\n", wLength);
- xfer->data = g_malloc(wLength+1);
- xfer->data_alloced = wLength;
- }
- xfer->data_length = wLength;
-
- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
- if (!dev) {
- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
- xhci->slots[xfer->slotid-1].port);
- return -1;
- }
xfer->in_xfer = bmRequestType & USB_DIR_IN;
xfer->iso_xfer = false;
- xhci_setup_packet(xfer, dev);
- xfer->packet.parameter = trb_setup->parameter;
- if (!xfer->in_xfer) {
- xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
+ if (xhci_setup_packet(xfer) < 0) {
+ return -1;
}
+ xfer->packet.parameter = trb_setup->parameter;
- ret = usb_handle_packet(dev, &xfer->packet);
+ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret);
if (!xfer->running_async && !xfer->running_retry) {
return 0;
}
-static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
+ XHCIEPContext *epctx, uint64_t mfindex)
{
- XHCIPort *port;
- USBDevice *dev;
- int ret;
-
- DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
- xfer->in_xfer = epctx->type>>2;
-
- if (xfer->data && xfer->data_alloced < xfer->data_length) {
- xfer->data_alloced = 0;
- g_free(xfer->data);
- xfer->data = NULL;
- }
- if (!xfer->data && xfer->data_length) {
- DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
- xfer->data = g_malloc(xfer->data_length);
- xfer->data_alloced = xfer->data_length;
- }
- if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
- if (!xfer->bg_xfer) {
- xfer->pkts = 1;
+ if (xfer->trbs[0].control & TRB_TR_SIA) {
+ uint64_t asap = ((mfindex + epctx->interval - 1) &
+ ~(epctx->interval-1));
+ if (asap >= epctx->mfindex_last &&
+ asap <= epctx->mfindex_last + epctx->interval * 4) {
+ xfer->mfindex_kick = epctx->mfindex_last + epctx->interval;
+ } else {
+ xfer->mfindex_kick = asap;
}
} else {
- xfer->pkts = 0;
+ xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT)
+ & TRB_TR_FRAMEID_MASK;
+ xfer->mfindex_kick |= mfindex & ~0x3fff;
+ if (xfer->mfindex_kick < mfindex) {
+ xfer->mfindex_kick += 0x4000;
+ }
}
+}
- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
- if (!dev) {
- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
- xhci->slots[xfer->slotid-1].port);
- return -1;
+static void xhci_check_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) +
+ (xfer->mfindex_kick - mfindex) * 125000);
+ xfer->running_retry = 1;
+ } else {
+ epctx->mfindex_last = xfer->mfindex_kick;
+ qemu_del_timer(epctx->kick_timer);
+ xfer->running_retry = 0;
}
+}
+
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+ uint64_t mfindex;
+ int ret;
+
+ DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
- xhci_setup_packet(xfer, dev);
+ xfer->in_xfer = epctx->type>>2;
switch(epctx->type) {
case ET_INTR_OUT:
case ET_INTR_IN:
case ET_BULK_OUT:
case ET_BULK_IN:
+ xfer->pkts = 0;
+ xfer->iso_xfer = false;
break;
case ET_ISO_OUT:
case ET_ISO_IN:
- FIXME();
+ xfer->pkts = 1;
+ xfer->iso_xfer = true;
+ mfindex = xhci_mfindex_get(xhci);
+ xhci_calc_iso_kick(xhci, xfer, epctx, mfindex);
+ xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
+ if (xfer->running_retry) {
+ return -1;
+ }
break;
default:
fprintf(stderr, "xhci: unknown or unhandled EP "
return -1;
}
- if (!xfer->in_xfer) {
- xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
+ if (xhci_setup_packet(xfer) < 0) {
+ return -1;
}
- ret = usb_handle_packet(dev, &xfer->packet);
+ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret);
if (!xfer->running_async && !xfer->running_retry) {
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
{
- int i;
- unsigned int length = 0;
- XHCITRB *trb;
-
- for (i = 0; i < xfer->trb_count; i++) {
- trb = &xfer->trbs[i];
- if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
- length += trb->status & 0x1ffff;
- }
- }
-
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
-
- if (!epctx->has_bg) {
- xfer->data_length = length;
- xfer->backgrounded = 0;
- return xhci_submit(xhci, xfer, epctx);
- } else {
- if (!epctx->bg_running) {
- for (i = 0; i < BG_XFERS; i++) {
- XHCITransfer *t = &epctx->bg_transfers[i];
- t->xhci = xhci;
- t->epid = xfer->epid;
- t->slotid = xfer->slotid;
- t->pkts = BG_PKTS;
- t->pktsize = epctx->max_psize;
- t->data_length = t->pkts * t->pktsize;
- t->bg_xfer = 1;
- if (xhci_submit(xhci, t, epctx) < 0) {
- fprintf(stderr, "xhci: bg submit failed\n");
- return -1;
- }
- }
- epctx->bg_running = 1;
- }
- xfer->backgrounded = 1;
- xhci_bg_update(xhci, epctx);
- return 0;
- }
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ return xhci_submit(xhci, xfer, epctx);
}
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
{
XHCIEPContext *epctx;
+ uint64_t mfindex;
int length;
int i;
}
if (epctx->retry) {
- /* retry nak'ed transfer */
XHCITransfer *xfer = epctx->retry;
int result;
trace_usb_xhci_xfer_retry(xfer);
assert(xfer->running_retry);
- xhci_setup_packet(xfer, xfer->packet.ep->dev);
- result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
- if (result == USB_RET_NAK) {
- return;
+ if (xfer->iso_xfer) {
+ /* retry delayed iso transfer */
+ mfindex = xhci_mfindex_get(xhci);
+ xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
+ if (xfer->running_retry) {
+ return;
+ }
+ if (xhci_setup_packet(xfer) < 0) {
+ return;
+ }
+ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ assert(result != USB_RET_NAK);
+ xhci_complete_packet(xfer, result);
+ } else {
+ /* retry nak'ed transfer */
+ if (xhci_setup_packet(xfer) < 0) {
+ return;
+ }
+ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ if (result == USB_RET_NAK) {
+ return;
+ }
+ xhci_complete_packet(xfer, result);
}
- xhci_complete_packet(xfer, result);
assert(!xfer->running_retry);
epctx->retry = NULL;
}
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
- if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
+ if (xfer->running_async || xfer->running_retry) {
break;
}
length = xhci_ring_chain_length(xhci, &epctx->ring);
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
} else {
- fprintf(stderr, "xhci: error firing data transfer\n");
+ if (!xfer->iso_xfer) {
+ fprintf(stderr, "xhci: error firing data transfer\n");
+ }
}
}
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
port = (slot_ctx[1]>>16) & 0xFF;
- dev = xhci->ports[port-1].port.dev;
+ dev = xhci->ports[port-1].uport->dev;
- if (port < 1 || port > MAXPORTS) {
+ if (port < 1 || port > xhci->numports) {
fprintf(stderr, "xhci: bad port %d\n", port);
return CC_TRB_ERROR;
} else if (!dev) {
static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
{
dma_addr_t ctx;
- uint8_t bw_ctx[MAXPORTS+1];
+ uint8_t bw_ctx[xhci->numports+1];
DPRINTF("xhci_get_port_bandwidth()\n");
/* TODO: actually implement real values here */
bw_ctx[0] = 0;
- memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
+ memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx));
return CC_SUCCESS;
static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
{
- int nr = port->port.index + 1;
-
port->portsc = PORTSC_PP;
- if (port->port.dev && port->port.dev->attached && !is_detach) {
+ if (port->uport->dev && port->uport->dev->attached && !is_detach &&
+ (1 << port->uport->dev->speed) & port->speedmask) {
port->portsc |= PORTSC_CCS;
- switch (port->port.dev->speed) {
+ switch (port->uport->dev->speed) {
case USB_SPEED_LOW:
port->portsc |= PORTSC_SPEED_LOW;
break;
case USB_SPEED_HIGH:
port->portsc |= PORTSC_SPEED_HIGH;
break;
+ case USB_SPEED_SUPER:
+ port->portsc |= PORTSC_SPEED_SUPER;
+ break;
}
}
if (xhci_running(xhci)) {
port->portsc |= PORTSC_CSC;
- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+ port->portnr << 24};
xhci_event(xhci, &ev);
- DPRINTF("xhci: port change event for port %d\n", nr);
+ DPRINTF("xhci: port change event for port %d\n", port->portnr);
}
}
xhci_disable_slot(xhci, i+1);
}
- for (i = 0; i < MAXPORTS; i++) {
+ for (i = 0; i < xhci->numports; i++) {
xhci_update_port(xhci, xhci->ports + i, 0);
}
- xhci->mfindex = 0;
xhci->iman = 0;
xhci->imod = 0;
xhci->erstsz = 0;
xhci->er_full = 0;
xhci->ev_buffer_put = 0;
xhci->ev_buffer_get = 0;
+
+ xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
+ xhci_mfwrap_update(xhci);
}
static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
ret = 0x01000000 | LEN_CAP;
break;
case 0x04: /* HCSPARAMS 1 */
- ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+ ret = ((xhci->numports_2+xhci->numports_3)<<24)
+ | (MAXINTRS<<8) | MAXSLOTS;
break;
case 0x08: /* HCSPARAMS 2 */
ret = 0x0000000f;
ret = 0x20425455; /* "USB " */
break;
case 0x28: /* Supported Protocol:08 */
- ret = 0x00000001 | (USB2_PORTS<<8);
+ ret = 0x00000001 | (xhci->numports_2<<8);
break;
case 0x2c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
ret = 0x20425455; /* "USB " */
break;
case 0x38: /* Supported Protocol:08 */
- ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+ ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
break;
case 0x3c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
uint32_t port = reg >> 4;
uint32_t ret;
- if (port >= MAXPORTS) {
+ if (port >= xhci->numports) {
fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
ret = 0;
goto out;
trace_usb_xhci_port_write(port, reg & 0x0f, val);
- if (port >= MAXPORTS) {
+ if (port >= xhci->numports) {
fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
return;
}
/* write-1-to-start bits */
if (val & PORTSC_PR) {
DPRINTF("xhci: port %d reset\n", port);
- usb_device_reset(xhci->ports[port].port.dev);
+ usb_device_reset(xhci->ports[port].uport->dev);
portsc |= PORTSC_PRC | PORTSC_PED;
}
xhci->ports[port].portsc = portsc;
xhci_stop(xhci);
}
xhci->usbcmd = val & 0xc0f;
+ xhci_mfwrap_update(xhci);
if (val & USBCMD_HCRST) {
xhci_reset(&xhci->pci_dev.qdev);
}
switch (reg) {
case 0x00: /* MFINDEX */
- fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
- ret = xhci->mfindex;
+ ret = xhci_mfindex_get(xhci) & 0x3fff;
break;
case 0x20: /* IMAN */
ret = xhci->iman;
static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
{
- trace_usb_xhci_runtime_read(reg, val);
+ trace_usb_xhci_runtime_write(reg, val);
switch (reg) {
case 0x20: /* IMAN */
static void xhci_attach(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
xhci_update_port(xhci, port, 0);
}
static void xhci_detach(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
xhci_update_port(xhci, port, 1);
}
static void xhci_wakeup(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
- int nr = port->port.index + 1;
- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
+ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+ port->portnr << 24};
uint32_t pls;
pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
{
- int i;
+ XHCIPort *port;
+ int i, usbports, speedmask;
xhci->usbsts = USBSTS_HCH;
+ if (xhci->numports_2 > MAXPORTS_2) {
+ xhci->numports_2 = MAXPORTS_2;
+ }
+ if (xhci->numports_3 > MAXPORTS_3) {
+ xhci->numports_3 = MAXPORTS_3;
+ }
+ 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);
- for (i = 0; i < MAXPORTS; i++) {
- memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
- usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
- &xhci_port_ops,
- USB_SPEED_MASK_LOW |
- USB_SPEED_MASK_FULL |
- USB_SPEED_MASK_HIGH);
- }
- for (i = 0; i < MAXSLOTS; i++) {
- xhci->slots[i].enabled = 0;
+ for (i = 0; i < usbports; i++) {
+ speedmask = 0;
+ if (i < xhci->numports_2) {
+ port = &xhci->ports[i];
+ port->portnr = i + 1;
+ port->uport = &xhci->uports[i];
+ port->speedmask =
+ USB_SPEED_MASK_LOW |
+ USB_SPEED_MASK_FULL |
+ USB_SPEED_MASK_HIGH;
+ speedmask |= port->speedmask;
+ }
+ if (i < xhci->numports_3) {
+ port = &xhci->ports[i + xhci->numports_2];
+ port->portnr = i + 1 + xhci->numports_2;
+ port->uport = &xhci->uports[i];
+ port->speedmask = USB_SPEED_MASK_SUPER;
+ speedmask |= port->speedmask;
+ }
+ usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i,
+ &xhci_port_ops, speedmask);
}
}
usb_xhci_init(xhci, &dev->qdev);
+ xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
+
xhci->irq = xhci->pci_dev.irq[0];
memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
static Property xhci_properties[] = {
DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
+ DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
+ DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
DEFINE_PROP_END_OF_LIST(),
};