Skip to content

Commit

Permalink
risc-v/mpfs: usb: fix infinite loop issue
Browse files Browse the repository at this point in the history
mpfs_write_tx_fifo() gets stuck in the following case:
  - CDCACM is used
  - ttyACM0 is opened and then closed from the remote end,
    such as Linux or Windows
  - data is written into ttyACM0 from NuttX
  - tx fifo will never get empty and the system is stuck

Fix this by issuing an error code if the transmit fifo doesn't
proceed as expected. The error code is then propagated into
higher level keeping the system functional.

Signed-off-by: Eero Nurkkala <eero.nurkkala@offcode.fi>
  • Loading branch information
eenurkka authored and xiaoxiang781216 committed Aug 29, 2022
1 parent a97a6e1 commit 90d9b6b
Showing 1 changed file with 38 additions and 12 deletions.
50 changes: 38 additions & 12 deletions arch/risc-v/src/mpfs/mpfs_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#define MPFS_TRACEERR_EP0SETUPOUTSIZE 0x0015
#define MPFS_TRACEERR_EPOUTQEMPTY 0x0016
#define MPFS_TRACEERR_EP0PREMATURETERM 0x0017
#define MPFS_TRACEERR_TXHALT 0x0018

/* USB trace interrupt codes */

Expand Down Expand Up @@ -570,12 +571,12 @@ static void mpfs_req_cancel(struct mpfs_ep_s *privep, int16_t result)
* epno - Endpoint number
*
* Returned Value:
* None
* OK, or error
*
****************************************************************************/

static void mpfs_write_tx_fifo(const void *in_data, uint32_t length,
uint8_t epno)
static int mpfs_write_tx_fifo(const void *in_data, uint32_t length,
uint8_t epno)
{
uint32_t i;
uint32_t *temp;
Expand All @@ -584,6 +585,7 @@ static void mpfs_write_tx_fifo(const void *in_data, uint32_t length,
uint16_t words = length / 4;
uint16_t bytes = length - words * 4;
uint16_t offset;
uint16_t retries = 10000;

temp = (uint32_t *)in_data;
temp_8bit = (uint8_t *)in_data;
Expand All @@ -597,7 +599,13 @@ static void mpfs_write_tx_fifo(const void *in_data, uint32_t length,
tx_csr = getreg16(MPFS_USB_ENDPOINT(epno) +
MPFS_USB_ENDPOINT_TX_CSR_OFFSET);
}
while (tx_csr & TXCSRL_REG_EPN_TX_FIFO_NE_MASK);
while ((tx_csr & TXCSRL_REG_EPN_TX_FIFO_NE_MASK) && --retries);
}

if (retries == 0)
{
usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_TXHALT), epno);
return -EIO;
}

/* Send 32-bit words first */
Expand All @@ -615,6 +623,8 @@ static void mpfs_write_tx_fifo(const void *in_data, uint32_t length,
{
mpfs_putreg8((uint8_t)temp_8bit[i], MPFS_USB_FIFO(epno));
}

return OK;
}

/****************************************************************************
Expand All @@ -629,18 +639,19 @@ static void mpfs_write_tx_fifo(const void *in_data, uint32_t length,
* privreq - The actual write request
*
* Returned Value:
* None
* OK if success, error otherwise
*
****************************************************************************/

static void mpfs_req_wrsetup(struct mpfs_usbdev_s *priv,
struct mpfs_ep_s *privep,
struct mpfs_req_s *privreq)
static int mpfs_req_wrsetup(struct mpfs_usbdev_s *priv,
struct mpfs_ep_s *privep,
struct mpfs_req_s *privreq)
{
const uint8_t *buf;
uint32_t packetsize;
uint8_t epno;
int nbytes;
int ret;

epno = USB_EPNO(privep->ep.eplog);

Expand Down Expand Up @@ -693,7 +704,11 @@ static void mpfs_req_wrsetup(struct mpfs_usbdev_s *priv,

if (nbytes > packetsize)
{
mpfs_write_tx_fifo(buf, packetsize, epno);
ret = mpfs_write_tx_fifo(buf, packetsize, epno);
if (ret != OK)
{
return ret;
}

if (epno == EP0)
{
Expand All @@ -709,11 +724,15 @@ static void mpfs_req_wrsetup(struct mpfs_usbdev_s *priv,
}

privreq->inflight = packetsize;
return;
return OK;
}
else
{
mpfs_write_tx_fifo(buf, nbytes, epno);
ret = mpfs_write_tx_fifo(buf, nbytes, epno);
if (ret != OK)
{
return ret;
}
}

privreq->req.xfrd += nbytes;
Expand All @@ -734,6 +753,8 @@ static void mpfs_req_wrsetup(struct mpfs_usbdev_s *priv,
TXCSRL_REG_EPN_UNDERRUN_MASK,
TXCSRL_REG_EPN_TX_PKT_RDY_MASK);
}

return OK;
}

/****************************************************************************
Expand Down Expand Up @@ -835,6 +856,7 @@ static int mpfs_req_write(struct mpfs_usbdev_s *priv,
struct mpfs_req_s *privreq;
uint8_t epno;
int bytesleft;
int ret;

epno = USB_EPNO(privep->ep.eplog);

Expand Down Expand Up @@ -881,7 +903,11 @@ static int mpfs_req_write(struct mpfs_usbdev_s *priv,
{
/* Perform the write operation. */

mpfs_req_wrsetup(priv, privep, privreq);
ret = mpfs_req_wrsetup(priv, privep, privreq);
if (ret != OK)
{
return ret;
}
}
else if ((privreq->req.len == 0) && !privep->zlpsent)
{
Expand Down

0 comments on commit 90d9b6b

Please sign in to comment.