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 432ef71ed7980767e1f8981ef59bd86a871ccb54
Author: Justin Hammond <[email protected]>
AuthorDate: Sun Aug 16 17:15:27 2026 +0800

    drivers/usbhost: Describe devices to an xHCI controller correctly.
    
    What a controller is told about a device before it will accept it.  A DWC3
    core validates these where QEMU's controller does not.
    
    - HCCPARAMS1 says whether context structures are 32 or 64 bytes, and the
      wider form was refused outright with -EIO; the EIC7700X reports
      0x0220fe45 on both of its controllers, so this driver could not have
      driven either.  A wide context is the same fields with reserved space
      after them, so only the stride changes.  Read it at start up and use it
      wherever a context array is walked.
    - Contexts must be 64 byte aligned, since every device context base
      address array entry points at one, and the output context came from
      kmm_zalloc().
    - The slot context never carried the device speed, which has no valid
      zero, so a validating controller answers Address Device with a parameter
      error.  The speed was already implied by the endpoint context's maximum
      packet size.  The numbering is xHCI's own, hence the mapping.
    - The output device context was cleared and never flushed.  That context
      is the controller's to write, so what stays behind is a dirty line of
      zeros written back over the slot state, and the next command against the
      slot is refused with a context state error.  Enumeration reached
      SET_ADDRESS and stopped.
    - A buffer copied through an aligned stand-in was copied back using buflen,
      which control transfers deliberately leave zero, so a descriptor read
      copied nothing back and the caller was handed whatever its buffer held
      before.  Keep the requested length separately, and maintain the cache
      over the whole stand-in rather than the part in use.
    - A buffer the controller cannot reach is now copied through a stand-in
      rather than refused.  -EFAULT works for a caller with somewhere better
      to put the data, and fails outright for one without: reading a block
      device directly from a user program returned an error where the transfer
      could have gone through a stand-in.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 drivers/usbhost/usbhost_xhci.c | 281 +++++++++++++++++++++++++++++++----------
 drivers/usbhost/usbhost_xhci.h |  16 +++
 2 files changed, 233 insertions(+), 64 deletions(-)

diff --git a/drivers/usbhost/usbhost_xhci.c b/drivers/usbhost/usbhost_xhci.c
index 144a4b66faa..377791bce5c 100644
--- a/drivers/usbhost/usbhost_xhci.c
+++ b/drivers/usbhost/usbhost_xhci.c
@@ -27,6 +27,7 @@
 #include <assert.h>
 #include <nuttx/debug.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <string.h>
 
 #include <sys/endian.h>
@@ -81,6 +82,14 @@
  */
 
 #define XHCI_PORT_RESET_MS       (500)
+
+/* How much memory a context occupies, which depends on the stride the
+ * controller asked for.  One entry for the slot and one per endpoint, and
+ * the input context carries its control entry in front of both.
+ */
+
+#define XHCI_DEVCTX_SIZE(priv)   ((1 + XHCI_MAX_ENDPOINTS) * (priv)->ctxsize)
+#define XHCI_INCTX_SIZE(priv)    ((2 + XHCI_MAX_ENDPOINTS) * (priv)->ctxsize)
 #define XHCI_BUFSIZE             (512)
 
 /* Port numbers macros */
@@ -146,6 +155,8 @@ struct xhci_epinfo_s
   size_t             buflen;       /* Buffer length used for transfer */
   FAR uint8_t       *buffer;       /* The caller's buffer, for cache 
maintenance */
   FAR uint8_t       *bounce;       /* Aligned stand-in for it, or NULL */
+  size_t             dmalen;       /* Length the cache is maintained over */
+  size_t             dmacopy;      /* Length to copy back out of a stand-in */
   bool               dmain;        /* Direction this buffer was prepared for */
   sem_t              iocsem;       /* Semaphore used to wait for transfer 
completion */
 #ifdef CONFIG_USBHOST_ASYNCH
@@ -251,6 +262,7 @@ struct usbhost_xhci_s
   FAR const struct xhci_bus_ops_s *ops;     /* Bus operations */
   FAR void                     *arg;        /* Bus private data */
   FAR const char               *name;       /* What to call this controller */
+  uint8_t                       ctxsize;    /* Context stride, 32 or 64 bytes 
*/
   uint32_t                      pending;    /* IRQ pending status */
   struct work_s                 work;       /* IRQ work */
   struct work_s                 pscwork;    /* Port status change work */
@@ -418,7 +430,17 @@ static ssize_t xhci_transfer_wait(FAR struct 
usbhost_xhci_s *priv,
                                   FAR struct xhci_epinfo_s *epinfo);
 static bool xhci_dmacapable(FAR struct usbhost_xhci_s *priv,
                             FAR uint8_t *buffer, size_t buflen);
-static FAR uint8_t *xhci_dma_prepare(FAR struct xhci_epinfo_s *epinfo,
+static uint32_t xhci_speed_id(uint8_t speed);
+static inline FAR struct xhci_slot_ctx_s *
+xhci_in_slot(FAR struct usbhost_xhci_s *priv,
+             FAR struct xhci_input_dev_ctx_s *input);
+static inline FAR struct xhci_ep_ctx_s *
+xhci_in_ep(FAR struct usbhost_xhci_s *priv,
+           FAR struct xhci_input_dev_ctx_s *input, int epidx);
+static inline FAR struct xhci_slot_ctx_s *
+xhci_out_slot(FAR struct xhci_dev_ctx_s *ctx);
+static FAR uint8_t *xhci_dma_prepare(FAR struct usbhost_xhci_s *priv,
+                                     FAR struct xhci_epinfo_s *epinfo,
                                      FAR uint8_t *buffer, size_t buflen,
                                      bool dirin);
 static void xhci_dma_finish(FAR struct xhci_epinfo_s *epinfo);
@@ -1614,7 +1636,7 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s *priv,
    * Initialize all fields to 0.
    */
 
-  memset(dev->input, 0, sizeof(struct xhci_input_dev_ctx_s));
+  memset(dev->input, 0, XHCI_INCTX_SIZE(priv));
 
   /* Step 2. Initialize the Input Control Context by setting the A0 and
    * A1 flags to 1 (Slot flag and EP0 flag).
@@ -1624,9 +1646,16 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s 
*priv,
            XHCI_IN_CTX1_A(XHCI_EP0_FLAG);
   xhci_context_ctrl(priv, dev, 0, regval);
 
-  /* Step 3. Initialize the Input Slot Context */
+  /* Step 3. Initialize the Input Slot Context.
+   *
+   * The speed field has no valid zero.  This is the only place the
+   * controller learns the device's speed, and one that checks refuses
+   * Address Device with a parameter error without it.
+   */
 
-  regval = XHCI_ST_CTX0_CTXENT_SET(1);
+  regval = XHCI_ST_CTX0_CTXENT_SET(1) |
+           XHCI_ST_CTX0_SPEED_SET(
+             xhci_speed_id(dev->rhport->hport.hport.speed));
 
 #ifdef CONFIG_USBHOST_HUB
   /* TODO:
@@ -1638,7 +1667,7 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s *priv,
 #  warning missing logic
 #endif
 
-  dev->input->slot.ctx[0] = htole32(regval);
+  xhci_in_slot(priv, dev->input)->ctx[0] = htole32(regval);
 
   /* Configure Root Hub Port Number (starts from 1) */
 
@@ -1647,7 +1676,7 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s *priv,
   /* TODO: configure number of ports */
 
   regval |= XHCI_ST_CTX1_PORTS_SET(0);
-  dev->input->slot.ctx[1] = htole32(regval);
+  xhci_in_slot(priv, dev->input)->ctx[1] = htole32(regval);
 
   /* Step 4. the Transfer Ring for the Default Control Endpoint is already
    * allocated.
@@ -1673,7 +1702,7 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s *priv,
 
   DEBUGASSERT(drdp != 0);
   xhci_ep_configure(priv,
-                    &dev->input->ep[0],
+                    xhci_in_ep(priv, dev->input, 0),
                     XHCI_EPTYPE_CTRL, maxpkt,
                     0, drdp,
                     0, 0);
@@ -1682,13 +1711,22 @@ static int xhci_slot_init(FAR struct usbhost_xhci_s 
*priv,
    * Initialize all fields to 0.
    */
 
-  memset(dev->ctx, 0, sizeof(struct xhci_dev_ctx_s));
+  memset(dev->ctx, 0, XHCI_DEVCTX_SIZE(priv));
 
-  /* Flush Device input context */
+  /* Flush both contexts.
+   *
+   * The output context is the controller's to write, so clearing it must
+   * reach memory: the dirty zeros left in cache are written back later, on
+   * top of what the controller has put there.  The slot state lives in
+   * that context, and losing it fails the next command against the slot.
+   */
+
+  up_flush_dcache((uintptr_t)dev->ctx,
+                  (uintptr_t)dev->ctx + XHCI_DEVCTX_SIZE(priv));
 
   up_flush_dcache((uintptr_t)dev->input,
                   (uintptr_t)dev->input +
-                  sizeof(struct xhci_input_dev_ctx_s));
+                  XHCI_INCTX_SIZE(priv));
 
   /* Step 7. Load the appropriate (Device Slot ID) entry in the Device
    * Context Base Address Array with a pointer to the Output Device
@@ -1826,8 +1864,15 @@ static int xhci_device_deinit(FAR struct usbhost_xhci_s 
*priv,
 
   rhport->dev->state = XHCI_SLOT_DISABLED;
 
-  memset(rhport->dev->ctx, 0, sizeof(struct xhci_dev_ctx_s));
-  memset(rhport->dev->input, 0, sizeof(struct xhci_input_dev_ctx_s));
+  memset(rhport->dev->ctx, 0, XHCI_DEVCTX_SIZE(priv));
+  memset(rhport->dev->input, 0, XHCI_INCTX_SIZE(priv));
+
+  /* And push both, so nothing is left to be written back later */
+
+  up_flush_dcache((uintptr_t)rhport->dev->ctx,
+                  (uintptr_t)rhport->dev->ctx + XHCI_DEVCTX_SIZE(priv));
+  up_flush_dcache((uintptr_t)rhport->dev->input,
+                  (uintptr_t)rhport->dev->input + XHCI_INCTX_SIZE(priv));
 
   /* Remove reference to a device slot */
 
@@ -1896,8 +1941,8 @@ static void xhci_context_ctrl(FAR struct usbhost_xhci_s 
*priv,
         }
     }
 
-  dev->input->slot.ctx[0] &= ~XHCI_ST_CTX0_CTXENT_MASK;
-  dev->input->slot.ctx[0] |= XHCI_ST_CTX0_CTXENT_SET(i);
+  xhci_in_slot(priv, dev->input)->ctx[0] &= ~XHCI_ST_CTX0_CTXENT_MASK;
+  xhci_in_slot(priv, dev->input)->ctx[0] |= XHCI_ST_CTX0_CTXENT_SET(i);
 }
 
 /****************************************************************************
@@ -2324,7 +2369,7 @@ static int xhci_control_setup(FAR struct xhci_rhport_s 
*rhport,
 
   if (buffer)
     {
-      buffer = xhci_dma_prepare(epinfo, buffer, buflen,
+      buffer = xhci_dma_prepare(priv, epinfo, buffer, buflen,
                                 (req->type & USB_REQ_DIR_IN) != 0);
       if (buffer == NULL)
         {
@@ -2405,7 +2450,8 @@ static int xhci_normal_setup(FAR struct xhci_rhport_s 
*rhport,
 
   /* Make the buffer safe for the controller to reach */
 
-  buffer = xhci_dma_prepare(epinfo, buffer, buflen, epinfo->dirin != 0);
+  buffer = xhci_dma_prepare(priv, epinfo, buffer, buflen,
+                            epinfo->dirin != 0);
   if (buffer == NULL)
     {
       return -ENOMEM;
@@ -2431,8 +2477,8 @@ static int xhci_normal_setup(FAR struct xhci_rhport_s 
*rhport,
 
       if (++n >= XHCI_TD_MAX)
         {
-          uerr("transfer of %zu needs more TRBs than the ring holds\n",
-               buflen);
+          uerr("transfer of %zu from pa %" PRIxPTR " needs more than %d "
+               "TRBs\n", buflen, pa, XHCI_TD_MAX);
           return -EINVAL;
         }
 
@@ -2489,7 +2535,8 @@ static int xhci_isoc_setup(FAR struct xhci_rhport_s 
*rhport,
 
   /* Make the buffer safe for the controller to reach */
 
-  buffer = xhci_dma_prepare(epinfo, buffer, buflen, epinfo->dirin != 0);
+  buffer = xhci_dma_prepare(priv, epinfo, buffer, buflen,
+                            epinfo->dirin != 0);
   if (buffer == NULL)
     {
       return -ENOMEM;
@@ -2788,6 +2835,82 @@ static void xhci_portsc_work(FAR void *arg)
     }
 }
 
+/****************************************************************************
+ * Name: xhci_in_slot / xhci_in_ep / xhci_out_slot
+ *
+ * Description:
+ *   Reach into a device context.
+ *
+ *   A context is an array of equally sized entries, and how big they are is
+ *   a property of the controller rather than of the specification: it
+ *   reports either thirty-two or sixty-four bytes, and the wider form is
+ *   the same fields with reserved space after them.  So these are the same
+ *   structures at a different stride, and only the arithmetic to find the
+ *   n'th one has to know which.
+ *
+ *   Output context:  slot, then endpoints 1 upward.
+ *   Input context:   input control, then slot, then endpoints.
+ *
+ *   The first entry of either is at offset zero, so only the ones after it
+ *   need this.
+ *
+ ****************************************************************************/
+
+static inline FAR struct xhci_slot_ctx_s *
+xhci_in_slot(FAR struct usbhost_xhci_s *priv,
+             FAR struct xhci_input_dev_ctx_s *input)
+{
+  return (FAR struct xhci_slot_ctx_s *)((uintptr_t)input + priv->ctxsize);
+}
+
+static inline FAR struct xhci_ep_ctx_s *
+xhci_in_ep(FAR struct usbhost_xhci_s *priv,
+           FAR struct xhci_input_dev_ctx_s *input, int epidx)
+{
+  return (FAR struct xhci_ep_ctx_s *)((uintptr_t)input +
+                                      (epidx + 2) * priv->ctxsize);
+}
+
+static inline FAR struct xhci_slot_ctx_s *
+xhci_out_slot(FAR struct xhci_dev_ctx_s *ctx)
+{
+  return (FAR struct xhci_slot_ctx_s *)ctx;
+}
+
+/****************************************************************************
+ * Name: xhci_speed_id
+ *
+ * Description:
+ *   Turn the speed the USB host stack uses into the one a slot context
+ *   wants, which is a different numbering with no relation to it.
+ *
+ ****************************************************************************/
+
+static uint32_t xhci_speed_id(uint8_t speed)
+{
+  switch (speed)
+    {
+      case USB_SPEED_LOW:
+        return XHCI_SPEED_LOW;
+      case USB_SPEED_FULL:
+        return XHCI_SPEED_FULL;
+      case USB_SPEED_HIGH:
+        return XHCI_SPEED_HIGH;
+      case USB_SPEED_SUPER:
+        return XHCI_SPEED_SUPER;
+      case USB_SPEED_SUPER_PLUS:
+        return XHCI_SPEED_SUPER_PLUS;
+      default:
+
+        /* Nothing else can be described to a controller, and full speed
+         * is the safe answer.
+         */
+
+        uwarn("no speed ID for USB speed %d\n", speed);
+        return XHCI_SPEED_FULL;
+    }
+}
+
 /****************************************************************************
  * Name: xhci_dmacapable
  *
@@ -2842,30 +2965,53 @@ static bool xhci_dmacapable(FAR struct usbhost_xhci_s 
*priv,
  *
  ****************************************************************************/
 
-static FAR uint8_t *xhci_dma_prepare(FAR struct xhci_epinfo_s *epinfo,
+static FAR uint8_t *xhci_dma_prepare(FAR struct usbhost_xhci_s *priv,
+                                     FAR struct xhci_epinfo_s *epinfo,
                                      FAR uint8_t *buffer, size_t buflen,
                                      bool dirin)
 {
-  size_t line = up_get_dcache_linesize();
+  size_t line      = up_get_dcache_linesize();
+  bool   reachable = xhci_dmacapable(priv, buffer, buflen);
+
+  epinfo->buffer  = buffer;
+  epinfo->bounce  = NULL;
+  epinfo->dmalen  = buflen;
+
+  /* How much to bring back afterwards.  This cannot be taken from buflen
+   * at completion time: that field means the length of a data transfer and
+   * control transfers deliberately leave it zero, so a descriptor read
+   * would copy nothing back and the caller would see whatever its buffer
+   * held before.
+   */
 
-  epinfo->buffer = buffer;
-  epinfo->bounce = NULL;
-  epinfo->dmain  = dirin;
+  epinfo->dmacopy = buflen;
+  epinfo->dmain   = dirin;
 
-  /* No cache to maintain, so nothing to arrange */
+  /* Nothing to arrange: no cache to maintain, and an address the
+   * controller can be pointed at as it stands.
+   */
 
-  if (line == 0)
+  if (line == 0 && reachable)
     {
       return buffer;
     }
 
-  if (((uintptr_t)buffer & (line - 1)) != 0 || (buflen & (line - 1)) != 0)
+  if (!reachable ||
+      ((uintptr_t)buffer & (line - 1)) != 0 || (buflen & (line - 1)) != 0)
     {
       /* The buffer shares a line with something else.  Work in a stand-in
        * that does not.
        */
 
-      epinfo->bounce = kmm_memalign(line, (buflen + line - 1) & ~(line - 1));
+      /* Maintain the whole stand-in, not just the part in use: cache
+       * operations work a line at a time and this chip rejects a partial
+       * range.
+       */
+
+      epinfo->dmalen = line ? ((buflen + line - 1) & ~(line - 1)) : buflen;
+
+      epinfo->bounce = kmm_memalign(line ? line : sizeof(uintptr_t),
+                                    epinfo->dmalen);
       if (epinfo->bounce == NULL)
         {
           return NULL;
@@ -2886,11 +3032,13 @@ static FAR uint8_t *xhci_dma_prepare(FAR struct 
xhci_epinfo_s *epinfo,
 
   if (dirin)
     {
-      up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+      up_invalidate_dcache((uintptr_t)buffer,
+                           (uintptr_t)buffer + epinfo->dmalen);
     }
   else
     {
-      up_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+      up_clean_dcache((uintptr_t)buffer,
+                      (uintptr_t)buffer + epinfo->dmalen);
     }
 
   return buffer;
@@ -2917,11 +3065,12 @@ static void xhci_dma_finish(FAR struct xhci_epinfo_s 
*epinfo)
 
   if (dirin)
     {
-      up_invalidate_dcache((uintptr_t)dma, (uintptr_t)dma + epinfo->buflen);
+      up_invalidate_dcache((uintptr_t)dma,
+                           (uintptr_t)dma + epinfo->dmalen);
 
       if (epinfo->bounce != NULL && epinfo->buffer != NULL)
         {
-          memcpy(epinfo->buffer, epinfo->bounce, epinfo->buflen);
+          memcpy(epinfo->buffer, epinfo->bounce, epinfo->dmacopy);
         }
     }
 
@@ -3557,8 +3706,11 @@ static int xhci_ep0configure(FAR struct usbhost_driver_s 
*drvr,
     {
       /* Update max packet size */
 
-      rhport->dev->input->ep[0].ctx1 &= ~XHCI_EP_CTX1_MAXPKT_MASK;
-      rhport->dev->input->ep[0].ctx1 |= XHCI_EP_CTX1_MAXPKT(maxpacketsize);
+      FAR struct xhci_ep_ctx_s *ep0ctx =
+        xhci_in_ep(priv, rhport->dev->input, 0);
+
+      ep0ctx->ctx1 &= ~XHCI_EP_CTX1_MAXPKT_MASK;
+      ep0ctx->ctx1 |= XHCI_EP_CTX1_MAXPKT(maxpacketsize);
 
       /* Add Slot Context and EP0 Context */
 
@@ -3570,13 +3722,17 @@ static int xhci_ep0configure(FAR struct 
usbhost_driver_s *drvr,
 
       up_flush_dcache((uintptr_t)rhport->dev->input,
                       (uintptr_t)rhport->dev->input +
-                      sizeof(struct xhci_input_dev_ctx_s));
+                      XHCI_INCTX_SIZE(priv));
 
       /* Free mutex before command execution */
 
       nxmutex_unlock(&priv->lock);
 
       ctx = up_addrenv_va_to_pa(rhport->dev->input);
+
+      uinfo("slot %d funcaddr %d speed %d maxpacket %d\n",
+            epinfo->slot, funcaddr, speed, maxpacketsize);
+
       ret = xhci_cmd_evalctx(priv, epinfo->slot, ctx);
     }
 
@@ -3627,6 +3783,12 @@ static int xhci_epalloc(FAR struct usbhost_driver_s 
*drvr,
               && ep != NULL);
   hport = epdesc->hport;
 
+  /* Only the tracing alternative below and the hub logic further down use
+   * this, and a configuration may have neither.
+   */
+
+  UNUSED(hport);
+
   /* Terse output only if we are tracing */
 
 #ifdef CONFIG_USBHOST_TRACE
@@ -3747,7 +3909,7 @@ static int xhci_epalloc(FAR struct usbhost_driver_s *drvr,
    * Max Burst Size set for 0 for now (USB3.0 specific)
    */
 
-  xhci_ep_configure(priv, &dev->input->ep[idx - 1],
+  xhci_ep_configure(priv, xhci_in_ep(priv, dev->input, idx - 1),
                     eptype, epdesc->mxpacketsize, 0,
                     up_addrenv_va_to_pa(epinfo->td.ring),
                     0, epinfo->interval);
@@ -3758,7 +3920,7 @@ static int xhci_epalloc(FAR struct usbhost_driver_s *drvr,
 
   up_flush_dcache((uintptr_t)dev->input,
                   (uintptr_t)dev->input +
-                  sizeof(struct xhci_input_dev_ctx_s));
+                  XHCI_INCTX_SIZE(priv));
 
   /* Configure EP */
 
@@ -4058,13 +4220,6 @@ static int xhci_ctrl_xfer(FAR struct usbhost_driver_s 
*drvr,
 
   len = xhci_getle16(req->len);
 
-  /* Refuse a buffer the controller cannot reach, as for bulk transfers */
-
-  if (buffer != NULL && len > 0 && !xhci_dmacapable(priv, buffer, len))
-    {
-      return -EFAULT;
-    }
-
   /* Terse output only if we are tracing */
 
 #ifdef CONFIG_USBHOST_TRACE
@@ -4095,13 +4250,14 @@ static int xhci_ctrl_xfer(FAR struct usbhost_driver_s 
*drvr,
 
           up_invalidate_dcache((uintptr_t)rhport->dev->ctx,
                                (uintptr_t)rhport->dev->ctx +
-                               sizeof(struct xhci_dev_ctx_s));
+                               XHCI_DEVCTX_SIZE(priv));
 
           /* Store USB Device Address assigned by xHCI */
 
           ep0info->devaddr =
-            XHCI_ST_CTX3_ADDR_GET(rhport->dev->ctx->slot.ctx[3]);
-          rhport->dev->input->slot.ctx[3] = rhport->dev->ctx->slot.ctx[3];
+            XHCI_ST_CTX3_ADDR_GET(xhci_out_slot(rhport->dev->ctx)->ctx[3]);
+          xhci_in_slot(priv, rhport->dev->input)->ctx[3] =
+            xhci_out_slot(rhport->dev->ctx)->ctx[3];
         }
 
       return OK;
@@ -4237,16 +4393,6 @@ static ssize_t xhci_transfer(FAR struct usbhost_driver_s 
*drvr,
 
   DEBUGASSERT(priv && rhport && epinfo && buffer && buflen > 0);
 
-  /* Refuse a buffer the controller cannot reach rather than pointing it at
-   * the wrong memory.  A caller that has somewhere better to put the data
-   * will try again with it; the FAT filesystem does exactly that.
-   */
-
-  if (!xhci_dmacapable(priv, buffer, buflen))
-    {
-      return -EFAULT;
-    }
-
   /* We must have exclusive access to the xHCI hardware and data
    * structures.
    */
@@ -4615,12 +4761,14 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s 
*priv)
 
   /* Get data form Host Controller Capability 1 Parameters */
 
+  /* Context entry stride, 32 or 64 bytes as the controller reports.  The
+   * wider form is the same fields with padding.
+   */
+
   regval = xhci_capa_getreg(priv, XHCI_HCCPARAMS1);
-  if (regval & XHCI_HCCPARAMS1_CSZ)
-    {
-      uerr("Only 32 byte Context data structures supported!\n");
-      return -EIO;
-    }
+  priv->ctxsize = (regval & XHCI_HCCPARAMS1_CSZ) ? 64 : 32;
+
+  uinfo("context size = %d\n", priv->ctxsize);
 
   /* Get data from Structural Parameters 1 register */
 
@@ -4787,7 +4935,12 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s 
*priv)
     {
       /* Allocate Device Context */
 
-      priv->devs[i].ctx = kmm_zalloc(sizeof(struct xhci_dev_ctx_s));
+      /* The base address array holds these, and every entry in it must be
+       * 64 byte aligned, so the allocation has to be too.
+       */
+
+      priv->devs[i].ctx = kmm_memalign(XHCI_CTX_ALIGN,
+                                       XHCI_DEVCTX_SIZE(priv));
       if (!priv->devs[i].ctx)
         {
           uerr("dev ctx zalloc failed!\n");
@@ -4799,7 +4952,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv)
        */
 
       priv->devs[i].input = kmm_memalign((XHCI_PAGE_SIZE / 2),
-                            sizeof(struct xhci_input_dev_ctx_s));
+                            XHCI_INCTX_SIZE(priv));
       if (!priv->devs[i].input)
         {
           uerr("dev input zalloc failed!\n");
diff --git a/drivers/usbhost/usbhost_xhci.h b/drivers/usbhost/usbhost_xhci.h
index 2fc2aa207ad..3339721895d 100644
--- a/drivers/usbhost/usbhost_xhci.h
+++ b/drivers/usbhost/usbhost_xhci.h
@@ -506,6 +506,22 @@
 #define XHCI_ST_CTX0_RTSTR_MASK      (0xfffff << XHCI_ST_CTX0_RTSTR_SHIFT)
 #define XHCI_ST_CTX0_SPEED_SHIFT     (20)                  /* Bits 20:23: 
Speed */
 #define XHCI_ST_CTX0_SPEED_MASK      (0xf << XHCI_ST_CTX0_SPEED_SHIFT)
+#define XHCI_ST_CTX0_SPEED_SET(x)    (((x) << XHCI_ST_CTX0_SPEED_SHIFT) & \
+                                      XHCI_ST_CTX0_SPEED_MASK)
+
+/* Port Speed IDs, which xHCI numbers its own way rather than USB's.  These
+ * are the values every controller reports in PORTSC and expects back in a
+ * slot context; a device is described to the controller with one of them
+ * and with nothing else, so zero is not a default but an invalid context.
+ *
+ * Reference: Table 7-13: Default USB Speed ID Mapping
+ */
+
+#define XHCI_SPEED_FULL              (1)
+#define XHCI_SPEED_LOW               (2)
+#define XHCI_SPEED_HIGH              (3)
+#define XHCI_SPEED_SUPER             (4)
+#define XHCI_SPEED_SUPER_PLUS        (5)
 #define XHCI_ST_CTX0_MTT             (1 << 25)             /* Bit 25: Multi-TT 
*/
                                                            /* Bit 24: Reserved 
*/
 #define XHCI_ST_CTX0_HUB             (1 << 26)             /* Bit 26: Hub */

Reply via email to