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 <pbonz...@redhat.com> --- 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