From 3c610d5d7034f02b7a40ba9f3339b61a6bd50d00 Mon Sep 17 00:00:00 2001 From: Petteri Aimonen Date: Wed, 24 Mar 2021 09:02:51 +0200 Subject: [PATCH] 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. --- arch/arm/src/stm32/stm32_otgfsdev.c | 41 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/arch/arm/src/stm32/stm32_otgfsdev.c b/arch/arm/src/stm32/stm32_otgfsdev.c index 909588a018..e2511270c6 100644 --- a/arch/arm/src/stm32/stm32_otgfsdev.c +++ b/arch/arm/src/stm32/stm32_otgfsdev.c @@ -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 {