diff options
-rw-r--r-- | hw/input/pckbd.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index fbd57bf..516ed5a 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -23,7 +23,9 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/timer.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "hw/acpi/aml-build.h" @@ -132,11 +134,14 @@ #define KBD_PENDING_KBD 1 #define KBD_PENDING_AUX 2 +#define KBD_MIGR_TIMER_PENDING 0x1 + typedef struct KBDState { uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ uint8_t status; uint8_t mode; uint8_t outport; + uint32_t migration_flags; bool outport_present; bool extended_state; /* Bitmask of devices with data available. */ @@ -144,6 +149,7 @@ typedef struct KBDState { uint8_t obdata; void *kbd; void *mouse; + QEMUTimer *throttle_timer; qemu_irq irq_kbd; qemu_irq irq_mouse; @@ -208,6 +214,10 @@ static void kbd_safe_update_irq(KBDState *s) if (s->status & KBD_STAT_OBF) { return; } + /* the throttle timer is pending and will call kbd_update_irq() */ + if (s->throttle_timer && timer_pending(s->throttle_timer)) { + return; + } if (s->pending) { kbd_update_irq(s); } @@ -237,6 +247,15 @@ static void kbd_update_aux_irq(void *opaque, int level) kbd_safe_update_irq(s); } +static void kbd_throttle_timeout(void *opaque) +{ + KBDState *s = opaque; + + if (s->pending) { + kbd_update_irq(s); + } +} + static uint64_t kbd_read_status(void *opaque, hwaddr addr, unsigned size) { @@ -358,6 +377,10 @@ static uint64_t kbd_read_data(void *opaque, hwaddr addr, if (status & KBD_STAT_MOUSE_OBF) { s->obdata = ps2_read_data(s->mouse); } else { + if (s->throttle_timer) { + timer_mod(s->throttle_timer, + qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 1000); + } s->obdata = ps2_read_data(s->kbd); } } @@ -419,6 +442,9 @@ static void kbd_reset(void *opaque) s->outport_present = false; s->pending = 0; kbd_deassert_irq(s); + if (s->throttle_timer) { + timer_del(s->throttle_timer); + } } static uint8_t kbd_outport_default(KBDState *s) @@ -453,6 +479,29 @@ static const VMStateDescription vmstate_kbd_outport = { } }; +static int kbd_extended_state_pre_save(void *opaque) +{ + KBDState *s = opaque; + + s->migration_flags = 0; + if (s->throttle_timer && timer_pending(s->throttle_timer)) { + s->migration_flags |= KBD_MIGR_TIMER_PENDING; + } + + return 0; +} + +static int kbd_extended_state_post_load(void *opaque, int version_id) +{ + KBDState *s = opaque; + + if (s->migration_flags & KBD_MIGR_TIMER_PENDING) { + kbd_throttle_timeout(s); + } + + return 0; +} + static bool kbd_extended_state_needed(void *opaque) { KBDState *s = opaque; @@ -462,8 +511,11 @@ static bool kbd_extended_state_needed(void *opaque) static const VMStateDescription vmstate_kbd_extended_state = { .name = "pckbd/extended_state", + .post_load = kbd_extended_state_post_load, + .pre_save = kbd_extended_state_pre_save, .needed = kbd_extended_state_needed, .fields = (VMStateField[]) { + VMSTATE_UINT32(migration_flags, KBDState), VMSTATE_UINT8(obdata, KBDState), VMSTATE_END_OF_LIST() } @@ -554,6 +606,7 @@ struct ISAKBDState { ISADevice parent_obj; KBDState kbd; + bool kbd_throttle; MemoryRegion io[2]; }; @@ -626,6 +679,13 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); + if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) { + warn_report(TYPE_I8042 ": can't enable kbd-throttle without" + " extended-state, disabling kbd-throttle"); + } else if (isa_s->kbd_throttle) { + s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, + kbd_throttle_timeout, s); + } qemu_register_reset(kbd_reset, s); } @@ -659,6 +719,7 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope) static Property i8042_properties[] = { DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true), + DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false), DEFINE_PROP_END_OF_LIST(), }; |