summary refs log tree commit diff stats
path: root/hw/input
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2013-03-01 13:59:19 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2013-04-08 18:13:12 +0200
commit49ab747f668f421138d5b40d83fa279c4c5e278d (patch)
tree943225a04eac885aed038731adf058f2250a2f40 /hw/input
parentce3b494cb504f96992f2d37ebc8f56deed202b06 (diff)
downloadfocaccia-qemu-49ab747f668f421138d5b40d83fa279c4c5e278d.tar.gz
focaccia-qemu-49ab747f668f421138d5b40d83fa279c4c5e278d.zip
hw: move target-independent files to subdirectories
This patch tackles all files that are compiled once, moving
them to subdirectories of hw/.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/input')
-rw-r--r--hw/input/Makefile.objs9
-rw-r--r--hw/input/adb.c581
-rw-r--r--hw/input/hid.c498
-rw-r--r--hw/input/lm832x.c521
-rw-r--r--hw/input/pckbd.c527
-rw-r--r--hw/input/pl050.c199
-rw-r--r--hw/input/ps2.c676
-rw-r--r--hw/input/stellaris_input.c89
-rw-r--r--hw/input/tsc2005.c593
-rw-r--r--hw/input/vmmouse.c301
10 files changed, 3994 insertions, 0 deletions
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index e69de29bb2..824997e367 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -0,0 +1,9 @@
+common-obj-$(CONFIG_ADB) += adb.o
+common-obj-y += hid.o
+common-obj-$(CONFIG_LM832X) += lm832x.o
+common-obj-$(CONFIG_PCKBD) += pckbd.o
+common-obj-$(CONFIG_PL050) += pl050.o
+common-obj-y += ps2.o
+common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
+common-obj-$(CONFIG_TSC2005) += tsc2005.o
+common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
diff --git a/hw/input/adb.c b/hw/input/adb.c
new file mode 100644
index 0000000000..a75d3fd7b9
--- /dev/null
+++ b/hw/input/adb.c
@@ -0,0 +1,581 @@
+/*
+ * QEMU ADB support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/input/adb.h"
+#include "ui/console.h"
+
+/* debug ADB */
+//#define DEBUG_ADB
+
+#ifdef DEBUG_ADB
+#define ADB_DPRINTF(fmt, ...) \
+do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define ADB_DPRINTF(fmt, ...)
+#endif
+
+/* ADB commands */
+#define ADB_BUSRESET		0x00
+#define ADB_FLUSH               0x01
+#define ADB_WRITEREG		0x08
+#define ADB_READREG		0x0c
+
+/* ADB device commands */
+#define ADB_CMD_SELF_TEST		0xff
+#define ADB_CMD_CHANGE_ID		0xfe
+#define ADB_CMD_CHANGE_ID_AND_ACT	0xfd
+#define ADB_CMD_CHANGE_ID_AND_ENABLE	0x00
+
+/* ADB default device IDs (upper 4 bits of ADB command byte) */
+#define ADB_DEVID_DONGLE   1
+#define ADB_DEVID_KEYBOARD 2
+#define ADB_DEVID_MOUSE    3
+#define ADB_DEVID_TABLET   4
+#define ADB_DEVID_MODEM    5
+#define ADB_DEVID_MISC     7
+
+/* error codes */
+#define ADB_RET_NOTPRESENT (-2)
+
+static void adb_device_reset(ADBDevice *d)
+{
+    qdev_reset_all(DEVICE(d));
+}
+
+int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+{
+    ADBDevice *d;
+    int devaddr, cmd, i;
+
+    cmd = buf[0] & 0xf;
+    if (cmd == ADB_BUSRESET) {
+        for(i = 0; i < s->nb_devices; i++) {
+            d = s->devices[i];
+            adb_device_reset(d);
+        }
+        return 0;
+    }
+    devaddr = buf[0] >> 4;
+    for(i = 0; i < s->nb_devices; i++) {
+        d = s->devices[i];
+        if (d->devaddr == devaddr) {
+            ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d);
+            return adc->devreq(d, obuf, buf, len);
+        }
+    }
+    return ADB_RET_NOTPRESENT;
+}
+
+/* XXX: move that to cuda ? */
+int adb_poll(ADBBusState *s, uint8_t *obuf)
+{
+    ADBDevice *d;
+    int olen, i;
+    uint8_t buf[1];
+
+    olen = 0;
+    for(i = 0; i < s->nb_devices; i++) {
+        if (s->poll_index >= s->nb_devices)
+            s->poll_index = 0;
+        d = s->devices[s->poll_index];
+        buf[0] = ADB_READREG | (d->devaddr << 4);
+        olen = adb_request(s, obuf + 1, buf, 1);
+        /* if there is data, we poll again the same device */
+        if (olen > 0) {
+            obuf[0] = buf[0];
+            olen++;
+            break;
+        }
+        s->poll_index++;
+    }
+    return olen;
+}
+
+static const TypeInfo adb_bus_type_info = {
+    .name = TYPE_ADB_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(ADBBusState),
+};
+
+static void adb_device_realizefn(DeviceState *dev, Error **errp)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
+
+    if (bus->nb_devices >= MAX_ADB_DEVICES) {
+        return;
+    }
+
+    bus->devices[bus->nb_devices++] = d;
+}
+
+static void adb_device_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = adb_device_realizefn;
+    dc->bus_type = TYPE_ADB_BUS;
+}
+
+static const TypeInfo adb_device_type_info = {
+    .name = TYPE_ADB_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(ADBDevice),
+    .abstract = true,
+    .class_init = adb_device_class_init,
+};
+
+/***************************************************************/
+/* Keyboard ADB device */
+
+#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD)
+
+typedef struct KBDState {
+    /*< private >*/
+    ADBDevice parent_obj;
+    /*< public >*/
+
+    uint8_t data[128];
+    int rptr, wptr, count;
+} KBDState;
+
+#define ADB_KEYBOARD_CLASS(class) \
+    OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD)
+#define ADB_KEYBOARD_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD)
+
+typedef struct ADBKeyboardClass {
+    /*< private >*/
+    ADBDeviceClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+} ADBKeyboardClass;
+
+static const uint8_t pc_to_adb_keycode[256] = {
+  0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+ 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,  0,  1,
+  2,  3,  5,  4, 38, 40, 37, 41, 39, 50, 56, 42,  6,  7,  8,  9,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+ 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+ 84, 85, 82, 65,  0,  0, 10,103,111,  0,  0,110, 81,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0, 94,  0, 93,  0,  0,  0,  0,  0,  0,104,102,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 76,125,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,105,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0, 75,  0,  0,124,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,115, 62,116,  0, 59,  0, 60,  0,119,
+ 61,121,114,117,  0,  0,  0,  0,  0,  0,  0, 55,126,  0,127,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0, 95,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+};
+
+static void adb_kbd_put_keycode(void *opaque, int keycode)
+{
+    KBDState *s = opaque;
+
+    if (s->count < sizeof(s->data)) {
+        s->data[s->wptr] = keycode;
+        if (++s->wptr == sizeof(s->data))
+            s->wptr = 0;
+        s->count++;
+    }
+}
+
+static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
+{
+    static int ext_keycode;
+    KBDState *s = ADB_KEYBOARD(d);
+    int adb_keycode, keycode;
+    int olen;
+
+    olen = 0;
+    for(;;) {
+        if (s->count == 0)
+            break;
+        keycode = s->data[s->rptr];
+        if (++s->rptr == sizeof(s->data))
+            s->rptr = 0;
+        s->count--;
+
+        if (keycode == 0xe0) {
+            ext_keycode = 1;
+        } else {
+            if (ext_keycode)
+                adb_keycode =  pc_to_adb_keycode[keycode | 0x80];
+            else
+                adb_keycode =  pc_to_adb_keycode[keycode & 0x7f];
+            obuf[0] = adb_keycode | (keycode & 0x80);
+            /* NOTE: could put a second keycode if needed */
+            obuf[1] = 0xff;
+            olen = 2;
+            ext_keycode = 0;
+            break;
+        }
+    }
+    return olen;
+}
+
+static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
+                           const uint8_t *buf, int len)
+{
+    KBDState *s = ADB_KEYBOARD(d);
+    int cmd, reg, olen;
+
+    if ((buf[0] & 0x0f) == ADB_FLUSH) {
+        /* flush keyboard fifo */
+        s->wptr = s->rptr = s->count = 0;
+        return 0;
+    }
+
+    cmd = buf[0] & 0xc;
+    reg = buf[0] & 0x3;
+    olen = 0;
+    switch(cmd) {
+    case ADB_WRITEREG:
+        switch(reg) {
+        case 2:
+            /* LED status */
+            break;
+        case 3:
+            switch(buf[2]) {
+            case ADB_CMD_SELF_TEST:
+                break;
+            case ADB_CMD_CHANGE_ID:
+            case ADB_CMD_CHANGE_ID_AND_ACT:
+            case ADB_CMD_CHANGE_ID_AND_ENABLE:
+                d->devaddr = buf[1] & 0xf;
+                break;
+            default:
+                /* XXX: check this */
+                d->devaddr = buf[1] & 0xf;
+                d->handler = buf[2];
+                break;
+            }
+        }
+        break;
+    case ADB_READREG:
+        switch(reg) {
+        case 0:
+            olen = adb_kbd_poll(d, obuf);
+            break;
+        case 1:
+            break;
+        case 2:
+            obuf[0] = 0x00; /* XXX: check this */
+            obuf[1] = 0x07; /* led status */
+            olen = 2;
+            break;
+        case 3:
+            obuf[0] = d->handler;
+            obuf[1] = d->devaddr;
+            olen = 2;
+            break;
+        }
+        break;
+    }
+    return olen;
+}
+
+static const VMStateDescription vmstate_adb_kbd = {
+    .name = "adb_kbd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BUFFER(data, KBDState),
+        VMSTATE_INT32(rptr, KBDState),
+        VMSTATE_INT32(wptr, KBDState),
+        VMSTATE_INT32(count, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void adb_kbd_reset(DeviceState *dev)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    KBDState *s = ADB_KEYBOARD(dev);
+
+    d->handler = 1;
+    d->devaddr = ADB_DEVID_KEYBOARD;
+    memset(s->data, 0, sizeof(s->data));
+    s->rptr = 0;
+    s->wptr = 0;
+    s->count = 0;
+}
+
+static void adb_kbd_realizefn(DeviceState *dev, Error **errp)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev);
+
+    akc->parent_realize(dev, errp);
+
+    qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
+}
+
+static void adb_kbd_initfn(Object *obj)
+{
+    ADBDevice *d = ADB_DEVICE(obj);
+
+    d->devaddr = ADB_DEVID_KEYBOARD;
+}
+
+static void adb_kbd_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
+    ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc);
+
+    akc->parent_realize = dc->realize;
+    dc->realize = adb_kbd_realizefn;
+
+    adc->devreq = adb_kbd_request;
+    dc->reset = adb_kbd_reset;
+    dc->vmsd = &vmstate_adb_kbd;
+}
+
+static const TypeInfo adb_kbd_type_info = {
+    .name = TYPE_ADB_KEYBOARD,
+    .parent = TYPE_ADB_DEVICE,
+    .instance_size = sizeof(KBDState),
+    .instance_init = adb_kbd_initfn,
+    .class_init = adb_kbd_class_init,
+    .class_size = sizeof(ADBKeyboardClass),
+};
+
+/***************************************************************/
+/* Mouse ADB device */
+
+#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE)
+
+typedef struct MouseState {
+    /*< public >*/
+    ADBDevice parent_obj;
+    /*< private >*/
+
+    int buttons_state, last_buttons_state;
+    int dx, dy, dz;
+} MouseState;
+
+#define ADB_MOUSE_CLASS(class) \
+    OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE)
+#define ADB_MOUSE_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE)
+
+typedef struct ADBMouseClass {
+    /*< public >*/
+    ADBDeviceClass parent_class;
+    /*< private >*/
+
+    DeviceRealize parent_realize;
+} ADBMouseClass;
+
+static void adb_mouse_event(void *opaque,
+                            int dx1, int dy1, int dz1, int buttons_state)
+{
+    MouseState *s = opaque;
+
+    s->dx += dx1;
+    s->dy += dy1;
+    s->dz += dz1;
+    s->buttons_state = buttons_state;
+}
+
+
+static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
+{
+    MouseState *s = ADB_MOUSE(d);
+    int dx, dy;
+
+    if (s->last_buttons_state == s->buttons_state &&
+        s->dx == 0 && s->dy == 0)
+        return 0;
+
+    dx = s->dx;
+    if (dx < -63)
+        dx = -63;
+    else if (dx > 63)
+        dx = 63;
+
+    dy = s->dy;
+    if (dy < -63)
+        dy = -63;
+    else if (dy > 63)
+        dy = 63;
+
+    s->dx -= dx;
+    s->dy -= dy;
+    s->last_buttons_state = s->buttons_state;
+
+    dx &= 0x7f;
+    dy &= 0x7f;
+
+    if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
+        dy |= 0x80;
+    if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
+        dx |= 0x80;
+
+    obuf[0] = dy;
+    obuf[1] = dx;
+    return 2;
+}
+
+static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
+                             const uint8_t *buf, int len)
+{
+    MouseState *s = ADB_MOUSE(d);
+    int cmd, reg, olen;
+
+    if ((buf[0] & 0x0f) == ADB_FLUSH) {
+        /* flush mouse fifo */
+        s->buttons_state = s->last_buttons_state;
+        s->dx = 0;
+        s->dy = 0;
+        s->dz = 0;
+        return 0;
+    }
+
+    cmd = buf[0] & 0xc;
+    reg = buf[0] & 0x3;
+    olen = 0;
+    switch(cmd) {
+    case ADB_WRITEREG:
+        ADB_DPRINTF("write reg %d val 0x%2.2x\n", reg, buf[1]);
+        switch(reg) {
+        case 2:
+            break;
+        case 3:
+            switch(buf[2]) {
+            case ADB_CMD_SELF_TEST:
+                break;
+            case ADB_CMD_CHANGE_ID:
+            case ADB_CMD_CHANGE_ID_AND_ACT:
+            case ADB_CMD_CHANGE_ID_AND_ENABLE:
+                d->devaddr = buf[1] & 0xf;
+                break;
+            default:
+                /* XXX: check this */
+                d->devaddr = buf[1] & 0xf;
+                break;
+            }
+        }
+        break;
+    case ADB_READREG:
+        switch(reg) {
+        case 0:
+            olen = adb_mouse_poll(d, obuf);
+            break;
+        case 1:
+            break;
+        case 3:
+            obuf[0] = d->handler;
+            obuf[1] = d->devaddr;
+            olen = 2;
+            break;
+        }
+        ADB_DPRINTF("read reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x\n", reg,
+                    obuf[0], obuf[1]);
+        break;
+    }
+    return olen;
+}
+
+static void adb_mouse_reset(DeviceState *dev)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    MouseState *s = ADB_MOUSE(dev);
+
+    d->handler = 2;
+    d->devaddr = ADB_DEVID_MOUSE;
+    s->last_buttons_state = s->buttons_state = 0;
+    s->dx = s->dy = s->dz = 0;
+}
+
+static const VMStateDescription vmstate_adb_mouse = {
+    .name = "adb_mouse",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(buttons_state, MouseState),
+        VMSTATE_INT32(last_buttons_state, MouseState),
+        VMSTATE_INT32(dx, MouseState),
+        VMSTATE_INT32(dy, MouseState),
+        VMSTATE_INT32(dz, MouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void adb_mouse_realizefn(DeviceState *dev, Error **errp)
+{
+    MouseState *s = ADB_MOUSE(dev);
+    ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev);
+
+    amc->parent_realize(dev, errp);
+
+    qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse");
+}
+
+static void adb_mouse_initfn(Object *obj)
+{
+    ADBDevice *d = ADB_DEVICE(obj);
+
+    d->devaddr = ADB_DEVID_MOUSE;
+}
+
+static void adb_mouse_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
+    ADBMouseClass *amc = ADB_MOUSE_CLASS(oc);
+
+    amc->parent_realize = dc->realize;
+    dc->realize = adb_mouse_realizefn;
+
+    adc->devreq = adb_mouse_request;
+    dc->reset = adb_mouse_reset;
+    dc->vmsd = &vmstate_adb_mouse;
+}
+
+static const TypeInfo adb_mouse_type_info = {
+    .name = TYPE_ADB_MOUSE,
+    .parent = TYPE_ADB_DEVICE,
+    .instance_size = sizeof(MouseState),
+    .instance_init = adb_mouse_initfn,
+    .class_init = adb_mouse_class_init,
+    .class_size = sizeof(ADBMouseClass),
+};
+
+
+static void adb_register_types(void)
+{
+    type_register_static(&adb_bus_type_info);
+    type_register_static(&adb_device_type_info);
+    type_register_static(&adb_kbd_type_info);
+    type_register_static(&adb_mouse_type_info);
+}
+
+type_init(adb_register_types)
diff --git a/hw/input/hid.c b/hw/input/hid.c
new file mode 100644
index 0000000000..5fbde98f65
--- /dev/null
+++ b/hw/input/hid.c
@@ -0,0 +1,498 @@
+/*
+ * QEMU HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+#include "hw/input/hid.h"
+
+#define HID_USAGE_ERROR_ROLLOVER        0x01
+#define HID_USAGE_POSTFAIL              0x02
+#define HID_USAGE_ERROR_UNDEFINED       0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
+static const uint8_t hid_usage_keys[0x100] = {
+    0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+    0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+    0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+    0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+    0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+    0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+    0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+    0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+    0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+    0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+    0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+    0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+    0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+    0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+    0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+    0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+bool hid_has_events(HIDState *hs)
+{
+    return hs->n > 0 || hs->idle_pending;
+}
+
+static void hid_idle_timer(void *opaque)
+{
+    HIDState *hs = opaque;
+
+    hs->idle_pending = true;
+    hs->event(hs);
+}
+
+static void hid_del_idle_timer(HIDState *hs)
+{
+    if (hs->idle_timer) {
+        qemu_del_timer(hs->idle_timer);
+        qemu_free_timer(hs->idle_timer);
+        hs->idle_timer = NULL;
+    }
+}
+
+void hid_set_next_idle(HIDState *hs)
+{
+    if (hs->idle) {
+        uint64_t expire_time = qemu_get_clock_ns(vm_clock) +
+                               get_ticks_per_sec() * hs->idle * 4 / 1000;
+        if (!hs->idle_timer) {
+            hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs);
+        }
+        qemu_mod_timer_ns(hs->idle_timer, expire_time);
+    } else {
+        hid_del_idle_timer(hs);
+    }
+}
+
+static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
+{
+    e->xdx = e->ydy = e->dz = 0;
+    e->buttons_state = buttons;
+}
+
+static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
+                                      int x1, int y1, int z1) {
+    if (xyrel) {
+        e->xdx += x1;
+        e->ydy += y1;
+    } else {
+        e->xdx = x1;
+        e->ydy = y1;
+        /* Windows drivers do not like the 0/0 position and ignore such
+         * events. */
+        if (!(x1 | y1)) {
+            e->xdx = 1;
+        }
+    }
+    e->dz += z1;
+}
+
+static void hid_pointer_event(void *opaque,
+                              int x1, int y1, int z1, int buttons_state)
+{
+    HIDState *hs = opaque;
+    unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
+    unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
+
+    /* We combine events where feasible to keep the queue small.  We shouldn't
+     * combine anything with the first event of a particular button state, as
+     * that would change the location of the button state change.  When the
+     * queue is empty, a second event is needed because we don't know if
+     * the first event changed the button state.  */
+    if (hs->n == QUEUE_LENGTH) {
+        /* Queue full.  Discard old button state, combine motion normally.  */
+        hs->ptr.queue[use_slot].buttons_state = buttons_state;
+    } else if (hs->n < 2 ||
+               hs->ptr.queue[use_slot].buttons_state != buttons_state ||
+               hs->ptr.queue[previous_slot].buttons_state !=
+               hs->ptr.queue[use_slot].buttons_state) {
+        /* Cannot or should not combine, so add an empty item to the queue.  */
+        QUEUE_INCR(use_slot);
+        hs->n++;
+        hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
+    }
+    hid_pointer_event_combine(&hs->ptr.queue[use_slot],
+                              hs->kind == HID_MOUSE,
+                              x1, y1, z1);
+    hs->event(hs);
+}
+
+static void hid_keyboard_event(void *opaque, int keycode)
+{
+    HIDState *hs = opaque;
+    int slot;
+
+    if (hs->n == QUEUE_LENGTH) {
+        fprintf(stderr, "usb-kbd: warning: key event queue full\n");
+        return;
+    }
+    slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+    hs->kbd.keycodes[slot] = keycode;
+    hs->event(hs);
+}
+
+static void hid_keyboard_process_keycode(HIDState *hs)
+{
+    uint8_t hid_code, key;
+    int i, keycode, slot;
+
+    if (hs->n == 0) {
+        return;
+    }
+    slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
+    keycode = hs->kbd.keycodes[slot];
+
+    key = keycode & 0x7f;
+    hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
+    hs->kbd.modifiers &= ~(1 << 8);
+
+    switch (hid_code) {
+    case 0x00:
+        return;
+
+    case 0xe0:
+        if (hs->kbd.modifiers & (1 << 9)) {
+            hs->kbd.modifiers ^= 3 << 8;
+            return;
+        }
+    case 0xe1 ... 0xe7:
+        if (keycode & (1 << 7)) {
+            hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
+            return;
+        }
+    case 0xe8 ... 0xef:
+        hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
+        return;
+    }
+
+    if (keycode & (1 << 7)) {
+        for (i = hs->kbd.keys - 1; i >= 0; i--) {
+            if (hs->kbd.key[i] == hid_code) {
+                hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
+                hs->kbd.key[hs->kbd.keys] = 0x00;
+                break;
+            }
+        }
+        if (i < 0) {
+            return;
+        }
+    } else {
+        for (i = hs->kbd.keys - 1; i >= 0; i--) {
+            if (hs->kbd.key[i] == hid_code) {
+                break;
+            }
+        }
+        if (i < 0) {
+            if (hs->kbd.keys < sizeof(hs->kbd.key)) {
+                hs->kbd.key[hs->kbd.keys++] = hid_code;
+            }
+        } else {
+            return;
+        }
+    }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+    if (val < vmin) {
+        return vmin;
+    } else if (val > vmax) {
+        return vmax;
+    } else {
+        return val;
+    }
+}
+
+void hid_pointer_activate(HIDState *hs)
+{
+    if (!hs->ptr.mouse_grabbed) {
+        qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
+        hs->ptr.mouse_grabbed = 1;
+    }
+}
+
+int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
+{
+    int dx, dy, dz, b, l;
+    int index;
+    HIDPointerEvent *e;
+
+    hs->idle_pending = false;
+
+    hid_pointer_activate(hs);
+
+    /* When the buffer is empty, return the last event.  Relative
+       movements will all be zero.  */
+    index = (hs->n ? hs->head : hs->head - 1);
+    e = &hs->ptr.queue[index & QUEUE_MASK];
+
+    if (hs->kind == HID_MOUSE) {
+        dx = int_clamp(e->xdx, -127, 127);
+        dy = int_clamp(e->ydy, -127, 127);
+        e->xdx -= dx;
+        e->ydy -= dy;
+    } else {
+        dx = e->xdx;
+        dy = e->ydy;
+    }
+    dz = int_clamp(e->dz, -127, 127);
+    e->dz -= dz;
+
+    b = 0;
+    if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
+        b |= 0x01;
+    }
+    if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
+        b |= 0x02;
+    }
+    if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
+        b |= 0x04;
+    }
+
+    if (hs->n &&
+        !e->dz &&
+        (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
+        /* that deals with this event */
+        QUEUE_INCR(hs->head);
+        hs->n--;
+    }
+
+    /* Appears we have to invert the wheel direction */
+    dz = 0 - dz;
+    l = 0;
+    switch (hs->kind) {
+    case HID_MOUSE:
+        if (len > l) {
+            buf[l++] = b;
+        }
+        if (len > l) {
+            buf[l++] = dx;
+        }
+        if (len > l) {
+            buf[l++] = dy;
+        }
+        if (len > l) {
+            buf[l++] = dz;
+        }
+        break;
+
+    case HID_TABLET:
+        if (len > l) {
+            buf[l++] = b;
+        }
+        if (len > l) {
+            buf[l++] = dx & 0xff;
+        }
+        if (len > l) {
+            buf[l++] = dx >> 8;
+        }
+        if (len > l) {
+            buf[l++] = dy & 0xff;
+        }
+        if (len > l) {
+            buf[l++] = dy >> 8;
+        }
+        if (len > l) {
+            buf[l++] = dz;
+        }
+        break;
+
+    default:
+        abort();
+    }
+
+    return l;
+}
+
+int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
+{
+    hs->idle_pending = false;
+
+    if (len < 2) {
+        return 0;
+    }
+
+    hid_keyboard_process_keycode(hs);
+
+    buf[0] = hs->kbd.modifiers & 0xff;
+    buf[1] = 0;
+    if (hs->kbd.keys > 6) {
+        memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+    } else {
+        memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
+    }
+
+    return MIN(8, len);
+}
+
+int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
+{
+    if (len > 0) {
+        int ledstate = 0;
+        /* 0x01: Num Lock LED
+         * 0x02: Caps Lock LED
+         * 0x04: Scroll Lock LED
+         * 0x08: Compose LED
+         * 0x10: Kana LED */
+        hs->kbd.leds = buf[0];
+        if (hs->kbd.leds & 0x04) {
+            ledstate |= QEMU_SCROLL_LOCK_LED;
+        }
+        if (hs->kbd.leds & 0x01) {
+            ledstate |= QEMU_NUM_LOCK_LED;
+        }
+        if (hs->kbd.leds & 0x02) {
+            ledstate |= QEMU_CAPS_LOCK_LED;
+        }
+        kbd_put_ledstate(ledstate);
+    }
+    return 0;
+}
+
+void hid_reset(HIDState *hs)
+{
+    switch (hs->kind) {
+    case HID_KEYBOARD:
+        memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
+        memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
+        hs->kbd.keys = 0;
+        break;
+    case HID_MOUSE:
+    case HID_TABLET:
+        memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
+        break;
+    }
+    hs->head = 0;
+    hs->n = 0;
+    hs->protocol = 1;
+    hs->idle = 0;
+    hs->idle_pending = false;
+    hid_del_idle_timer(hs);
+}
+
+void hid_free(HIDState *hs)
+{
+    switch (hs->kind) {
+    case HID_KEYBOARD:
+        qemu_remove_kbd_event_handler();
+        break;
+    case HID_MOUSE:
+    case HID_TABLET:
+        qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
+        break;
+    }
+    hid_del_idle_timer(hs);
+}
+
+void hid_init(HIDState *hs, int kind, HIDEventFunc event)
+{
+    hs->kind = kind;
+    hs->event = event;
+
+    if (hs->kind == HID_KEYBOARD) {
+        qemu_add_kbd_event_handler(hid_keyboard_event, hs);
+    } else if (hs->kind == HID_MOUSE) {
+        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+                                                        0, "QEMU HID Mouse");
+    } else if (hs->kind == HID_TABLET) {
+        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+                                                        1, "QEMU HID Tablet");
+    }
+}
+
+static int hid_post_load(void *opaque, int version_id)
+{
+    HIDState *s = opaque;
+
+    hid_set_next_idle(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_hid_ptr_queue = {
+    .name = "HIDPointerEventQueue",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(xdx, HIDPointerEvent),
+        VMSTATE_INT32(ydy, HIDPointerEvent),
+        VMSTATE_INT32(dz, HIDPointerEvent),
+        VMSTATE_INT32(buttons_state, HIDPointerEvent),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+const VMStateDescription vmstate_hid_ptr_device = {
+    .name = "HIDPointerDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = hid_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
+                             vmstate_hid_ptr_queue, HIDPointerEvent),
+        VMSTATE_UINT32(head, HIDState),
+        VMSTATE_UINT32(n, HIDState),
+        VMSTATE_INT32(protocol, HIDState),
+        VMSTATE_UINT8(idle, HIDState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+const VMStateDescription vmstate_hid_keyboard_device = {
+    .name = "HIDKeyboardDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = hid_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
+        VMSTATE_UINT32(head, HIDState),
+        VMSTATE_UINT32(n, HIDState),
+        VMSTATE_UINT16(kbd.modifiers, HIDState),
+        VMSTATE_UINT8(kbd.leds, HIDState),
+        VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
+        VMSTATE_INT32(kbd.keys, HIDState),
+        VMSTATE_INT32(protocol, HIDState),
+        VMSTATE_UINT8(idle, HIDState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c
new file mode 100644
index 0000000000..bacbeb2343
--- /dev/null
+++ b/hw/input/lm832x.c
@@ -0,0 +1,521 @@
+/*
+ * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+
+typedef struct {
+    I2CSlave i2c;
+    uint8_t i2c_dir;
+    uint8_t i2c_cycle;
+    uint8_t reg;
+
+    qemu_irq nirq;
+    uint16_t model;
+
+    struct {
+        qemu_irq out[2];
+        int in[2][2];
+    } mux;
+
+    uint8_t config;
+    uint8_t status;
+    uint8_t acttime;
+    uint8_t error;
+    uint8_t clock;
+
+    struct {
+        uint16_t pull;
+        uint16_t mask;
+        uint16_t dir;
+        uint16_t level;
+        qemu_irq out[16];
+    } gpio;
+
+    struct {
+        uint8_t dbnctime;
+        uint8_t size;
+        uint8_t start;
+        uint8_t len;
+        uint8_t fifo[16];
+    } kbd;
+
+    struct {
+        uint16_t file[256];
+	uint8_t faddr;
+        uint8_t addr[3];
+        QEMUTimer *tm[3];
+    } pwm;
+} LM823KbdState;
+
+#define INT_KEYPAD		(1 << 0)
+#define INT_ERROR		(1 << 3)
+#define INT_NOINIT		(1 << 4)
+#define INT_PWMEND(n)		(1 << (5 + n))
+
+#define ERR_BADPAR		(1 << 0)
+#define ERR_CMDUNK		(1 << 1)
+#define ERR_KEYOVR		(1 << 2)
+#define ERR_FIFOOVR		(1 << 6)
+
+static void lm_kbd_irq_update(LM823KbdState *s)
+{
+    qemu_set_irq(s->nirq, !s->status);
+}
+
+static void lm_kbd_gpio_update(LM823KbdState *s)
+{
+}
+
+static void lm_kbd_reset(LM823KbdState *s)
+{
+    s->config = 0x80;
+    s->status = INT_NOINIT;
+    s->acttime = 125;
+    s->kbd.dbnctime = 3;
+    s->kbd.size = 0x33;
+    s->clock = 0x08;
+
+    lm_kbd_irq_update(s);
+    lm_kbd_gpio_update(s);
+}
+
+static void lm_kbd_error(LM823KbdState *s, int err)
+{
+    s->error |= err;
+    s->status |= INT_ERROR;
+    lm_kbd_irq_update(s);
+}
+
+static void lm_kbd_pwm_tick(LM823KbdState *s, int line)
+{
+}
+
+static void lm_kbd_pwm_start(LM823KbdState *s, int line)
+{
+    lm_kbd_pwm_tick(s, line);
+}
+
+static void lm_kbd_pwm0_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 0);
+}
+static void lm_kbd_pwm1_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 1);
+}
+static void lm_kbd_pwm2_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 2);
+}
+
+enum {
+    LM832x_CMD_READ_ID		= 0x80, /* Read chip ID. */
+    LM832x_CMD_WRITE_CFG	= 0x81, /* Set configuration item. */
+    LM832x_CMD_READ_INT		= 0x82, /* Get interrupt status. */
+    LM832x_CMD_RESET		= 0x83, /* Reset, same as external one */
+    LM823x_CMD_WRITE_PULL_DOWN	= 0x84, /* Select GPIO pull-up/down. */
+    LM832x_CMD_WRITE_PORT_SEL	= 0x85, /* Select GPIO in/out. */
+    LM832x_CMD_WRITE_PORT_STATE	= 0x86, /* Set GPIO pull-up/down. */
+    LM832x_CMD_READ_PORT_SEL	= 0x87, /* Get GPIO in/out. */
+    LM832x_CMD_READ_PORT_STATE	= 0x88, /* Get GPIO pull-up/down. */
+    LM832x_CMD_READ_FIFO	= 0x89, /* Read byte from FIFO. */
+    LM832x_CMD_RPT_READ_FIFO	= 0x8a, /* Read FIFO (no increment). */
+    LM832x_CMD_SET_ACTIVE	= 0x8b, /* Set active time. */
+    LM832x_CMD_READ_ERROR	= 0x8c, /* Get error status. */
+    LM832x_CMD_READ_ROTATOR	= 0x8e, /* Read rotator status. */
+    LM832x_CMD_SET_DEBOUNCE	= 0x8f, /* Set debouncing time. */
+    LM832x_CMD_SET_KEY_SIZE	= 0x90, /* Set keypad size. */
+    LM832x_CMD_READ_KEY_SIZE	= 0x91, /* Get keypad size. */
+    LM832x_CMD_READ_CFG		= 0x92, /* Get configuration item. */
+    LM832x_CMD_WRITE_CLOCK	= 0x93, /* Set clock config. */
+    LM832x_CMD_READ_CLOCK	= 0x94, /* Get clock config. */
+    LM832x_CMD_PWM_WRITE	= 0x95, /* Write PWM script. */
+    LM832x_CMD_PWM_START	= 0x96, /* Start PWM engine. */
+    LM832x_CMD_PWM_STOP		= 0x97, /* Stop PWM engine. */
+    LM832x_GENERAL_ERROR	= 0xff, /* There was one error.
+                                           Previously was represented by -1
+                                           This is not a command */
+};
+
+#define LM832x_MAX_KPX		8
+#define LM832x_MAX_KPY		12
+
+static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte)
+{
+    int ret;
+
+    switch (reg) {
+    case LM832x_CMD_READ_ID:
+        ret = 0x0400;
+        break;
+
+    case LM832x_CMD_READ_INT:
+        ret = s->status;
+        if (!(s->status & INT_NOINIT)) {
+            s->status = 0;
+            lm_kbd_irq_update(s);
+        }
+        break;
+
+    case LM832x_CMD_READ_PORT_SEL:
+        ret = s->gpio.dir;
+        break;
+    case LM832x_CMD_READ_PORT_STATE:
+        ret = s->gpio.mask;
+        break;
+
+    case LM832x_CMD_READ_FIFO:
+        if (s->kbd.len <= 1)
+            return 0x00;
+
+        /* Example response from the two commands after a INT_KEYPAD
+         * interrupt caused by the key 0x3c being pressed:
+         * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         *     READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         *
+         * 55 is the code of the key release event serviced in the previous
+         * interrupt handling.
+         *
+         * TODO: find out whether the FIFO is advanced a single character
+         * before reading every byte or the whole size of the FIFO at the
+         * last LM832x_CMD_READ_FIFO.  This affects LM832x_CMD_RPT_READ_FIFO
+         * output in cases where there are more than one event in the FIFO.
+         * Assume 0xbc and 0x3c events are in the FIFO:
+         * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+         *     READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+         * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c?
+         */
+        s->kbd.start ++;
+        s->kbd.start &= sizeof(s->kbd.fifo) - 1;
+        s->kbd.len --;
+
+        return s->kbd.fifo[s->kbd.start];
+    case LM832x_CMD_RPT_READ_FIFO:
+        if (byte >= s->kbd.len)
+            return 0x00;
+
+        return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)];
+
+    case LM832x_CMD_READ_ERROR:
+        return s->error;
+
+    case LM832x_CMD_READ_ROTATOR:
+        return 0;
+
+    case LM832x_CMD_READ_KEY_SIZE:
+        return s->kbd.size;
+
+    case LM832x_CMD_READ_CFG:
+        return s->config & 0xf;
+
+    case LM832x_CMD_READ_CLOCK:
+        return (s->clock & 0xfc) | 2;
+
+    default:
+        lm_kbd_error(s, ERR_CMDUNK);
+        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+        return 0x00;
+    }
+
+    return ret >> (byte << 3);
+}
+
+static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
+{
+    switch (reg) {
+    case LM832x_CMD_WRITE_CFG:
+        s->config = value;
+        /* This must be done whenever s->mux.in is updated (never).  */
+        if ((s->config >> 1) & 1)			/* MUX1EN */
+            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]);
+        if ((s->config >> 3) & 1)			/* MUX2EN */
+            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]);
+        /* TODO: check that this is issued only following the chip reset
+         * and not in the middle of operation and that it is followed by
+         * the GPIO ports re-resablishing through WRITE_PORT_SEL and
+         * WRITE_PORT_STATE (using a timer perhaps) and otherwise output
+         * warnings.  */
+        s->status = 0;
+        lm_kbd_irq_update(s);
+        s->kbd.len = 0;
+        s->kbd.start = 0;
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM832x_CMD_RESET:
+        if (value == 0xaa)
+            lm_kbd_reset(s);
+        else
+            lm_kbd_error(s, ERR_BADPAR);
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM823x_CMD_WRITE_PULL_DOWN:
+        if (!byte)
+            s->gpio.pull = value;
+        else {
+            s->gpio.pull |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_WRITE_PORT_SEL:
+        if (!byte)
+            s->gpio.dir = value;
+        else {
+            s->gpio.dir |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_WRITE_PORT_STATE:
+        if (!byte)
+            s->gpio.mask = value;
+        else {
+            s->gpio.mask |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+
+    case LM832x_CMD_SET_ACTIVE:
+        s->acttime = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM832x_CMD_SET_DEBOUNCE:
+        s->kbd.dbnctime = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!value)
+            lm_kbd_error(s, ERR_BADPAR);
+        break;
+
+    case LM832x_CMD_SET_KEY_SIZE:
+        s->kbd.size = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if (
+                        (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY ||
+                        (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX)
+            lm_kbd_error(s, ERR_BADPAR);
+        break;
+
+    case LM832x_CMD_WRITE_CLOCK:
+        s->clock = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if ((value & 3) && (value & 3) != 3) {
+            lm_kbd_error(s, ERR_BADPAR);
+            fprintf(stderr, "%s: invalid clock setting in RCPWM\n",
+                            __FUNCTION__);
+        }
+        /* TODO: Validate that the command is only issued once */
+        break;
+
+    case LM832x_CMD_PWM_WRITE:
+        if (byte == 0) {
+            if (!(value & 3) || (value >> 2) > 59) {
+                lm_kbd_error(s, ERR_BADPAR);
+                s->reg = LM832x_GENERAL_ERROR;
+                break;
+            }
+
+            s->pwm.faddr = value;
+            s->pwm.file[s->pwm.faddr] = 0;
+        } else if (byte == 1) {
+            s->pwm.file[s->pwm.faddr] |= value << 8;
+        } else if (byte == 2) {
+            s->pwm.file[s->pwm.faddr] |= value << 0;
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_PWM_START:
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!(value & 3) || (value >> 2) > 59) {
+            lm_kbd_error(s, ERR_BADPAR);
+            break;
+        }
+
+        s->pwm.addr[(value & 3) - 1] = value >> 2;
+        lm_kbd_pwm_start(s, (value & 3) - 1);
+        break;
+    case LM832x_CMD_PWM_STOP:
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!(value & 3)) {
+            lm_kbd_error(s, ERR_BADPAR);
+            break;
+        }
+
+        qemu_del_timer(s->pwm.tm[(value & 3) - 1]);
+        break;
+
+    case LM832x_GENERAL_ERROR:
+        lm_kbd_error(s, ERR_BADPAR);
+        break;
+    default:
+        lm_kbd_error(s, ERR_CMDUNK);
+        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+        break;
+    }
+}
+
+static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    switch (event) {
+    case I2C_START_RECV:
+    case I2C_START_SEND:
+        s->i2c_cycle = 0;
+        s->i2c_dir = (event == I2C_START_SEND);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static int lm_i2c_rx(I2CSlave *i2c)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    return lm_kbd_read(s, s->reg, s->i2c_cycle ++);
+}
+
+static int lm_i2c_tx(I2CSlave *i2c, uint8_t data)
+{
+    LM823KbdState *s = (LM823KbdState *) i2c;
+
+    if (!s->i2c_cycle)
+        s->reg = data;
+    else
+        lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data);
+    s->i2c_cycle ++;
+
+    return 0;
+}
+
+static int lm_kbd_post_load(void *opaque, int version_id)
+{
+    LM823KbdState *s = opaque;
+
+    lm_kbd_irq_update(s);
+    lm_kbd_gpio_update(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_lm_kbd = {
+    .name = "LM8323",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = lm_kbd_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_I2C_SLAVE(i2c, LM823KbdState),
+        VMSTATE_UINT8(i2c_dir, LM823KbdState),
+        VMSTATE_UINT8(i2c_cycle, LM823KbdState),
+        VMSTATE_UINT8(reg, LM823KbdState),
+        VMSTATE_UINT8(config, LM823KbdState),
+        VMSTATE_UINT8(status, LM823KbdState),
+        VMSTATE_UINT8(acttime, LM823KbdState),
+        VMSTATE_UINT8(error, LM823KbdState),
+        VMSTATE_UINT8(clock, LM823KbdState),
+        VMSTATE_UINT16(gpio.pull, LM823KbdState),
+        VMSTATE_UINT16(gpio.mask, LM823KbdState),
+        VMSTATE_UINT16(gpio.dir, LM823KbdState),
+        VMSTATE_UINT16(gpio.level, LM823KbdState),
+        VMSTATE_UINT8(kbd.dbnctime, LM823KbdState),
+        VMSTATE_UINT8(kbd.size, LM823KbdState),
+        VMSTATE_UINT8(kbd.start, LM823KbdState),
+        VMSTATE_UINT8(kbd.len, LM823KbdState),
+        VMSTATE_BUFFER(kbd.fifo, LM823KbdState),
+        VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256),
+        VMSTATE_UINT8(pwm.faddr, LM823KbdState),
+        VMSTATE_BUFFER(pwm.addr, LM823KbdState),
+        VMSTATE_TIMER_ARRAY(pwm.tm, LM823KbdState, 3),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static int lm8323_init(I2CSlave *i2c)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    s->model = 0x8323;
+    s->pwm.tm[0] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm0_tick, s);
+    s->pwm.tm[1] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm1_tick, s);
+    s->pwm.tm[2] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm2_tick, s);
+    qdev_init_gpio_out(&i2c->qdev, &s->nirq, 1);
+
+    lm_kbd_reset(s);
+
+    qemu_register_reset((void *) lm_kbd_reset, s);
+    return 0;
+}
+
+void lm832x_key_event(DeviceState *dev, int key, int state)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev));
+
+    if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
+        return;
+
+    if (s->kbd.len >= sizeof(s->kbd.fifo)) {
+        lm_kbd_error(s, ERR_FIFOOVR);
+        return;
+    }
+
+    s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] =
+            key | (state << 7);
+
+    /* We never set ERR_KEYOVR because we support multiple keys fine.  */
+    s->status |= INT_KEYPAD;
+    lm_kbd_irq_update(s);
+}
+
+static void lm8323_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = lm8323_init;
+    k->event = lm_i2c_event;
+    k->recv = lm_i2c_rx;
+    k->send = lm_i2c_tx;
+    dc->vmsd = &vmstate_lm_kbd;
+}
+
+static const TypeInfo lm8323_info = {
+    .name          = "lm8323",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(LM823KbdState),
+    .class_init    = lm8323_class_init,
+};
+
+static void lm832x_register_types(void)
+{
+    type_register_static(&lm8323_info);
+}
+
+type_init(lm832x_register_types)
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
new file mode 100644
index 0000000000..08ceb9fe8a
--- /dev/null
+++ b/hw/input/pckbd.c
@@ -0,0 +1,527 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+#include "hw/input/ps2.h"
+#include "sysemu/sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+#ifdef DEBUG_KBD
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/*	Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE	0x20	/* Read mode bits */
+#define KBD_CCMD_WRITE_MODE	0x60	/* Write mode bits */
+#define KBD_CCMD_GET_VERSION	0xA1	/* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE	0xA7	/* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE	0xA8	/* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE	0xA9	/* Mouse interface test */
+#define KBD_CCMD_SELF_TEST	0xAA	/* Controller self test */
+#define KBD_CCMD_KBD_TEST	0xAB	/* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE	0xAD	/* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE	0xAE	/* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT    0xC0    /* read input port */
+#define KBD_CCMD_READ_OUTPORT	0xD0    /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT	0xD1    /* write output port */
+#define KBD_CCMD_WRITE_OBUF	0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF	0xD3    /* Write to output buffer as if
+					   initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE	0xD4	/* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20    0xDD    /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20     0xDF    /* HP vectra only ? */
+#define KBD_CCMD_PULSE_BITS_3_0 0xF0    /* Pulse bits 3-0 of the output port P2. */
+#define KBD_CCMD_RESET          0xFE    /* Pulse bit 0 of the output port P2 = CPU reset. */
+#define KBD_CCMD_NO_OP          0xFF    /* Pulse no bits of the output port P2. */
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
+#define KBD_CMD_ECHO     	0xEE
+#define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
+#define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
+#define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
+#define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET		0xFF	/* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR		0xAA	/* Power on reset */
+#define KBD_REPLY_ACK		0xFA	/* Command ACK */
+#define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 		0x01	/* Keyboard output buffer full */
+#define KBD_STAT_IBF 		0x02	/* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST	0x04	/* Self test successful */
+#define KBD_STAT_CMD		0x08	/* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED	0x10	/* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF	0x20	/* Mouse output buffer full */
+#define KBD_STAT_GTO 		0x40	/* General receive/xmit timeout */
+#define KBD_STAT_PERR 		0x80	/* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT	0x01	/* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT	0x02	/* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 		0x04	/* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK	0x08	/* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD	0x10	/* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE	0x20	/* Disable mouse interface */
+#define KBD_MODE_KCC 		0x40	/* Scan code conversion to PC format */
+#define KBD_MODE_RFU		0x80
+
+/* Output Port Bits */
+#define KBD_OUT_RESET           0x01    /* 1=normal mode, 0=reset */
+#define KBD_OUT_A20             0x02    /* x86 only */
+#define KBD_OUT_OBF             0x10    /* Keyboard output buffer full */
+#define KBD_OUT_MOUSE_OBF       0x20    /* Mouse output buffer full */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
+#define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
+#define AUX_SET_RES		0xE8	/* Set resolution */
+#define AUX_GET_SCALE		0xE9	/* Get scaling factor */
+#define AUX_SET_STREAM		0xEA	/* Set stream mode */
+#define AUX_POLL		0xEB	/* Poll */
+#define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
+#define AUX_SET_WRAP		0xEE	/* Set wrap mode */
+#define AUX_SET_REMOTE		0xF0	/* Set remote mode */
+#define AUX_GET_TYPE		0xF2	/* Get type */
+#define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
+#define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
+#define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
+#define AUX_SET_DEFAULT		0xF6
+#define AUX_RESET		0xFF	/* Reset aux device */
+#define AUX_ACK			0xFA	/* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define KBD_PENDING_KBD         1
+#define KBD_PENDING_AUX         2
+
+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;
+    /* Bitmask of devices with data available.  */
+    uint8_t pending;
+    void *kbd;
+    void *mouse;
+
+    qemu_irq irq_kbd;
+    qemu_irq irq_mouse;
+    qemu_irq *a20_out;
+    hwaddr mask;
+} KBDState;
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+   incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+    int irq_kbd_level, irq_mouse_level;
+
+    irq_kbd_level = 0;
+    irq_mouse_level = 0;
+    s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+    s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
+    if (s->pending) {
+        s->status |= KBD_STAT_OBF;
+        s->outport |= KBD_OUT_OBF;
+        /* kbd data takes priority over aux data.  */
+        if (s->pending == KBD_PENDING_AUX) {
+            s->status |= KBD_STAT_MOUSE_OBF;
+            s->outport |= KBD_OUT_MOUSE_OBF;
+            if (s->mode & KBD_MODE_MOUSE_INT)
+                irq_mouse_level = 1;
+        } else {
+            if ((s->mode & KBD_MODE_KBD_INT) &&
+                !(s->mode & KBD_MODE_DISABLE_KBD))
+                irq_kbd_level = 1;
+        }
+    }
+    qemu_set_irq(s->irq_kbd, irq_kbd_level);
+    qemu_set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_KBD;
+    else
+        s->pending &= ~KBD_PENDING_KBD;
+    kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_AUX;
+    else
+        s->pending &= ~KBD_PENDING_AUX;
+    kbd_update_irq(s);
+}
+
+static uint64_t kbd_read_status(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    KBDState *s = opaque;
+    int val;
+    val = s->status;
+    DPRINTF("kbd: read status=0x%02x\n", val);
+    return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+    if (aux)
+        ps2_queue(s->mouse, b);
+    else
+        ps2_queue(s->kbd, b);
+}
+
+static void outport_write(KBDState *s, uint32_t val)
+{
+    DPRINTF("kbd: write outport=0x%02x\n", val);
+    s->outport = val;
+    if (s->a20_out) {
+        qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+    }
+    if (!(val & 1)) {
+        qemu_system_reset_request();
+    }
+}
+
+static void kbd_write_command(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    KBDState *s = opaque;
+
+    DPRINTF("kbd: write cmd=0x%02x\n", val);
+
+    /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
+     * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
+     * command specify the output port bits to be pulsed.
+     * 0: Bit should be pulsed. 1: Bit should not be modified.
+     * The only useful version of this command is pulsing bit 0,
+     * which does a CPU reset.
+     */
+    if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
+        if(!(val & 1))
+            val = KBD_CCMD_RESET;
+        else
+            val = KBD_CCMD_NO_OP;
+    }
+
+    switch(val) {
+    case KBD_CCMD_READ_MODE:
+        kbd_queue(s, s->mode, 0);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+    case KBD_CCMD_WRITE_OBUF:
+    case KBD_CCMD_WRITE_AUX_OBUF:
+    case KBD_CCMD_WRITE_MOUSE:
+    case KBD_CCMD_WRITE_OUTPORT:
+        s->write_cmd = val;
+        break;
+    case KBD_CCMD_MOUSE_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_MOUSE_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_TEST_MOUSE:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_SELF_TEST:
+        s->status |= KBD_STAT_SELFTEST;
+        kbd_queue(s, 0x55, 0);
+        break;
+    case KBD_CCMD_KBD_TEST:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_KBD_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_KBD_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_READ_INPORT:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_READ_OUTPORT:
+        kbd_queue(s, s->outport, 0);
+        break;
+    case KBD_CCMD_ENABLE_A20:
+        if (s->a20_out) {
+            qemu_irq_raise(*s->a20_out);
+        }
+        s->outport |= KBD_OUT_A20;
+        break;
+    case KBD_CCMD_DISABLE_A20:
+        if (s->a20_out) {
+            qemu_irq_lower(*s->a20_out);
+        }
+        s->outport &= ~KBD_OUT_A20;
+        break;
+    case KBD_CCMD_RESET:
+        qemu_system_reset_request();
+        break;
+    case KBD_CCMD_NO_OP:
+        /* ignore that */
+        break;
+    default:
+        fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
+        break;
+    }
+}
+
+static uint64_t kbd_read_data(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    KBDState *s = opaque;
+    uint32_t val;
+
+    if (s->pending == KBD_PENDING_AUX)
+        val = ps2_read_data(s->mouse);
+    else
+        val = ps2_read_data(s->kbd);
+
+    DPRINTF("kbd: read data=0x%02x\n", val);
+    return val;
+}
+
+static void kbd_write_data(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    KBDState *s = opaque;
+
+    DPRINTF("kbd: write data=0x%02x\n", val);
+
+    switch(s->write_cmd) {
+    case 0:
+        ps2_write_keyboard(s->kbd, val);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+        s->mode = val;
+        ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+        /* ??? */
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_WRITE_OBUF:
+        kbd_queue(s, val, 0);
+        break;
+    case KBD_CCMD_WRITE_AUX_OBUF:
+        kbd_queue(s, val, 1);
+        break;
+    case KBD_CCMD_WRITE_OUTPORT:
+        outport_write(s, val);
+        break;
+    case KBD_CCMD_WRITE_MOUSE:
+        ps2_write_mouse(s->mouse, val);
+        break;
+    default:
+        break;
+    }
+    s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+    KBDState *s = opaque;
+
+    s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+    s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+    s->outport = KBD_OUT_RESET | KBD_OUT_A20;
+}
+
+static const VMStateDescription vmstate_kbd = {
+    .name = "pckbd",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(write_cmd, KBDState),
+        VMSTATE_UINT8(status, KBDState),
+        VMSTATE_UINT8(mode, KBDState),
+        VMSTATE_UINT8(pending, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Memory mapped interface */
+static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        return kbd_read_status(s, 0, 1) & 0xff;
+    else
+        return kbd_read_data(s, 0, 1) & 0xff;
+}
+
+static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        kbd_write_command(s, 0, value & 0xff, 1);
+    else
+        kbd_write_data(s, 0, value & 0xff, 1);
+}
+
+static const MemoryRegionOps i8042_mmio_ops = {
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .old_mmio = {
+        .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
+        .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
+    },
+};
+
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+                   MemoryRegion *region, ram_addr_t size,
+                   hwaddr mask)
+{
+    KBDState *s = g_malloc0(sizeof(KBDState));
+
+    s->irq_kbd = kbd_irq;
+    s->irq_mouse = mouse_irq;
+    s->mask = mask;
+
+    vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+    memory_region_init_io(region, &i8042_mmio_ops, s, "i8042", size);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+    qemu_register_reset(kbd_reset, s);
+}
+
+typedef struct ISAKBDState {
+    ISADevice dev;
+    KBDState kbd;
+    MemoryRegion io[2];
+} ISAKBDState;
+
+void i8042_isa_mouse_fake_event(void *opaque)
+{
+    ISADevice *dev = opaque;
+    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+    ps2_mouse_fake_event(s->mouse);
+}
+
+void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
+{
+    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+    s->a20_out = a20_out;
+}
+
+static const VMStateDescription vmstate_kbd_isa = {
+    .name = "pckbd",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const MemoryRegionOps i8042_data_ops = {
+    .read = kbd_read_data,
+    .write = kbd_write_data,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps i8042_cmd_ops = {
+    .read = kbd_read_status,
+    .write = kbd_write_command,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int i8042_initfn(ISADevice *dev)
+{
+    ISAKBDState *isa_s = DO_UPCAST(ISAKBDState, dev, dev);
+    KBDState *s = &isa_s->kbd;
+
+    isa_init_irq(dev, &s->irq_kbd, 1);
+    isa_init_irq(dev, &s->irq_mouse, 12);
+
+    memory_region_init_io(isa_s->io + 0, &i8042_data_ops, s, "i8042-data", 1);
+    isa_register_ioport(dev, isa_s->io + 0, 0x60);
+
+    memory_region_init_io(isa_s->io + 1, &i8042_cmd_ops, s, "i8042-cmd", 1);
+    isa_register_ioport(dev, isa_s->io + 1, 0x64);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+    qemu_register_reset(kbd_reset, s);
+    return 0;
+}
+
+static void i8042_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = i8042_initfn;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_kbd_isa;
+}
+
+static const TypeInfo i8042_info = {
+    .name          = "i8042",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAKBDState),
+    .class_init    = i8042_class_initfn,
+};
+
+static void i8042_register_types(void)
+{
+    type_register_static(&i8042_info);
+}
+
+type_init(i8042_register_types)
diff --git a/hw/input/pl050.c b/hw/input/pl050.c
new file mode 100644
index 0000000000..7dd8a59dd4
--- /dev/null
+++ b/hw/input/pl050.c
@@ -0,0 +1,199 @@
+/*
+ * Arm PrimeCell PL050 Keyboard / Mouse Interface
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/input/ps2.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    void *dev;
+    uint32_t cr;
+    uint32_t clk;
+    uint32_t last;
+    int pending;
+    qemu_irq irq;
+    int is_mouse;
+} pl050_state;
+
+static const VMStateDescription vmstate_pl050 = {
+    .name = "pl050",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr, pl050_state),
+        VMSTATE_UINT32(clk, pl050_state),
+        VMSTATE_UINT32(last, pl050_state),
+        VMSTATE_INT32(pending, pl050_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define PL050_TXEMPTY         (1 << 6)
+#define PL050_TXBUSY          (1 << 5)
+#define PL050_RXFULL          (1 << 4)
+#define PL050_RXBUSY          (1 << 3)
+#define PL050_RXPARITY        (1 << 2)
+#define PL050_KMIC            (1 << 1)
+#define PL050_KMID            (1 << 0)
+
+static const unsigned char pl050_id[] =
+{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl050_update(void *opaque, int level)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    int raise;
+
+    s->pending = level;
+    raise = (s->pending && (s->cr & 0x10) != 0)
+            || (s->cr & 0x08) != 0;
+    qemu_set_irq(s->irq, raise);
+}
+
+static uint64_t pl050_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    if (offset >= 0xfe0 && offset < 0x1000)
+        return pl050_id[(offset - 0xfe0) >> 2];
+
+    switch (offset >> 2) {
+    case 0: /* KMICR */
+        return s->cr;
+    case 1: /* KMISTAT */
+        {
+            uint8_t val;
+            uint32_t stat;
+
+            val = s->last;
+            val = val ^ (val >> 4);
+            val = val ^ (val >> 2);
+            val = (val ^ (val >> 1)) & 1;
+
+            stat = PL050_TXEMPTY;
+            if (val)
+                stat |= PL050_RXPARITY;
+            if (s->pending)
+                stat |= PL050_RXFULL;
+
+            return stat;
+        }
+    case 2: /* KMIDATA */
+        if (s->pending)
+            s->last = ps2_read_data(s->dev);
+        return s->last;
+    case 3: /* KMICLKDIV */
+        return s->clk;
+    case 4: /* KMIIR */
+        return s->pending | 2;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl050_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl050_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    switch (offset >> 2) {
+    case 0: /* KMICR */
+        s->cr = value;
+        pl050_update(s, s->pending);
+        /* ??? Need to implement the enable/disable bit.  */
+        break;
+    case 2: /* KMIDATA */
+        /* ??? This should toggle the TX interrupt line.  */
+        /* ??? This means kbd/mouse can block each other.  */
+        if (s->is_mouse) {
+            ps2_write_mouse(s->dev, value);
+        } else {
+            ps2_write_keyboard(s->dev, value);
+        }
+        break;
+    case 3: /* KMICLKDIV */
+        s->clk = value;
+        return;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl050_write: Bad offset %x\n", (int)offset);
+    }
+}
+static const MemoryRegionOps pl050_ops = {
+    .read = pl050_read,
+    .write = pl050_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl050_init(SysBusDevice *dev, int is_mouse)
+{
+    pl050_state *s = FROM_SYSBUS(pl050_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl050_ops, s, "pl050", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    s->is_mouse = is_mouse;
+    if (s->is_mouse)
+        s->dev = ps2_mouse_init(pl050_update, s);
+    else
+        s->dev = ps2_kbd_init(pl050_update, s);
+    return 0;
+}
+
+static int pl050_init_keyboard(SysBusDevice *dev)
+{
+    return pl050_init(dev, 0);
+}
+
+static int pl050_init_mouse(SysBusDevice *dev)
+{
+    return pl050_init(dev, 1);
+}
+
+static void pl050_kbd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl050_init_keyboard;
+    dc->vmsd = &vmstate_pl050;
+}
+
+static const TypeInfo pl050_kbd_info = {
+    .name          = "pl050_keyboard",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl050_state),
+    .class_init    = pl050_kbd_class_init,
+};
+
+static void pl050_mouse_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl050_init_mouse;
+    dc->vmsd = &vmstate_pl050;
+}
+
+static const TypeInfo pl050_mouse_info = {
+    .name          = "pl050_mouse",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl050_state),
+    .class_init    = pl050_mouse_class_init,
+};
+
+static void pl050_register_types(void)
+{
+    type_register_static(&pl050_kbd_info);
+    type_register_static(&pl050_mouse_info);
+}
+
+type_init(pl050_register_types)
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
new file mode 100644
index 0000000000..34120796b1
--- /dev/null
+++ b/hw/input/ps2.c
@@ -0,0 +1,676 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/input/ps2.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
+#define KBD_CMD_ECHO     	0xEE
+#define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
+#define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
+#define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
+#define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
+#define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET		0xFF	/* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR		0xAA	/* Power on reset */
+#define KBD_REPLY_ID		0xAB	/* Keyboard ID */
+#define KBD_REPLY_ACK		0xFA	/* Command ACK */
+#define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
+#define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
+#define AUX_SET_RES		0xE8	/* Set resolution */
+#define AUX_GET_SCALE		0xE9	/* Get scaling factor */
+#define AUX_SET_STREAM		0xEA	/* Set stream mode */
+#define AUX_POLL		0xEB	/* Poll */
+#define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
+#define AUX_SET_WRAP		0xEE	/* Set wrap mode */
+#define AUX_SET_REMOTE		0xF0	/* Set remote mode */
+#define AUX_GET_TYPE		0xF2	/* Get type */
+#define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
+#define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
+#define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
+#define AUX_SET_DEFAULT		0xF6
+#define AUX_RESET		0xFF	/* Reset aux device */
+#define AUX_ACK			0xFA	/* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+    uint8_t data[PS2_QUEUE_SIZE];
+    int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+    PS2Queue queue;
+    int32_t write_cmd;
+    void (*update_irq)(void *, int);
+    void *update_arg;
+} PS2State;
+
+typedef struct {
+    PS2State common;
+    int scan_enabled;
+    /* QEMU uses translated PC scancodes internally.  To avoid multiple
+       conversions we do the translation (if any) in the PS/2 emulation
+       not the keyboard controller.  */
+    int translate;
+    int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
+    int ledstate;
+} PS2KbdState;
+
+typedef struct {
+    PS2State common;
+    uint8_t mouse_status;
+    uint8_t mouse_resolution;
+    uint8_t mouse_sample_rate;
+    uint8_t mouse_wrap;
+    uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+    uint8_t mouse_detect_state;
+    int mouse_dx; /* current values, needed for 'poll' mode */
+    int mouse_dy;
+    int mouse_dz;
+    uint8_t mouse_buttons;
+} PS2MouseState;
+
+/* Table to convert from PC scancodes to raw scancodes.  */
+static const unsigned char ps2_raw_keycode[128] = {
+  0, 118,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
+ 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  20,  28,  27,
+ 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  93,  26,  34,  33,  42,
+ 50,  49,  58,  65,  73,  74,  89, 124,  17,  41,  88,   5,   6,   4,  12,   3,
+ 11,   2,  10,   1,   9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105,
+114, 122, 112, 113, 127,  96,  97, 120,   7,  15,  23,  31,  39,  47,  55,  63,
+ 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
+ 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
+};
+static const unsigned char ps2_raw_keycode_set3[128] = {
+  0,   8,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
+ 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  17,  28,  27,
+ 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  92,  26,  34,  33,  42,
+ 50,  49,  58,  65,  73,  74,  89, 126,  25,  41,  20,   7,  15,  23,  31,  39,
+ 47,   2,  63,  71,  79, 118,  95, 108, 117, 125, 132, 107, 115, 116, 124, 105,
+114, 122, 112, 113, 127,  96,  97,  86,  94,  15,  23,  31,  39,  47,  55,  63,
+ 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
+ 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
+};
+
+void ps2_queue(void *opaque, int b)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q = &s->queue;
+
+    if (q->count >= PS2_QUEUE_SIZE)
+        return;
+    q->data[q->wptr] = b;
+    if (++q->wptr == PS2_QUEUE_SIZE)
+        q->wptr = 0;
+    q->count++;
+    s->update_irq(s->update_arg, 1);
+}
+
+/*
+   keycode is expressed as follow:
+   bit 7    - 0 key pressed, 1 = key released
+   bits 6-0 - translated scancode set 2
+ */
+static void ps2_put_keycode(void *opaque, int keycode)
+{
+    PS2KbdState *s = opaque;
+
+    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    /* XXX: add support for scancode set 1 */
+    if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
+        if (keycode & 0x80) {
+            ps2_queue(&s->common, 0xf0);
+        }
+        if (s->scancode_set == 2) {
+            keycode = ps2_raw_keycode[keycode & 0x7f];
+        } else if (s->scancode_set == 3) {
+            keycode = ps2_raw_keycode_set3[keycode & 0x7f];
+        }
+      }
+    ps2_queue(&s->common, keycode);
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q;
+    int val, index;
+
+    q = &s->queue;
+    if (q->count == 0) {
+        /* NOTE: if no data left, we return the last keyboard one
+           (needed for EMM386) */
+        /* XXX: need a timer to do things correctly */
+        index = q->rptr - 1;
+        if (index < 0)
+            index = PS2_QUEUE_SIZE - 1;
+        val = q->data[index];
+    } else {
+        val = q->data[q->rptr];
+        if (++q->rptr == PS2_QUEUE_SIZE)
+            q->rptr = 0;
+        q->count--;
+        /* reading deasserts IRQ */
+        s->update_irq(s->update_arg, 0);
+        /* reassert IRQs if data left */
+        s->update_irq(s->update_arg, q->count != 0);
+    }
+    return val;
+}
+
+static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
+{
+    s->ledstate = ledstate;
+    kbd_put_ledstate(ledstate);
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+    s->scan_enabled = 1;
+    s->scancode_set = 2;
+    ps2_set_ledstate(s, 0);
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        switch(val) {
+        case 0x00:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case 0x05:
+            ps2_queue(&s->common, KBD_REPLY_RESEND);
+            break;
+        case KBD_CMD_GET_ID:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            /* We emulate a MF2 AT keyboard here */
+            ps2_queue(&s->common, KBD_REPLY_ID);
+            if (s->translate)
+                ps2_queue(&s->common, 0x41);
+            else
+                ps2_queue(&s->common, 0x83);
+            break;
+        case KBD_CMD_ECHO:
+            ps2_queue(&s->common, KBD_CMD_ECHO);
+            break;
+        case KBD_CMD_ENABLE:
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_SCANCODE:
+        case KBD_CMD_SET_LEDS:
+        case KBD_CMD_SET_RATE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_DISABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 0;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_ENABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET:
+            ps2_reset_keyboard(s);
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            ps2_queue(&s->common, KBD_REPLY_POR);
+            break;
+        default:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        }
+        break;
+    case KBD_CMD_SCANCODE:
+        if (val == 0) {
+            if (s->scancode_set == 1)
+                ps2_put_keycode(s, 0x43);
+            else if (s->scancode_set == 2)
+                ps2_put_keycode(s, 0x41);
+            else if (s->scancode_set == 3)
+                ps2_put_keycode(s, 0x3f);
+        } else {
+            if (val >= 1 && val <= 3)
+                s->scancode_set = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+        }
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_LEDS:
+        ps2_set_ledstate(s, val);
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_RATE:
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+/* Set the scancode translation mode.
+   0 = raw scancodes.
+   1 = translated scancodes (used by qemu internally).  */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+    s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+    unsigned int b;
+    int dx1, dy1, dz1;
+
+    dx1 = s->mouse_dx;
+    dy1 = s->mouse_dy;
+    dz1 = s->mouse_dz;
+    /* XXX: increase range to 8 bits ? */
+    if (dx1 > 127)
+        dx1 = 127;
+    else if (dx1 < -127)
+        dx1 = -127;
+    if (dy1 > 127)
+        dy1 = 127;
+    else if (dy1 < -127)
+        dy1 = -127;
+    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+    ps2_queue(&s->common, b);
+    ps2_queue(&s->common, dx1 & 0xff);
+    ps2_queue(&s->common, dy1 & 0xff);
+    /* extra byte for IMPS/2 or IMEX */
+    switch(s->mouse_type) {
+    default:
+        break;
+    case 3:
+        if (dz1 > 127)
+            dz1 = 127;
+        else if (dz1 < -127)
+                dz1 = -127;
+        ps2_queue(&s->common, dz1 & 0xff);
+        break;
+    case 4:
+        if (dz1 > 7)
+            dz1 = 7;
+        else if (dz1 < -7)
+            dz1 = -7;
+        b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+        ps2_queue(&s->common, b);
+        break;
+    }
+
+    /* update deltas */
+    s->mouse_dx -= dx1;
+    s->mouse_dy -= dy1;
+    s->mouse_dz -= dz1;
+}
+
+static void ps2_mouse_event(void *opaque,
+                            int dx, int dy, int dz, int buttons_state)
+{
+    PS2MouseState *s = opaque;
+
+    /* check if deltas are recorded when disabled */
+    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+        return;
+
+    s->mouse_dx += dx;
+    s->mouse_dy -= dy;
+    s->mouse_dz += dz;
+    /* XXX: SDL sometimes generates nul events: we delete them */
+    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+        s->mouse_buttons == buttons_state)
+	return;
+    s->mouse_buttons = buttons_state;
+
+    if (buttons_state) {
+        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    }
+
+    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+        for(;;) {
+            /* if not remote, send event. Multiple events are sent if
+               too big deltas */
+            ps2_mouse_send_packet(s);
+            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+                break;
+        }
+    }
+}
+
+void ps2_mouse_fake_event(void *opaque)
+{
+    ps2_mouse_event(opaque, 1, 0, 0, 0);
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+    PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+    printf("kbd: write mouse 0x%02x\n", val);
+#endif
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        /* mouse command */
+        if (s->mouse_wrap) {
+            if (val == AUX_RESET_WRAP) {
+                s->mouse_wrap = 0;
+                ps2_queue(&s->common, AUX_ACK);
+                return;
+            } else if (val != AUX_RESET) {
+                ps2_queue(&s->common, val);
+                return;
+            }
+        }
+        switch(val) {
+        case AUX_SET_SCALE11:
+            s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_SCALE21:
+            s->mouse_status |= MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_STREAM:
+            s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_WRAP:
+            s->mouse_wrap = 1;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_REMOTE:
+            s->mouse_status |= MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_TYPE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        case AUX_SET_RES:
+        case AUX_SET_SAMPLE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_SCALE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_status);
+            ps2_queue(&s->common, s->mouse_resolution);
+            ps2_queue(&s->common, s->mouse_sample_rate);
+            break;
+        case AUX_POLL:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_mouse_send_packet(s);
+            break;
+        case AUX_ENABLE_DEV:
+            s->mouse_status |= MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_DISABLE_DEV:
+            s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_DEFAULT:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_RESET:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            s->mouse_type = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, 0xaa);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        default:
+            break;
+        }
+        break;
+    case AUX_SET_SAMPLE:
+        s->mouse_sample_rate = val;
+        /* detect IMPS/2 or IMEX */
+        switch(s->mouse_detect_state) {
+        default:
+        case 0:
+            if (val == 200)
+                s->mouse_detect_state = 1;
+            break;
+        case 1:
+            if (val == 100)
+                s->mouse_detect_state = 2;
+            else if (val == 200)
+                s->mouse_detect_state = 3;
+            else
+                s->mouse_detect_state = 0;
+            break;
+        case 2:
+            if (val == 80)
+                s->mouse_type = 3; /* IMPS/2 */
+            s->mouse_detect_state = 0;
+            break;
+        case 3:
+            if (val == 80)
+                s->mouse_type = 4; /* IMEX */
+            s->mouse_detect_state = 0;
+            break;
+        }
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case AUX_SET_RES:
+        s->mouse_resolution = val;
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+static void ps2_common_reset(PS2State *s)
+{
+    PS2Queue *q;
+    s->write_cmd = -1;
+    q = &s->queue;
+    q->rptr = 0;
+    q->wptr = 0;
+    q->count = 0;
+    s->update_irq(s->update_arg, 0);
+}
+
+static void ps2_kbd_reset(void *opaque)
+{
+    PS2KbdState *s = (PS2KbdState *) opaque;
+
+    ps2_common_reset(&s->common);
+    s->scan_enabled = 0;
+    s->translate = 0;
+    s->scancode_set = 0;
+}
+
+static void ps2_mouse_reset(void *opaque)
+{
+    PS2MouseState *s = (PS2MouseState *) opaque;
+
+    ps2_common_reset(&s->common);
+    s->mouse_status = 0;
+    s->mouse_resolution = 0;
+    s->mouse_sample_rate = 0;
+    s->mouse_wrap = 0;
+    s->mouse_type = 0;
+    s->mouse_detect_state = 0;
+    s->mouse_dx = 0;
+    s->mouse_dy = 0;
+    s->mouse_dz = 0;
+    s->mouse_buttons = 0;
+}
+
+static const VMStateDescription vmstate_ps2_common = {
+    .name = "PS2 Common State",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(write_cmd, PS2State),
+        VMSTATE_INT32(queue.rptr, PS2State),
+        VMSTATE_INT32(queue.wptr, PS2State),
+        VMSTATE_INT32(queue.count, PS2State),
+        VMSTATE_BUFFER(queue.data, PS2State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static bool ps2_keyboard_ledstate_needed(void *opaque)
+{
+    PS2KbdState *s = opaque;
+
+    return s->ledstate != 0; /* 0 is default state */
+}
+
+static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
+{
+    PS2KbdState *s = opaque;
+
+    kbd_put_ledstate(s->ledstate);
+    return 0;
+}
+
+static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
+    .name = "ps2kbd/ledstate",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = ps2_kbd_ledstate_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(ledstate, PS2KbdState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int ps2_kbd_post_load(void* opaque, int version_id)
+{
+    PS2KbdState *s = (PS2KbdState*)opaque;
+
+    if (version_id == 2)
+        s->scancode_set=2;
+    return 0;
+}
+
+static const VMStateDescription vmstate_ps2_keyboard = {
+    .name = "ps2kbd",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = ps2_kbd_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
+        VMSTATE_INT32(scan_enabled, PS2KbdState),
+        VMSTATE_INT32(translate, PS2KbdState),
+        VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_ps2_keyboard_ledstate,
+            .needed = ps2_keyboard_ledstate_needed,
+        }, {
+            /* empty */
+        }
+    }
+};
+
+static const VMStateDescription vmstate_ps2_mouse = {
+    .name = "ps2mouse",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
+        VMSTATE_UINT8(mouse_status, PS2MouseState),
+        VMSTATE_UINT8(mouse_resolution, PS2MouseState),
+        VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
+        VMSTATE_UINT8(mouse_wrap, PS2MouseState),
+        VMSTATE_UINT8(mouse_type, PS2MouseState),
+        VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
+        VMSTATE_INT32(mouse_dx, PS2MouseState),
+        VMSTATE_INT32(mouse_dy, PS2MouseState),
+        VMSTATE_INT32(mouse_dz, PS2MouseState),
+        VMSTATE_UINT8(mouse_buttons, PS2MouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    s->scancode_set = 2;
+    vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
+    qemu_add_kbd_event_handler(ps2_put_keycode, s);
+    qemu_register_reset(ps2_kbd_reset, s);
+    return s;
+}
+
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
+    qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+    qemu_register_reset(ps2_mouse_reset, s);
+    return s;
+}
diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c
new file mode 100644
index 0000000000..f83fc3f288
--- /dev/null
+++ b/hw/input/stellaris_input.c
@@ -0,0 +1,89 @@
+/*
+ * Gamepad style buttons connected to IRQ/GPIO lines
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+#include "hw/hw.h"
+#include "hw/arm/devices.h"
+#include "ui/console.h"
+
+typedef struct {
+    qemu_irq irq;
+    int keycode;
+    uint8_t pressed;
+} gamepad_button;
+
+typedef struct {
+    gamepad_button *buttons;
+    int num_buttons;
+    int extension;
+} gamepad_state;
+
+static void stellaris_gamepad_put_key(void * opaque, int keycode)
+{
+    gamepad_state *s = (gamepad_state *)opaque;
+    int i;
+    int down;
+
+    if (keycode == 0xe0 && !s->extension) {
+        s->extension = 0x80;
+        return;
+    }
+
+    down = (keycode & 0x80) == 0;
+    keycode = (keycode & 0x7f) | s->extension;
+
+    for (i = 0; i < s->num_buttons; i++) {
+        if (s->buttons[i].keycode == keycode
+                && s->buttons[i].pressed != down) {
+            s->buttons[i].pressed = down;
+            qemu_set_irq(s->buttons[i].irq, down);
+        }
+    }
+
+    s->extension = 0;
+}
+
+static const VMStateDescription vmstate_stellaris_button = {
+    .name = "stellaris_button",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(pressed, gamepad_button),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_stellaris_gamepad = {
+    .name = "stellaris_gamepad",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(extension, gamepad_state),
+        VMSTATE_STRUCT_VARRAY_INT32(buttons, gamepad_state, num_buttons, 0,
+                              vmstate_stellaris_button, gamepad_button),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Returns an array 5 ouput slots.  */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
+{
+    gamepad_state *s;
+    int i;
+
+    s = (gamepad_state *)g_malloc0(sizeof (gamepad_state));
+    s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button));
+    for (i = 0; i < n; i++) {
+        s->buttons[i].irq = irq[i];
+        s->buttons[i].keycode = keycode[i];
+    }
+    s->num_buttons = n;
+    qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
+    vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s);
+}
diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c
new file mode 100644
index 0000000000..34ee1fb3cf
--- /dev/null
+++ b/hw/input/tsc2005.c
@@ -0,0 +1,593 @@
+/*
+ * TI TSC2005 emulator.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+#include "hw/arm/devices.h"
+
+#define TSC_CUT_RESOLUTION(value, p)	((value) >> (16 - (p ? 12 : 10)))
+
+typedef struct {
+    qemu_irq pint;	/* Combination of the nPENIRQ and DAV signals */
+    QEMUTimer *timer;
+    uint16_t model;
+
+    int x, y;
+    int pressure;
+
+    int state, reg, irq, command;
+    uint16_t data, dav;
+
+    int busy;
+    int enabled;
+    int host_mode;
+    int function;
+    int nextfunction;
+    int precision;
+    int nextprecision;
+    int filter;
+    int pin_func;
+    int timing[2];
+    int noise;
+    int reset;
+    int pdst;
+    int pnd0;
+    uint16_t temp_thr[2];
+    uint16_t aux_thr[2];
+
+    int tr[8];
+} TSC2005State;
+
+enum {
+    TSC_MODE_XYZ_SCAN	= 0x0,
+    TSC_MODE_XY_SCAN,
+    TSC_MODE_X,
+    TSC_MODE_Y,
+    TSC_MODE_Z,
+    TSC_MODE_AUX,
+    TSC_MODE_TEMP1,
+    TSC_MODE_TEMP2,
+    TSC_MODE_AUX_SCAN,
+    TSC_MODE_X_TEST,
+    TSC_MODE_Y_TEST,
+    TSC_MODE_TS_TEST,
+    TSC_MODE_RESERVED,
+    TSC_MODE_XX_DRV,
+    TSC_MODE_YY_DRV,
+    TSC_MODE_YX_DRV,
+};
+
+static const uint16_t mode_regs[16] = {
+    0xf000,	/* X, Y, Z scan */
+    0xc000,	/* X, Y scan */
+    0x8000,	/* X */
+    0x4000,	/* Y */
+    0x3000,	/* Z */
+    0x0800,	/* AUX */
+    0x0400,	/* TEMP1 */
+    0x0200,	/* TEMP2 */
+    0x0800,	/* AUX scan */
+    0x0040,	/* X test */
+    0x0020,	/* Y test */
+    0x0080,	/* Short-circuit test */
+    0x0000,	/* Reserved */
+    0x0000,	/* X+, X- drivers */
+    0x0000,	/* Y+, Y- drivers */
+    0x0000,	/* Y+, X- drivers */
+};
+
+#define X_TRANSFORM(s)			\
+    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s)			\
+    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+#define Z1_TRANSFORM(s)			\
+    ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+#define Z2_TRANSFORM(s)			\
+    ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
+#define AUX_VAL				(700 << 4)	/* +/- 3 at 12-bit */
+#define TEMP1_VAL			(1264 << 4)	/* +/- 5 at 12-bit */
+#define TEMP2_VAL			(1531 << 4)	/* +/- 5 at 12-bit */
+
+static uint16_t tsc2005_read(TSC2005State *s, int reg)
+{
+    uint16_t ret;
+
+    switch (reg) {
+    case 0x0:	/* X */
+        s->dav &= ~mode_regs[TSC_MODE_X];
+        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+                (s->noise & 3);
+    case 0x1:	/* Y */
+        s->dav &= ~mode_regs[TSC_MODE_Y];
+        s->noise ++;
+        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+                (s->noise & 3);
+    case 0x2:	/* Z1 */
+        s->dav &= 0xdfff;
+        return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
+                (s->noise & 3);
+    case 0x3:	/* Z2 */
+        s->dav &= 0xefff;
+        return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
+                (s->noise & 3);
+
+    case 0x4:	/* AUX */
+        s->dav &= ~mode_regs[TSC_MODE_AUX];
+        return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
+
+    case 0x5:	/* TEMP1 */
+        s->dav &= ~mode_regs[TSC_MODE_TEMP1];
+        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+                (s->noise & 5);
+    case 0x6:	/* TEMP2 */
+        s->dav &= 0xdfff;
+        s->dav &= ~mode_regs[TSC_MODE_TEMP2];
+        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+                (s->noise & 3);
+
+    case 0x7:	/* Status */
+        ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
+        s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
+                        mode_regs[TSC_MODE_TS_TEST]);
+        s->reset = 1;
+        return ret;
+
+    case 0x8:	/* AUX high treshold */
+        return s->aux_thr[1];
+    case 0x9:	/* AUX low treshold */
+        return s->aux_thr[0];
+
+    case 0xa:	/* TEMP high treshold */
+        return s->temp_thr[1];
+    case 0xb:	/* TEMP low treshold */
+        return s->temp_thr[0];
+
+    case 0xc:	/* CFR0 */
+        return (s->pressure << 15) | ((!s->busy) << 14) |
+                (s->nextprecision << 13) | s->timing[0]; 
+    case 0xd:	/* CFR1 */
+        return s->timing[1];
+    case 0xe:	/* CFR2 */
+        return (s->pin_func << 14) | s->filter;
+
+    case 0xf:	/* Function select status */
+        return s->function >= 0 ? 1 << s->function : 0;
+    }
+
+    /* Never gets here */
+    return 0xffff;
+}
+
+static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
+{
+    switch (reg) {
+    case 0x8:	/* AUX high treshold */
+        s->aux_thr[1] = data;
+        break;
+    case 0x9:	/* AUX low treshold */
+        s->aux_thr[0] = data;
+        break;
+
+    case 0xa:	/* TEMP high treshold */
+        s->temp_thr[1] = data;
+        break;
+    case 0xb:	/* TEMP low treshold */
+        s->temp_thr[0] = data;
+        break;
+
+    case 0xc:	/* CFR0 */
+        s->host_mode = data >> 15;
+        if (s->enabled != !(data & 0x4000)) {
+            s->enabled = !(data & 0x4000);
+            fprintf(stderr, "%s: touchscreen sense %sabled\n",
+                            __FUNCTION__, s->enabled ? "en" : "dis");
+            if (s->busy && !s->enabled)
+                qemu_del_timer(s->timer);
+            s->busy &= s->enabled;
+        }
+        s->nextprecision = (data >> 13) & 1;
+        s->timing[0] = data & 0x1fff;
+        if ((s->timing[0] >> 11) == 3)
+            fprintf(stderr, "%s: illegal conversion clock setting\n",
+                            __FUNCTION__);
+        break;
+    case 0xd:	/* CFR1 */
+        s->timing[1] = data & 0xf07;
+        break;
+    case 0xe:	/* CFR2 */
+        s->pin_func = (data >> 14) & 3;
+        s->filter = data & 0x3fff;
+        break;
+
+    default:
+        fprintf(stderr, "%s: write into read-only register %x\n",
+                        __FUNCTION__, reg);
+    }
+}
+
+/* This handles most of the chip's logic.  */
+static void tsc2005_pin_update(TSC2005State *s)
+{
+    int64_t expires;
+    int pin_state;
+
+    switch (s->pin_func) {
+    case 0:
+        pin_state = !s->pressure && !!s->dav;
+        break;
+    case 1:
+    case 3:
+    default:
+        pin_state = !s->dav;
+        break;
+    case 2:
+        pin_state = !s->pressure;
+    }
+
+    if (pin_state != s->irq) {
+        s->irq = pin_state;
+        qemu_set_irq(s->pint, s->irq);
+    }
+
+    switch (s->nextfunction) {
+    case TSC_MODE_XYZ_SCAN:
+    case TSC_MODE_XY_SCAN:
+        if (!s->host_mode && s->dav)
+            s->enabled = 0;
+        if (!s->pressure)
+            return;
+        /* Fall through */
+    case TSC_MODE_AUX_SCAN:
+        break;
+
+    case TSC_MODE_X:
+    case TSC_MODE_Y:
+    case TSC_MODE_Z:
+        if (!s->pressure)
+            return;
+        /* Fall through */
+    case TSC_MODE_AUX:
+    case TSC_MODE_TEMP1:
+    case TSC_MODE_TEMP2:
+    case TSC_MODE_X_TEST:
+    case TSC_MODE_Y_TEST:
+    case TSC_MODE_TS_TEST:
+        if (s->dav)
+            s->enabled = 0;
+        break;
+
+    case TSC_MODE_RESERVED:
+    case TSC_MODE_XX_DRV:
+    case TSC_MODE_YY_DRV:
+    case TSC_MODE_YX_DRV:
+    default:
+        return;
+    }
+
+    if (!s->enabled || s->busy)
+        return;
+
+    s->busy = 1;
+    s->precision = s->nextprecision;
+    s->function = s->nextfunction;
+    s->pdst = !s->pnd0;	/* Synchronised on internal clock */
+    expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 7);
+    qemu_mod_timer(s->timer, expires);
+}
+
+static void tsc2005_reset(TSC2005State *s)
+{
+    s->state = 0;
+    s->pin_func = 0;
+    s->enabled = 0;
+    s->busy = 0;
+    s->nextprecision = 0;
+    s->nextfunction = 0;
+    s->timing[0] = 0;
+    s->timing[1] = 0;
+    s->irq = 0;
+    s->dav = 0;
+    s->reset = 0;
+    s->pdst = 1;
+    s->pnd0 = 0;
+    s->function = -1;
+    s->temp_thr[0] = 0x000;
+    s->temp_thr[1] = 0xfff;
+    s->aux_thr[0] = 0x000;
+    s->aux_thr[1] = 0xfff;
+
+    tsc2005_pin_update(s);
+}
+
+static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
+{
+    TSC2005State *s = opaque;
+    uint32_t ret = 0;
+
+    switch (s->state ++) {
+    case 0:
+        if (value & 0x80) {
+            /* Command */
+            if (value & (1 << 1))
+                tsc2005_reset(s);
+            else {
+                s->nextfunction = (value >> 3) & 0xf;
+                s->nextprecision = (value >> 2) & 1;
+                if (s->enabled != !(value & 1)) {
+                    s->enabled = !(value & 1);
+                    fprintf(stderr, "%s: touchscreen sense %sabled\n",
+                                    __FUNCTION__, s->enabled ? "en" : "dis");
+                    if (s->busy && !s->enabled)
+                        qemu_del_timer(s->timer);
+                    s->busy &= s->enabled;
+                }
+                tsc2005_pin_update(s);
+            }
+
+            s->state = 0;
+        } else if (value) {
+            /* Data transfer */
+            s->reg = (value >> 3) & 0xf;
+            s->pnd0 = (value >> 1) & 1;
+            s->command = value & 1;
+
+            if (s->command) {
+                /* Read */
+                s->data = tsc2005_read(s, s->reg);
+                tsc2005_pin_update(s);
+            } else
+                s->data = 0;
+        } else
+            s->state = 0;
+        break;
+
+    case 1:
+        if (s->command)
+            ret = (s->data >> 8) & 0xff;
+        else
+            s->data |= value << 8;
+        break;
+
+    case 2:
+        if (s->command)
+            ret = s->data & 0xff;
+        else {
+            s->data |= value;
+            tsc2005_write(s, s->reg, s->data);
+            tsc2005_pin_update(s);
+        }
+
+        s->state = 0;
+        break;
+    }
+
+    return ret;
+}
+
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
+{
+    uint32_t ret = 0;
+
+    len &= ~7;
+    while (len > 0) {
+        len -= 8;
+        ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
+    }
+
+    return ret;
+}
+
+static void tsc2005_timer_tick(void *opaque)
+{
+    TSC2005State *s = opaque;
+
+    /* Timer ticked -- a set of conversions has been finished.  */
+
+    if (!s->busy)
+        return;
+
+    s->busy = 0;
+    s->dav |= mode_regs[s->function];
+    s->function = -1;
+    tsc2005_pin_update(s);
+}
+
+static void tsc2005_touchscreen_event(void *opaque,
+                int x, int y, int z, int buttons_state)
+{
+    TSC2005State *s = opaque;
+    int p = s->pressure;
+
+    if (buttons_state) {
+        s->x = x;
+        s->y = y;
+    }
+    s->pressure = !!buttons_state;
+
+    /*
+     * Note: We would get better responsiveness in the guest by
+     * signaling TS events immediately, but for now we simulate
+     * the first conversion delay for sake of correctness.
+     */
+    if (p != s->pressure)
+        tsc2005_pin_update(s);
+}
+
+static void tsc2005_save(QEMUFile *f, void *opaque)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+    int i;
+
+    qemu_put_be16(f, s->x);
+    qemu_put_be16(f, s->y);
+    qemu_put_byte(f, s->pressure);
+
+    qemu_put_byte(f, s->state);
+    qemu_put_byte(f, s->reg);
+    qemu_put_byte(f, s->command);
+
+    qemu_put_byte(f, s->irq);
+    qemu_put_be16s(f, &s->dav);
+    qemu_put_be16s(f, &s->data);
+
+    qemu_put_timer(f, s->timer);
+    qemu_put_byte(f, s->enabled);
+    qemu_put_byte(f, s->host_mode);
+    qemu_put_byte(f, s->function);
+    qemu_put_byte(f, s->nextfunction);
+    qemu_put_byte(f, s->precision);
+    qemu_put_byte(f, s->nextprecision);
+    qemu_put_be16(f, s->filter);
+    qemu_put_byte(f, s->pin_func);
+    qemu_put_be16(f, s->timing[0]);
+    qemu_put_be16(f, s->timing[1]);
+    qemu_put_be16s(f, &s->temp_thr[0]);
+    qemu_put_be16s(f, &s->temp_thr[1]);
+    qemu_put_be16s(f, &s->aux_thr[0]);
+    qemu_put_be16s(f, &s->aux_thr[1]);
+    qemu_put_be32(f, s->noise);
+    qemu_put_byte(f, s->reset);
+    qemu_put_byte(f, s->pdst);
+    qemu_put_byte(f, s->pnd0);
+
+    for (i = 0; i < 8; i ++)
+        qemu_put_be32(f, s->tr[i]);
+}
+
+static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+    int i;
+
+    s->x = qemu_get_be16(f);
+    s->y = qemu_get_be16(f);
+    s->pressure = qemu_get_byte(f);
+
+    s->state = qemu_get_byte(f);
+    s->reg = qemu_get_byte(f);
+    s->command = qemu_get_byte(f);
+
+    s->irq = qemu_get_byte(f);
+    qemu_get_be16s(f, &s->dav);
+    qemu_get_be16s(f, &s->data);
+
+    qemu_get_timer(f, s->timer);
+    s->enabled = qemu_get_byte(f);
+    s->host_mode = qemu_get_byte(f);
+    s->function = qemu_get_byte(f);
+    s->nextfunction = qemu_get_byte(f);
+    s->precision = qemu_get_byte(f);
+    s->nextprecision = qemu_get_byte(f);
+    s->filter = qemu_get_be16(f);
+    s->pin_func = qemu_get_byte(f);
+    s->timing[0] = qemu_get_be16(f);
+    s->timing[1] = qemu_get_be16(f);
+    qemu_get_be16s(f, &s->temp_thr[0]);
+    qemu_get_be16s(f, &s->temp_thr[1]);
+    qemu_get_be16s(f, &s->aux_thr[0]);
+    qemu_get_be16s(f, &s->aux_thr[1]);
+    s->noise = qemu_get_be32(f);
+    s->reset = qemu_get_byte(f);
+    s->pdst = qemu_get_byte(f);
+    s->pnd0 = qemu_get_byte(f);
+
+    for (i = 0; i < 8; i ++)
+        s->tr[i] = qemu_get_be32(f);
+
+    s->busy = qemu_timer_pending(s->timer);
+    tsc2005_pin_update(s);
+
+    return 0;
+}
+
+void *tsc2005_init(qemu_irq pintdav)
+{
+    TSC2005State *s;
+
+    s = (TSC2005State *)
+            g_malloc0(sizeof(TSC2005State));
+    s->x = 400;
+    s->y = 240;
+    s->pressure = 0;
+    s->precision = s->nextprecision = 0;
+    s->timer = qemu_new_timer_ns(vm_clock, tsc2005_timer_tick, s);
+    s->pint = pintdav;
+    s->model = 0x2005;
+
+    s->tr[0] = 0;
+    s->tr[1] = 1;
+    s->tr[2] = 1;
+    s->tr[3] = 0;
+    s->tr[4] = 1;
+    s->tr[5] = 0;
+    s->tr[6] = 1;
+    s->tr[7] = 0;
+
+    tsc2005_reset(s);
+
+    qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
+                    "QEMU TSC2005-driven Touchscreen");
+
+    qemu_register_reset((void *) tsc2005_reset, s);
+    register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
+
+    return s;
+}
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen.  Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc2005_set_transform(void *opaque, MouseTransformInfo *info)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+
+    /* This version assumes touchscreen X & Y axis are parallel or
+     * perpendicular to LCD's  X & Y axis in some way.  */
+    if (abs(info->a[0]) > abs(info->a[1])) {
+        s->tr[0] = 0;
+        s->tr[1] = -info->a[6] * info->x;
+        s->tr[2] = info->a[0];
+        s->tr[3] = -info->a[2] / info->a[0];
+        s->tr[4] = info->a[6] * info->y;
+        s->tr[5] = 0;
+        s->tr[6] = info->a[4];
+        s->tr[7] = -info->a[5] / info->a[4];
+    } else {
+        s->tr[0] = info->a[6] * info->y;
+        s->tr[1] = 0;
+        s->tr[2] = info->a[1];
+        s->tr[3] = -info->a[2] / info->a[1];
+        s->tr[4] = 0;
+        s->tr[5] = -info->a[6] * info->x;
+        s->tr[6] = info->a[3];
+        s->tr[7] = -info->a[5] / info->a[3];
+    }
+
+    s->tr[0] >>= 11;
+    s->tr[1] >>= 11;
+    s->tr[3] <<= 4;
+    s->tr[4] >>= 11;
+    s->tr[5] >>= 11;
+    s->tr[7] <<= 4;
+}
diff --git a/hw/input/vmmouse.c b/hw/input/vmmouse.c
new file mode 100644
index 0000000000..f4f9c9373d
--- /dev/null
+++ b/hw/input/vmmouse.c
@@ -0,0 +1,301 @@
+/*
+ * QEMU VMMouse emulation
+ *
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/input/ps2.h"
+#include "hw/i386/pc.h"
+#include "hw/qdev.h"
+
+/* debug only vmmouse */
+//#define DEBUG_VMMOUSE
+
+/* VMMouse Commands */
+#define VMMOUSE_GETVERSION	10
+#define VMMOUSE_DATA		39
+#define VMMOUSE_STATUS		40
+#define VMMOUSE_COMMAND		41
+
+#define VMMOUSE_READ_ID			0x45414552
+#define VMMOUSE_DISABLE			0x000000f5
+#define VMMOUSE_REQUEST_RELATIVE	0x4c455252
+#define VMMOUSE_REQUEST_ABSOLUTE	0x53424152
+
+#define VMMOUSE_QUEUE_SIZE	1024
+
+#define VMMOUSE_VERSION		0x3442554a
+
+#ifdef DEBUG_VMMOUSE
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+typedef struct _VMMouseState
+{
+    ISADevice dev;
+    uint32_t queue[VMMOUSE_QUEUE_SIZE];
+    int32_t queue_size;
+    uint16_t nb_queue;
+    uint16_t status;
+    uint8_t absolute;
+    QEMUPutMouseEntry *entry;
+    void *ps2_mouse;
+} VMMouseState;
+
+static uint32_t vmmouse_get_status(VMMouseState *s)
+{
+    DPRINTF("vmmouse_get_status()\n");
+    return (s->status << 16) | s->nb_queue;
+}
+
+static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state)
+{
+    VMMouseState *s = opaque;
+    int buttons = 0;
+
+    if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4))
+        return;
+
+    DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n",
+            x, y, dz, buttons_state);
+
+    if ((buttons_state & MOUSE_EVENT_LBUTTON))
+        buttons |= 0x20;
+    if ((buttons_state & MOUSE_EVENT_RBUTTON))
+        buttons |= 0x10;
+    if ((buttons_state & MOUSE_EVENT_MBUTTON))
+        buttons |= 0x08;
+
+    if (s->absolute) {
+        x <<= 1;
+        y <<= 1;
+    }
+
+    s->queue[s->nb_queue++] = buttons;
+    s->queue[s->nb_queue++] = x;
+    s->queue[s->nb_queue++] = y;
+    s->queue[s->nb_queue++] = dz;
+
+    /* need to still generate PS2 events to notify driver to
+       read from queue */
+    i8042_isa_mouse_fake_event(s->ps2_mouse);
+}
+
+static void vmmouse_remove_handler(VMMouseState *s)
+{
+    if (s->entry) {
+        qemu_remove_mouse_event_handler(s->entry);
+        s->entry = NULL;
+    }
+}
+
+static void vmmouse_update_handler(VMMouseState *s, int absolute)
+{
+    if (s->status != 0) {
+        return;
+    }
+    if (s->absolute != absolute) {
+        s->absolute = absolute;
+        vmmouse_remove_handler(s);
+    }
+    if (s->entry == NULL) {
+        s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event,
+                                                s, s->absolute,
+                                                "vmmouse");
+        qemu_activate_mouse_event_handler(s->entry);
+    }
+}
+
+static void vmmouse_read_id(VMMouseState *s)
+{
+    DPRINTF("vmmouse_read_id()\n");
+
+    if (s->nb_queue == VMMOUSE_QUEUE_SIZE)
+        return;
+
+    s->queue[s->nb_queue++] = VMMOUSE_VERSION;
+    s->status = 0;
+}
+
+static void vmmouse_request_relative(VMMouseState *s)
+{
+    DPRINTF("vmmouse_request_relative()\n");
+    vmmouse_update_handler(s, 0);
+}
+
+static void vmmouse_request_absolute(VMMouseState *s)
+{
+    DPRINTF("vmmouse_request_absolute()\n");
+    vmmouse_update_handler(s, 1);
+}
+
+static void vmmouse_disable(VMMouseState *s)
+{
+    DPRINTF("vmmouse_disable()\n");
+    s->status = 0xffff;
+    vmmouse_remove_handler(s);
+}
+
+static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
+{
+    int i;
+
+    DPRINTF("vmmouse_data(%d)\n", size);
+
+    if (size == 0 || size > 6 || size > s->nb_queue) {
+        printf("vmmouse: driver requested too much data %d\n", size);
+        s->status = 0xffff;
+        vmmouse_remove_handler(s);
+        return;
+    }
+
+    for (i = 0; i < size; i++)
+        data[i] = s->queue[i];
+
+    s->nb_queue -= size;
+    if (s->nb_queue)
+        memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue);
+}
+
+static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr)
+{
+    VMMouseState *s = opaque;
+    uint32_t data[6];
+    uint16_t command;
+
+    vmmouse_get_data(data);
+
+    command = data[2] & 0xFFFF;
+
+    switch (command) {
+    case VMMOUSE_STATUS:
+        data[0] = vmmouse_get_status(s);
+        break;
+    case VMMOUSE_COMMAND:
+        switch (data[1]) {
+        case VMMOUSE_DISABLE:
+            vmmouse_disable(s);
+            break;
+        case VMMOUSE_READ_ID:
+            vmmouse_read_id(s);
+            break;
+        case VMMOUSE_REQUEST_RELATIVE:
+            vmmouse_request_relative(s);
+            break;
+        case VMMOUSE_REQUEST_ABSOLUTE:
+            vmmouse_request_absolute(s);
+            break;
+        default:
+            printf("vmmouse: unknown command %x\n", data[1]);
+            break;
+        }
+        break;
+    case VMMOUSE_DATA:
+        vmmouse_data(s, data, data[1]);
+        break;
+    default:
+        printf("vmmouse: unknown command %x\n", command);
+        break;
+    }
+
+    vmmouse_set_data(data);
+    return data[0];
+}
+
+static int vmmouse_post_load(void *opaque, int version_id)
+{
+    VMMouseState *s = opaque;
+
+    vmmouse_remove_handler(s);
+    vmmouse_update_handler(s, s->absolute);
+    return 0;
+}
+
+static const VMStateDescription vmstate_vmmouse = {
+    .name = "vmmouse",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = vmmouse_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32_EQUAL(queue_size, VMMouseState),
+        VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE),
+        VMSTATE_UINT16(nb_queue, VMMouseState),
+        VMSTATE_UINT16(status, VMMouseState),
+        VMSTATE_UINT8(absolute, VMMouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vmmouse_reset(DeviceState *d)
+{
+    VMMouseState *s = container_of(d, VMMouseState, dev.qdev);
+
+    s->queue_size = VMMOUSE_QUEUE_SIZE;
+
+    vmmouse_disable(s);
+}
+
+static int vmmouse_initfn(ISADevice *dev)
+{
+    VMMouseState *s = DO_UPCAST(VMMouseState, dev, dev);
+
+    DPRINTF("vmmouse_init\n");
+
+    vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s);
+    vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s);
+    vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s);
+
+    return 0;
+}
+
+static Property vmmouse_properties[] = {
+    DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmmouse_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = vmmouse_initfn;
+    dc->no_user = 1;
+    dc->reset = vmmouse_reset;
+    dc->vmsd = &vmstate_vmmouse;
+    dc->props = vmmouse_properties;
+}
+
+static const TypeInfo vmmouse_info = {
+    .name          = "vmmouse",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(VMMouseState),
+    .class_init    = vmmouse_class_initfn,
+};
+
+static void vmmouse_register_types(void)
+{
+    type_register_static(&vmmouse_info);
+}
+
+type_init(vmmouse_register_types)