# HG changeset patch
# User kfraser@localhost.localdomain
# Node ID c33272c2571c7bab7056d8228490700d1df405f9
# Parent  b3d94f4ddffefed8a5cb8dd65a60da9491d460e7
[HVM] Fix Qemu-dm serial issues:
 1. Retry transmit via a polling timer if a byte cannot be written
    immediately to its destination.
 2. Turn off output processing of raw serial lines.

Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Keir Fraser <keir@xensource.com>

Index: ioemu/vl.c
===================================================================
--- ioemu.orig/vl.c	2007-05-10 15:35:24.000000000 +0100
+++ ioemu/vl.c	2007-05-10 15:35:25.000000000 +0100
@@ -1871,7 +1871,7 @@
 
     tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                           |INLCR|IGNCR|ICRNL|IXON);
-    tty.c_oflag |= OPOST;
+    tty.c_oflag &= ~OPOST; /* no output mangling of raw serial stream */
     tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
     tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
     switch(data_bits) {
Index: ioemu/hw/serial.c
===================================================================
--- ioemu.orig/hw/serial.c	2007-05-10 15:35:24.000000000 +0100
+++ ioemu/hw/serial.c	2007-05-10 15:35:25.000000000 +0100
@@ -73,6 +73,11 @@
 #define UART_LSR_OE	0x02	/* Overrun error indicator */
 #define UART_LSR_DR	0x01	/* Receiver data ready */
 
+/* Maximum retries for a single byte transmit. */
+#define WRITE_MAX_SINGLE_RETRIES 3
+/* Maximum retries for a sequence of back-to-back unsuccessful transmits. */
+#define WRITE_MAX_TOTAL_RETRIES 10
+
 struct SerialState {
     uint16_t divider;
     uint8_t rbr; /* receive register */
@@ -93,6 +98,19 @@
     int last_break_enable;
     target_ulong base;
     int it_shift;
+
+    /*
+     * If a character transmitted via UART cannot be written to its
+     * destination immediately we remember it here and retry a few times via
+     * a polling timer.
+     *  - write_single_retries: Number of write retries for current byte.
+     *  - write_total_retries:  Number of write retries for back-to-back
+     *                          unsuccessful transmits.
+     */
+    int write_single_retries;
+    int write_total_retries;
+    char write_chr;
+    QEMUTimer *write_retry_timer;
 };
 
 static void serial_update_irq(SerialState *s)
@@ -204,10 +222,37 @@
     tokens_avail--;
 }
 
+static void serial_chr_write(void *opaque)
+{
+    SerialState *s = opaque;
+
+    /* Cancel any outstanding retry if this is a new byte. */
+    qemu_del_timer(s->write_retry_timer);
+
+    /* Retry every 100ms for 300ms total. */
+    if (qemu_chr_write(s->chr, &s->write_chr, 1) == -1) {
+        s->write_total_retries++; 
+        if (s->write_single_retries++ >= WRITE_MAX_SINGLE_RETRIES)
+            fprintf(stderr, "serial: write error\n");
+        else if (s->write_total_retries <= WRITE_MAX_TOTAL_RETRIES) {
+            qemu_mod_timer(s->write_retry_timer,
+                           qemu_get_clock(vm_clock) + ticks_per_sec / 10);
+            return;
+        }
+    } else {
+        s->write_total_retries = 0;  /* if successful then reset counter */
+    }
+
+    /* Success: Notify guest that THR is empty. */
+    s->thr_ipending = 1;
+    s->lsr |= UART_LSR_THRE;
+    s->lsr |= UART_LSR_TEMT;
+    serial_update_irq(s);
+}
+
 static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     SerialState *s = opaque;
-    unsigned char ch;
     
     addr &= 7;
 #ifdef DEBUG_SERIAL
@@ -223,12 +268,9 @@
             s->thr_ipending = 0;
             s->lsr &= ~UART_LSR_THRE;
             serial_update_irq(s);
-            ch = val;
-            qemu_chr_write(s->chr, &ch, 1);
-            s->thr_ipending = 1;
-            s->lsr |= UART_LSR_THRE;
-            s->lsr |= UART_LSR_TEMT;
-            serial_update_irq(s);
+            s->write_chr = val;
+            s->write_single_retries = 0;
+            serial_chr_write(s);
         }
         break;
     case 1:
@@ -427,6 +469,7 @@
     s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
     s->iir = UART_IIR_NO_INT;
     s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+    s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
 
     register_savevm("serial", base, 2, serial_save, serial_load, s);
 
@@ -514,6 +557,7 @@
     s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
     s->base = base;
     s->it_shift = it_shift;
+    s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
 
     register_savevm("serial", base, 2, serial_save, serial_load, s);
 
