diff options
author | aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-05-04 21:42:00 +0000 |
---|---|---|
committer | aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-05-04 21:42:00 +0000 |
commit | 6936bfe514bbac7af4b24fad9ed9688b78b5be69 (patch) | |
tree | d7afac57aa8eb8b6d1677f64fe457b2b8b97a412 /hw | |
parent | 7caa33f7be98baa7d4ff6ceb280ef128c9c63bb2 (diff) |
8250: throttle TX-completion IRQs
(Jan Kiszka)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4335 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r-- | hw/serial.c | 55 |
1 files changed, 49 insertions, 6 deletions
diff --git a/hw/serial.c b/hw/serial.c index b70d739622..b5ac6f1dea 100644 --- a/hw/serial.c +++ b/hw/serial.c @@ -25,6 +25,7 @@ #include "qemu-char.h" #include "isa.h" #include "pc.h" +#include "qemu-timer.h" //#define DEBUG_SERIAL @@ -73,6 +74,13 @@ #define UART_LSR_OE 0x02 /* Overrun error indicator */ #define UART_LSR_DR 0x01 /* Receiver data ready */ +/* + * Delay TX IRQ after sending as much characters as the given interval would + * contain on real hardware. This avoids overloading the guest if it processes + * its output buffer in a loop inside the TX IRQ handler. + */ +#define THROTTLE_TX_INTERVAL 10 /* ms */ + struct SerialState { uint16_t divider; uint8_t rbr; /* receive register */ @@ -91,6 +99,8 @@ struct SerialState { int last_break_enable; target_phys_addr_t base; int it_shift; + QEMUTimer *tx_timer; + int tx_burst; }; static void serial_receive_byte(SerialState *s, int ch); @@ -111,6 +121,28 @@ static void serial_update_irq(SerialState *s) } } +static void serial_tx_done(void *opaque) +{ + SerialState *s = opaque; + + if (s->tx_burst < 0) { + uint16_t divider; + + if (s->divider) + divider = s->divider; + else + divider = 1; + + /* We assume 10 bits/char, OK for this purpose. */ + s->tx_burst = THROTTLE_TX_INTERVAL * 1000 / + (1000000 * 10 / (115200 / divider)); + } + s->thr_ipending = 1; + s->lsr |= UART_LSR_THRE; + s->lsr |= UART_LSR_TEMT; + serial_update_irq(s); +} + static void serial_update_parameters(SerialState *s) { int speed, parity, data_bits, stop_bits; @@ -166,15 +198,18 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (!(s->mcr & UART_MCR_LOOP)) { /* when not in loopback mode, send the char */ 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); - if (s->mcr & UART_MCR_LOOP) { + } else { /* in loopback mode, say that we just received a char */ serial_receive_byte(s, ch); } + if (s->tx_burst > 0) { + s->tx_burst--; + serial_tx_done(s); + } else if (s->tx_burst == 0) { + s->tx_burst--; + qemu_mod_timer(s->tx_timer, qemu_get_clock(vm_clock) + + ticks_per_sec * THROTTLE_TX_INTERVAL / 1000); + } } break; case 1: @@ -387,6 +422,10 @@ SerialState *serial_init(int base, qemu_irq irq, CharDriverState *chr) return NULL; s->irq = irq; + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s); + if (!s->tx_timer) + return NULL; + qemu_register_reset(serial_reset, s); serial_reset(s); @@ -486,6 +525,10 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, s->base = base; s->it_shift = it_shift; + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s); + if (!s->tx_timer) + return NULL; + qemu_register_reset(serial_reset, s); serial_reset(s); |