qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH] serial: transmit within the programmed baud rate


From: Paolo Bonzini
Subject: [Qemu-devel] [PATCH] serial: transmit within the programmed baud rate
Date: Fri, 8 Jan 2016 16:43:03 +0100

Code for throttling the serial port was removed by upstream commit fcfb4d6
("serial: add flow control to transmit", 2013-03-05).  Add it back.

The only non-obvious change is that tsr_retry can now become nonzero
also in loopback mode, so the assignment is moved out of the "if".

Signed-off-by: Paolo Bonzini <address@hidden>
---
 hw/char/serial.c         | 47 ++++++++++++++++++++++++++++++++++++++++++-----
 include/hw/char/serial.h |  1 +
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/hw/char/serial.c b/hw/char/serial.c
index 513d73c..d96ec4f 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -222,6 +222,7 @@ static void serial_update_msl(SerialState *s)
 static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
 {
     SerialState *s = opaque;
+    uint64_t new_xmit_ts;
 
     do {
         assert(!(s->lsr & UART_LSR_TEMT));
@@ -244,6 +245,16 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition 
cond, void *opaque)
             }
         }
 
+        new_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+        /* Do not transmit faster than the desired baud rate.  */
+        if (new_xmit_ts < s->last_xmit_ts + s->char_transmit_time) {
+            assert(s->tsr_retry <= 0);
+            s->tsr_retry++;
+            timer_mod(s->transmit_timer, s->last_xmit_ts + 
s->char_transmit_time);
+            return FALSE;
+        }
+
         if (s->mcr & UART_MCR_LOOP) {
             /* in loopback mode, say that we just received a char */
             serial_receive1(s, &s->tsr, 1);
@@ -254,21 +265,26 @@ static gboolean serial_xmit(GIOChannel *chan, 
GIOCondition cond, void *opaque)
                 s->tsr_retry++;
                 return FALSE;
             }
-            s->tsr_retry = 0;
-        } else {
-            s->tsr_retry = 0;
         }
 
+        s->tsr_retry = 0;
+        s->last_xmit_ts = new_xmit_ts;
+
         /* Transmit another byte if it is already available. It is only
            possible when FIFO is enabled and not empty. */
     } while (!(s->lsr & UART_LSR_THRE));
 
-    s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     s->lsr |= UART_LSR_TEMT;
-
     return FALSE;
 }
 
+static void serial_xmit_timer_cb(void *opaque)
+{
+    SerialState *s = opaque;
+
+    serial_xmit(NULL, G_IO_OUT, s);
+}
+
 
 /* Setter for FCR.
    is_load flag means, that value is set while loading VM state
@@ -688,6 +704,23 @@ static const VMStateDescription vmstate_serial_tsr = {
     }
 };
 
+static bool serial_xmit_timer_needed(void *opaque)
+{
+    SerialState *s = (SerialState *)opaque;
+    return timer_pending(s->transmit_timer);
+}
+
+static const VMStateDescription vmstate_serial_xmit_timer = {
+    .name = "serial/xmit_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = serial_xmit_timer_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER_PTR(transmit_timer, SerialState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static bool serial_recv_fifo_needed(void *opaque)
 {
     SerialState *s = (SerialState *)opaque;
@@ -803,6 +836,7 @@ const VMStateDescription vmstate_serial = {
         &vmstate_serial_fifo_timeout_timer,
         &vmstate_serial_timeout_ipending,
         &vmstate_serial_poll,
+        &vmstate_serial_xmit_timer,
         NULL
     }
 };
@@ -828,6 +862,7 @@ static void serial_reset(void *opaque)
     s->timeout_ipending = 0;
     timer_del(s->fifo_timeout_timer);
     timer_del(s->modem_status_poll);
+    timer_del(s->transmit_timer);
 
     fifo8_reset(&s->recv_fifo);
     fifo8_reset(&s->xmit_fifo);
@@ -852,6 +887,7 @@ void serial_realize_core(SerialState *s, Error **errp)
     s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) 
serial_update_msl, s);
 
     s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) 
fifo_timeout_int, s);
+    s->transmit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) 
serial_xmit_timer_cb, s);
     qemu_register_reset(serial_reset, s);
 
     qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
@@ -863,6 +899,7 @@ void serial_realize_core(SerialState *s, Error **errp)
 
 void serial_exit_core(SerialState *s)
 {
+    timer_del(s->transmit_timer);
     qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
     qemu_unregister_reset(serial_reset, s);
 }
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index 15beb6b..9949ad0 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -68,6 +68,7 @@ struct SerialState {
     QEMUTimer *fifo_timeout_timer;
     int timeout_ipending;           /* timeout interrupt pending state */
 
+    QEMUTimer *transmit_timer;
     uint64_t char_transmit_time;    /* time to transmit a char in ticks */
     int poll_msl;
 
-- 
2.5.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]