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 */
