[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] Interrupt-driven serial transmit for 8250/16550 UARTs.



ChangeSet 1.1670, 2005/06/04 18:35:19+01:00, kaf24@xxxxxxxxxxxxxxxxxxxx

        Interrupt-driven serial transmit for 8250/16550 UARTs.
        Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>



 arch/x86/cdb.c         |    5 +
 arch/x86/domain.c      |    4 +
 arch/x86/traps.c       |    1 
 drivers/char/console.c |   10 +++
 drivers/char/ns16550.c |   32 ++++++++----
 drivers/char/serial.c  |  126 +++++++++++++++++++++++++++++++++++++++++++++++--
 include/xen/console.h  |    3 +
 include/xen/serial.h   |   37 +++++++++++---
 8 files changed, 196 insertions(+), 22 deletions(-)


diff -Nru a/xen/arch/x86/cdb.c b/xen/arch/x86/cdb.c
--- a/xen/arch/x86/cdb.c        2005-06-04 14:02:05 -04:00
+++ b/xen/arch/x86/cdb.c        2005-06-04 14:02:05 -04:00
@@ -358,6 +358,7 @@
        local_irq_save(flags);
 
        watchdog_disable();
+       console_start_sync();
 
        /* Shouldn't really do this, but otherwise we stop for no
           obvious reason, which is Bad */
@@ -383,9 +384,13 @@
                        ASSERT(!local_irq_is_enabled());
                }
        }
+
+       console_end_sync();
        watchdog_enable();
        atomic_inc(&xendbg_running);
+
        local_irq_restore(flags);
+
        return 0;
 }
 
diff -Nru a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
--- a/xen/arch/x86/domain.c     2005-06-04 14:02:06 -04:00
+++ b/xen/arch/x86/domain.c     2005-06-04 14:02:06 -04:00
@@ -123,6 +123,9 @@
             safe_halt();
     }
 
+    watchdog_disable();
+    console_start_sync();
+
     local_irq_enable();
 
     /* Ensure we are the boot CPU. */
@@ -174,6 +177,7 @@
 void machine_halt(void)
 {
     watchdog_disable();
+    console_start_sync();
     smp_call_function(__machine_halt, NULL, 1, 0);
     __machine_halt(NULL);
 }
diff -Nru a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
--- a/xen/arch/x86/traps.c      2005-06-04 14:02:05 -04:00
+++ b/xen/arch/x86/traps.c      2005-06-04 14:02:05 -04:00
@@ -205,6 +205,7 @@
     };
 
     watchdog_disable();
+    console_start_sync();
 
     show_registers(regs);
 
diff -Nru a/xen/drivers/char/console.c b/xen/drivers/char/console.c
--- a/xen/drivers/char/console.c        2005-06-04 14:02:05 -04:00
+++ b/xen/drivers/char/console.c        2005-06-04 14:02:05 -04:00
@@ -467,6 +467,16 @@
     spin_lock(&console_lock);
 }
 
+void console_start_sync(void)
+{
+    serial_start_sync(sercon_handle);
+}
+
+void console_end_sync(void)
+{
+    serial_end_sync(sercon_handle);
+}
+
 void console_putc(char c)
 {
     serial_putc(sercon_handle, c);
diff -Nru a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
--- a/xen/drivers/char/ns16550.c        2005-06-04 14:02:06 -04:00
+++ b/xen/drivers/char/ns16550.c        2005-06-04 14:02:06 -04:00
@@ -101,16 +101,24 @@
 static void ns16550_interrupt(
     int irq, void *dev_id, struct cpu_user_regs *regs)
 {
-    serial_rx_interrupt(dev_id, regs);
+    struct serial_port *port = dev_id;
+    struct ns16550 *uart = port->uart;
+
+    if ( (ns_read_reg(uart, IIR) & 7) == 2 )
+        serial_tx_interrupt(port, regs);
+    else
+        serial_rx_interrupt(port, regs);
 }
 
-static void ns16550_putc(struct serial_port *port, char c)
+static int ns16550_tx_empty(struct serial_port *port)
 {
     struct ns16550 *uart = port->uart;
+    return !!(ns_read_reg(uart, LSR) & LSR_THRE);
+}
 
-    while ( !(ns_read_reg(uart, LSR) & LSR_THRE) )
-        cpu_relax();
-
+static void ns16550_putc(struct serial_port *port, char c)
+{
+    struct ns16550 *uart = port->uart;
     ns_write_reg(uart, THR, c);
 }
 
@@ -150,6 +158,10 @@
 
     /* Enable and clear the FIFOs. Set a large trigger threshold. */
     ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14);
+
+    /* Check this really is a 16550+. Otherwise we have no FIFOs. */
+    if ( (ns_read_reg(uart, IIR) & 0xc0) == 0xc0 )
+        port->tx_fifo_size = 16;
 }
 
 static void ns16550_init_postirq(struct serial_port *port)
@@ -157,20 +169,19 @@
     struct ns16550 *uart = port->uart;
     int rc;
 
+    serial_async_transmit(port);
+
     uart->irqaction.handler = ns16550_interrupt;
     uart->irqaction.name    = "ns16550";
     uart->irqaction.dev_id  = port;
     if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
         printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq);
 
-    /* For sanity, clear the receive FIFO. */
-    ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_TRG14);
-
     /* Master interrupt enable; also keep DTR/RTS asserted. */
     ns_write_reg(uart, MCR, MCR_OUT2 | MCR_DTR | MCR_RTS);
 
-    /* Enable receive interrupts. */
-    ns_write_reg(uart, IER, IER_ERDAI);
+    /* Enable receive and transmit interrupts. */
+    ns_write_reg(uart, IER, IER_ERDAI | IER_ETHREI);
 }
 
 #ifdef CONFIG_X86
@@ -188,6 +199,7 @@
     .init_preirq  = ns16550_init_preirq,
     .init_postirq = ns16550_init_postirq,
     .endboot      = ns16550_endboot,
+    .tx_empty     = ns16550_tx_empty,
     .putc         = ns16550_putc,
     .getc         = ns16550_getc
 };
diff -Nru a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c
--- a/xen/drivers/char/serial.c 2005-06-04 14:02:06 -04:00
+++ b/xen/drivers/char/serial.c 2005-06-04 14:02:06 -04:00
@@ -42,8 +42,8 @@
             fn = port->rx_hi;
         else if ( !(c & 0x80) && (port->rx_lo != NULL) )
             fn = port->rx_lo;
-        else if ( (port->rxbufp - port->rxbufc) != RXBUFSZ )
-            port->rxbuf[MASK_RXBUF_IDX(port->rxbufp++)] = c;            
+        else if ( (port->rxbufp - port->rxbufc) != SERIAL_RXBUFSZ )
+            port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufp++)] = c;            
 
         spin_unlock_irqrestore(&port->lock, flags);
 
@@ -56,6 +56,71 @@
     spin_unlock_irqrestore(&port->lock, flags);
 }
 
+void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
+{
+    int i;
+    unsigned long flags;
+
+    BUG_ON(!port->driver);
+    BUG_ON(!port->driver->tx_empty);
+    BUG_ON(!port->driver->putc);
+
+    spin_lock_irqsave(&port->lock, flags);
+
+    for ( i = 0; i < port->tx_fifo_size; i++ )
+    {
+        if ( port->txbufc == port->txbufp )
+            break;
+        port->driver->putc(
+            port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
+    }
+
+    spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __serial_putc(struct serial_port *port, char c)
+{
+    int i;
+
+    if ( (port->txbuf != NULL) && !port->sync )
+    {
+        /* Interrupt-driven (asynchronous) transmitter. */
+        if ( (port->txbufp - port->txbufc) == SERIAL_TXBUFSZ )
+        {
+            /* Buffer is full: we spin, but could alternatively drop chars. */
+            while ( !port->driver->tx_empty(port) )
+                cpu_relax();
+            for ( i = 0; i < port->tx_fifo_size; i++ )
+                port->driver->putc(
+                    port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
+            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
+        }
+        else if ( ((port->txbufp - port->txbufc) == 0) &&
+                  port->driver->tx_empty(port) )
+        {
+            /* Buffer and UART FIFO are both empty. */
+            port->driver->putc(port, c);
+        }
+        else
+        {
+            /* Normal case: buffer the character. */
+            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
+        }
+    }
+    else if ( port->driver->tx_empty )
+    {
+        /* Synchronous finite-capacity transmitter. */
+        while ( !port->driver->tx_empty(port) )
+            cpu_relax();
+        port->driver->putc(port, c);
+    }
+    else
+    {
+        /* Simple synchronous transmitter. */
+        port->driver->putc(port, c);
+    }
+}
+
 void serial_putc(int handle, char c)
 {
     struct serial_port *port = &com[handle & SERHND_IDX];
@@ -67,14 +132,14 @@
     spin_lock_irqsave(&port->lock, flags);
 
     if ( (c == '\n') && (handle & SERHND_COOKED) )
-        port->driver->putc(port, '\r');
+        __serial_putc(port, '\r');
 
     if ( handle & SERHND_HI )
         c |= 0x80;
     else if ( handle & SERHND_LO )

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.