This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new dcd135d965c arch/xtensa/esp32: fix double spinlock unlock in
esp32_txint
dcd135d965c is described below
commit dcd135d965c3e734e9111fde41f3c8d2350f5b06
Author: Swatantra Yadav <[email protected]>
AuthorDate: Sun Oct 18 14:34:50 2026 +0530
arch/xtensa/esp32: fix double spinlock unlock in esp32_txint
In esp32_txint(), when CONFIG_SUPPRESS_SERIAL_INTS was enabled,
spin_unlock_irqrestore(&priv->lock, flags) was invoked inside the
if (enable) block before calling uart_xmitchars(dev), and then
called a second time unconditionally after the if/else block.
Restructure esp32_txint() so that the spinlock is released exactly
once across all execution paths, and call uart_xmitchars(dev)
safely after releasing priv->lock to prevent recursive deadlocks.
Resolves #14594.
Signed-off-by: Swatantra Yadav <[email protected]>
---
arch/xtensa/src/esp32/esp32_serial.c | 313 ++++++++++++++++++-----------------
1 file changed, 158 insertions(+), 155 deletions(-)
diff --git a/arch/xtensa/src/esp32/esp32_serial.c
b/arch/xtensa/src/esp32/esp32_serial.c
index 9896c6b18b5..65bb35fd456 100644
--- a/arch/xtensa/src/esp32/esp32_serial.c
+++ b/arch/xtensa/src/esp32/esp32_serial.c
@@ -1123,6 +1123,7 @@ static inline void dma_enable_int(uint8_t dma_chan)
uint32_t int_mask = UHCI_OUT_TOTAL_EOF_INT_ENA_M |
UHCI_OUT_DSCR_ERR_INT_ENA_M;
+
putreg32(int_mask, UHCI_INT_ENA_REG(dma_chan));
}
@@ -1232,23 +1233,24 @@ static int esp32_interrupt_dma(int irq, void *context,
void *arg)
# ifdef CONFIG_ESP32_UART0_TXDMA
case UHCI_UART0_CE_M:
dev = &g_uart0port;
- break;
+ break;
# endif
# ifdef CONFIG_ESP32_UART1_TXDMA
case UHCI_UART1_CE_M:
dev = &g_uart1port;
- break;
+ break;
# endif
# ifdef CONFIG_ESP32_UART2_TXDMA
case UHCI_UART2_CE_M:
dev = &g_uart2port;
- break;
+ break;
# endif
default:
dmaerr("No UART selected\n");
+ break;
}
if (dev != NULL)
@@ -1451,195 +1453,192 @@ static int esp32_ioctl(struct file *filep, int cmd,
unsigned long arg)
switch (cmd)
{
#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
- case TIOCSERGSTRUCT:
- {
- struct esp32_dev_s *user = (struct esp32_dev_s *)arg;
- if (!user)
- {
- ret = -EINVAL;
- }
- else
- {
- memcpy(user, dev, sizeof(struct esp32_dev_s));
- }
- }
- break;
+ case TIOCSERGSTRUCT:
+ {
+ struct esp32_dev_s *user = (struct esp32_dev_s *)arg;
+
+ if (!user)
+ {
+ ret = -EINVAL;
+ }
+ else
+ {
+ memcpy(user, dev, sizeof(struct esp32_dev_s));
+ }
+ }
+ break;
#endif
#ifdef CONFIG_SERIAL_TERMIOS
- case TCGETS:
- {
- struct termios *termiosp = (struct termios *)arg;
- struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
+ case TCGETS:
+ {
+ struct termios *termiosp = (struct termios *)arg;
+ struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
- if (!termiosp)
- {
- ret = -EINVAL;
- break;
- }
+ if (!termiosp)
+ {
+ ret = -EINVAL;
+ break;
+ }
- /* Return parity */
+ /* Return parity */
- termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) |
- ((priv->parity == 1) ? PARODD : 0);
+ termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) |
+ ((priv->parity == 1) ? PARODD : 0);
- /* Return stop bits */
+ /* Return stop bits */
- termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0;
+ termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0;
- /* Return flow control */
+ /* Return flow control */
# ifdef CONFIG_SERIAL_OFLOWCONTROL
- termiosp->c_cflag |= (priv->oflow) ? CCTS_OFLOW : 0;
+ termiosp->c_cflag |= (priv->oflow) ? CCTS_OFLOW : 0;
# endif
# ifdef CONFIG_SERIAL_IFLOWCONTROL
- termiosp->c_cflag |= (priv->iflow) ? CRTS_IFLOW : 0;
+ termiosp->c_cflag |= (priv->iflow) ? CRTS_IFLOW : 0;
# endif
- /* Return baud */
-
- cfsetispeed(termiosp, priv->baud);
-
- /* Return number of bits */
-
- switch (priv->bits)
- {
- case 5:
- termiosp->c_cflag |= CS5;
- break;
-
- case 6:
- termiosp->c_cflag |= CS6;
- break;
-
- case 7:
- termiosp->c_cflag |= CS7;
- break;
-
- default:
- case 8:
- termiosp->c_cflag |= CS8;
- break;
-
- case 9:
- termiosp->c_cflag |= CS8 /* CS9 */ ;
- break;
- }
- }
- break;
-
- case TCSETS:
- {
- struct termios *termiosp = (struct termios *)arg;
- struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
- uint32_t baud;
- uint32_t intena;
- uint8_t parity;
- uint8_t nbits;
- bool stop2;
+ /* Return baud */
+
+ cfsetispeed(termiosp, priv->baud);
+
+ /* Return number of bits */
+
+ switch (priv->bits)
+ {
+ case 5:
+ termiosp->c_cflag |= CS5;
+ break;
+
+ case 6:
+ termiosp->c_cflag |= CS6;
+ break;
+
+ case 7:
+ termiosp->c_cflag |= CS7;
+ break;
+
+ default:
+ case 8:
+ termiosp->c_cflag |= CS8;
+ break;
+
+ case 9:
+ termiosp->c_cflag |= CS8 /* CS9 */ ;
+ break;
+ }
+ }
+ break;
+
+ case TCSETS:
+ {
+ struct termios *termiosp = (struct termios *)arg;
+ struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
+ uint32_t baud;
+ uint32_t intena;
+ uint8_t parity;
+ uint8_t nbits;
+ bool stop2;
# ifdef CONFIG_SERIAL_IFLOWCONTROL
- bool iflow;
+ bool iflow;
# endif
# ifdef CONFIG_SERIAL_OFLOWCONTROL
- bool oflow;
+ bool oflow;
# endif
- if (!termiosp)
- {
- ret = -EINVAL;
- break;
- }
+ if (!termiosp)
+ {
+ ret = -EINVAL;
+ break;
+ }
- /* Decode baud. */
+ /* Decode baud. */
- ret = OK;
- baud = cfgetispeed(termiosp);
+ ret = OK;
+ baud = cfgetispeed(termiosp);
- /* Decode number of bits */
+ /* Decode number of bits */
- switch (termiosp->c_cflag & CSIZE)
- {
- case CS5:
- nbits = 5;
- break;
+ switch (termiosp->c_cflag & CSIZE)
+ {
+ case CS5:
+ nbits = 5;
+ break;
- case CS6:
- nbits = 6;
- break;
+ case CS6:
+ nbits = 6;
+ break;
- case CS7:
- nbits = 7;
- break;
+ case CS7:
+ nbits = 7;
+ break;
- case CS8:
- nbits = 8;
- break;
-# if 0
- case CS9:
- nbits = 9;
- break;
-# endif
- default:
- ret = -EINVAL;
- break;
- }
+ case CS8:
+ nbits = 8;
+ break;
- /* Decode parity */
+ default:
+ ret = -EINVAL;
+ break;
+ }
- if ((termiosp->c_cflag & PARENB) != 0)
- {
- parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
- }
- else
- {
- parity = 0;
- }
+ /* Decode parity */
- /* Decode stop bits */
+ if ((termiosp->c_cflag & PARENB) != 0)
+ {
+ parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+ }
+ else
+ {
+ parity = 0;
+ }
+
+ /* Decode stop bits */
- stop2 = (termiosp->c_cflag & CSTOPB) != 0;
+ stop2 = (termiosp->c_cflag & CSTOPB) != 0;
- /* Decode flow control */
+ /* Decode flow control */
# ifdef CONFIG_SERIAL_IFLOWCONTROL
- iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+ iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
# endif
# ifdef CONFIG_SERIAL_OFLOWCONTROL
- oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+ oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
# endif
- /* Verify that all settings are valid before committing */
+ /* Verify that all settings are valid before committing */
- if (ret == OK)
- {
- /* Commit */
+ if (ret == OK)
+ {
+ /* Commit */
- priv->baud = baud;
- priv->parity = parity;
- priv->bits = nbits;
- priv->stopbits2 = stop2;
+ priv->baud = baud;
+ priv->parity = parity;
+ priv->bits = nbits;
+ priv->stopbits2 = stop2;
# ifdef CONFIG_SERIAL_IFLOWCONTROL
- priv->iflow = iflow;
+ priv->iflow = iflow;
# endif
# ifdef CONFIG_SERIAL_OFLOWCONTROL
- priv->oflow = oflow;
+ priv->oflow = oflow;
# endif
- /* effect the changes immediately - note that we do not
- * implement TCSADRAIN / TCSAFLUSH
- */
+ /* effect the changes immediately - note that we do not
+ * implement TCSADRAIN / TCSAFLUSH
+ */
- esp32_disableallints(priv, &intena);
- ret = esp32_setup(dev);
+ esp32_disableallints(priv, &intena);
+ ret = esp32_setup(dev);
- /* Restore the interrupt state */
+ /* Restore the interrupt state */
- esp32_restoreuartint(priv, intena);
- }
- }
- break;
+ esp32_restoreuartint(priv, intena);
+ }
+ }
+ break;
#endif /* CONFIG_SERIAL_TERMIOS */
- default:
- ret = -ENOTTY;
- break;
+ default:
+ ret = -ENOTTY;
+ break;
}
return ret;
@@ -1725,6 +1724,7 @@ static void esp32_rxint(struct uart_dev_s *dev, bool
enable)
static bool esp32_rxavailable(struct uart_dev_s *dev)
{
struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
+
return esp32_get_rx_fifo_len(priv) > 0;
}
@@ -1790,14 +1790,6 @@ static void esp32_txint(struct uart_dev_s *dev, bool
enable)
#ifndef CONFIG_SUPPRESS_SERIAL_INTS
modifyreg32(UART_INT_ENA_REG(priv->config->id),
0, (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA));
-
-#else
- /* Fake a TX interrupt here by just calling uart_xmitchars() with
- * interrupts disabled (note this may recurse).
- */
-
- spin_unlock_irqrestore(&priv->lock, flags);
- uart_xmitchars(dev);
#endif
}
else
@@ -1809,6 +1801,17 @@ static void esp32_txint(struct uart_dev_s *dev, bool
enable)
}
spin_unlock_irqrestore(&priv->lock, flags);
+
+#ifdef CONFIG_SUPPRESS_SERIAL_INTS
+ if (enable)
+ {
+ /* Fake a TX interrupt here by just calling uart_xmitchars()
+ * after the spinlock is released (note this may recurse).
+ */
+
+ uart_xmitchars(dev);
+ }
+#endif
#ifdef CONFIG_SERIAL_TXDMA
}
#endif
@@ -1981,8 +1984,8 @@ static bool esp32_rxflowcontrol(struct uart_dev_s *dev,
* SW RX FIFO.
*/
- esp32_rxint(dev, false);
- ret = true;
+ esp32_rxint(dev, false);
+ ret = true;
}
}