STM32 USB OTGFSDEV: Fix handling of SETUP OUT longer than 64 bytes.

For example Windows RNDIS driver issues SETUP requests that are 76 bytes
long. Previously NuttX would read them all, but only if they arrive at
the same time. If host transfer scheduling causes a pause between the
two DATA packets, stm32_ep0out_receive() would proceed with an incomplete
transfer. The rest of the data could either be skipped by the error handler
branch, or be left in NAK state forever, stopping any further communication
on the endpoint.

This commit changes it so that the whole transfer has to be received before
SETUP handler is called. Depending on CONFIG_USBDEV_SETUP_MAXDATASIZE any
excess bytes will be discarded, but doing this in a controlled way ensures
deterministic behavior. In the specific case of RNDIS, the trailing bytes
are unused padding bytes and can be safely discarded.
This commit is contained in:
Petteri Aimonen 2021-03-24 09:02:51 +02:00 committed by David Sidrane
parent d0c6104efb
commit 3c610d5d70

View File

@ -1587,22 +1587,42 @@ static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep,
if (priv->ep0state == EP0STATE_SETUP_OUT)
{
/* Read the data into our special buffer for SETUP data */
int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, bcnt);
stm32_rxfifo_read(privep, priv->ep0data, readlen);
if (priv->ep0datlen < CONFIG_USBDEV_SETUP_MAXDATASIZE)
{
/* Read the data into our special buffer for SETUP data */
int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE - priv->ep0datlen, bcnt);
stm32_rxfifo_read(privep, priv->ep0data, readlen);
priv->ep0datlen += readlen;
bcnt -= readlen;
}
/* Do we have to discard any excess bytes? */
stm32_rxfifo_discard(privep, bcnt - readlen);
if (bcnt > 0)
{
stm32_rxfifo_discard(privep, bcnt);
priv->ep0datlen += bcnt;
}
/* Now we can process the setup command */
/* Is the transfer complete? */
privep->active = false;
priv->ep0state = EP0STATE_SETUP_READY;
priv->ep0datlen = readlen;
if (priv->ep0datlen >= GETUINT16(priv->ctrlreq.len))
{
/* Now we can process the setup command */
stm32_ep0out_setup(priv);
privep->active = false;
priv->ep0state = EP0STATE_SETUP_READY;
priv->ep0datlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, priv->ep0datlen);
stm32_ep0out_setup(priv);
}
else
{
/* More data to come, clear NAKSTS */
uint32_t regval = stm32_getreg(STM32_OTGFS_DOEPCTL0);
regval |= OTGFS_DOEPCTL0_CNAK;
stm32_putreg(regval, STM32_OTGFS_DOEPCTL0);
}
}
else
{
@ -3360,6 +3380,7 @@ static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv)
/* Wait for the data phase. */
priv->ep0state = EP0STATE_SETUP_OUT;
priv->ep0datlen = 0;
}
else
{