-/* $OpenBSD: xhci.c,v 1.49 2014/12/21 11:46:53 mpi Exp $ */
+/* $OpenBSD: xhci.c,v 1.50 2015/01/02 18:06:25 mpi Exp $ */
/*
* Copyright (c) 2014 Martin Pieuchot
{
}
+/* Number of packets remaining in the TD after the corresponding TRB. */
+static inline uint32_t
+xhci_xfer_tdsize(struct usbd_xfer *xfer, uint32_t remain, uint32_t len)
+{
+ uint32_t npkt, mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
+
+ if (len == 0)
+ return XHCI_TRB_TDREM(0);
+
+ npkt = (remain - len) / mps;
+ if (npkt > 31)
+ npkt = 31;
+
+ return XHCI_TRB_TDREM(npkt);
+}
+
usbd_status
xhci_device_ctrl_transfer(struct usbd_xfer *xfer)
{
trb->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
trb->trb_status = htole32(
- XHCI_TRB_INTR(0) | XHCI_TRB_TDREM(1) | XHCI_TRB_LEN(len)
+ XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len) |
+ xhci_xfer_tdsize(xfer, len, len)
);
trb->trb_flags = htole32(flags);
struct xhci_trb *trb0, *trb;
uint32_t len, remain, flags;
uint32_t len0, mps;
- uint64_t paddr;
+ uint64_t paddr = DMAADDR(&xfer->dmabuf, 0);
uint8_t toggle0, toggle;
int s, i, ntrb;
return (USBD_IOERROR);
/* How many TRBs do we need for this transfer? */
- mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
- ntrb = (xfer->length + mps - 1) / mps;
+ ntrb = (xfer->length + XHCI_TRB_MAXSIZE - 1) / XHCI_TRB_MAXSIZE;
+
+ /* If the buffer crosses a 64k boundary, we need one more. */
+ len0 = XHCI_TRB_MAXSIZE - (paddr & (XHCI_TRB_MAXSIZE - 1));
+ if (len0 < xfer->length)
+ ntrb++;
+ else
+ len0 = xfer->length;
/* If we need to append a zero length packet, we need one more. */
+ mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) &&
(xfer->length % mps == 0))
ntrb++;
/* We'll do the first TRB once we're finished with the chain. */
trb0 = xhci_xfer_get_trb(sc, xfer, &toggle0, (ntrb == 1));
- len0 = min(xfer->length, mps);
remain = xfer->length - len0;
- paddr = DMAADDR(&xfer->dmabuf, 0) + len0;
- len = min(remain, mps);
+ paddr += len0;
+ len = min(remain, XHCI_TRB_MAXSIZE);
/* Chain more TRBs if needed. */
for (i = ntrb - 1; i > 0; i--) {
trb->trb_paddr = htole64(paddr);
trb->trb_status = htole32(
- XHCI_TRB_INTR(0) | XHCI_TRB_TDREM(i) |
- XHCI_TRB_LEN(len)
+ XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len) |
+ xhci_xfer_tdsize(xfer, remain, len)
);
trb->trb_flags = htole32(flags);
remain -= len;
paddr += len;
- len = min(remain, mps);
+ len = min(remain, XHCI_TRB_MAXSIZE);
}
/* First TRB. */
trb0->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
trb0->trb_status = htole32(
- XHCI_TRB_INTR(0) | XHCI_TRB_TDREM(ntrb) | XHCI_TRB_LEN(len0)
+ XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len0) |
+ xhci_xfer_tdsize(xfer, xfer->length, len0)
);
trb0->trb_flags = htole32(flags);