Changeset View
Standalone View
usr.sbin/bhyve/pci_xhci.c
Show First 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | |||||
struct pci_xhci_dev_ep { | struct pci_xhci_dev_ep { | ||||
union { | union { | ||||
struct xhci_trb *_epu_tr; | struct xhci_trb *_epu_tr; | ||||
struct xhci_stream_ctx *_epu_sctx; | struct xhci_stream_ctx *_epu_sctx; | ||||
} _ep_trbsctx; | } _ep_trbsctx; | ||||
#define ep_tr _ep_trbsctx._epu_tr | #define ep_tr _ep_trbsctx._epu_tr | ||||
#define ep_sctx _ep_trbsctx._epu_sctx | #define ep_sctx _ep_trbsctx._epu_sctx | ||||
uint32_t ep_pstreams; | |||||
markj: It'd be helpful to have a comment indicating that this is a shadow of the max_pstreams field of… | |||||
markjUnsubmitted Not Done Inline Actions(Also note that ep_maxpstreams might be a more accurate name.) markj: (Also note that ep_maxpstreams might be a more accurate name.) | |||||
jhbAuthorUnsubmitted Done Inline ActionsI will do both of those. Much of this file is under-commented though and would seem to assume knowledge of the XHCI spec in which case a more properly named field (I agree with ep_maxpstreams or possibly even ep_MaxPStreams to match the spec) is perhaps sufficient? The comment would likely say something like "Cached value of the MaxPStreams field from the endpoint context." jhb: I will do both of those. Much of this file is under-commented though and would seem to assume… | |||||
union { | union { | ||||
struct pci_xhci_trb_ring _epu_trb; | struct pci_xhci_trb_ring _epu_trb; | ||||
struct pci_xhci_trb_ring *_epu_sctx_trbs; | struct pci_xhci_trb_ring *_epu_sctx_trbs; | ||||
} _ep_trb_rings; | } _ep_trb_rings; | ||||
#define ep_ringaddr _ep_trb_rings._epu_trb.ringaddr | #define ep_ringaddr _ep_trb_rings._epu_trb.ringaddr | ||||
#define ep_ccs _ep_trb_rings._epu_trb.ccs | #define ep_ccs _ep_trb_rings._epu_trb.ccs | ||||
#define ep_sctx_trbs _ep_trb_rings._epu_sctx_trbs | #define ep_sctx_trbs _ep_trb_rings._epu_sctx_trbs | ||||
▲ Show 20 Lines • Show All 487 Lines • ▼ Show 20 Lines | pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid) | ||||
} else { | } else { | ||||
DPRINTF(("init_ep %d with no pstreams", epid)); | DPRINTF(("init_ep %d with no pstreams", epid)); | ||||
devep->ep_ringaddr = ep_ctx->qwEpCtx2 & | devep->ep_ringaddr = ep_ctx->qwEpCtx2 & | ||||
XHCI_EPCTX_2_TR_DQ_PTR_MASK; | XHCI_EPCTX_2_TR_DQ_PTR_MASK; | ||||
devep->ep_ccs = XHCI_EPCTX_2_DCS_GET(ep_ctx->qwEpCtx2); | devep->ep_ccs = XHCI_EPCTX_2_DCS_GET(ep_ctx->qwEpCtx2); | ||||
devep->ep_tr = XHCI_GADDR(dev->xsc, devep->ep_ringaddr); | devep->ep_tr = XHCI_GADDR(dev->xsc, devep->ep_ringaddr); | ||||
DPRINTF(("init_ep tr DCS %x", devep->ep_ccs)); | DPRINTF(("init_ep tr DCS %x", devep->ep_ccs)); | ||||
} | } | ||||
devep->ep_pstreams = pstreams; | |||||
if (devep->ep_xfer == NULL) { | if (devep->ep_xfer == NULL) { | ||||
devep->ep_xfer = malloc(sizeof(struct usb_data_xfer)); | devep->ep_xfer = malloc(sizeof(struct usb_data_xfer)); | ||||
USB_DATA_XFER_INIT(devep->ep_xfer); | USB_DATA_XFER_INIT(devep->ep_xfer); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
pci_xhci_disable_ep(struct pci_xhci_dev_emu *dev, int epid) | pci_xhci_disable_ep(struct pci_xhci_dev_emu *dev, int epid) | ||||
{ | { | ||||
struct xhci_dev_ctx *dev_ctx; | struct xhci_dev_ctx *dev_ctx; | ||||
struct pci_xhci_dev_ep *devep; | struct pci_xhci_dev_ep *devep; | ||||
struct xhci_endp_ctx *ep_ctx; | struct xhci_endp_ctx *ep_ctx; | ||||
DPRINTF(("pci_xhci disable_ep %d", epid)); | DPRINTF(("pci_xhci disable_ep %d", epid)); | ||||
dev_ctx = dev->dev_ctx; | dev_ctx = dev->dev_ctx; | ||||
ep_ctx = &dev_ctx->ctx_ep[epid]; | ep_ctx = &dev_ctx->ctx_ep[epid]; | ||||
ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_DISABLED; | ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_DISABLED; | ||||
devep = &dev->eps[epid]; | devep = &dev->eps[epid]; | ||||
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0 && | if (devep->ep_pstreams > 0) | ||||
devep->ep_sctx_trbs != NULL) | |||||
free(devep->ep_sctx_trbs); | free(devep->ep_sctx_trbs); | ||||
if (devep->ep_xfer != NULL) { | if (devep->ep_xfer != NULL) { | ||||
free(devep->ep_xfer); | free(devep->ep_xfer); | ||||
devep->ep_xfer = NULL; | devep->ep_xfer = NULL; | ||||
} | } | ||||
memset(devep, 0, sizeof(struct pci_xhci_dev_ep)); | memset(devep, 0, sizeof(struct pci_xhci_dev_ep)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 443 Lines • ▼ Show 20 Lines | pci_xhci_cmd_reset_ep(struct pci_xhci_softc *sc, uint32_t slot, | ||||
dev_ctx = dev->dev_ctx; | dev_ctx = dev->dev_ctx; | ||||
assert(dev_ctx != NULL); | assert(dev_ctx != NULL); | ||||
ep_ctx = &dev_ctx->ctx_ep[epid]; | ep_ctx = &dev_ctx->ctx_ep[epid]; | ||||
ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED; | ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED; | ||||
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) == 0) | if (devep->ep_pstreams == 0) | ||||
ep_ctx->qwEpCtx2 = devep->ep_ringaddr | devep->ep_ccs; | ep_ctx->qwEpCtx2 = devep->ep_ringaddr | devep->ep_ccs; | ||||
DPRINTF(("pci_xhci: reset ep[%u] %08x %08x %016lx %08x", | DPRINTF(("pci_xhci: reset ep[%u] %08x %08x %016lx %08x", | ||||
epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2, | epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2, | ||||
ep_ctx->dwEpCtx4)); | ep_ctx->dwEpCtx4)); | ||||
if (type == XHCI_TRB_TYPE_RESET_EP && | if (type == XHCI_TRB_TYPE_RESET_EP && | ||||
(dev->dev_ue->ue_reset == NULL || | (dev->dev_ue->ue_reset == NULL || | ||||
dev->dev_ue->ue_reset(dev->dev_sc) < 0)) { | dev->dev_ue->ue_reset(dev->dev_sc) < 0)) { | ||||
cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON; | cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON; | ||||
goto done; | goto done; | ||||
} | } | ||||
done: | done: | ||||
return (cmderr); | return (cmderr); | ||||
} | } | ||||
static uint32_t | static uint32_t | ||||
pci_xhci_find_stream(struct pci_xhci_softc *sc, struct xhci_endp_ctx *ep, | pci_xhci_find_stream(struct pci_xhci_softc *sc, struct xhci_endp_ctx *ep, | ||||
uint32_t streamid, struct xhci_stream_ctx **osctx) | struct pci_xhci_dev_ep *devep, uint32_t streamid, | ||||
struct xhci_stream_ctx **osctx) | |||||
{ | { | ||||
struct xhci_stream_ctx *sctx; | struct xhci_stream_ctx *sctx; | ||||
uint32_t maxpstreams; | |||||
maxpstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep->dwEpCtx0); | if (devep->ep_pstreams == 0) | ||||
if (maxpstreams == 0) | |||||
return (XHCI_TRB_ERROR_TRB); | return (XHCI_TRB_ERROR_TRB); | ||||
if (maxpstreams > XHCI_STREAMS_MAX) | if (devep->ep_pstreams > XHCI_STREAMS_MAX) | ||||
return (XHCI_TRB_ERROR_INVALID_SID); | return (XHCI_TRB_ERROR_INVALID_SID); | ||||
if (XHCI_EPCTX_0_LSA_GET(ep->dwEpCtx0) == 0) { | if (XHCI_EPCTX_0_LSA_GET(ep->dwEpCtx0) == 0) { | ||||
DPRINTF(("pci_xhci: find_stream; LSA bit not set")); | DPRINTF(("pci_xhci: find_stream; LSA bit not set")); | ||||
return (XHCI_TRB_ERROR_INVALID_SID); | return (XHCI_TRB_ERROR_INVALID_SID); | ||||
} | } | ||||
/* only support primary stream */ | /* only support primary stream */ | ||||
if (streamid > maxpstreams) | if (streamid > devep->ep_pstreams) | ||||
return (XHCI_TRB_ERROR_STREAM_TYPE); | return (XHCI_TRB_ERROR_STREAM_TYPE); | ||||
sctx = XHCI_GADDR(sc, ep->qwEpCtx2 & ~0xFUL) + streamid; | sctx = XHCI_GADDR(sc, ep->qwEpCtx2 & ~0xFUL) + streamid; | ||||
markjUnsubmitted Not Done Inline ActionsI don't quite understand this line, shouldn't it be adding streamid * sizeof(*sctx) or something like that? markj: I don't quite understand this line, shouldn't it be adding `streamid * sizeof(*sctx)` or… | |||||
jhbAuthorUnsubmitted Done Inline ActionsHmm, or we should be casting the result of XHCI_GADDR to the right type, yes. jhb: Hmm, or we should be casting the result of XHCI_GADDR to the right type, yes. | |||||
if (!XHCI_SCTX_0_SCT_GET(sctx->qwSctx0)) | if (!XHCI_SCTX_0_SCT_GET(sctx->qwSctx0)) | ||||
return (XHCI_TRB_ERROR_STREAM_TYPE); | return (XHCI_TRB_ERROR_STREAM_TYPE); | ||||
*osctx = sctx; | *osctx = sctx; | ||||
return (XHCI_TRB_ERROR_SUCCESS); | return (XHCI_TRB_ERROR_SUCCESS); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | pci_xhci_cmd_set_tr(struct pci_xhci_softc *sc, uint32_t slot, | ||||
default: | default: | ||||
DPRINTF(("pci_xhci cmd set_tr invalid state %x", | DPRINTF(("pci_xhci cmd set_tr invalid state %x", | ||||
XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0))); | XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0))); | ||||
cmderr = XHCI_TRB_ERROR_CONTEXT_STATE; | cmderr = XHCI_TRB_ERROR_CONTEXT_STATE; | ||||
goto done; | goto done; | ||||
} | } | ||||
streamid = XHCI_TRB_2_STREAM_GET(trb->dwTrb2); | streamid = XHCI_TRB_2_STREAM_GET(trb->dwTrb2); | ||||
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0) { | if (devep->ep_pstreams > 0) { | ||||
struct xhci_stream_ctx *sctx; | struct xhci_stream_ctx *sctx; | ||||
sctx = NULL; | sctx = NULL; | ||||
cmderr = pci_xhci_find_stream(sc, ep_ctx, streamid, &sctx); | cmderr = pci_xhci_find_stream(sc, ep_ctx, devep, streamid, | ||||
&sctx); | |||||
if (sctx != NULL) { | if (sctx != NULL) { | ||||
assert(devep->ep_sctx != NULL); | assert(devep->ep_sctx != NULL); | ||||
devep->ep_sctx[streamid].qwSctx0 = trb->qwTrb0; | devep->ep_sctx[streamid].qwSctx0 = trb->qwTrb0; | ||||
markjUnsubmitted Not Done Inline ActionsIt looks like this code is supposed to be using sctx instead of devep->ep_sctx[streamid]. markj: It looks like this code is supposed to be using `sctx` instead of `devep->ep_sctx[streamid]`. | |||||
jhbAuthorUnsubmitted Done Inline ActionsI bet this then is the other half of the bug you found above (or rather, this makes the bug you found above harmless since the incorrect pointer isn't used). I can clean this up though in a followup commit. jhb: I bet this then is the other half of the bug you found above (or rather, this makes the bug you… | |||||
markjUnsubmitted Not Done Inline ActionsThanks. The other half will be caught eventually when we bump WARNS (arithmetic on a void pointer), but this should be cleaned up too. BTW, I think the other half of the bug is not quite harmless since pci_xhci_find_stream() checks some bits in sctx->qwSctx0, but I guess it happens to work out. markj: Thanks. The other half will be caught eventually when we bump WARNS (arithmetic on a void… | |||||
devep->ep_sctx_trbs[streamid].ringaddr = | devep->ep_sctx_trbs[streamid].ringaddr = | ||||
trb->qwTrb0 & ~0xF; | trb->qwTrb0 & ~0xF; | ||||
devep->ep_sctx_trbs[streamid].ccs = | devep->ep_sctx_trbs[streamid].ccs = | ||||
XHCI_EPCTX_2_DCS_GET(trb->qwTrb0); | XHCI_EPCTX_2_DCS_GET(trb->qwTrb0); | ||||
} | } | ||||
} else { | } else { | ||||
if (streamid != 0) { | if (streamid != 0) { | ||||
DPRINTF(("pci_xhci cmd set_tr streamid %x != 0", | DPRINTF(("pci_xhci cmd set_tr streamid %x != 0", | ||||
▲ Show 20 Lines • Show All 349 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
pci_xhci_update_ep_ring(struct pci_xhci_softc *sc, struct pci_xhci_dev_emu *dev, | pci_xhci_update_ep_ring(struct pci_xhci_softc *sc, struct pci_xhci_dev_emu *dev, | ||||
struct pci_xhci_dev_ep *devep, struct xhci_endp_ctx *ep_ctx, | struct pci_xhci_dev_ep *devep, struct xhci_endp_ctx *ep_ctx, | ||||
uint32_t streamid, uint64_t ringaddr, int ccs) | uint32_t streamid, uint64_t ringaddr, int ccs) | ||||
{ | { | ||||
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) { | if (devep->ep_pstreams != 0) { | ||||
devep->ep_sctx[streamid].qwSctx0 = (ringaddr & ~0xFUL) | | devep->ep_sctx[streamid].qwSctx0 = (ringaddr & ~0xFUL) | | ||||
(ccs & 0x1); | (ccs & 0x1); | ||||
devep->ep_sctx_trbs[streamid].ringaddr = ringaddr & ~0xFUL; | devep->ep_sctx_trbs[streamid].ringaddr = ringaddr & ~0xFUL; | ||||
devep->ep_sctx_trbs[streamid].ccs = ccs & 0x1; | devep->ep_sctx_trbs[streamid].ccs = ccs & 0x1; | ||||
ep_ctx->qwEpCtx2 = (ep_ctx->qwEpCtx2 & ~0x1) | (ccs & 0x1); | ep_ctx->qwEpCtx2 = (ep_ctx->qwEpCtx2 & ~0x1) | (ccs & 0x1); | ||||
DPRINTF(("xhci update ep-ring stream %d, addr %lx", | DPRINTF(("xhci update ep-ring stream %d, addr %lx", | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | pci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot, | ||||
/* handle pending transfers */ | /* handle pending transfers */ | ||||
if (devep->ep_xfer->ndata > 0) { | if (devep->ep_xfer->ndata > 0) { | ||||
pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid); | pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid); | ||||
return; | return; | ||||
} | } | ||||
/* get next trb work item */ | /* get next trb work item */ | ||||
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) { | if (devep->ep_pstreams != 0) { | ||||
struct xhci_stream_ctx *sctx; | struct xhci_stream_ctx *sctx; | ||||
/* | /* | ||||
* Stream IDs of 0, 65535 (any stream), and 65534 | * Stream IDs of 0, 65535 (any stream), and 65534 | ||||
* (prime) are invalid. | * (prime) are invalid. | ||||
*/ | */ | ||||
if (streamid == 0 || streamid == 65534 || streamid == 65535) { | if (streamid == 0 || streamid == 65534 || streamid == 65535) { | ||||
DPRINTF(("pci_xhci: invalid stream %u", streamid)); | DPRINTF(("pci_xhci: invalid stream %u", streamid)); | ||||
return; | return; | ||||
} | } | ||||
sctx = NULL; | sctx = NULL; | ||||
pci_xhci_find_stream(sc, ep_ctx, streamid, &sctx); | pci_xhci_find_stream(sc, ep_ctx, devep, streamid, &sctx); | ||||
if (sctx == NULL) { | if (sctx == NULL) { | ||||
DPRINTF(("pci_xhci: invalid stream %u", streamid)); | DPRINTF(("pci_xhci: invalid stream %u", streamid)); | ||||
return; | return; | ||||
} | } | ||||
sctx_tr = &devep->ep_sctx_trbs[streamid]; | sctx_tr = &devep->ep_sctx_trbs[streamid]; | ||||
ringaddr = sctx_tr->ringaddr; | ringaddr = sctx_tr->ringaddr; | ||||
ccs = sctx_tr->ccs; | ccs = sctx_tr->ccs; | ||||
trb = XHCI_GADDR(sc, sctx_tr->ringaddr & ~0xFUL); | trb = XHCI_GADDR(sc, sctx_tr->ringaddr & ~0xFUL); | ||||
▲ Show 20 Lines • Show All 1,227 Lines • Show Last 20 Lines |
It'd be helpful to have a comment indicating that this is a shadow of the max_pstreams field of epctx0.