aboutsummaryrefslogtreecommitdiff
path: root/hw/input/goldfish_events.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/input/goldfish_events.c')
-rw-r--r--hw/input/goldfish_events.c757
1 files changed, 757 insertions, 0 deletions
diff --git a/hw/input/goldfish_events.c b/hw/input/goldfish_events.c
new file mode 100644
index 0000000000..8a6f5aa7ff
--- /dev/null
+++ b/hw/input/goldfish_events.c
@@ -0,0 +1,757 @@
+/*
+ * Goldfish 'events' device model
+ *
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * Copyright (c) 2014 Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include "hw/sysbus.h"
+#include "ui/input.h"
+#include "ui/console.h"
+#include "hw/input/linux_keycodes.h"
+
+/* Multitouch specific code is defined out via EVDEV_MULTITOUCH currently,
+ * because upstream has no multitouch related APIs.
+ */
+/* #define EVDEV_MULTITOUCH */
+
+#define MAX_EVENTS (256 * 4)
+
+/* Event types (as per Linux input event layer) */
+#define EV_SYN 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_SW 0x05
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+/* Absolute axes */
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_MT_SLOT 0x2f /* MT slot being modified */
+#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
+#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
+#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
+#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
+#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
+#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
+#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
+#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
+#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
+#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
+#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
+#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
+#define ABS_MAX 0x3f
+
+/* Relative axes */
+#define REL_X 0x00
+#define REL_Y 0x01
+
+#define BTN_TOUCH 0x14a
+#define BTN_MOUSE 0x110
+
+
+enum {
+ REG_READ = 0x00,
+ REG_SET_PAGE = 0x00,
+ REG_LEN = 0x04,
+ REG_DATA = 0x08,
+
+ PAGE_NAME = 0x00000,
+ PAGE_EVBITS = 0x10000,
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
+};
+
+/* These corresponds to the state of the driver.
+ * Unfortunately, we have to buffer events coming
+ * from the UI, since the kernel driver is not
+ * capable of receiving them until XXXXXX
+ */
+enum {
+ STATE_INIT = 0, /* The device is initialized */
+ STATE_BUFFERED, /* Events have been buffered, but no IRQ raised yet */
+ STATE_LIVE /* Events can be sent directly to the kernel */
+};
+
+/* NOTE: The ev_bits arrays are used to indicate to the kernel
+ * which events can be sent by the emulated hardware.
+ */
+
+#define TYPE_GOLDFISHEVDEV "goldfish-events"
+#define GOLDFISHEVDEV(obj) \
+ OBJECT_CHECK(GoldfishEvDevState, (obj), TYPE_GOLDFISHEVDEV)
+
+typedef struct GoldfishEvDevState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ /* Device properties (TODO: actually make these props) */
+ bool have_dpad;
+ bool have_trackball;
+ bool have_camera;
+ bool have_keyboard;
+ bool have_keyboard_lid;
+ bool have_touch;
+ bool have_multitouch;
+
+ /* Actual device state */
+ int32_t page;
+ uint32_t events[MAX_EVENTS];
+ uint32_t first;
+ uint32_t last;
+ uint32_t state;
+
+ uint32_t modifier_state;
+
+ /* All data below here is set up at realize and not modified thereafter */
+
+ const char *name;
+
+ struct {
+ size_t len;
+ uint8_t *bits;
+ } ev_bits[EV_MAX + 1];
+
+ int32_t *abs_info;
+ size_t abs_info_count;
+} GoldfishEvDevState;
+
+/* Bitfield meanings for modifier_state. */
+#define MODSTATE_SHIFT (1 << 0)
+#define MODSTATE_CTRL (1 << 1)
+#define MODSTATE_ALT (1 << 2)
+#define MODSTATE_MASK (MODSTATE_SHIFT | MODSTATE_CTRL | MODSTATE_ALT)
+
+/* An entry in the array of ABS_XXX values */
+typedef struct ABSEntry {
+ /* Minimum ABS_XXX value. */
+ uint32_t min;
+ /* Maximum ABS_XXX value. */
+ uint32_t max;
+ /* 'fuzz;, and 'flat' ABS_XXX values are always zero here. */
+ uint32_t fuzz;
+ uint32_t flat;
+} ABSEntry;
+
+static const VMStateDescription vmstate_gf_evdev = {
+ .name = "goldfish-events",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(page, GoldfishEvDevState),
+ VMSTATE_UINT32_ARRAY(events, GoldfishEvDevState, MAX_EVENTS),
+ VMSTATE_UINT32(first, GoldfishEvDevState),
+ VMSTATE_UINT32(last, GoldfishEvDevState),
+ VMSTATE_UINT32(state, GoldfishEvDevState),
+ VMSTATE_UINT32(modifier_state, GoldfishEvDevState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void enqueue_event(GoldfishEvDevState *s,
+ unsigned int type, unsigned int code, int value)
+{
+ int enqueued = s->last - s->first;
+
+ if (enqueued < 0) {
+ enqueued += MAX_EVENTS;
+ }
+
+ if (enqueued + 3 > MAX_EVENTS) {
+ fprintf(stderr, "##KBD: Full queue, lose event\n");
+ return;
+ }
+
+ if (s->first == s->last) {
+ if (s->state == STATE_LIVE) {
+ qemu_irq_raise(s->irq);
+ } else {
+ s->state = STATE_BUFFERED;
+ }
+ }
+
+ s->events[s->last] = type;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+ s->events[s->last] = code;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+ s->events[s->last] = value;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+}
+
+static unsigned dequeue_event(GoldfishEvDevState *s)
+{
+ unsigned n;
+
+ if (s->first == s->last) {
+ return 0;
+ }
+
+ n = s->events[s->first];
+
+ s->first = (s->first + 1) & (MAX_EVENTS - 1);
+
+ if (s->first == s->last) {
+ qemu_irq_lower(s->irq);
+ }
+#ifdef TARGET_I386
+ /*
+ * Adding the logic to handle edge-triggered interrupts for x86
+ * because the exisiting goldfish events device basically provides
+ * level-trigger interrupts only.
+ *
+ * Logic: When an event (including the type/code/value) is fetched
+ * by the driver, if there is still another event in the event
+ * queue, the goldfish event device will re-assert the IRQ so that
+ * the driver can be notified to fetch the event again.
+ */
+ else if (((s->first + 2) & (MAX_EVENTS - 1)) < s->last ||
+ (s->first & (MAX_EVENTS - 1)) > s->last) {
+ /* if there still is an event */
+ qemu_irq_lower(s->irq);
+ qemu_irq_raise(s->irq);
+ }
+#endif
+ return n;
+}
+
+static int get_page_len(GoldfishEvDevState *s)
+{
+ int page = s->page;
+ if (page == PAGE_NAME) {
+ const char *name = s->name;
+ return strlen(name);
+ }
+ if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX) {
+ return s->ev_bits[page - PAGE_EVBITS].len;
+ }
+ if (page == PAGE_ABSDATA) {
+ return s->abs_info_count * sizeof(s->abs_info[0]);
+ }
+ return 0;
+}
+
+static int get_page_data(GoldfishEvDevState *s, int offset)
+{
+ int page_len = get_page_len(s);
+ int page = s->page;
+ if (offset > page_len) {
+ return 0;
+ }
+ if (page == PAGE_NAME) {
+ const char *name = s->name;
+ return name[offset];
+ }
+ if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX) {
+ return s->ev_bits[page - PAGE_EVBITS].bits[offset];
+ }
+ if (page == PAGE_ABSDATA) {
+ return s->abs_info[offset / sizeof(s->abs_info[0])];
+ }
+ return 0;
+}
+
+static uint64_t events_read(void *opaque, hwaddr offset, unsigned size)
+{
+ GoldfishEvDevState *s = (GoldfishEvDevState *)opaque;
+
+ /* This gross hack below is used to ensure that we
+ * only raise the IRQ when the kernel driver is
+ * properly ready! If done before this, the driver
+ * becomes confused and ignores all input events
+ * as soon as one was buffered!
+ */
+ if (offset == REG_LEN && s->page == PAGE_ABSDATA) {
+ if (s->state == STATE_BUFFERED) {
+ qemu_irq_raise(s->irq);
+ }
+ s->state = STATE_LIVE;
+ }
+
+ switch (offset) {
+ case REG_READ:
+ return dequeue_event(s);
+ case REG_LEN:
+ return get_page_len(s);
+ default:
+ if (offset >= REG_DATA) {
+ return get_page_data(s, offset - REG_DATA);
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "goldfish events device read: bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void events_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ GoldfishEvDevState *s = (GoldfishEvDevState *)opaque;
+ switch (offset) {
+ case REG_SET_PAGE:
+ s->page = val;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "goldfish events device write: bad offset %x\n",
+ (int)offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps gf_evdev_ops = {
+ .read = events_read,
+ .write = events_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void gf_evdev_put_mouse(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ GoldfishEvDevState *s = (GoldfishEvDevState *)opaque;
+
+ /* Note that unlike the "classic" Android emulator, we don't
+ * have the "dz == 0 for touchscreen, == 1 for trackball"
+ * distinction.
+ */
+#ifdef EVDEV_MULTITOUCH
+ if (s->have_multitouch) {
+ /* Convert mouse event into multi-touch event */
+ multitouch_update_pointer(MTES_MOUSE, 0, dx, dy,
+ (buttons_state & 1) ? 0x81 : 0);
+ return;
+ }
+#endif
+ if (s->have_touch) {
+ enqueue_event(s, EV_ABS, ABS_X, dx);
+ enqueue_event(s, EV_ABS, ABS_Y, dy);
+ enqueue_event(s, EV_ABS, ABS_Z, dz);
+ enqueue_event(s, EV_KEY, BTN_TOUCH, buttons_state & 1);
+ enqueue_event(s, EV_SYN, 0, 0);
+ return;
+ }
+ if (s->have_trackball) {
+ enqueue_event(s, EV_REL, REL_X, dx);
+ enqueue_event(s, EV_REL, REL_Y, dy);
+ enqueue_event(s, EV_SYN, 0, 0);
+ return;
+ }
+}
+
+/* set bits [bitl..bith] in the ev_bits[type] array
+ */
+static void
+events_set_bits(GoldfishEvDevState *s, int type, int bitl, int bith)
+{
+ uint8_t *bits;
+ uint8_t maskl, maskh;
+ int il, ih;
+ il = bitl / 8;
+ ih = bith / 8;
+ if (ih >= s->ev_bits[type].len) {
+ bits = g_malloc0(ih + 1);
+ if (bits == NULL) {
+ return;
+ }
+ memcpy(bits, s->ev_bits[type].bits, s->ev_bits[type].len);
+ g_free(s->ev_bits[type].bits);
+ s->ev_bits[type].bits = bits;
+ s->ev_bits[type].len = ih + 1;
+ } else {
+ bits = s->ev_bits[type].bits;
+ }
+ maskl = 0xffU << (bitl & 7);
+ maskh = 0xffU >> (7 - (bith & 7));
+ if (il >= ih) {
+ maskh &= maskl;
+ } else {
+ bits[il] |= maskl;
+ while (++il < ih) {
+ bits[il] = 0xff;
+ }
+ }
+ bits[ih] |= maskh;
+}
+
+static void
+events_set_bit(GoldfishEvDevState *s, int type, int bit)
+{
+ events_set_bits(s, type, bit, bit);
+}
+
+static void
+events_clr_bit(GoldfishEvDevState *s, int type, int bit)
+{
+ int ii = bit / 8;
+ if (ii < s->ev_bits[type].len) {
+ uint8_t *bits = s->ev_bits[type].bits;
+ uint8_t mask = 0x01U << (bit & 7);
+ bits[ii] &= ~mask;
+ }
+}
+
+/* Keycode mappings to match the classic Android emulator as documented in
+ * http://developer.android.com/tools/help/emulator.html
+ */
+static const uint16_t hardbutton_map[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_HOME] = LINUX_KEY_HOME,
+ [Q_KEY_CODE_F2] = LINUX_KEY_SOFT1, /* Android menu key */
+ [Q_KEY_CODE_PGUP] = LINUX_KEY_SOFT1,
+ [Q_KEY_CODE_PGDN] = LINUX_KEY_SOFT2,
+ [Q_KEY_CODE_ESC] = LINUX_KEY_BACK,
+ [Q_KEY_CODE_F3] = LINUX_KEY_SEND, /* dial */
+ [Q_KEY_CODE_F4] = LINUX_KEY_END, /* end call */
+ [Q_KEY_CODE_F5] = LINUX_KEY_SEARCH,
+ [Q_KEY_CODE_F7] = LINUX_KEY_POWER,
+ [Q_KEY_CODE_KP_ADD] = LINUX_KEY_VOLUMEUP,
+ [Q_KEY_CODE_KP_SUBTRACT] = LINUX_KEY_VOLUMEDOWN,
+};
+
+static const uint16_t hardbutton_shift_map[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_F2] = LINUX_KEY_SOFT2,
+};
+
+static const uint16_t hardbutton_control_map[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_F5] = LINUX_KEY_VOLUMEUP,
+ [Q_KEY_CODE_F6] = LINUX_KEY_VOLUMEDOWN,
+ /* ctrl-kp5, ctrl-f3 -> LINUX_KEY_CAMERA (if have_camera) */
+};
+
+static const int dpad_map[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_KP_4] = LINUX_KEY_LEFT,
+ [Q_KEY_CODE_KP_6] = LINUX_KEY_RIGHT,
+ [Q_KEY_CODE_KP_8] = LINUX_KEY_UP,
+ [Q_KEY_CODE_KP_2] = LINUX_KEY_DOWN,
+ [Q_KEY_CODE_KP_5] = LINUX_KEY_CENTER,
+};
+
+static void gf_evdev_handle_keyevent(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ /* Handle a key event. Minimal implementation which just handles
+ * the required hardware buttons and the dpad.
+ * This should be extended to also honour have_keyboard, and
+ * possibly also the control keys which affect the emulator itself.
+ */
+
+ GoldfishEvDevState *s = GOLDFISHEVDEV(dev);
+ int qcode;
+ int lkey = 0;
+ int mod;
+
+ assert(evt->kind == INPUT_EVENT_KIND_KEY);
+
+ qcode = qemu_input_key_value_to_qcode(evt->key->key);
+
+ /* Keep our modifier state up to date */
+ switch (qcode) {
+ case Q_KEY_CODE_SHIFT:
+ case Q_KEY_CODE_SHIFT_R:
+ mod = MODSTATE_SHIFT;
+ break;
+ case Q_KEY_CODE_ALT:
+ case Q_KEY_CODE_ALT_R:
+ mod = MODSTATE_ALT;
+ break;
+ case Q_KEY_CODE_CTRL:
+ case Q_KEY_CODE_CTRL_R:
+ mod = MODSTATE_CTRL;
+ break;
+ default:
+ mod = 0;
+ break;
+ }
+
+ if (mod) {
+ if (evt->key->down) {
+ s->modifier_state |= mod;
+ } else {
+ s->modifier_state &= ~mod;
+ }
+ }
+
+ if (s->modifier_state & MODSTATE_ALT) {
+ /* No alt-keys defined currently */
+ } else if (s->modifier_state & MODSTATE_CTRL) {
+ lkey = hardbutton_control_map[qcode];
+ } else if (s->modifier_state & MODSTATE_SHIFT) {
+ lkey = hardbutton_shift_map[qcode];
+ } else {
+ lkey = hardbutton_map[qcode];
+ }
+
+ if (!lkey && s->have_dpad && s->modifier_state == 0) {
+ lkey = dpad_map[qcode];
+ }
+
+ if (lkey) {
+ enqueue_event(s, EV_KEY, lkey, evt->key->down);
+ }
+}
+
+static QemuInputHandler gf_evdev_key_input_handler = {
+ .name = "goldfish event device key handler",
+ .mask = INPUT_EVENT_MASK_KEY,
+ .event = gf_evdev_handle_keyevent,
+};
+
+static void gf_evdev_init(Object *obj)
+{
+ GoldfishEvDevState *s = GOLDFISHEVDEV(obj);
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, obj, &gf_evdev_ops, s,
+ "goldfish-events", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+
+ qemu_input_handler_register(dev, &gf_evdev_key_input_handler);
+ qemu_add_mouse_event_handler(gf_evdev_put_mouse, s, 1, "goldfish-events");
+}
+
+static void gf_evdev_realize(DeviceState *dev, Error **errp)
+{
+ GoldfishEvDevState *s = GOLDFISHEVDEV(dev);
+
+ /* now set the events capability bits depending on hardware configuration */
+ /* apparently, the EV_SYN array is used to indicate which other
+ * event classes to consider.
+ */
+
+ /* XXX PMM properties ? */
+ s->name = "qwerty2";
+
+ if (s->have_multitouch) {
+ s->have_touch = true;
+ }
+
+ /* configure EV_KEY array
+ *
+ * All Android devices must have the following keys:
+ * KEY_HOME, KEY_BACK, KEY_SEND (Call), KEY_END (EndCall),
+ * KEY_SOFT1 (Menu), VOLUME_UP, VOLUME_DOWN
+ *
+ * Note that previous models also had a KEY_SOFT2,
+ * and a KEY_POWER which we still support here.
+ *
+ * Newer models have a KEY_SEARCH key, which we always
+ * enable here.
+ *
+ * A Dpad will send: KEY_DOWN / UP / LEFT / RIGHT / CENTER
+ *
+ * The KEY_CAMERA button isn't very useful if there is no camera.
+ *
+ * BTN_MOUSE is sent when the trackball is pressed
+ * BTN_TOUCH is sent when the touchscreen is pressed
+ */
+ events_set_bit(s, EV_SYN, EV_KEY);
+
+ events_set_bit(s, EV_KEY, LINUX_KEY_HOME);
+ events_set_bit(s, EV_KEY, LINUX_KEY_BACK);
+ events_set_bit(s, EV_KEY, LINUX_KEY_SEND);
+ events_set_bit(s, EV_KEY, LINUX_KEY_END);
+ events_set_bit(s, EV_KEY, LINUX_KEY_SOFT1);
+ events_set_bit(s, EV_KEY, LINUX_KEY_VOLUMEUP);
+ events_set_bit(s, EV_KEY, LINUX_KEY_VOLUMEDOWN);
+ events_set_bit(s, EV_KEY, LINUX_KEY_SOFT2);
+ events_set_bit(s, EV_KEY, LINUX_KEY_POWER);
+ events_set_bit(s, EV_KEY, LINUX_KEY_SEARCH);
+
+ if (s->have_dpad) {
+ events_set_bit(s, EV_KEY, LINUX_KEY_DOWN);
+ events_set_bit(s, EV_KEY, LINUX_KEY_UP);
+ events_set_bit(s, EV_KEY, LINUX_KEY_LEFT);
+ events_set_bit(s, EV_KEY, LINUX_KEY_RIGHT);
+ events_set_bit(s, EV_KEY, LINUX_KEY_CENTER);
+ }
+
+ if (s->have_trackball) {
+ events_set_bit(s, EV_KEY, BTN_MOUSE);
+ }
+ if (s->have_touch) {
+ events_set_bit(s, EV_KEY, BTN_TOUCH);
+ }
+
+ if (s->have_camera) {
+ /* Camera emulation is enabled. */
+ events_set_bit(s, EV_KEY, LINUX_KEY_CAMERA);
+ }
+
+ if (s->have_keyboard) {
+ /* since we want to implement Unicode reverse-mapping
+ * allow any kind of key, even those not available on
+ * the skin.
+ *
+ * the previous code did set the [1..0x1ff] range, but
+ * we don't want to enable certain bits in the middle
+ * of the range that are registered for mouse/trackball/joystick
+ * events.
+ *
+ * see "linux_keycodes.h" for the list of events codes.
+ */
+ events_set_bits(s, EV_KEY, 1, 0xff);
+ events_set_bits(s, EV_KEY, 0x160, 0x1ff);
+
+ /* If there is a keyboard, but no DPad, we need to clear the
+ * corresponding bits. Doing this is simpler than trying to exclude
+ * the DPad values from the ranges above.
+ */
+ if (!s->have_dpad) {
+ events_clr_bit(s, EV_KEY, LINUX_KEY_DOWN);
+ events_clr_bit(s, EV_KEY, LINUX_KEY_UP);
+ events_clr_bit(s, EV_KEY, LINUX_KEY_LEFT);
+ events_clr_bit(s, EV_KEY, LINUX_KEY_RIGHT);
+ events_clr_bit(s, EV_KEY, LINUX_KEY_CENTER);
+ }
+ }
+
+ /* configure EV_REL array
+ *
+ * EV_REL events are sent when the trackball is moved
+ */
+ if (s->have_trackball) {
+ events_set_bit(s, EV_SYN, EV_REL);
+ events_set_bits(s, EV_REL, REL_X, REL_Y);
+ }
+
+ /* configure EV_ABS array.
+ *
+ * EV_ABS events are sent when the touchscreen is pressed
+ */
+ if (s->have_touch) {
+ ABSEntry *abs_values;
+
+ events_set_bit(s, EV_SYN, EV_ABS);
+ events_set_bits(s, EV_ABS, ABS_X, ABS_Z);
+ /* Allocate the absinfo to report the min/max bounds for each
+ * absolute dimension. The array must contain 3, or ABS_MAX tuples
+ * of (min,max,fuzz,flat) 32-bit values.
+ *
+ * min and max are the bounds
+ * fuzz corresponds to the device's fuziness, we set it to 0
+ * flat corresponds to the flat position for JOEYDEV devices,
+ * we also set it to 0.
+ *
+ * There is no need to save/restore this array in a snapshot
+ * since the values only depend on the hardware configuration.
+ */
+ s->abs_info_count = s->have_multitouch ? ABS_MAX * 4 : 3 * 4;
+ s->abs_info = g_new0(int32_t, s->abs_info_count);
+ abs_values = (ABSEntry *)s->abs_info;
+
+ /* QEMU provides absolute coordinates in the [0,0x7fff] range
+ * regardless of the display resolution.
+ */
+ abs_values[ABS_X].max = 0x7fff;
+ abs_values[ABS_Y].max = 0x7fff;
+ abs_values[ABS_Z].max = 1;
+
+#ifdef EVDEV_MULTITOUCH
+ if (s->have_multitouch) {
+ /*
+ * Setup multitouch.
+ */
+ events_set_bit(s, EV_ABS, ABS_MT_SLOT);
+ events_set_bit(s, EV_ABS, ABS_MT_POSITION_X);
+ events_set_bit(s, EV_ABS, ABS_MT_POSITION_Y);
+ events_set_bit(s, EV_ABS, ABS_MT_TRACKING_ID);
+ events_set_bit(s, EV_ABS, ABS_MT_TOUCH_MAJOR);
+ events_set_bit(s, EV_ABS, ABS_MT_PRESSURE);
+
+ abs_values[ABS_MT_SLOT].max = multitouch_get_max_slot();
+ abs_values[ABS_MT_TRACKING_ID].max
+ = abs_values[ABS_MT_SLOT].max + 1;
+ abs_values[ABS_MT_POSITION_X].max = abs_values[ABS_X].max;
+ abs_values[ABS_MT_POSITION_Y].max = abs_values[ABS_Y].max;
+ /* TODO : make next 2 less random */
+ abs_values[ABS_MT_TOUCH_MAJOR].max = 0x7fffffff;
+ abs_values[ABS_MT_PRESSURE].max = 0x100;
+ }
+#endif
+ }
+
+ /* configure EV_SW array
+ *
+ * EV_SW events are sent to indicate that the keyboard lid
+ * was closed or opened (done when we switch layouts through
+ * KP-7 or KP-9).
+ *
+ * We only support this when hw.keyboard.lid is true.
+ */
+ if (s->have_keyboard && s->have_keyboard_lid) {
+ events_set_bit(s, EV_SYN, EV_SW);
+ events_set_bit(s, EV_SW, 0);
+ }
+}
+
+static void gf_evdev_reset(DeviceState *dev)
+{
+ GoldfishEvDevState *s = GOLDFISHEVDEV(dev);
+
+ s->state = STATE_INIT;
+ s->first = 0;
+ s->last = 0;
+ s->state = 0;
+}
+
+static Property gf_evdev_props[] = {
+ DEFINE_PROP_BOOL("have-dpad", GoldfishEvDevState, have_dpad, false),
+ DEFINE_PROP_BOOL("have-trackball", GoldfishEvDevState,
+ have_trackball, false),
+ DEFINE_PROP_BOOL("have-camera", GoldfishEvDevState, have_camera, false),
+ DEFINE_PROP_BOOL("have-keyboard", GoldfishEvDevState, have_keyboard, false),
+ DEFINE_PROP_BOOL("have-lidswitch", GoldfishEvDevState,
+ have_keyboard_lid, false),
+ DEFINE_PROP_BOOL("have-touch", GoldfishEvDevState,
+ have_touch, true),
+ DEFINE_PROP_BOOL("have-multitouch", GoldfishEvDevState,
+ have_multitouch, false),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void gf_evdev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = gf_evdev_realize;
+ dc->reset = gf_evdev_reset;
+ dc->props = gf_evdev_props;
+ dc->vmsd = &vmstate_gf_evdev;
+}
+
+static const TypeInfo gf_evdev_info = {
+ .name = TYPE_GOLDFISHEVDEV,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GoldfishEvDevState),
+ .instance_init = gf_evdev_init,
+ .class_init = gf_evdev_class_init,
+};
+
+static void gf_evdev_register_types(void)
+{
+ type_register_static(&gf_evdev_info);
+}
+
+type_init(gf_evdev_register_types)