summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2015-01-02 18:06:26 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2015-01-02 18:06:26 +0000
commitb73a7b97bee1c7e62e7e4054650b23e440854fc0 (patch)
tree746d2ba853e8e4007f7fe55349b2b81984cf73fc /sys
parent9086276036e2deb946037850718933557ba4febe (diff)
When chaining TRBs, calculate the TD Size as described in section
4.11.2.4 instead of using one TRB per packet. Also make sure that it is not greater than 31. While here be paranoid about xfer buffer crossing a 64k boundary and use a supplementary TRB in such case. Fix a problem with uplcom(4) on Intel xHCI reported by jasper@.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/xhci.c48
-rw-r--r--sys/dev/usb/xhcireg.h3
2 files changed, 38 insertions, 13 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 118a5770b56..7f50578a4e7 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -1,4 +1,4 @@
-/* $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
@@ -2298,6 +2298,22 @@ xhci_root_intr_done(struct usbd_xfer *xfer)
{
}
+/* 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)
{
@@ -2341,7 +2357,8 @@ xhci_device_ctrl_start(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);
@@ -2418,7 +2435,7 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
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;
@@ -2428,10 +2445,17 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
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++;
@@ -2441,11 +2465,10 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
/* 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--) {
@@ -2458,14 +2481,14 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
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. */
@@ -2476,7 +2499,8 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
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);
diff --git a/sys/dev/usb/xhcireg.h b/sys/dev/usb/xhcireg.h
index 30233f56730..74e5edad8bb 100644
--- a/sys/dev/usb/xhcireg.h
+++ b/sys/dev/usb/xhcireg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: xhcireg.h,v 1.8 2014/12/21 11:20:24 mpi Exp $ */
+/* $OpenBSD: xhcireg.h,v 1.9 2015/01/02 18:06:25 mpi Exp $ */
/*-
* Copyright (c) 2014 Martin Pieuchot. All rights reserved.
@@ -336,6 +336,7 @@ struct xhci_inctx {
struct xhci_trb {
uint64_t trb_paddr;
#define XHCI_TRB_PORTID(x) (((x) & (0xff << 24)) >> 24) /* Port ID */
+#define XHCI_TRB_MAXSIZE (64 * 1024)
uint32_t trb_status;
#define XHCI_TRB_GET_CODE(x) (((x) >> 24) & 0xff)