mirror of https://github.com/xemu-project/xemu.git
usb-xhci: usb3 streams
Add streams support to the xhci emulation. No secondary streams yet, only linear stream arays are supported for now. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
8550a02d12
commit
024426acc0
|
@ -34,8 +34,8 @@
|
||||||
#else
|
#else
|
||||||
#define DPRINTF(...) do {} while (0)
|
#define DPRINTF(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
|
#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \
|
||||||
__func__, __LINE__); abort(); } while (0)
|
__func__, __LINE__, _msg); abort(); } while (0)
|
||||||
|
|
||||||
#define MAXPORTS_2 15
|
#define MAXPORTS_2 15
|
||||||
#define MAXPORTS_3 15
|
#define MAXPORTS_3 15
|
||||||
|
@ -301,6 +301,8 @@ typedef enum TRBCCode {
|
||||||
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
|
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
|
||||||
|
|
||||||
typedef struct XHCIState XHCIState;
|
typedef struct XHCIState XHCIState;
|
||||||
|
typedef struct XHCIStreamContext XHCIStreamContext;
|
||||||
|
typedef struct XHCIEPContext XHCIEPContext;
|
||||||
|
|
||||||
#define get_field(data, field) \
|
#define get_field(data, field) \
|
||||||
(((data) >> field##_SHIFT) & field##_MASK)
|
(((data) >> field##_SHIFT) & field##_MASK)
|
||||||
|
@ -351,6 +353,7 @@ typedef struct XHCITransfer {
|
||||||
unsigned int iso_pkts;
|
unsigned int iso_pkts;
|
||||||
unsigned int slotid;
|
unsigned int slotid;
|
||||||
unsigned int epid;
|
unsigned int epid;
|
||||||
|
unsigned int streamid;
|
||||||
bool in_xfer;
|
bool in_xfer;
|
||||||
bool iso_xfer;
|
bool iso_xfer;
|
||||||
|
|
||||||
|
@ -367,7 +370,14 @@ typedef struct XHCITransfer {
|
||||||
uint64_t mfindex_kick;
|
uint64_t mfindex_kick;
|
||||||
} XHCITransfer;
|
} XHCITransfer;
|
||||||
|
|
||||||
typedef struct XHCIEPContext {
|
struct XHCIStreamContext {
|
||||||
|
dma_addr_t pctx;
|
||||||
|
unsigned int sct;
|
||||||
|
XHCIRing ring;
|
||||||
|
XHCIStreamContext *sstreams;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XHCIEPContext {
|
||||||
XHCIState *xhci;
|
XHCIState *xhci;
|
||||||
unsigned int slotid;
|
unsigned int slotid;
|
||||||
unsigned int epid;
|
unsigned int epid;
|
||||||
|
@ -382,11 +392,17 @@ typedef struct XHCIEPContext {
|
||||||
unsigned int max_psize;
|
unsigned int max_psize;
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
|
|
||||||
|
/* streams */
|
||||||
|
unsigned int max_pstreams;
|
||||||
|
bool lsa;
|
||||||
|
unsigned int nr_pstreams;
|
||||||
|
XHCIStreamContext *pstreams;
|
||||||
|
|
||||||
/* iso xfer scheduling */
|
/* iso xfer scheduling */
|
||||||
unsigned int interval;
|
unsigned int interval;
|
||||||
int64_t mfindex_last;
|
int64_t mfindex_last;
|
||||||
QEMUTimer *kick_timer;
|
QEMUTimer *kick_timer;
|
||||||
} XHCIEPContext;
|
};
|
||||||
|
|
||||||
typedef struct XHCISlot {
|
typedef struct XHCISlot {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
@ -482,7 +498,7 @@ enum xhci_flags {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
|
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,
|
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
unsigned int epid);
|
unsigned int epid);
|
||||||
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
|
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||||
|
@ -1068,18 +1084,116 @@ static void xhci_stop(XHCIState *xhci)
|
||||||
xhci->crcr_low &= ~CRCR_CRR;
|
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));
|
||||||
|
fprintf(stderr, "%s: init sctx #%d @ %lx: %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;
|
||||||
|
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,
|
static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||||
uint32_t state)
|
XHCIStreamContext *sctx, uint32_t state)
|
||||||
{
|
{
|
||||||
uint32_t ctx[5];
|
uint32_t ctx[5];
|
||||||
|
uint32_t ctx2[2];
|
||||||
|
|
||||||
|
fprintf(stderr, "%s: epid %d, state %d\n",
|
||||||
|
__func__, epctx->epid, state);
|
||||||
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||||
ctx[0] &= ~EP_STATE_MASK;
|
ctx[0] &= ~EP_STATE_MASK;
|
||||||
ctx[0] |= state;
|
ctx[0] |= state;
|
||||||
|
|
||||||
|
/* 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[2] = epctx->ring.dequeue | epctx->ring.ccs;
|
||||||
ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
|
ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
|
||||||
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
|
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
|
||||||
epctx->pctx, state, ctx[3], ctx[2]);
|
epctx->pctx, state, ctx[3], ctx[2]);
|
||||||
|
}
|
||||||
|
|
||||||
xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||||
epctx->state = state;
|
epctx->state = state;
|
||||||
}
|
}
|
||||||
|
@ -1087,7 +1201,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||||
static void xhci_ep_kick_timer(void *opaque)
|
static void xhci_ep_kick_timer(void *opaque)
|
||||||
{
|
{
|
||||||
XHCIEPContext *epctx = 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,
|
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
|
@ -1117,16 +1231,22 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
slot->eps[epid-1] = epctx;
|
slot->eps[epid-1] = epctx;
|
||||||
|
|
||||||
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
|
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;
|
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);
|
DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
|
||||||
epctx->pctx = pctx;
|
epctx->pctx = pctx;
|
||||||
epctx->max_psize = ctx[1]>>16;
|
epctx->max_psize = ctx[1]>>16;
|
||||||
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
|
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",
|
DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
|
||||||
epid/2, epid%2, epctx->max_psize);
|
epid/2, epid%2, epctx->max_psize);
|
||||||
|
if (epctx->max_pstreams) {
|
||||||
|
xhci_alloc_streams(epctx, dequeue);
|
||||||
|
} else {
|
||||||
|
xhci_ring_init(xhci, &epctx->ring, dequeue);
|
||||||
|
epctx->ring.ccs = ctx[2] & 1;
|
||||||
|
}
|
||||||
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
|
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
|
||||||
usb_packet_init(&epctx->transfers[i].packet);
|
usb_packet_init(&epctx->transfers[i].packet);
|
||||||
}
|
}
|
||||||
|
@ -1227,7 +1347,11 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
|
|
||||||
epctx = slot->eps[epid-1];
|
epctx = slot->eps[epid-1];
|
||||||
|
|
||||||
xhci_set_ep_state(xhci, epctx, EP_DISABLED);
|
if (epctx->nr_pstreams) {
|
||||||
|
xhci_free_streams(epctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
|
||||||
|
|
||||||
qemu_free_timer(epctx->kick_timer);
|
qemu_free_timer(epctx->kick_timer);
|
||||||
g_free(epctx);
|
g_free(epctx);
|
||||||
|
@ -1264,7 +1388,11 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
|
|
||||||
epctx = slot->eps[epid-1];
|
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;
|
return CC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -1315,16 +1443,22 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
|
||||||
return CC_USB_TRANSACTION_ERROR;
|
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;
|
return CC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
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;
|
XHCISlot *slot;
|
||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
|
XHCIStreamContext *sctx;
|
||||||
dma_addr_t dequeue;
|
dma_addr_t dequeue;
|
||||||
|
|
||||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
@ -1334,7 +1468,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||||
return CC_TRB_ERROR;
|
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);
|
dequeue = xhci_mask64(pdequeue);
|
||||||
|
|
||||||
slot = &xhci->slots[slotid-1];
|
slot = &xhci->slots[slotid-1];
|
||||||
|
@ -1346,16 +1480,26 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||||
|
|
||||||
epctx = slot->eps[epid-1];
|
epctx = slot->eps[epid-1];
|
||||||
|
|
||||||
|
|
||||||
if (epctx->state != EP_STOPPED) {
|
if (epctx->state != EP_STOPPED) {
|
||||||
fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
|
fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
|
||||||
return CC_CONTEXT_STATE_ERROR;
|
return CC_CONTEXT_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
|
||||||
epctx->ring.ccs = dequeue & 1;
|
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;
|
return CC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -1484,12 +1628,22 @@ static void xhci_stall_ep(XHCITransfer *xfer)
|
||||||
XHCIState *xhci = xfer->xhci;
|
XHCIState *xhci = xfer->xhci;
|
||||||
XHCISlot *slot = &xhci->slots[xfer->slotid-1];
|
XHCISlot *slot = &xhci->slots[xfer->slotid-1];
|
||||||
XHCIEPContext *epctx = slot->eps[xfer->epid-1];
|
XHCIEPContext *epctx = slot->eps[xfer->epid-1];
|
||||||
|
uint32_t err;
|
||||||
|
XHCIStreamContext *sctx;
|
||||||
|
|
||||||
|
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.dequeue = xfer->trbs[0].addr;
|
||||||
epctx->ring.ccs = xfer->trbs[0].ccs;
|
epctx->ring.ccs = xfer->trbs[0].ccs;
|
||||||
xhci_set_ep_state(xhci, epctx, EP_HALTED);
|
xhci_set_ep_state(xhci, epctx, NULL, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
|
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
|
||||||
|
@ -1518,7 +1672,7 @@ static int xhci_setup_packet(XHCITransfer *xfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
|
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
|
||||||
usb_packet_setup(&xfer->packet, dir, ep, 0,
|
usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid,
|
||||||
xfer->trbs[0].addr, false, xfer->int_req);
|
xfer->trbs[0].addr, false, xfer->int_req);
|
||||||
usb_packet_map(&xfer->packet, &xfer->sgl);
|
usb_packet_map(&xfer->packet, &xfer->sgl);
|
||||||
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
||||||
|
@ -1572,7 +1726,7 @@ static int xhci_complete_packet(XHCITransfer *xfer)
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
|
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
|
||||||
xfer->packet.status);
|
xfer->packet.status);
|
||||||
FIXME();
|
FIXME("unhandled USB_RET_*");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1585,7 +1739,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
|
||||||
trb_setup = &xfer->trbs[0];
|
trb_setup = &xfer->trbs[0];
|
||||||
trb_status = &xfer->trbs[xfer->trb_count-1];
|
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 */
|
/* at most one Event Data TRB allowed after STATUS */
|
||||||
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
|
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
|
||||||
|
@ -1627,7 +1781,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
|
||||||
|
|
||||||
xhci_complete_packet(xfer);
|
xhci_complete_packet(xfer);
|
||||||
if (!xfer->running_async && !xfer->running_retry) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1710,26 +1864,29 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
|
||||||
|
|
||||||
xhci_complete_packet(xfer);
|
xhci_complete_packet(xfer);
|
||||||
if (!xfer->running_async && !xfer->running_retry) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
|
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);
|
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;
|
XHCIEPContext *epctx;
|
||||||
|
XHCIRing *ring;
|
||||||
USBEndpoint *ep = NULL;
|
USBEndpoint *ep = NULL;
|
||||||
uint64_t mfindex;
|
uint64_t mfindex;
|
||||||
int length;
|
int length;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_ep_kick(slotid, epid);
|
trace_usb_xhci_ep_kick(slotid, epid, streamid);
|
||||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
assert(epid >= 1 && epid <= 31);
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
|
@ -1782,14 +1939,28 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||||
return;
|
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->base != 0);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
|
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
|
||||||
if (xfer->running_async || xfer->running_retry) {
|
if (xfer->running_async || xfer->running_retry) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
length = xhci_ring_chain_length(xhci, &epctx->ring);
|
length = xhci_ring_chain_length(xhci, ring);
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
break;
|
break;
|
||||||
} else if (length == 0) {
|
} else if (length == 0) {
|
||||||
|
@ -1808,11 +1979,12 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||||
xfer->trb_count = length;
|
xfer->trb_count = length;
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
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->xhci = xhci;
|
||||||
xfer->epid = epid;
|
xfer->epid = epid;
|
||||||
xfer->slotid = slotid;
|
xfer->slotid = slotid;
|
||||||
|
xfer->streamid = streamid;
|
||||||
|
|
||||||
if (epid == 1) {
|
if (epid == 1) {
|
||||||
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
||||||
|
@ -2357,11 +2529,14 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CR_SET_TR_DEQUEUE:
|
case CR_SET_TR_DEQUEUE:
|
||||||
|
fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__);
|
||||||
slotid = xhci_get_slot(xhci, &event, &trb);
|
slotid = xhci_get_slot(xhci, &event, &trb);
|
||||||
if (slotid) {
|
if (slotid) {
|
||||||
unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
|
unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
|
||||||
& TRB_CR_EPID_MASK;
|
& 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);
|
trb.parameter);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2554,9 +2729,9 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||||
break;
|
break;
|
||||||
case 0x10: /* HCCPARAMS */
|
case 0x10: /* HCCPARAMS */
|
||||||
if (sizeof(dma_addr_t) == 4) {
|
if (sizeof(dma_addr_t) == 4) {
|
||||||
ret = 0x00081000;
|
ret = 0x00087000;
|
||||||
} else {
|
} else {
|
||||||
ret = 0x00081001;
|
ret = 0x00087001;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x14: /* DBOFF */
|
case 0x14: /* DBOFF */
|
||||||
|
@ -2880,6 +3055,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
||||||
uint64_t val, unsigned size)
|
uint64_t val, unsigned size)
|
||||||
{
|
{
|
||||||
XHCIState *xhci = ptr;
|
XHCIState *xhci = ptr;
|
||||||
|
unsigned int epid, streamid;
|
||||||
|
|
||||||
trace_usb_xhci_doorbell_write(reg, val);
|
trace_usb_xhci_doorbell_write(reg, val);
|
||||||
|
|
||||||
|
@ -2898,13 +3074,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
||||||
(uint32_t)val);
|
(uint32_t)val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
epid = val & 0xff;
|
||||||
|
streamid = (val >> 16) & 0xffff;
|
||||||
if (reg > xhci->numslots) {
|
if (reg > xhci->numslots) {
|
||||||
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
|
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",
|
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
|
||||||
(int)reg, (uint32_t)val);
|
(int)reg, (uint32_t)val);
|
||||||
} else {
|
} else {
|
||||||
xhci_kick_ep(xhci, reg, val);
|
xhci_kick_ep(xhci, reg, epid, streamid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2988,7 +3166,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
xhci_complete_packet(xfer);
|
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)
|
static void xhci_child_detach(USBPort *uport, USBDevice *child)
|
||||||
|
@ -3045,7 +3223,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
|
||||||
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
|
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
|
||||||
return;
|
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 = {
|
static USBBusOps xhci_bus_ops = {
|
||||||
|
|
|
@ -370,11 +370,11 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
|
||||||
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
|
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
|
||||||
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||||
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||||
usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64
|
usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64
|
||||||
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d"
|
||||||
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||||
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||||
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
|
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d"
|
||||||
usb_xhci_xfer_async(void *xfer) "%p"
|
usb_xhci_xfer_async(void *xfer) "%p"
|
||||||
usb_xhci_xfer_nak(void *xfer) "%p"
|
usb_xhci_xfer_nak(void *xfer) "%p"
|
||||||
usb_xhci_xfer_retry(void *xfer) "%p"
|
usb_xhci_xfer_retry(void *xfer) "%p"
|
||||||
|
|
Loading…
Reference in New Issue