usb host: cdcacm: IN transition failure
- Dominant language
- C
- Stars
- 4k
- Forks
- 1.7k
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 237
Description
## Issue Description
When usb as host to connect cdcacm device, using read() API to get data from cdcacm device many times, EHCI driver, such as **sam_transfer() - sam_ioc_setup()**, will trigger assert in **sam_ioc_setup()**, because **epinfo->iocwait** is true.
``` c
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait);
```
## Analysis
read() API caller as follows:
read() - uart_read() - uart_disablerxint()【usbhost_rxint()】 - uart_enablerxint()【usbhost_rxint()】 - usbhost_rxdata_work() - DRVR_TRANSFER()【sam_transfer()】.
In **usbhost_rxdata_work()**, while loop call **DRVR_TRANSFER()【sam_transfer()】** to excute usb IN transition. DRVR_TRANSFER() is a blocking method, this functions will not return until the transfer has completed, the comments as follows:
```
* Name: sam_transfer
*
* Description:
* Process a request to handle a transfer descriptor. This method will
* enqueue the transfer request, blocking until the transfer completes.
* Only one transfer may be queued; Neither this method nor the ctrlin or
* ctrlout methods can be called again until this function returns.
*
* This is a blocking method; this functions will not return until the
* transfer has completed.
```
But every read() operation, call uart_disablerxint() first, then call uart_enablerxint(). So excute read() one time, call DRVR_TRANSFER() one time, even DRVR_TRANSFER() in progress or not finish. Thus will cause usb transfer abnormal, such as **epinfo->iocwait** is true.
## Solution
I have two sulution ideas.
- fix uart_read() logic,delete serial.c 986 line —— uart_disablerxint(dev).
- fix usbhost_rxint() logic, cancel disable opertion. When rx is enabled, keep **priv->rxena** always enable.
```
static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable)
{
FAR struct usbhost_cdcacm_s *priv;
int ret;
DEBUGASSERT(uartdev && uartdev->priv);
priv = (FAR struct usbhost_cdcacm_s *)uartdev->priv;
/* Are we enabling or disabling RX reception? */
if (enable && !priv->rxena)
{
/* Cancel any pending, delayed RX data reception work */
work_cancel(LPWORK, &priv->rxwork);
/* Restart immediate RX data reception work (unless RX flow control
* is in effect.
*/
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->rts)
#endif
{
ret = work_queue(LPWORK, &priv->rxwork,
usbhost_rxdata_work, priv, 0);
DEBUGASSERT(ret >= 0);
UNUSED(ret);
}
/* Save the new RX enable state */
priv->rxena = enable;
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.