msmouse: fix misc issues, switch to new input interface.

input: add trace events for full queues.
 input-linux: better capability checks and event handling.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJXhKhUAAoJEEy22O7T6HE46/IQAJFITOA8hb50O9HWADnPxLYN
 3BzNSUys018Slv8ue886br6utKj054xclL7SKBBeuM42KuWsDS89l6Pdz0c4Mqll
 xCb7IYBFFmHVQMzj47fis4M2Zg49BNtr/0EXueVhODFaWe7OGgUCbyGW0pj0nL64
 ZRMedMfP2zc6O1R9PdU7SErhurbpkyrFqNIBgkKfz79A2kM5DSBrJHfGUolB07xJ
 LYq64T5ZHQpSBXmB/RBqv++2HtODDqJPBc9pvJ/1ZRLc8vhuRpOBjL9rvsdFSohq
 JQl9XRHXuEsEDnDSkw/EQqwRszMFlz31gfZQ/OBq3QNFcivMFKYpPUZ3m3ayh8bs
 gt7HKD/4UloUABAvycV9k2gsxj4yEZaYzZkBSsPBABW1bYzsFtwypSe2D6tPVGGL
 85pr9fbIUTh2VXJlq4aLL2SlxH9eAh3iYoep4QPu5GP0Re3p0/iANSXsQAtWDRaK
 kUKh8QPqEM0NJJIeSobkaZ0G3h9DZQ3DFn4YTCLwJIsVqRgCZtGbYj7u5IhDSh7/
 CED0gIlUudVI411ICh4qnyulbmbijL0TanL46v1ihbdawD9cU60s3JfCktsBl5pz
 BR6+rxzl/+ffYfR5JaNk2LXKmpQZdDrEdmDVJbfBz5Z4J05cn9jSFRkGwdwDxqhB
 Vqos1hdHzqPLxbhfTzfT
 =wPt1
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/pull-input-20160712-1' into staging

msmouse: fix misc issues, switch to new input interface.
input: add trace events for full queues.
input-linux: better capability checks and event handling.

# gpg: Signature made Tue 12 Jul 2016 09:20:36 BST
# gpg:                using RSA key 0x4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/pull-input-20160712-1:
  input-linux: better capability checks, merge input_linux_event_{mouse, keyboard}
  input-linux: factor out input_linux_handle_keyboard
  input-linux: factor out input_linux_handle_mouse
  input: add trace events for full queues
  msmouse: send short messages if possible.
  msmouse: switch to new input interface
  msmouse: fix buffer handling
  msmouse: add MouseState, unregister handler on close

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-07-12 10:58:14 +01:00
commit 494edbf0b4
5 changed files with 256 additions and 147 deletions

View file

@ -25,16 +25,51 @@
#include "qemu-common.h"
#include "sysemu/char.h"
#include "ui/console.h"
#include "ui/input.h"
#define MSMOUSE_LO6(n) ((n) & 0x3f)
#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
static void msmouse_event(void *opaque,
int dx, int dy, int dz, int buttons_state)
{
CharDriverState *chr = (CharDriverState *)opaque;
typedef struct {
CharDriverState *chr;
QemuInputHandlerState *hs;
int axis[INPUT_AXIS__MAX];
bool btns[INPUT_BUTTON__MAX];
bool btnc[INPUT_BUTTON__MAX];
uint8_t outbuf[32];
int outlen;
} MouseState;
static void msmouse_chr_accept_input(CharDriverState *chr)
{
MouseState *mouse = chr->opaque;
int len;
len = qemu_chr_be_can_write(chr);
if (len > mouse->outlen) {
len = mouse->outlen;
}
if (!len) {
return;
}
qemu_chr_be_write(chr, mouse->outbuf, len);
mouse->outlen -= len;
if (mouse->outlen) {
memmove(mouse->outbuf, mouse->outbuf + len, mouse->outlen);
}
}
static void msmouse_queue_event(MouseState *mouse)
{
unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
int dx, dy, count = 3;
dx = mouse->axis[INPUT_AXIS_X];
mouse->axis[INPUT_AXIS_X] = 0;
dy = mouse->axis[INPUT_AXIS_Y];
mouse->axis[INPUT_AXIS_Y] = 0;
/* Movement deltas */
bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx);
@ -42,14 +77,54 @@ static void msmouse_event(void *opaque,
bytes[2] |= MSMOUSE_LO6(dy);
/* Buttons */
bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00);
bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00);
bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00);
bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT] ? 0x20 : 0x00);
bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT] ? 0x10 : 0x00);
if (mouse->btns[INPUT_BUTTON_MIDDLE] ||
mouse->btnc[INPUT_BUTTON_MIDDLE]) {
bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00);
mouse->btnc[INPUT_BUTTON_MIDDLE] = false;
count = 4;
}
/* We always send the packet of, so that we do not have to keep track
of previous state of the middle button. This can potentially confuse
some very old drivers for two button mice though. */
qemu_chr_be_write(chr, bytes, 4);
if (mouse->outlen <= sizeof(mouse->outbuf) - count) {
memcpy(mouse->outbuf + mouse->outlen, bytes, count);
mouse->outlen += count;
} else {
/* queue full -> drop event */
}
}
static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
MouseState *mouse = (MouseState *)dev;
InputMoveEvent *move;
InputBtnEvent *btn;
switch (evt->type) {
case INPUT_EVENT_KIND_REL:
move = evt->u.rel.data;
mouse->axis[move->axis] += move->value;
break;
case INPUT_EVENT_KIND_BTN:
btn = evt->u.btn.data;
mouse->btns[btn->button] = btn->down;
mouse->btnc[btn->button] = true;
break;
default:
/* keep gcc happy */
break;
}
}
static void msmouse_input_sync(DeviceState *dev)
{
MouseState *mouse = (MouseState *)dev;
msmouse_queue_event(mouse);
msmouse_chr_accept_input(mouse->chr);
}
static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
@ -60,26 +135,41 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
static void msmouse_chr_close (struct CharDriverState *chr)
{
g_free (chr);
MouseState *mouse = chr->opaque;
qemu_input_handler_unregister(mouse->hs);
g_free(mouse);
g_free(chr);
}
static QemuInputHandler msmouse_handler = {
.name = "QEMU Microsoft Mouse",
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
.event = msmouse_input_event,
.sync = msmouse_input_sync,
};
static CharDriverState *qemu_chr_open_msmouse(const char *id,
ChardevBackend *backend,
ChardevReturn *ret,
Error **errp)
{
ChardevCommon *common = backend->u.msmouse.data;
MouseState *mouse;
CharDriverState *chr;
chr = qemu_chr_alloc(common, errp);
if (!chr) {
return NULL;
}
chr->chr_write = msmouse_chr_write;
chr->chr_close = msmouse_chr_close;
chr->chr_accept_input = msmouse_chr_accept_input;
chr->explicit_be_open = true;
qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
mouse = g_new0(MouseState, 1);
mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
&msmouse_handler);
mouse->chr = chr;
chr->opaque = mouse;
return chr;
}

View file

@ -27,6 +27,7 @@
#include "ui/console.h"
#include "qemu/timer.h"
#include "hw/input/hid.h"
#include "trace.h"
#define HID_USAGE_ERROR_ROLLOVER 0x01
#define HID_USAGE_POSTFAIL 0x02
@ -234,7 +235,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
key->down,
scancodes);
if (hs->n + count > QUEUE_LENGTH) {
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
trace_hid_kbd_queue_full();
return;
}
for (i = 0; i < count; i++) {

View file

@ -23,3 +23,9 @@ milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %
milkymist_softusb_mevt(uint8_t m) "m %d"
milkymist_softusb_kevt(uint8_t m) "m %d"
milkymist_softusb_pulse_irq(void) "Pulse IRQ"
# hw/input/hid.c
hid_kbd_queue_full(void) "queue full"
# hw/input/virtio
virtio_input_queue_full(void) "queue full"

View file

@ -7,6 +7,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/iov.h"
#include "trace.h"
#include "hw/qdev.h"
#include "hw/virtio/virtio.h"
@ -47,7 +48,7 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
if (have < need) {
vinput->qindex = 0;
fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__);
trace_virtio_input_queue_full();
return;
}

View file

@ -129,6 +129,17 @@ static int qemu_input_linux_to_qcode(unsigned int lnx)
return linux_to_qcode[lnx];
}
static bool linux_is_button(unsigned int lnx)
{
if (lnx < 0x100) {
return false;
}
if (lnx >= 0x160 && lnx < 0x2c0) {
return false;
}
return true;
}
#define TYPE_INPUT_LINUX "input-linux"
#define INPUT_LINUX(obj) \
OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
@ -153,6 +164,12 @@ struct InputLinux {
int keycount;
int wheel;
bool initialized;
bool has_rel_x;
bool has_abs_x;
int num_keys;
int num_btns;
QTAILQ_ENTRY(InputLinux) next;
};
@ -188,71 +205,55 @@ static void input_linux_toggle_grab(InputLinux *il)
}
}
static void input_linux_event_keyboard(void *opaque)
static void input_linux_handle_keyboard(InputLinux *il,
struct input_event *event)
{
InputLinux *il = opaque;
struct input_event event;
int rc;
for (;;) {
rc = read(il->fd, &event, sizeof(event));
if (rc != sizeof(event)) {
if (rc < 0 && errno != EAGAIN) {
fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
close(il->fd);
}
break;
if (event->type == EV_KEY) {
if (event->value > 2 || (event->value > 1 && !il->repeat)) {
/*
* ignore autorepeat + unknown key events
* 0 == up, 1 == down, 2 == autorepeat, other == undefined
*/
return;
}
if (event->code >= KEY_CNT) {
/*
* Should not happen. But better safe than sorry,
* and we make Coverity happy too.
*/
return;
}
switch (event.type) {
case EV_KEY:
if (event.value > 2 || (event.value > 1 && !il->repeat)) {
/*
* ignore autorepeat + unknown key events
* 0 == up, 1 == down, 2 == autorepeat, other == undefined
*/
continue;
}
if (event.code >= KEY_CNT) {
/*
* Should not happen. But better safe than sorry,
* and we make Coverity happy too.
*/
continue;
}
/* keep track of key state */
if (!il->keydown[event.code] && event.value) {
il->keydown[event.code] = true;
il->keycount++;
}
if (il->keydown[event.code] && !event.value) {
il->keydown[event.code] = false;
il->keycount--;
}
/* keep track of key state */
if (!il->keydown[event->code] && event->value) {
il->keydown[event->code] = true;
il->keycount++;
}
if (il->keydown[event->code] && !event->value) {
il->keydown[event->code] = false;
il->keycount--;
}
/* send event to guest when grab is active */
if (il->grab_active) {
int qcode = qemu_input_linux_to_qcode(event.code);
qemu_input_event_send_key_qcode(NULL, qcode, event.value);
}
/* send event to guest when grab is active */
if (il->grab_active) {
int qcode = qemu_input_linux_to_qcode(event->code);
qemu_input_event_send_key_qcode(NULL, qcode, event->value);
}
/* hotkey -> record switch request ... */
if (il->keydown[KEY_LEFTCTRL] &&
il->keydown[KEY_RIGHTCTRL]) {
il->grab_request = true;
}
/* hotkey -> record switch request ... */
if (il->keydown[KEY_LEFTCTRL] &&
il->keydown[KEY_RIGHTCTRL]) {
il->grab_request = true;
}
/*
* ... and do the switch when all keys are lifted, so we
* confuse neither guest nor host with keys which seem to
* be stuck due to missing key-up events.
*/
if (il->grab_request && !il->keycount) {
il->grab_request = false;
input_linux_toggle_grab(il);
}
break;
/*
* ... and do the switch when all keys are lifted, so we
* confuse neither guest nor host with keys which seem to
* be stuck due to missing key-up events.
*/
if (il->grab_request && !il->keycount) {
il->grab_request = false;
input_linux_toggle_grab(il);
}
}
}
@ -265,7 +266,59 @@ static void input_linux_event_mouse_button(int button)
qemu_input_event_sync();
}
static void input_linux_event_mouse(void *opaque)
static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
{
if (!il->grab_active) {
return;
}
switch (event->type) {
case EV_KEY:
switch (event->code) {
case BTN_LEFT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
break;
case BTN_RIGHT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
break;
case BTN_MIDDLE:
qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
break;
case BTN_GEAR_UP:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
break;
case BTN_GEAR_DOWN:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
event->value);
break;
};
break;
case EV_REL:
switch (event->code) {
case REL_X:
qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
break;
case REL_Y:
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
break;
case REL_WHEEL:
il->wheel = event->value;
break;
}
break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
input_linux_event_mouse_button((il->wheel > 0)
? INPUT_BUTTON_WHEEL_UP
: INPUT_BUTTON_WHEEL_DOWN);
il->wheel = 0;
}
break;
}
}
static void input_linux_event(void *opaque)
{
InputLinux *il = opaque;
struct input_event event;
@ -282,54 +335,11 @@ static void input_linux_event_mouse(void *opaque)
break;
}
/* only send event to guest when grab is active */
if (!il->grab_active) {
continue;
if (il->num_keys) {
input_linux_handle_keyboard(il, &event);
}
switch (event.type) {
case EV_KEY:
switch (event.code) {
case BTN_LEFT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value);
break;
case BTN_RIGHT:
qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value);
break;
case BTN_MIDDLE:
qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value);
break;
case BTN_GEAR_UP:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value);
break;
case BTN_GEAR_DOWN:
qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
event.value);
break;
};
break;
case EV_REL:
switch (event.code) {
case REL_X:
qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value);
break;
case REL_Y:
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value);
break;
case REL_WHEEL:
il->wheel = event.value;
break;
}
break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
input_linux_event_mouse_button((il->wheel > 0)
? INPUT_BUTTON_WHEEL_UP
: INPUT_BUTTON_WHEEL_DOWN);
il->wheel = 0;
}
break;
if (il->has_rel_x && il->num_btns) {
input_linux_handle_mouse(il, &event);
}
}
}
@ -337,7 +347,8 @@ static void input_linux_event_mouse(void *opaque)
static void input_linux_complete(UserCreatable *uc, Error **errp)
{
InputLinux *il = INPUT_LINUX(uc);
uint32_t evtmap, relmap, absmap;
uint8_t evtmap, relmap, absmap, keymap[KEY_CNT / 8];
unsigned int i;
int rc, ver;
if (!il->evdev) {
@ -365,36 +376,36 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
}
if (evtmap & (1 << EV_REL)) {
relmap = 0;
rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
if (rc < 0) {
relmap = 0;
if (relmap & (1 << REL_X)) {
il->has_rel_x = true;
}
}
if (evtmap & (1 << EV_ABS)) {
ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
if (rc < 0) {
absmap = 0;
absmap = 0;
rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
if (absmap & (1 << ABS_X)) {
il->has_abs_x = true;
}
}
if ((evtmap & (1 << EV_REL)) &&
(relmap & (1 << REL_X))) {
/* has relative x axis -> assume mouse */
qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il);
} else if ((evtmap & (1 << EV_ABS)) &&
(absmap & (1 << ABS_X))) {
/* has absolute x axis -> not supported */
error_setg(errp, "tablet/touchscreen not supported");
goto err_close;
} else if (evtmap & (1 << EV_KEY)) {
/* has keys/buttons (and no x axis) -> assume keyboard */
qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il);
} else {
/* Huh? What is this? */
error_setg(errp, "unknown kind of input device");
goto err_close;
if (evtmap & (1 << EV_KEY)) {
memset(keymap, 0, sizeof(keymap));
rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap);
for (i = 0; i < KEY_CNT; i++) {
if (keymap[i / 8] & (1 << (i % 8))) {
if (linux_is_button(i)) {
il->num_btns++;
} else {
il->num_keys++;
}
}
}
}
qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
input_linux_toggle_grab(il);
QTAILQ_INSERT_TAIL(&inputs, il, next);
il->initialized = true;