Commit 494edbf0 authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 910789c2 2e6a64cb
Loading
Loading
Loading
Loading
+106 −16
Original line number Diff line number Diff line
@@ -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)
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)
{
    CharDriverState *chr = (CharDriverState *)opaque;
    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);

    /* 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);
    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;
    }

    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)
{
    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;
}
+2 −1
Original line number Diff line number Diff line
@@ -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++) {
+6 −0
Original line number Diff line number Diff line
@@ -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"
+2 −1
Original line number Diff line number Diff line
@@ -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;
    }

+140 −129
Original line number Diff line number Diff line
@@ -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,53 +205,39 @@ 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;
        }

        switch (event.type) {
        case EV_KEY:
            if (event.value > 2 || (event.value > 1 && !il->repeat)) {
    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
             */
                continue;
            return;
        }
            if (event.code >= KEY_CNT) {
        if (event->code >= KEY_CNT) {
            /*
             * Should not happen.  But better safe than sorry,
             * and we make Coverity happy too.
             */
                continue;
            return;
        }

        /* keep track of key state */
            if (!il->keydown[event.code] && event.value) {
                il->keydown[event.code] = true;
        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;
        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);
            int qcode = qemu_input_linux_to_qcode(event->code);
            qemu_input_event_send_key_qcode(NULL, qcode, event->value);
        }

        /* hotkey -> record switch request ... */
@@ -252,8 +255,6 @@ static void input_linux_event_keyboard(void *opaque)
            il->grab_request = false;
            input_linux_toggle_grab(il);
        }
            break;
        }
    }
}

@@ -265,59 +266,43 @@ 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)
{
    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;
        }

        /* only send event to guest when grab is active */
    if (!il->grab_active) {
            continue;
        return;
    }

        switch (event.type) {
    switch (event->type) {
    case EV_KEY:
            switch (event.code) {
        switch (event->code) {
        case BTN_LEFT:
                qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value);
            qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
            break;
        case BTN_RIGHT:
                qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value);
            qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
            break;
        case BTN_MIDDLE:
                qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value);
            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);
            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);
                                 event->value);
            break;
        };
        break;
    case EV_REL:
            switch (event.code) {
        switch (event->code) {
        case REL_X:
                qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value);
            qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
            break;
        case REL_Y:
                qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value);
            qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
            break;
        case REL_WHEEL:
                il->wheel = event.value;
            il->wheel = event->value;
            break;
        }
        break;
@@ -332,12 +317,38 @@ static void input_linux_event_mouse(void *opaque)
        break;
    }
}

static void input_linux_event(void *opaque)
{
    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 (il->num_keys) {
            input_linux_handle_keyboard(il, &event);
        }
        if (il->has_rel_x && il->num_btns) {
            input_linux_handle_mouse(il, &event);
        }
    }
}

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)) {
        rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
        if (rc < 0) {
        relmap = 0;
        rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
        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;
        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);
    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 {
        /* Huh? What is this? */
        error_setg(errp, "unknown kind of input device");
        goto err_close;
                    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;