/* Copyright (C) 2007-2008 The Android Open Source Project
**
** 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 "qemu_file.h"
#include "android/hw-events.h"
#include "android/charmap.h"
#include "android/globals.h" /* for android_hw */
#include "android/multitouch-screen.h"
#include "irq.h"
#include "user-events.h"
#include "console.h"
#define MAX_EVENTS 256*4
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.
*/
typedef struct
{
uint32_t base;
qemu_irq irq;
int pending;
int page;
unsigned events[MAX_EVENTS];
unsigned first;
unsigned last;
unsigned state;
const char *name;
struct {
size_t len;
uint8_t *bits;
} ev_bits[EV_MAX + 1];
int32_t *abs_info;
size_t abs_info_count;
} events_state;
/* 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;
/* modify this each time you change the events_device structure. you
* will also need to upadte events_state_load and events_state_save
*/
#define EVENTS_STATE_SAVE_VERSION 2
#undef QFIELD_STRUCT
#define QFIELD_STRUCT events_state
QFIELD_BEGIN(events_state_fields)
QFIELD_INT32(pending),
QFIELD_INT32(page),
QFIELD_BUFFER(events),
QFIELD_INT32(first),
QFIELD_INT32(last),
QFIELD_INT32(state),
QFIELD_END
static void events_state_save(QEMUFile* f, void* opaque)
{
events_state* s = opaque;
qemu_put_struct(f, events_state_fields, s);
}
static int events_state_load(QEMUFile* f, void* opaque, int version_id)
{
events_state* s = opaque;
if (version_id != EVENTS_STATE_SAVE_VERSION)
return -1;
return qemu_get_struct(f, events_state_fields, s);
}
static void enqueue_event(events_state *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;
}
}
//fprintf(stderr, "##KBD: type=%d code=%d value=%d\n", type, code, value);
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(events_state *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(events_state *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(events_state *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 uint32_t events_read(void *x, target_phys_addr_t off)
{
events_state *s = (events_state *) x;
int offset = off; // - s->base;
/* 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;
}
if (offset == REG_READ)
return dequeue_event(s);
else if (offset == REG_LEN)
return get_page_len(s);
else if (offset >= REG_DATA)
return get_page_data(s, offset - REG_DATA);
return 0; // this shouldn't happen, if the driver does the right thing
}
static void events_write(void *x, target_phys_addr_t off, uint32_t val)
{
events_state *s = (events_state *) x;
int offset = off; // - s->base;
if (offset == REG_SET_PAGE)
s->page = val;
}
static CPUReadMemoryFunc *events_readfn[] = {
events_read,
events_read,
events_read
};
static CPUWriteMemoryFunc *events_writefn[] = {
events_write,
events_write,
events_write
};
static void events_put_keycode(void *x, int keycode)
{
events_state *s = (events_state *) x;
enqueue_event(s, EV_KEY, keycode&0x1ff, (keycode&0x200) ? 1 : 0);
}
static void events_put_mouse(void *opaque, int dx, int dy, int dz, int buttons_state)
{
events_state *s = (events_state *) opaque;
/* in the Android emulator, we use dz == 0 for touchscreen events,
* and dz == 1 for trackball events. See the kbd_mouse_event calls
* in android/skin/trackball.c and android/skin/window.c
*/
if (dz == 0) {
if (androidHwConfig_isScreenMultiTouch(android_hw)) {
/* Convert mouse event into multi-touch event */
multitouch_update_pointer(MTES_MOUSE, 0, dx, dy,
(buttons_state & 1) ? 0x81 : 0);
} else if (androidHwConfig_isScreenTouch(android_hw)) {
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);
}
} else {
enqueue_event(s, EV_REL, REL_X, dx);
enqueue_event(s, EV_REL, REL_Y, dy);
enqueue_event(s, EV_SYN, 0, 0);
}
}
static void events_put_generic(void* opaque, int type, int code, int value)
{
events_state *s = (events_state *) opaque;
enqueue_event(s, type, code, value);
}
/* set bits [bitl..bith] in the ev_bits[type] array
*/
static void
events_set_bits(events_state *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 = qemu_mallocz(ih + 1);
if (bits == NULL)
return;
memcpy(bits, s->ev_bits[type].bits, s->ev_bits[type].len);
qemu_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(events_state* s, int type, int bit)
{
events_set_bits(s, type, bit, bit);
}
static void
events_clr_bit(events_state* 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;
}
}
void events_dev_init(uint32_t base, qemu_irq irq)
{
events_state *s;
int iomemtype;
AndroidHwConfig* config = android_hw;
s = (events_state *) qemu_mallocz(sizeof(events_state));
/* 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.
*/
/* 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, KEY_HOME);
events_set_bit(s, EV_KEY, KEY_BACK);
events_set_bit(s, EV_KEY, KEY_SEND);
events_set_bit(s, EV_KEY, KEY_END);
events_set_bit(s, EV_KEY, KEY_SOFT1);
events_set_bit(s, EV_KEY, KEY_VOLUMEUP);
events_set_bit(s, EV_KEY, KEY_VOLUMEDOWN);
events_set_bit(s, EV_KEY, KEY_SOFT2);
events_set_bit(s, EV_KEY, KEY_POWER);
events_set_bit(s, EV_KEY, KEY_SEARCH);
if (config->hw_dPad) {
events_set_bit(s, EV_KEY, KEY_DOWN);
events_set_bit(s, EV_KEY, KEY_UP);
events_set_bit(s, EV_KEY, KEY_LEFT);
events_set_bit(s, EV_KEY, KEY_RIGHT);
events_set_bit(s, EV_KEY, KEY_CENTER);
}
if (config->hw_trackBall) {
events_set_bit(s, EV_KEY, BTN_MOUSE);
}
if (androidHwConfig_isScreenTouch(config)) {
events_set_bit(s, EV_KEY, BTN_TOUCH);
}
if (strcmp(config->hw_camera_back, "none") ||
strcmp(config->hw_camera_front, "none")) {
/* Camera emulation is enabled. */
events_set_bit(s, EV_KEY, KEY_CAMERA);
}
if (config->hw_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 (!config->hw_dPad) {
events_clr_bit(s, EV_KEY, KEY_DOWN);
events_clr_bit(s, EV_KEY, KEY_UP);
events_clr_bit(s, EV_KEY, KEY_LEFT);
events_clr_bit(s, EV_KEY, KEY_RIGHT);
events_clr_bit(s, EV_KEY, KEY_CENTER);
}
}
/* configure EV_REL array
*
* EV_REL events are sent when the trackball is moved
*/
if (config->hw_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 (!androidHwConfig_isScreenNoTouch(config)) {
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 = androidHwConfig_isScreenMultiTouch(config) ? ABS_MAX * 4 : 3 * 4;
const int abs_size = sizeof(uint32_t) * s->abs_info_count;
s->abs_info = malloc(abs_size);
memset(s->abs_info, 0, abs_size);
abs_values = (ABSEntry*)s->abs_info;
abs_values[ABS_X].max = config->hw_lcd_width-1;
abs_values[ABS_Y].max = config->hw_lcd_height-1;
abs_values[ABS_Z].max = 1;
if (androidHwConfig_isScreenMultiTouch(config)) {
/*
* 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;
abs_values[ABS_MT_TOUCH_MAJOR].max = 0x7fffffff; // TODO: Make it less random
abs_values[ABS_MT_PRESSURE].max = 0x100; // TODO: Make it less random
}
}
/* 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 (config->hw_keyboard && config->hw_keyboard_lid) {
events_set_bit(s, EV_SYN, EV_SW);
events_set_bit(s, EV_SW, 0);
}
iomemtype = cpu_register_io_memory(events_readfn, events_writefn, s);
cpu_register_physical_memory(base, 0xfff, iomemtype);
qemu_add_kbd_event_handler(events_put_keycode, s);
qemu_add_mouse_event_handler(events_put_mouse, s, 1, "goldfish-events");
s->base = base;
s->irq = irq;
s->first = 0;
s->last = 0;
s->state = STATE_INIT;
s->name = qemu_strdup(config->hw_keyboard_charmap);
/* This function migh fire buffered events to the device, so
* ensure that it is called after initialization is complete
*/
user_event_register_generic(s, events_put_generic);
register_savevm( "events_state", 0, EVENTS_STATE_SAVE_VERSION,
events_state_save, events_state_load, s );
}