When chaining TRBs, calculate the TD Size as described in section
authormpi <mpi@openbsd.org>
Fri, 2 Jan 2015 18:06:25 +0000 (18:06 +0000)
committermpi <mpi@openbsd.org>
Fri, 2 Jan 2015 18:06:25 +0000 (18:06 +0000)
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@.

sys/dev/usb/xhci.c
sys/dev/usb/xhcireg.h

index 118a577..7f50578 100644 (file)
@@ -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);
 
index 30233f5..74e5eda 100644 (file)
@@ -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)