Use the logic for building a 'struct xhci_trb' that was in
xhci_xfer_queue() up so that command and ring TRBs can also use that
functionality. This eliminates the need to manually generate the
xhci_trb struct from those code paths.
Signed-off-by: Kevin O'Connor <kevin(a)koconnor.net>
---
src/hw/usb-xhci.c | 162 +++++++++++++++++++-----------------------------------
1 file changed, 57 insertions(+), 105 deletions(-)
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c
index 50b3b86..a92a162 100644
--- a/src/hw/usb-xhci.c
+++ b/src/hw/usb-xhci.c
@@ -641,13 +641,16 @@ xhci_setup(void)
* End point communication
****************************************************************/
+// Signal the hardware to process events on a TRB ring
static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value)
{
+ dprintf(5, "%s: slotid %d, epid %d\n", __func__, slotid, value);
struct xhci_db *db = xhci->db;
void *addr = &db[slotid].doorbell;
writel(addr, value);
}
+// Dequeue events on the XHCI command ring generated by the hardware
static void xhci_process_events(struct usb_xhci_s *xhci)
{
struct xhci_ring *evts = xhci->evts;
@@ -712,6 +715,7 @@ static void xhci_process_events(struct usb_xhci_s *xhci)
}
}
+// Check if a ring has any pending TRBs
static int xhci_ring_busy(struct xhci_ring *ring)
{
u32 eidx = ring->eidx;
@@ -719,6 +723,7 @@ static int xhci_ring_busy(struct xhci_ring *ring)
return (eidx != nidx);
}
+// Wait for a ring to empty (all TRBs processed by hardware)
static int xhci_event_wait(struct usb_xhci_s *xhci,
struct xhci_ring *ring,
u32 timeout)
@@ -739,69 +744,54 @@ static int xhci_event_wait(struct usb_xhci_s *xhci,
}
}
-static void xhci_trb_queue(struct xhci_ring *ring,
- struct xhci_trb *trb)
+// Add a TRB to the given ring
+static void xhci_trb_fill(struct xhci_ring *ring
+ , void *data, u32 xferlen, u32 flags)
{
- u32 nidx = ring->nidx;
- u32 cs = ring->cs;
- struct xhci_trb *dst;
- u32 control;
-
- if (nidx == XHCI_RING_ITEMS-1) {
- dst = ring->ring + nidx;
- control = (TR_LINK << 10); // trb type
- control |= TRB_LK_TC;
- control |= (cs ? TRB_C : 0);
- dst->ptr_low = (u32)&ring[0];
+ struct xhci_trb *dst = &ring->ring[ring->nidx];
+ if (flags & TRB_TR_IDT) {
+ memcpy(&dst->ptr_low, data, xferlen);
+ } else {
+ dst->ptr_low = (u32)data;
dst->ptr_high = 0;
- dst->status = 0;
- dst->control = control;
- nidx = 0;
- cs = cs ? 0 : 1;
- ring->nidx = nidx;
- ring->cs = cs;
+ }
+ dst->status = xferlen;
+ dst->control = flags | (ring->cs ? TRB_C : 0);
+}
+// Queue a TRB onto a ring, wrapping ring as needed
+static void xhci_trb_queue(struct xhci_ring *ring,
+ void *data, u32 xferlen, u32 flags)
+{
+ if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1) {
+ xhci_trb_fill(ring, ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC);
+ ring->nidx = 0;
+ ring->cs ^= 1;
dprintf(5, "%s: ring %p [linked]\n", __func__, ring);
}
- dst = ring->ring + nidx;
- control = trb->control | (cs ? TRB_C : 0);
-
- dst->ptr_low = trb->ptr_low;
- dst->ptr_high = trb->ptr_high;
- dst->status = trb->status;
- dst->control = control;
- nidx++;
- ring->nidx = nidx;
-
+ xhci_trb_fill(ring, data, xferlen, flags);
+ ring->nidx++;
dprintf(5, "%s: ring %p [nidx %d, len %d]\n",
- __func__, ring, nidx,
- trb->status & 0xffff);
+ __func__, ring, ring->nidx, xferlen);
}
-static int xhci_cmd_submit(struct usb_xhci_s *xhci,
- struct xhci_trb *cmd)
+// Submit a command to the xhci controller ring
+static int xhci_cmd_submit(struct usb_xhci_s *xhci, struct xhci_inctx *inctx
+ , u32 flags)
{
- int rc;
-
mutex_lock(&xhci->cmds->lock);
- xhci_trb_queue(xhci->cmds, cmd);
+ xhci_trb_queue(xhci->cmds, inctx, 0, flags);
xhci_doorbell(xhci, 0, 0);
- rc = xhci_event_wait(xhci, xhci->cmds, 1000);
+ int rc = xhci_event_wait(xhci, xhci->cmds, 1000);
mutex_unlock(&xhci->cmds->lock);
return rc;
}
static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci)
{
- struct xhci_trb cmd = {
- .ptr_low = 0,
- .ptr_high = 0,
- .status = 0,
- .control = (CR_ENABLE_SLOT << 10)
- };
dprintf(3, "%s:\n", __func__);
- int cc = xhci_cmd_submit(xhci, &cmd);
+ int cc = xhci_cmd_submit(xhci, NULL, CR_ENABLE_SLOT << 10);
if (cc != CC_SUCCESS)
return -1;
return (xhci->cmds->evt.control >> 24) & 0xff;
@@ -809,55 +799,34 @@ static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci)
static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid)
{
- struct xhci_trb cmd = {
- .ptr_low = 0,
- .ptr_high = 0,
- .status = 0,
- .control = (slotid << 24) | (CR_DISABLE_SLOT << 10)
- };
dprintf(3, "%s: slotid %d\n", __func__, slotid);
- return xhci_cmd_submit(xhci, &cmd);
+ return xhci_cmd_submit(xhci, NULL, (CR_DISABLE_SLOT << 10) | (slotid << 24));
}
static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid
, struct xhci_inctx *inctx)
{
- struct xhci_trb cmd = {
- .ptr_low = (u32)inctx,
- .ptr_high = 0,
- .status = 0,
- .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10)
- };
dprintf(3, "%s: slotid %d\n", __func__, slotid);
- return xhci_cmd_submit(xhci, &cmd);
+ return xhci_cmd_submit(xhci, inctx
+ , (CR_ADDRESS_DEVICE << 10) | (slotid << 24));
}
static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid
, struct xhci_inctx *inctx)
{
- struct xhci_trb cmd = {
- .ptr_low = (u32)inctx,
- .ptr_high = 0,
- .status = 0,
- .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10)
- };
dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__,
slotid, inctx->add, inctx->del);
- return xhci_cmd_submit(xhci, &cmd);
+ return xhci_cmd_submit(xhci, inctx
+ , (CR_CONFIGURE_ENDPOINT << 10) | (slotid << 24));
}
static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid
, struct xhci_inctx *inctx)
{
- struct xhci_trb cmd = {
- .ptr_low = (u32)inctx,
- .ptr_high = 0,
- .status = 0,
- .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10)
- };
dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__,
slotid, inctx->add, inctx->del);
- return xhci_cmd_submit(xhci, &cmd);
+ return xhci_cmd_submit(xhci, inctx
+ , (CR_EVALUATE_CONTEXT << 10) | (slotid << 24));
}
static struct xhci_inctx *
@@ -1092,37 +1061,29 @@ xhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
return upipe;
}
-static void xhci_xfer_queue(struct xhci_pipe *pipe,
- void *data, int datalen, u32 flags)
-{
- struct xhci_trb trb;
- memset(&trb, 0, sizeof(trb));
- if (flags & TRB_TR_IDT)
- memcpy(&trb.ptr_low, data, datalen);
- else
- trb.ptr_low = (u32)data;
- trb.status = datalen;
- trb.control = flags;
- xhci_trb_queue(&pipe->reqs, &trb);
-}
-
-static void xhci_xfer_kick(struct xhci_pipe *pipe)
+static void xhci_xfer_setup(struct xhci_pipe *pipe, int dir, void *cmd
+ , void *data, int datalen)
{
struct usb_xhci_s *xhci = container_of(
pipe->pipe.cntl, struct usb_xhci_s, usb);
- u32 slotid = pipe->slotid;
- u32 epid = pipe->epid;
-
- dprintf(5, "%s: ring %p, slotid %d, epid %d\n",
- __func__, &pipe->reqs, slotid, epid);
- xhci_doorbell(xhci, slotid, epid);
+ xhci_trb_queue(&pipe->reqs, cmd, USB_CONTROL_SETUP_SIZE
+ , (TR_SETUP << 10) | TRB_TR_IDT
+ | ((datalen ? (dir ? 3 : 2) : 0) << 16));
+ if (datalen)
+ xhci_trb_queue(&pipe->reqs, data, datalen, (TR_DATA << 10)
+ | ((dir ? 1 : 0) << 16));
+ xhci_trb_queue(&pipe->reqs, NULL, 0, (TR_STATUS << 10) | TRB_TR_IOC
+ | ((dir ? 0 : 1) << 16));
+ xhci_doorbell(xhci, pipe->slotid, pipe->epid);
}
static void xhci_xfer_normal(struct xhci_pipe *pipe,
void *data, int datalen)
{
- xhci_xfer_queue(pipe, data, datalen, (TR_NORMAL << 10) | TRB_TR_IOC);
- xhci_xfer_kick(pipe);
+ struct usb_xhci_s *xhci = container_of(
+ pipe->pipe.cntl, struct usb_xhci_s, usb);
+ xhci_trb_queue(&pipe->reqs, data, datalen, (TR_NORMAL << 10) | TRB_TR_IOC);
+ xhci_doorbell(xhci, pipe->slotid, pipe->epid);
}
int
@@ -1140,16 +1101,7 @@ xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd
if (req->bRequest == USB_REQ_SET_ADDRESS)
// Set address command sent during xhci_alloc_pipe.
return 0;
-
- xhci_xfer_queue(pipe, (void*)req, USB_CONTROL_SETUP_SIZE
- , (TR_SETUP << 10) | TRB_TR_IDT
- | ((datalen ? (dir ? 3 : 2) : 0) << 16));
- if (datalen)
- xhci_xfer_queue(pipe, data, datalen, (TR_DATA << 10)
- | ((dir ? 1 : 0) << 16));
- xhci_xfer_queue(pipe, NULL, 0, (TR_STATUS << 10) | TRB_TR_IOC
- | ((dir ? 0 : 1) << 16));
- xhci_xfer_kick(pipe);
+ xhci_xfer_setup(pipe, dir, (void*)req, data, datalen);
} else {
xhci_xfer_normal(pipe, data, datalen);
}
--
2.9.5