*/
#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"
#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. */
uint8_t obdata;
void *kbd;
void *mouse;
+ QEMUTimer *throttle_timer;
qemu_irq irq_kbd;
qemu_irq irq_mouse;
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);
}
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)
{
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);
}
}
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)
}
};
+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;
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()
}
ISADevice parent_obj;
KBDState kbd;
+ bool kbd_throttle;
MemoryRegion io[2];
};
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);
}
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(),
};