From 6c7284e609546a922e70009d739c7b3ec3697258 Mon Sep 17 00:00:00 2001 From: mpi Date: Fri, 2 Jan 2015 18:06:25 +0000 Subject: [PATCH] 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@. --- sys/dev/usb/xhci.c | 48 ++++++++++++++++++++++++++++++++----------- sys/dev/usb/xhcireg.h | 3 ++- 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) -- 2.20.1