STM32 OTGHS host: stm32_in_transfer() fails and returns NAK if a short transfer is received. This causes problems from class drivers like CDC/ACM where short packets are expected. In those protocols, any transfer may be terminated by sending short or NUL packet.

This commit is contained in:
Janne Rosberg 2017-03-07 06:57:06 -06:00 committed by Gregory Nutt
parent 6b0d7335a2
commit 3331e9c49a
2 changed files with 42 additions and 27 deletions

View File

@ -1832,20 +1832,20 @@ static ssize_t stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx,
FAR uint8_t *buffer, size_t buflen)
{
FAR struct stm32_chan_s *chan;
systime_t start;
systime_t elapsed;
ssize_t xfrd;
int ret;
/* Loop until the transfer completes (i.e., buflen is decremented to zero)
* or a fatal error occurs (any error other than a simple NAK)
* or a fatal error occurs any error other than a simple NAK. NAK would
* simply indicate the end of the transfer (short-transfer).
*/
chan = &priv->chan[chidx];
chan->buffer = buffer;
chan->buflen = buflen;
chan->xfrd = 0;
xfrd = 0;
start = clock_systimer();
while (chan->xfrd < chan->buflen)
{
/* Set up for the wait BEFORE starting the transfer */
@ -1870,36 +1870,48 @@ static ssize_t stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx,
ret = stm32_chan_wait(priv, chan);
/* EAGAIN indicates that the device NAKed the transfer and we need
* do try again. Anything else (success or other errors) will
* cause use to return
/* EAGAIN indicates that the device NAKed the transfer. In the case
* of a NAK, we do not retry but rather assume that the transfer is
* commplete and return the data that we have received. Retry could
* be handled in class driver.
*/
if (ret < 0)
{
usbhost_trace1(OTGHS_TRACE1_TRNSFRFAILED, ret);
/* Check for a special case: If (1) the transfer was NAKed and (2)
* no Tx FIFO empty or Rx FIFO not-empty event occurred, then we
* should be able to just flush the Rx and Tx FIFOs and try again.
* We can detect this latter case because the then the transfer
* buffer pointer and buffer size will be unaltered.
/* The transfer failed. If we received a NAK, return all data
* buffered so far (if any).
*/
elapsed = clock_systimer() - start;
if (ret != -EAGAIN || /* Not a NAK condition OR */
elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */
chan->xfrd > 0) /* Data has been partially transferred */
if (ret == -EAGAIN)
{
/* Break out and return the error */
/* Was data buffered prior to the NAK? */
uerr("ERROR: stm32_chan_wait failed: %d\n", ret);
return (ssize_t)ret;
if (xfrd > 0)
{
/* Yes, return the amount of data received */
return xfrd;
}
else
{
/* No... Break out and return the NAK */
return (ssize_t)ret;
}
}
usbhost_trace1(OTGHS_TRACE1_TRNSFRFAILED, ret);
/* Break out and return the error */
uerr("ERROR: stm32_chan_wait failed: %d\n", ret);
return (ssize_t)ret;
}
xfrd += chan->xfrd;
}
return (ssize_t)chan->xfrd;
return xfrd;
}
/****************************************************************************
@ -2562,7 +2574,8 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
#if 1
/* Halt the interrupt channel */
if (chan->eptype == OTGHS_EPTYPE_INTR)
if (chan->eptype == OTGHS_EPTYPE_INTR ||
chan->eptype == OTGHS_EPTYPE_BULK)
{
/* Halt the channel -- the CHH interrupt is expected next */
@ -2573,18 +2586,20 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv,
* REVISIT: This can cause a lot of interrupts!
*/
else if (chan->eptype == OTGHS_EPTYPE_CTRL ||
chan->eptype == OTGHS_EPTYPE_BULK)
else if (chan->eptype == OTGHS_EPTYPE_CTRL /*||
chan->eptype == OTGHS_EPTYPE_BULK*/)
{
/* Re-activate the channel by clearing CHDIS and assuring that
* CHENA is set
*/
// TODO: set channel reason to NACK?
regval = stm32_getreg(STM32_OTGHS_HCCHAR(chidx));
regval |= OTGHS_HCCHAR_CHENA;
regval &= ~OTGHS_HCCHAR_CHDIS;
stm32_putreg(STM32_OTGHS_HCCHAR(chidx), regval);
}
#else
/* Halt all transfers on the NAK -- the CHH interrupt is expected next */

View File

@ -831,8 +831,8 @@ static uint16_t recvfrom_tcpinterrupt(FAR struct net_driver_s *dev,
{
ninfo("TCP resume\n");
/* The TCP receive buffer is full. Return now and don't allow
* any further TCP call backs.
/* The TCP receive buffer is non-empty. Return now and don't
* allow any further TCP call backs.
*/
pstate->rf_cb->flags = 0;