]> Git Repo - linux.git/commitdiff
serial: 8250: fix XOFF/XON sending when DMA is used
authorIlpo Järvinen <[email protected]>
Mon, 14 Mar 2022 09:14:32 +0000 (11:14 +0200)
committerGreg Kroah-Hartman <[email protected]>
Fri, 18 Mar 2022 12:30:54 +0000 (13:30 +0100)
When 8250 UART is using DMA, x_char (XON/XOFF) is never sent
to the wire. After this change, x_char is injected correctly.

Create uart_xchar_out() helper for sending the x_char out and
accounting related to it. It seems that almost every driver
does these same steps with x_char. Except for 8250, however,
almost all currently lack .serial_out so they cannot immediately
take advantage of this new helper.

The downside of this patch is that it might reintroduce
the problems some devices faced with mixed DMA/non-DMA transfer
which caused revert f967fc8f165f (Revert "serial: 8250_dma:
don't bother DMA with small transfers"). However, the impact
should be limited to cases with XON/XOFF (that didn't work
with DMA capable devices to begin with so this problem is not
very likely to cause a major issue, if any at all).

Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
Reported-by: Gilles Buloz <[email protected]>
Tested-by: Gilles Buloz <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Ilpo Järvinen <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
drivers/tty/serial/8250/8250_dma.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/serial_core.c
include/linux/serial_core.h

index 890fa7ddaa7f36650a1b69a8554e5567fd674f84..b3c3f7e5851aba3a17ccb7f80f18586c5aac4e4c 100644 (file)
@@ -64,10 +64,19 @@ int serial8250_tx_dma(struct uart_8250_port *p)
        struct uart_8250_dma            *dma = p->dma;
        struct circ_buf                 *xmit = &p->port.state->xmit;
        struct dma_async_tx_descriptor  *desc;
+       struct uart_port                *up = &p->port;
        int ret;
 
-       if (dma->tx_running)
+       if (dma->tx_running) {
+               if (up->x_char) {
+                       dmaengine_pause(dma->txchan);
+                       uart_xchar_out(up, UART_TX);
+                       dmaengine_resume(dma->txchan);
+               }
                return 0;
+       } else if (up->x_char) {
+               uart_xchar_out(up, UART_TX);
+       }
 
        if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
                /* We have been called from __dma_tx_complete() */
index 267026892264a355259ff195e898e73551f2ad2e..318af6f1360504b6222cd3483b02fd83f3790e13 100644 (file)
@@ -1822,9 +1822,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
        int count;
 
        if (port->x_char) {
-               serial_out(up, UART_TX, port->x_char);
-               port->icount.tx++;
-               port->x_char = 0;
+               uart_xchar_out(port, UART_TX);
                return;
        }
        if (uart_tx_stopped(port)) {
index a1688a3414112d79201496c812f8ea26ab139bd6..6a8963caf954a02f1fef0da9f3352d52cb09b352 100644 (file)
@@ -641,6 +641,20 @@ static void uart_flush_buffer(struct tty_struct *tty)
        tty_port_tty_wakeup(&state->port);
 }
 
+/*
+ * This function performs low-level write of high-priority XON/XOFF
+ * character and accounting for it.
+ *
+ * Requires uart_port to implement .serial_out().
+ */
+void uart_xchar_out(struct uart_port *uport, int offset)
+{
+       serial_port_out(uport, offset, uport->x_char);
+       uport->icount.tx++;
+       uport->x_char = 0;
+}
+EXPORT_SYMBOL_GPL(uart_xchar_out);
+
 /*
  * This function is used to send a high-priority XON/XOFF character to
  * the device
index 14ae35f68abbf30f9aee4b2e564fb69cd89fff7f..d4828e69087a405ed748a70034ac5551d2b58379 100644 (file)
@@ -458,6 +458,8 @@ extern void uart_handle_cts_change(struct uart_port *uport,
 extern void uart_insert_char(struct uart_port *port, unsigned int status,
                 unsigned int overrun, unsigned int ch, unsigned int flag);
 
+void uart_xchar_out(struct uart_port *uport, int offset);
+
 #ifdef CONFIG_MAGIC_SYSRQ_SERIAL
 #define SYSRQ_TIMEOUT  (HZ * 5)
 
This page took 0.067994 seconds and 4 git commands to generate.