diff options
Diffstat (limited to 'hw/input/goldfish_events.c')
-rw-r--r-- | hw/input/goldfish_events.c | 757 |
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) |