driver/serial/cdcacm: config to enable or disable txbuf

To be compatible with the previous method, add a buf between cdcacm
and serial. Because when using usbreqbuf directly as the buf of serial,
the amount of data sent may be insufficient due to the limit of the
number of reqs. For example, when the number of reqs is 4, the number
of data send through cdcacm is 5, and each data is a separate USB
packet, if the host does not read in time, resulting in blocking send.

Signed-off-by: yangsong8 <yangsong8@xiaomi.com>
This commit is contained in:
yangsong8 2025-03-27 11:58:48 +08:00 committed by Lup Yuen Lee
parent f0b52a8b2d
commit e8f9570ffd
2 changed files with 123 additions and 2 deletions

View file

@ -433,6 +433,7 @@ menuconfig CDCACM
default n
select SERIAL_REMOVABLE
select SERIAL_RXDMA if !CDCACM_DISABLE_RXBUF
select SERIAL_TXDMA if !CDCACM_DISABLE_TXBUF
---help---
Enables USB Modem (CDC/ACM) support
@ -703,6 +704,28 @@ config CDCACM_RXBUFSIZE
endif # !CDCACM_DISABLE_RXBUF
config CDCACM_DISABLE_TXBUF
bool "CDC/ACM use usb tx req buf directly"
default n
---help---
The interaction between cdcacm and serial uses usb req buf
directly instead of using a separate buffer.
if !CDCACM_DISABLE_TXBUF
config CDCACM_TXBUFSIZE
int "Transmit buffer size"
default 769 if USBDEV_DUALSPEED
default 193 if !USBDEV_DUALSPEED
---help---
Size of the serial transmit buffers. The actual amount of data that
can be held in the buffer is this number minus one due to the way
that the circular buffer is managed. So a TX buffer size of 769
will hold one request of size 768; a buffer size of 193 will hold
two requests of size 96 bytes.
endif # !CDCACM_DISABLE_TXBUF
if !CDCACM_COMPOSITE
# In a composite device the Vendor- and Product-ID is given by the composite

View file

@ -135,7 +135,11 @@ struct cdcacm_dev_s
#else
char rxbuffer[CONFIG_CDCACM_RXBUFSIZE];
#endif
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
FAR struct cdcacm_wrreq_s *wrcontainer;
#else
char txbuffer[CONFIG_CDCACM_TXBUFSIZE];
#endif
};
/* The internal version of the class driver */
@ -235,6 +239,9 @@ static ssize_t cdcuart_recvbuf(FAR struct uart_dev_s *dev,
static bool cdcuart_txready(FAR struct uart_dev_s *dev);
static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
FAR const void *buf, size_t len);
#ifndef CONFIG_CDCACM_DISABLE_TXBUF
static void cdcuart_dmasend(FAR struct uart_dev_s *dev);
#endif
#ifndef CONFIG_CDCACM_DISABLE_RXBUF
static void cdcuart_dmareceive(FAR struct uart_dev_s *dev);
#endif
@ -280,8 +287,12 @@ static const struct uart_ops_s g_uartops =
cdcuart_rxflowcontrol, /* rxflowcontrol */
#endif
#ifdef CONFIG_SERIAL_TXDMA
#ifndef CONFIG_CDCACM_DISABLE_TXBUF
cdcuart_dmasend, /* dmasend */
#else
NULL, /* dmasend */
#endif
#endif
#ifdef CONFIG_SERIAL_RXDMA
#ifndef CONFIG_CDCACM_DISABLE_RXBUF
cdcuart_dmareceive, /* dmareceive */
@ -395,12 +406,15 @@ static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
{
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
FAR struct usbdev_ep_s *ep = priv->epbulkin;
FAR struct uart_dev_s *dev = &priv->serdev;
FAR struct cdcacm_wrreq_s *wrcontainer;
FAR struct usbdev_req_s *req;
irqstate_t flags;
int ret;
#endif
irqstate_t flags;
#ifdef CONFIG_DEBUG_FEATURES
if (priv == NULL)
@ -420,6 +434,7 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
priv->serdev.xmit.head, priv->serdev.xmit.tail,
priv->nwrq, sq_empty(&priv->txfree));
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
if (priv->wrcontainer)
{
wrcontainer = priv->wrcontainer;
@ -454,6 +469,12 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
dev->xmit.tail = 0;
uart_datasent(dev);
}
#else
if (!sq_empty(&priv->txfree))
{
uart_xmitchars_dma(&priv->serdev);
}
#endif
out:
leave_critical_section(flags);
@ -1113,7 +1134,9 @@ static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
case OK: /* Normal completion */
{
usbtrace(TRACE_CLASSWRCOMPLETE, priv->nwrq);
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
if (priv->wrcontainer == NULL)
#endif
{
cdcacm_sndpacket(priv);
}
@ -1348,11 +1371,13 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
leave_critical_section(flags);
}
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
priv->wrcontainer = (FAR struct cdcacm_wrreq_s *)
sq_remfirst(&priv->txfree);
priv->serdev.xmit.buffer = (FAR char *)priv->wrcontainer->req->buf;
priv->serdev.xmit.size = reqlen + 1;
priv->nwrq--;
#endif
/* Report if we are selfpowered (unless we are part of a
* composite device)
@ -2153,7 +2178,9 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct cdcacm_dev_s *priv = inode->i_private;
#if defined(CONFIG_CDCACM_DISABLE_TXBUF) || defined(CONFIG_CDCACM_DISABLE_RXBUF)
FAR struct uart_dev_s *serdev = &priv->serdev;
#endif
int ret = OK;
switch (cmd)
@ -2188,6 +2215,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
break;
#endif
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
/* Get the number of bytes that have been written to the TX
* buffer.
*/
@ -2256,6 +2284,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
*(FAR int *)((uintptr_t)arg) = count;
}
break;
#endif
case TCFLSH:
{
@ -2297,6 +2326,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
#endif
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
if (arg == TCOFLUSH || arg == TCIOFLUSH)
{
irqstate_t flags = enter_critical_section();
@ -2330,6 +2360,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
leave_critical_section(flags);
}
#endif
}
break;
@ -2871,11 +2902,17 @@ static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
/* When all of the allocated write requests have been returned to the
* txfree, then there is no longer any TX data in flight.
* dev->xmit.buffer always take one req, so just compare
*/
#ifdef CONFIG_CDCACM_DISABLE_TXBUF
/* dev->xmit.buffer always take one req, so just compare
* CONFIG_CDCACM_NWRREQS - 1.
*/
empty = priv->nwrq >= (CONFIG_CDCACM_NWRREQS - 1);
#else
empty = priv->nwrq >= CONFIG_CDCACM_NWRREQS;
#endif
leave_critical_section(flags);
return empty;
@ -2903,6 +2940,63 @@ static int cdcuart_release(FAR struct uart_dev_s *dev)
return OK;
}
#ifndef CONFIG_CDCACM_DISABLE_TXBUF
/****************************************************************************
* Name: cdcuart_dmasend
*
* Description:
* Set up to transfer bytes from the TX circular buffer.
*
****************************************************************************/
static void cdcuart_dmasend(FAR struct uart_dev_s *dev)
{
FAR struct uart_dmaxfer_s *xfer = &dev->dmatx;
FAR struct cdcacm_dev_s *priv = dev->priv;
FAR struct usbdev_ep_s *ep = priv->epbulkin;
FAR struct cdcacm_wrreq_s *wrcontainer;
FAR struct usbdev_req_s *req;
size_t nbytes;
size_t reqlen;
int ret;
/* Get the maximum number of bytes that will fit into one bulk IN request */
reqlen = MIN(CONFIG_CDCACM_BULKIN_REQLEN, ep->maxpacket);
/* Peek at the request in the container at the head of the list */
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
req = wrcontainer->req;
priv->nwrq--;
/* Fill the request with serial TX data */
nbytes = MIN(reqlen, xfer->length);
memcpy(req->buf, xfer->buffer, nbytes);
req->len = nbytes;
nbytes = MIN(reqlen - nbytes, xfer->nlength);
memcpy(req->buf + req->len, xfer->nbuffer, nbytes);
req->len += nbytes;
xfer->nbytes = req->len;
uart_xmitchars_done(dev);
/* Then submit the request to the endpoint */
req->priv = wrcontainer;
req->flags = USBDEV_REQFLAGS_NULLPKT;
ret = EP_SUBMIT(ep, req);
if (ret < 0)
{
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL),
(uint16_t)-ret);
}
}
#endif
#ifndef CONFIG_CDCACM_DISABLE_RXBUF
/****************************************************************************
@ -3168,6 +3262,10 @@ int cdcacm_classobject(int minor, FAR struct usbdev_devinfo_s *devinfo,
#ifndef CONFIG_CDCACM_DISABLE_RXBUF
priv->serdev.recv.size = CONFIG_CDCACM_RXBUFSIZE;
priv->serdev.recv.buffer = priv->rxbuffer;
#endif
#ifndef CONFIG_CDCACM_DISABLE_TXBUF
priv->serdev.xmit.size = CONFIG_CDCACM_TXBUFSIZE;
priv->serdev.xmit.buffer = priv->txbuffer;
#endif
priv->serdev.ops = &g_uartops;
priv->serdev.priv = priv;