mirror of
https://github.com/apache/nuttx.git
synced 2026-10-06 05:47:54 +00:00
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 <justin@dynam.ac>
This commit is contained in:
parent
c288d9f9e4
commit
432ef71ed7
2 changed files with 233 additions and 64 deletions
|
|
@ -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->dmain = dirin;
|
||||
epinfo->buffer = buffer;
|
||||
epinfo->bounce = NULL;
|
||||
epinfo->dmalen = buflen;
|
||||
|
||||
/* No cache to maintain, so nothing to arrange */
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
if (line == 0)
|
||||
epinfo->dmacopy = buflen;
|
||||
epinfo->dmain = dirin;
|
||||
|
||||
/* Nothing to arrange: no cache to maintain, and an address the
|
||||
* controller can be pointed at as it stands.
|
||||
*/
|
||||
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue