This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 9e1e3a272a02189db5ed859ed213297a3dfa1a06 Author: Justin Hammond <[email protected]> AuthorDate: Sun Aug 16 17:12:59 2026 +0800 drivers/usbhost: Chain xHCI TRBs across a 64K boundary. A Normal TRB describes one run of memory that may not cross a 64K boundary, and the block layer hands down whole multi-sector reads whose length is bounded by nothing here. One TRB was programmed regardless, so a long enough transfer, or merely one starting near the wrong side of a boundary, produced a descriptor the controller is entitled to reject or to satisfy in part. Program as many as the run needs, chained, asking for the completion interrupt only on the last so one event still arrives for the transfer. A transfer needing more TRBs than the ring holds is refused. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <[email protected]> --- drivers/usbhost/usbhost_xhci.c | 53 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/usbhost/usbhost_xhci.c b/drivers/usbhost/usbhost_xhci.c index 7b076098a97..0c649ce8fc5 100644 --- a/drivers/usbhost/usbhost_xhci.c +++ b/drivers/usbhost/usbhost_xhci.c @@ -2365,15 +2365,56 @@ static int xhci_normal_setup(FAR struct xhci_rhport_s *rhport, FAR struct usbhost_xhci_s *priv = XHCI_PRIV_FROM_RHPORT(rhport); struct xhci_trb_s trb; - /* Prepare TRB */ + size_t left; + size_t chunk; + uintptr_t pa; + int n = 0; + + /* One TRB describes one run of memory, and that run may not cross a 64K + * boundary. A longer transfer, or one starting near the wrong side of a + * boundary, becomes several TRBs chained into a single transfer, with + * the interrupt asked for only on the last so that one completion + * arrives for the whole of it. + */ - trb.d0 = up_addrenv_va_to_pa(buffer); - trb.d1 = XHCI_TRB_D1_IRQ_SET(0) | XHCI_TRB_D1_TXLEN_SET(buflen); - trb.d2 = XHCI_TRB_D2_IOC | XHCI_TRB_D2_TYPE_SET(XHCI_TRB_TYPE_NORMAL); + pa = up_addrenv_va_to_pa(buffer); + left = buflen; - /* Add TRBs to ring */ + while (left > 0) + { + chunk = XHCI_TD_LEN_MAX - (pa & (XHCI_TD_LEN_MAX - 1)); + if (chunk > left) + { + chunk = left; + } - xhci_add_trb(priv, &epinfo->td, &trb, 1); + if (++n >= XHCI_TD_MAX) + { + uerr("transfer of %zu needs more TRBs than the ring holds\n", + buflen); + return -EINVAL; + } + + trb.d0 = pa; + trb.d1 = XHCI_TRB_D1_IRQ_SET(0) | XHCI_TRB_D1_TXLEN_SET(chunk); + trb.d2 = XHCI_TRB_D2_TYPE_SET(XHCI_TRB_TYPE_NORMAL); + + left -= chunk; + pa += chunk; + + /* Chain everything but the last, and interrupt only on the last */ + + if (left > 0) + { + trb.d2 |= XHCI_TRB_D2_CH; + } + else + { + trb.d2 |= XHCI_TRB_D2_IOC; + } + + xhci_add_trb(priv, &epinfo->td, &trb, 1); + } /* Trigger transfer */
