Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ config ARCH_CHIP_RP2040
select ARM_HAVE_WFE_SEV
select ARCH_BOARD_COMMON
select ARCH_HAVE_CUSTOM_TESTSET
select ARCH_USBDEV_STALLQUEUE
---help---
Raspberry Pi RP2040 architectures (ARM dual Cortex-M0+).

Expand All @@ -395,6 +396,7 @@ config ARCH_CHIP_RP23XX
select ARCH_HAVE_FPU
select ARCH_HAVE_CUSTOM_TESTSET
select ARCH_BOARD_COMMON
select ARCH_USBDEV_STALLQUEUE
---help---
Raspberry Pi RP23XX architectures (ARM dual Cortex-M33 or RISC-V).

Expand Down
144 changes: 102 additions & 42 deletions arch/arm/src/rp2040/rp2040_usbdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,6 @@ static void rp2040_update_buffer_control(struct rp2040_ep_s *privep,
static int rp2040_epwrite(struct rp2040_ep_s *privep, uint8_t *buf,
uint16_t nbytes);
static int rp2040_epread(struct rp2040_ep_s *privep, uint16_t nbytes);
static void rp2040_abortrequest(struct rp2040_ep_s *privep,
struct rp2040_req_s *privreq,
int16_t result);
static void rp2040_reqcomplete(struct rp2040_ep_s *privep, int16_t result);
static void rp2040_txcomplete(struct rp2040_ep_s *privep);
static int rp2040_wrrequest(struct rp2040_ep_s *privep);
Expand Down Expand Up @@ -586,30 +583,6 @@ static int rp2040_epread(struct rp2040_ep_s *privep, uint16_t nbytes)
return OK;
}

/****************************************************************************
* Name: rp2040_abortrequest
*
* Description:
* Discard a request
*
****************************************************************************/

static void rp2040_abortrequest(struct rp2040_ep_s *privep,
struct rp2040_req_s *privreq,
int16_t result)
{
usbtrace(TRACE_DEVERROR(RP2040_TRACEERR_REQABORTED),
(uint16_t)privep->epphy);

/* Save the result in the request structure */

privreq->req.result = result;

/* Callback to the request completion handler */

privreq->req.callback(&privep->ep, &privreq->req);
}

/****************************************************************************
* Name: rp2040_reqcomplete
*
Expand Down Expand Up @@ -1052,6 +1025,11 @@ static void rp2040_ep0setup(struct rp2040_usbdev_s *priv)
}
else
{
uint8_t response[2];

response[0] = 0;
response[1] = 0;

switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
Expand All @@ -1068,10 +1046,23 @@ static void rp2040_ep0setup(struct rp2040_usbdev_s *priv)
priv->ctrl.type);
priv->stalled = true;
}
else if (privep->stalled)
{
response[0] = 1; /* Endpoint HALTed */
}
}
break;

case USB_REQ_RECIPIENT_DEVICE:
usbtrace(TRACE_INTDECODE(
RP2040_TRACEINTID_GETIFDEV),
0);
if (priv->selfpowered)
{
response[0] = 1 << USB_FEATURE_SELFPOWERED;
}
break;

case USB_REQ_RECIPIENT_INTERFACE:
usbtrace(TRACE_INTDECODE(
RP2040_TRACEINTID_GETIFDEV),
Expand All @@ -1087,6 +1078,23 @@ static void rp2040_ep0setup(struct rp2040_usbdev_s *priv)
}
break;
}

/* Send the two-byte status response. This was previously
* missing entirely: the request was validated but no data
* stage was ever queued, so EP0 NAKed the host's IN
* forever and the control transfer timed out. macOS
* queries GET STATUS (endpoint) on a halted bulk pipe
* before running Bulk-Only reset recovery, so this hole
* broke all mass-storage error recovery on that host.
* (The status stage - the host's zero-length OUT - is
* armed by rp2040_handle_zlp after this handler returns,
* exactly as for class-dispatched IN transfers.)
*/

if (!priv->stalled)
{
rp2040_epwrite(ep0, response, 2);
}
}
}
break;
Expand Down Expand Up @@ -1394,7 +1402,16 @@ static bool rp2040_usbintr_buffstat(struct rp2040_usbdev_s *priv)

if (privep->in)
{
if (!rp2040_rqempty(privep))
if (privep->epphy != 0 && privep->stalled)
{
/* A completion latched for a transfer that a halt
* just aborted: the request was already canceled by
* rp2040_epstall and any queued requests belong to
* the post-halt phase. Do not attribute the stale
* buffer event to them.
*/
}
else if (!rp2040_rqempty(privep))
{
rp2040_txcomplete(privep);
}
Expand Down Expand Up @@ -1694,24 +1711,25 @@ static int rp2040_epsubmit(struct usbdev_ep_s *ep,

flags = enter_critical_section();

if (privep->stalled && privep->in)
{
rp2040_abortrequest(privep, privreq, -EBUSY);
ret = -EBUSY;
}

/* Handle IN (device-to-host) requests */

else if (privep->in)
if (privep->in)
{
/* Add the new request to the request queue for the IN endpoint */
/* Add the new request to the request queue for the IN endpoint.
* If the endpoint is halted, the request is only queued; it will
* be armed when the halt is cleared (see rp2040_epstall). This
* stall-queuing is required by the mass storage class: the CSW is
* submitted while the bulk IN endpoint is still halted and must
* survive until the host clears the halt and collects it
* (ARCH_USBDEV_STALLQUEUE).
*/

bool empty = rp2040_rqempty(privep);

rp2040_rqenqueue(privep, privreq);
usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);

if (empty)
if (empty && !privep->stalled)
{
rp2040_wrrequest(privep);
}
Expand All @@ -1721,7 +1739,11 @@ static int rp2040_epsubmit(struct usbdev_ep_s *ep,

else
{
/* Add the new request to the request queue for the OUT endpoint */
/* Add the new request to the request queue for the OUT endpoint.
* As for IN endpoints, do not arm the hardware while the endpoint
* is halted: arming rewrites the buffer control word and would
* silently clear the STALL bit.
*/

bool empty = rp2040_rqempty(privep);

Expand All @@ -1731,7 +1753,7 @@ static int rp2040_epsubmit(struct usbdev_ep_s *ep,

/* This there a incoming data pending the availability of a request? */

if (empty)
if (empty && !privep->stalled)
{
ret = rp2040_rdrequest(privep);
}
Expand Down Expand Up @@ -1829,14 +1851,17 @@ static int rp2040_epstall(struct usbdev_ep_s *ep, bool resume)
{
struct rp2040_ep_s *privep = (struct rp2040_ep_s *)ep;
struct rp2040_usbdev_s *priv = privep->dev;
irqstate_t irqflags;
irqstate_t flags;

irqflags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);

if (resume)
{
usbtrace(TRACE_EPRESUME, privep->epphy);
privep->stalled = false;
privep->pending_stall = false;
if (privep->epphy == 0)
{
clrbits_reg32(privep->in ?
Expand All @@ -1849,8 +1874,29 @@ static int rp2040_epstall(struct usbdev_ep_s *ep, bool resume)
~(RP2040_USBCTRL_DPSRAM_EP_BUFF_CTRL_STALL),
0);

/* Halt clearing resets the data toggle to DATA0 (USB 2.0 9.4.5) */

privep->next_pid = 0;
priv->zlp_stat = RP2040_ZLP_NONE;

spin_unlock_irqrestore(&priv->lock, flags);

/* Restart any requests that were queued (but not armed) while the
* endpoint was halted -- e.g. the mass storage CSW that concludes
* a failed command (ARCH_USBDEV_STALLQUEUE).
*/

if (privep->epphy != 0 && !rp2040_rqempty(privep))
{
if (privep->in)
{
rp2040_wrrequest(privep);
}
else
{
rp2040_rdrequest(privep);
}
}
}
else
{
Expand All @@ -1861,18 +1907,32 @@ static int rp2040_epstall(struct usbdev_ep_s *ep, bool resume)
/* EP0 IN Transfer ongoing : postpone the stall until the end */

privep->pending_stall = true;
priv->zlp_stat = RP2040_ZLP_NONE;
spin_unlock_irqrestore(&priv->lock, flags);
}
else
{
/* Stall immediately */

rp2040_epstall_exec_nolock(ep);
}
priv->zlp_stat = RP2040_ZLP_NONE;
spin_unlock_irqrestore(&priv->lock, flags);

/* Terminate any IN transfer that the halt just aborted: its
* hardware buffer was disarmed by the STALL write above and
* would otherwise never complete. Anything the class submits
* from the completion callbacks (or later, e.g. the mass
* storage CSW) stays queued until the host clears the halt.
*/

priv->zlp_stat = RP2040_ZLP_NONE;
if (privep->epphy != 0 && privep->in && !rp2040_rqempty(privep))
{
rp2040_cancelrequests(privep);
}
}
}

spin_unlock_irqrestore(&priv->lock, flags);
leave_critical_section(irqflags);

return OK;
}
Expand Down
Loading
Loading