drivers/usbdev/cdcacm.c: Use small lock to protect cdcacm

Use spin lock to replace enter_critical_section

Signed-off-by: yangsong8 <yangsong8@xiaomi.com>
This commit is contained in:
yangsong8
2025-04-23 21:39:12 +08:00
committed by Xiang Xiao
parent fe293ec9b5
commit cc067ab199
+57 -43
View File
@@ -106,6 +106,7 @@ struct cdcacm_dev_s
#endif #endif
bool rxenabled; /* true: UART RX "interrupts" enabled */ bool rxenabled; /* true: UART RX "interrupts" enabled */
bool ispolling; bool ispolling;
spinlock_t lock;
struct cdc_linecoding_s linecoding; /* Buffered line status */ struct cdc_linecoding_s linecoding; /* Buffered line status */
cdcacm_callback_t callback; /* Serial event callback function */ cdcacm_callback_t callback; /* Serial event callback function */
@@ -360,6 +361,7 @@ static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
FAR struct usbdev_ep_s *ep = priv->epbulkin; FAR struct usbdev_ep_s *ep = priv->epbulkin;
FAR struct cdcacm_wrreq_s *wrcontainer; FAR struct cdcacm_wrreq_s *wrcontainer;
FAR struct usbdev_req_s *req; FAR struct usbdev_req_s *req;
irqstate_t flags;
size_t reqlen; size_t reqlen;
size_t nbytes; size_t nbytes;
int ret; int ret;
@@ -370,9 +372,11 @@ static ssize_t cdcuart_sendbuf(FAR struct uart_dev_s *dev,
/* Peek at the request in the container at the head of the list */ /* Peek at the request in the container at the head of the list */
flags = spin_lock_irqsave(&priv->lock);
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree); wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
req = wrcontainer->req; req = wrcontainer->req;
priv->nwrq--; priv->nwrq--;
spin_unlock_irqrestore(&priv->lock, flags);
/* Fill the request with serial TX data */ /* Fill the request with serial TX data */
@@ -415,11 +419,10 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
FAR struct uart_dev_s *dev = &priv->serdev; FAR struct uart_dev_s *dev = &priv->serdev;
FAR struct cdcacm_wrreq_s *wrcontainer; FAR struct cdcacm_wrreq_s *wrcontainer;
FAR struct usbdev_req_s *req; FAR struct usbdev_req_s *req;
irqstate_t flags;
int ret; int ret;
#endif #endif
irqstate_t flags;
#ifdef CONFIG_DEBUG_FEATURES #ifdef CONFIG_DEBUG_FEATURES
if (priv == NULL) if (priv == NULL)
{ {
@@ -428,7 +431,6 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
} }
#endif #endif
flags = enter_critical_section();
if (priv->ispolling) if (priv->ispolling)
{ {
goto out; goto out;
@@ -454,17 +456,22 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
{ {
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL), usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL),
(uint16_t)-ret); (uint16_t)-ret);
flags = spin_lock_irqsave_nopreempt(&priv->lock);
dev->xmit.head = 0; dev->xmit.head = 0;
dev->xmit.tail = 0; dev->xmit.tail = 0;
uart_datasent(dev); uart_datasent(dev);
spin_unlock_irqrestore_nopreempt(&priv->lock, flags);
goto out; goto out;
} }
flags = spin_lock_irqsave(&priv->lock);
priv->wrcontainer = NULL; priv->wrcontainer = NULL;
spin_unlock_irqrestore(&priv->lock, flags);
} }
if (!sq_empty(&priv->txfree)) if (!sq_empty(&priv->txfree))
{ {
flags = spin_lock_irqsave_nopreempt(&priv->lock);
priv->wrcontainer = (FAR struct cdcacm_wrreq_s *) priv->wrcontainer = (FAR struct cdcacm_wrreq_s *)
sq_remfirst(&priv->txfree); sq_remfirst(&priv->txfree);
dev->xmit.buffer = (FAR char *)priv->wrcontainer->req->buf; dev->xmit.buffer = (FAR char *)priv->wrcontainer->req->buf;
@@ -472,6 +479,7 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
dev->xmit.head = 0; dev->xmit.head = 0;
dev->xmit.tail = 0; dev->xmit.tail = 0;
uart_datasent(dev); uart_datasent(dev);
spin_unlock_irqrestore_nopreempt(&priv->lock, flags);
} }
#else #else
if (!sq_empty(&priv->txfree)) if (!sq_empty(&priv->txfree))
@@ -481,7 +489,6 @@ static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
#endif #endif
out: out:
leave_critical_section(flags);
return OK; return OK;
} }
@@ -613,7 +620,7 @@ static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv)
* must be disabled throughout the following. * must be disabled throughout the following.
*/ */
flags = enter_critical_section(); flags = spin_lock_irqsave_nopreempt(&priv->lock);
if (priv->ispolling) if (priv->ispolling)
{ {
@@ -674,7 +681,7 @@ static int cdcacm_release_rxpending(FAR struct cdcacm_dev_s *priv)
} }
out: out:
leave_critical_section(flags); spin_unlock_irqrestore_nopreempt(&priv->lock, flags);
return ret; return ret;
} }
@@ -739,24 +746,26 @@ static int cdcacm_serialstate(FAR struct cdcacm_dev_s *priv)
usbtrace(CDCACM_CLASSAPI_FLOWCONTROL, (uint16_t)priv->serialstate); usbtrace(CDCACM_CLASSAPI_FLOWCONTROL, (uint16_t)priv->serialstate);
flags = enter_critical_section();
/* Use our interrupt IN endpoint for the transfer */ /* Use our interrupt IN endpoint for the transfer */
ep = priv->epintin; ep = priv->epintin;
/* Remove the next container from the request list */ /* Remove the next container from the request list */
flags = spin_lock_irqsave(&priv->lock);
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree); wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
if (wrcontainer == NULL) if (wrcontainer == NULL)
{ {
ret = -ENOMEM; ret = -ENOMEM;
spin_unlock_irqrestore(&priv->lock, flags);
goto errout_with_flags; goto errout_with_flags;
} }
/* Decrement the count of write requests */ /* Decrement the count of write requests */
priv->nwrq--; priv->nwrq--;
spin_unlock_irqrestore(&priv->lock, flags);
/* Format the SerialState notification */ /* Format the SerialState notification */
@@ -796,7 +805,6 @@ errout_with_flags:
priv->serialstate &= CDC_UART_CONSISTENT; priv->serialstate &= CDC_UART_CONSISTENT;
leave_critical_section(flags);
return ret; return ret;
} }
#endif #endif
@@ -1049,7 +1057,6 @@ static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
/* Process the received data unless this is some unusual condition */ /* Process the received data unless this is some unusual condition */
flags = enter_critical_section();
switch (req->result) switch (req->result)
{ {
case 0: /* Normal completion */ case 0: /* Normal completion */
@@ -1058,8 +1065,10 @@ static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
/* Place the incoming packet at the end of pending RX packet list. */ /* Place the incoming packet at the end of pending RX packet list. */
flags = spin_lock_irqsave(&priv->lock);
rdcontainer->offset = 0; rdcontainer->offset = 0;
sq_addlast((FAR sq_entry_t *)rdcontainer, &priv->rxpending); sq_addlast((FAR sq_entry_t *)rdcontainer, &priv->rxpending);
spin_unlock_irqrestore(&priv->lock, flags);
/* Then process all pending RX packet starting at the head of the /* Then process all pending RX packet starting at the head of the
* list * list
@@ -1072,10 +1081,13 @@ static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
case -ESHUTDOWN: /* Disconnection */ case -ESHUTDOWN: /* Disconnection */
{ {
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0); usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
flags = spin_lock_irqsave(&priv->lock);
if (priv->nrdq != 0) if (priv->nrdq != 0)
{ {
priv->nrdq--; priv->nrdq--;
} }
spin_unlock_irqrestore(&priv->lock, flags);
} }
break; break;
@@ -1087,8 +1099,6 @@ static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
break; break;
} }
} }
leave_critical_section(flags);
} }
/**************************************************************************** /****************************************************************************
@@ -1124,10 +1134,10 @@ static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
/* Return the write request to the free list */ /* Return the write request to the free list */
flags = enter_critical_section(); flags = spin_lock_irqsave(&priv->lock);
sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree); sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree);
priv->nwrq++; priv->nwrq++;
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
/* Send the next packet unless this was some unusual termination /* Send the next packet unless this was some unusual termination
* condition * condition
@@ -1369,18 +1379,20 @@ static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
wrcontainer->req->priv = wrcontainer; wrcontainer->req->priv = wrcontainer;
wrcontainer->req->callback = cdcacm_wrcomplete; wrcontainer->req->callback = cdcacm_wrcomplete;
flags = enter_critical_section(); flags = spin_lock_irqsave(&priv->lock);
sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree); sq_addlast((FAR sq_entry_t *)wrcontainer, &priv->txfree);
priv->nwrq++; /* Count of write requests available */ priv->nwrq++; /* Count of write requests available */
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
} }
#ifdef CONFIG_CDCACM_DISABLE_TXBUF #ifdef CONFIG_CDCACM_DISABLE_TXBUF
flags = spin_lock_irqsave(&priv->lock);
priv->wrcontainer = (FAR struct cdcacm_wrreq_s *) priv->wrcontainer = (FAR struct cdcacm_wrreq_s *)
sq_remfirst(&priv->txfree); sq_remfirst(&priv->txfree);
priv->serdev.xmit.buffer = (FAR char *)priv->wrcontainer->req->buf; priv->serdev.xmit.buffer = (FAR char *)priv->wrcontainer->req->buf;
priv->serdev.xmit.size = reqlen + 1; priv->serdev.xmit.size = reqlen + 1;
priv->nwrq--; priv->nwrq--;
spin_unlock_irqrestore(&priv->lock, flags);
#endif #endif
/* Report if we are selfpowered (unless we are part of a /* Report if we are selfpowered (unless we are part of a
@@ -1484,7 +1496,7 @@ static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
* of them) * of them)
*/ */
flags = enter_critical_section(); flags = spin_lock_irqsave(&priv->lock);
#ifdef CONFIG_CDCACM_DISABLE_TXBUF #ifdef CONFIG_CDCACM_DISABLE_TXBUF
DEBUGASSERT(priv->nwrq >= CONFIG_CDCACM_NWRREQS - 1); DEBUGASSERT(priv->nwrq >= CONFIG_CDCACM_NWRREQS - 1);
@@ -1503,7 +1515,7 @@ static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
} }
DEBUGASSERT(priv->nwrq == 0); DEBUGASSERT(priv->nwrq == 0);
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
#ifdef CONFIG_CDCACM_HAVE_EPINTIN #ifdef CONFIG_CDCACM_HAVE_EPINTIN
/* Free the interrupt IN endpoint */ /* Free the interrupt IN endpoint */
@@ -1982,7 +1994,6 @@ static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
* connection. * connection.
*/ */
flags = enter_critical_section();
#ifdef CONFIG_SERIAL_REMOVABLE #ifdef CONFIG_SERIAL_REMOVABLE
uart_connected(&priv->serdev, false); uart_connected(&priv->serdev, false);
#endif #endif
@@ -1993,9 +2004,10 @@ static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
/* Clear out all outgoing data in the circular buffer */ /* Clear out all outgoing data in the circular buffer */
flags = spin_lock_irqsave(&priv->lock);
priv->serdev.xmit.head = 0; priv->serdev.xmit.head = 0;
priv->serdev.xmit.tail = 0; priv->serdev.xmit.tail = 0;
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
/* Perform the soft connect function so that we will we can be /* Perform the soft connect function so that we will we can be
* re-enumerated (unless we are part of a composite device) * re-enumerated (unless we are part of a composite device)
@@ -2205,7 +2217,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR sq_entry_t *entry; FAR sq_entry_t *entry;
int count; int count;
irqstate_t flags = enter_critical_section(); irqstate_t flags = spin_lock_irqsave(&priv->lock);
/* Determine the number of bytes available in the RX buffer */ /* Determine the number of bytes available in the RX buffer */
@@ -2217,7 +2229,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
count += rdcontainer->req->xfrd; count += rdcontainer->req->xfrd;
} }
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
*(FAR int *)((uintptr_t)arg) = count; *(FAR int *)((uintptr_t)arg) = count;
} }
@@ -2236,7 +2248,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
int count; int count;
int i; int i;
irqstate_t flags = enter_critical_section(); irqstate_t flags = spin_lock_irqsave(&priv->lock);
/* Determine the number of bytes waiting in the TX buffer */ /* Determine the number of bytes waiting in the TX buffer */
@@ -2261,7 +2273,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
} }
} }
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
*(FAR int *)((uintptr_t)arg) = count; *(FAR int *)((uintptr_t)arg) = count;
} }
@@ -2274,7 +2286,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR sq_entry_t *entry; FAR sq_entry_t *entry;
int count = 0; int count = 0;
irqstate_t flags = enter_critical_section(); irqstate_t flags = spin_lock_irqsave(&priv->lock);
/* Determine the number of bytes free in the TX buffer */ /* Determine the number of bytes free in the TX buffer */
@@ -2288,7 +2300,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
count += serdev->xmit.size - 1; count += serdev->xmit.size - 1;
} }
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
*(FAR int *)((uintptr_t)arg) = count; *(FAR int *)((uintptr_t)arg) = count;
} }
@@ -2307,7 +2319,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR struct cdcacm_rdreq_s *rdcontainer; FAR struct cdcacm_rdreq_s *rdcontainer;
ret = OK; ret = OK;
irqstate_t flags = enter_critical_section(); irqstate_t flags = spin_lock_irqsave(&priv->lock);
if (priv->rdcontainer) if (priv->rdcontainer)
{ {
@@ -2326,19 +2338,20 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
serdev->recv.head = 0; serdev->recv.head = 0;
serdev->recv.tail = 0; serdev->recv.tail = 0;
spin_unlock_irqrestore(&priv->lock, flags);
#ifdef CONFIG_SERIAL_IFLOWCONTROL #ifdef CONFIG_SERIAL_IFLOWCONTROL
/* De-activate RX flow control. */ /* De-activate RX flow control. */
uart_rxflowcontrol(serdev, 0, false); uart_rxflowcontrol(serdev, 0, false);
#endif #endif
leave_critical_section(flags);
} }
#endif #endif
#ifdef CONFIG_CDCACM_DISABLE_TXBUF #ifdef CONFIG_CDCACM_DISABLE_TXBUF
if (arg == TCOFLUSH || arg == TCIOFLUSH) if (arg == TCOFLUSH || arg == TCIOFLUSH)
{ {
irqstate_t flags = enter_critical_section(); irqstate_t flags = spin_lock_irqsave_nopreempt(&priv->lock);
ret = OK; ret = OK;
if (priv->wrcontainer) if (priv->wrcontainer)
@@ -2367,7 +2380,7 @@ static int cdcuart_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
ret = -EBUSY; ret = -EBUSY;
} }
leave_critical_section(flags); spin_unlock_irqrestore_nopreempt(&priv->lock, flags);
} }
#endif #endif
} }
@@ -2635,13 +2648,13 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
* in the following. * in the following.
*/ */
flags = enter_critical_section();
if (enable) if (enable)
{ {
/* RX "interrupts" are enabled. Is this a transition from disabled /* RX "interrupts" are enabled. Is this a transition from disabled
* to enabled state? * to enabled state?
*/ */
flags = spin_lock_irqsave(&priv->lock);
if (!priv->rxenabled) if (!priv->rxenabled)
{ {
/* Yes.. RX "interrupts are no longer disabled */ /* Yes.. RX "interrupts are no longer disabled */
@@ -2649,6 +2662,8 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
priv->rxenabled = true; priv->rxenabled = true;
} }
spin_unlock_irqrestore(&priv->lock, flags);
/* During the time that RX interrupts was disabled, incoming /* During the time that RX interrupts was disabled, incoming
* packets were queued in priv->rxpending. We must now process * packets were queued in priv->rxpending. We must now process
* all of them (unless flow control is enabled) * all of them (unless flow control is enabled)
@@ -2666,10 +2681,10 @@ static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
else else
{ {
flags = spin_lock_irqsave(&priv->lock);
priv->rxenabled = false; priv->rxenabled = false;
spin_unlock_irqrestore(&priv->lock, flags);
} }
leave_critical_section(flags);
} }
/**************************************************************************** /****************************************************************************
@@ -2897,11 +2912,11 @@ static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
} }
#endif #endif
flags = enter_critical_section(); flags = spin_lock_irqsave(&priv->lock);
if (dev->disconnected) if (dev->disconnected)
{ {
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
return true; return true;
} }
@@ -2913,6 +2928,8 @@ static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
* txfree, then there is no longer any TX data in flight. * txfree, then there is no longer any TX data in flight.
*/ */
flags = spin_lock_irqsave(&priv->lock);
#ifdef CONFIG_CDCACM_DISABLE_TXBUF #ifdef CONFIG_CDCACM_DISABLE_TXBUF
/* dev->xmit.buffer always take one req, so just compare /* dev->xmit.buffer always take one req, so just compare
* CONFIG_CDCACM_NWRREQS - 1. * CONFIG_CDCACM_NWRREQS - 1.
@@ -2922,7 +2939,7 @@ static bool cdcuart_txempty(FAR struct uart_dev_s *dev)
#else #else
empty = priv->nwrq >= CONFIG_CDCACM_NWRREQS; empty = priv->nwrq >= CONFIG_CDCACM_NWRREQS;
#endif #endif
leave_critical_section(flags); spin_unlock_irqrestore(&priv->lock, flags);
return empty; return empty;
} }
@@ -2966,6 +2983,7 @@ static void cdcuart_dmasend(FAR struct uart_dev_s *dev)
FAR struct usbdev_ep_s *ep = priv->epbulkin; FAR struct usbdev_ep_s *ep = priv->epbulkin;
FAR struct cdcacm_wrreq_s *wrcontainer; FAR struct cdcacm_wrreq_s *wrcontainer;
FAR struct usbdev_req_s *req; FAR struct usbdev_req_s *req;
irqstate_t flags;
size_t nbytes; size_t nbytes;
size_t reqlen; size_t reqlen;
int ret; int ret;
@@ -2976,9 +2994,11 @@ static void cdcuart_dmasend(FAR struct uart_dev_s *dev)
/* Peek at the request in the container at the head of the list */ /* Peek at the request in the container at the head of the list */
flags = spin_lock_irqsave(&priv->lock);
wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree); wrcontainer = (FAR struct cdcacm_wrreq_s *)sq_remfirst(&priv->txfree);
req = wrcontainer->req; req = wrcontainer->req;
priv->nwrq--; priv->nwrq--;
spin_unlock_irqrestore(&priv->lock, flags);
/* Fill the request with serial TX data */ /* Fill the request with serial TX data */
@@ -3145,15 +3165,11 @@ ssize_t cdcacm_write(FAR const char *buffer, size_t buflen)
while (len < buflen) while (len < buflen)
{ {
irqstate_t flags;
if (!priv || !(priv->ctrlline & CDC_DTE_PRESENT)) if (!priv || !(priv->ctrlline & CDC_DTE_PRESENT))
{ {
return -EINVAL; return -EINVAL;
} }
flags = enter_critical_section();
if (cdcuart_txready(&priv->serdev)) if (cdcuart_txready(&priv->serdev))
{ {
ssize_t ret = cdcuart_sendbuf(&priv->serdev, ssize_t ret = cdcuart_sendbuf(&priv->serdev,
@@ -3161,14 +3177,11 @@ ssize_t cdcacm_write(FAR const char *buffer, size_t buflen)
buflen - len); buflen - len);
if (ret < 0) if (ret < 0)
{ {
leave_critical_section(flags);
return ret; return ret;
} }
len += ret; len += ret;
} }
leave_critical_section(flags);
} }
return buflen; return buflen;
@@ -3241,6 +3254,7 @@ int cdcacm_classobject(int minor, FAR struct usbdev_devinfo_s *devinfo,
memset(priv, 0, sizeof(struct cdcacm_dev_s)); memset(priv, 0, sizeof(struct cdcacm_dev_s));
sq_init(&priv->txfree); sq_init(&priv->txfree);
sq_init(&priv->rxpending); sq_init(&priv->rxpending);
spin_lock_init(&priv->lock);
priv->minor = minor; priv->minor = minor;