serial: Test for no tx data on tx restart
authorPeter Hurley <peter@hurleysoftware.com>
Sun, 6 Jul 2014 15:29:52 +0000 (11:29 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 Jul 2014 23:31:34 +0000 (16:31 -0700)
Commit 717f3bbab3c7628736ef738fdbf3d9a28578c26c,
'serial_core: Fix conditional start_tx on ring buffer not empty'
exposes an incorrect assumption in several drivers' start_tx methods;
the tx ring buffer can, in fact, be empty when restarting tx while
performing flow control.

Affected drivers:
sunsab.c
ip22zilog.c
pmac_zilog.c
sunzilog.c
m32r_sio.c
imx.c

Other in-tree serial drivers either are not affected or already
test for empty tx ring buffer before transmitting.

Test for empty tx ring buffer in start_tx() method, after transmitting
x_char (if applicable).

Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Cc: Seth Bollinger <sethb@digi.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Cc: stable <stable@vger.kernel.org> # 3.15
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/imx.c
drivers/tty/serial/ip22zilog.c
drivers/tty/serial/m32r_sio.c
drivers/tty/serial/pmac_zilog.c
drivers/tty/serial/sunsab.c
drivers/tty/serial/sunzilog.c

index e2f9387..56bd9aa 100644 (file)
@@ -567,6 +567,9 @@ static void imx_start_tx(struct uart_port *port)
        struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
 
+       if (uart_circ_empty(&port.state->xmit))
+               return;
+
        if (USE_IRDA(sport)) {
                /* half duplex in IrDA mode; have to disable receive mode */
                temp = readl(sport->port.membase + UCR4);
index 1efd4c3..99b7b86 100644 (file)
@@ -603,6 +603,8 @@ static void ip22zilog_start_tx(struct uart_port *port)
        } else {
                struct circ_buf *xmit = &port->state->xmit;
 
+               if (uart_circ_empty(xmit))
+                       return;
                writeb(xmit->buf[xmit->tail], &channel->data);
                ZSDELAY();
                ZS_WSYNC(channel);
index 68f2c53..5702828 100644 (file)
@@ -266,9 +266,11 @@ static void m32r_sio_start_tx(struct uart_port *port)
        if (!(up->ier & UART_IER_THRI)) {
                up->ier |= UART_IER_THRI;
                serial_out(up, UART_IER, up->ier);
-               serial_out(up, UART_TX, xmit->buf[xmit->tail]);
-               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-               up->port.icount.tx++;
+               if (!uart_circ_empty(xmit)) {
+                       serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+                       xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+                       up->port.icount.tx++;
+               }
        }
        while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY);
 #else
index 8193635..f7ad5b9 100644 (file)
@@ -653,6 +653,8 @@ static void pmz_start_tx(struct uart_port *port)
        } else {
                struct circ_buf *xmit = &port->state->xmit;
 
+               if (uart_circ_empty(xmit))
+                       goto out;
                write_zsdata(uap, xmit->buf[xmit->tail]);
                zssync(uap);
                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -661,6 +663,7 @@ static void pmz_start_tx(struct uart_port *port)
                if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                        uart_write_wakeup(&uap->port);
        }
+ out:
        pmz_debug("pmz: start_tx() done.\n");
 }
 
index 80a58ec..2f57df9 100644 (file)
@@ -427,6 +427,9 @@ static void sunsab_start_tx(struct uart_port *port)
        struct circ_buf *xmit = &up->port.state->xmit;
        int i;
 
+       if (uart_circ_empty(xmit))
+               return;
+
        up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
        writeb(up->interrupt_mask1, &up->regs->w.imr1);
        
index a85db8b..02df394 100644 (file)
@@ -703,6 +703,8 @@ static void sunzilog_start_tx(struct uart_port *port)
        } else {
                struct circ_buf *xmit = &port->state->xmit;
 
+               if (uart_circ_empty(xmit))
+                       return;
                writeb(xmit->buf[xmit->tail], &channel->data);
                ZSDELAY();
                ZS_WSYNC(channel);