qemu-patch-raspberry4/hw/input/hid.c
Eric Blake 32bafa8fdd qapi: Don't special-case simple union wrappers
Simple unions were carrying a special case that hid their 'data'
QMP member from the resulting C struct, via the hack method
QAPISchemaObjectTypeVariant.simple_union_type().  But by using
the work we started by unboxing flat union and alternate
branches, coupled with the ability to visit the members of an
implicit type, we can now expose the simple union's implicit
type in qapi-types.h:

| struct q_obj_ImageInfoSpecificQCow2_wrapper {
|     ImageInfoSpecificQCow2 *data;
| };
|
| struct q_obj_ImageInfoSpecificVmdk_wrapper {
|     ImageInfoSpecificVmdk *data;
| };
...
| struct ImageInfoSpecific {
|     ImageInfoSpecificKind type;
|     union { /* union tag is @type */
|         void *data;
|-        ImageInfoSpecificQCow2 *qcow2;
|-        ImageInfoSpecificVmdk *vmdk;
|+        q_obj_ImageInfoSpecificQCow2_wrapper qcow2;
|+        q_obj_ImageInfoSpecificVmdk_wrapper vmdk;
|     } u;
| };

Doing this removes asymmetry between QAPI's QMP side and its
C side (both sides now expose 'data'), and means that the
treatment of a simple union as sugar for a flat union is now
equivalent in both languages (previously the two approaches used
a different layer of dereferencing, where the simple union could
be converted to a flat union with equivalent C layout but
different {} on the wire, or to an equivalent QMP wire form
but with different C representation).  Using the implicit type
also lets us get rid of the simple_union_type() hack.

Of course, now all clients of simple unions have to adjust from
using su->u.member to using su->u.member.data; while this touches
a number of files in the tree, some earlier cleanup patches
helped minimize the change to the initialization of a temporary
variable rather than every single member access.  The generated
qapi-visit.c code is also affected by the layout change:

|@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member
|     }
|     switch (obj->type) {
|     case IMAGE_INFO_SPECIFIC_KIND_QCOW2:
|-        visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err);
|+        visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err);
|         break;
|     case IMAGE_INFO_SPECIFIC_KIND_VMDK:
|-        visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err);
|+        visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err);
|         break;
|     default:
|         abort();

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 10:29:26 +01:00

619 lines
18 KiB
C

/*
* QEMU HID devices
*
* Copyright (c) 2005 Fabrice Bellard
* Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "qemu/timer.h"
#include "hw/input/hid.h"
#define HID_USAGE_ERROR_ROLLOVER 0x01
#define HID_USAGE_POSTFAIL 0x02
#define HID_USAGE_ERROR_UNDEFINED 0x03
/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
* above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
static const uint8_t hid_usage_keys[0x100] = {
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44,
0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
bool hid_has_events(HIDState *hs)
{
return hs->n > 0 || hs->idle_pending;
}
static void hid_idle_timer(void *opaque)
{
HIDState *hs = opaque;
hs->idle_pending = true;
hs->event(hs);
}
static void hid_del_idle_timer(HIDState *hs)
{
if (hs->idle_timer) {
timer_del(hs->idle_timer);
timer_free(hs->idle_timer);
hs->idle_timer = NULL;
}
}
void hid_set_next_idle(HIDState *hs)
{
if (hs->idle) {
uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
get_ticks_per_sec() * hs->idle * 4 / 1000;
if (!hs->idle_timer) {
hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
}
timer_mod_ns(hs->idle_timer, expire_time);
} else {
hid_del_idle_timer(hs);
}
}
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
static const int bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_RIGHT] = 0x02,
[INPUT_BUTTON_MIDDLE] = 0x04,
};
HIDState *hs = (HIDState *)dev;
HIDPointerEvent *e;
InputMoveEvent *move;
InputBtnEvent *btn;
assert(hs->n < QUEUE_LENGTH);
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
switch (evt->type) {
case INPUT_EVENT_KIND_REL:
move = evt->u.rel.data;
if (move->axis == INPUT_AXIS_X) {
e->xdx += move->value;
} else if (move->axis == INPUT_AXIS_Y) {
e->ydy += move->value;
}
break;
case INPUT_EVENT_KIND_ABS:
move = evt->u.abs.data;
if (move->axis == INPUT_AXIS_X) {
e->xdx = move->value;
} else if (move->axis == INPUT_AXIS_Y) {
e->ydy = move->value;
}
break;
case INPUT_EVENT_KIND_BTN:
btn = evt->u.btn.data;
if (btn->down) {
e->buttons_state |= bmap[btn->button];
if (btn->button == INPUT_BUTTON_WHEEL_UP) {
e->dz--;
} else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
e->dz++;
}
} else {
e->buttons_state &= ~bmap[btn->button];
}
break;
default:
/* keep gcc happy */
break;
}
}
static void hid_pointer_sync(DeviceState *dev)
{
HIDState *hs = (HIDState *)dev;
HIDPointerEvent *prev, *curr, *next;
bool event_compression = false;
if (hs->n == QUEUE_LENGTH-1) {
/*
* Queue full. We are losing information, but we at least
* keep track of most recent button state.
*/
return;
}
prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
if (hs->n > 0) {
/*
* No button state change between previous and current event
* (and previous wasn't seen by the guest yet), so there is
* motion information only and we can combine the two event
* into one.
*/
if (curr->buttons_state == prev->buttons_state) {
event_compression = true;
}
}
if (event_compression) {
/* add current motion to previous, clear current */
if (hs->kind == HID_MOUSE) {
prev->xdx += curr->xdx;
curr->xdx = 0;
prev->ydy += curr->ydy;
curr->ydy = 0;
} else {
prev->xdx = curr->xdx;
prev->ydy = curr->ydy;
}
prev->dz += curr->dz;
curr->dz = 0;
} else {
/* prepate next (clear rel, copy abs + btns) */
if (hs->kind == HID_MOUSE) {
next->xdx = 0;
next->ydy = 0;
} else {
next->xdx = curr->xdx;
next->ydy = curr->ydy;
}
next->dz = 0;
next->buttons_state = curr->buttons_state;
/* make current guest visible, notify guest */
hs->n++;
hs->event(hs);
}
}
static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
HIDState *hs = (HIDState *)dev;
int scancodes[3], i, count;
int slot;
InputKeyEvent *key = evt->u.key.data;
count = qemu_input_key_value_to_scancode(key->key,
key->down,
scancodes);
if (hs->n + count > QUEUE_LENGTH) {
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
return;
}
for (i = 0; i < count; i++) {
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
hs->kbd.keycodes[slot] = scancodes[i];
}
hs->event(hs);
}
static void hid_keyboard_process_keycode(HIDState *hs)
{
uint8_t hid_code, index, key;
int i, keycode, slot;
if (hs->n == 0) {
return;
}
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
keycode = hs->kbd.keycodes[slot];
key = keycode & 0x7f;
index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
hid_code = hid_usage_keys[index];
hs->kbd.modifiers &= ~(1 << 8);
switch (hid_code) {
case 0x00:
return;
case 0xe0:
assert(key == 0x1d);
if (hs->kbd.modifiers & (1 << 9)) {
/* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0.
* Here we're processing the second hid_code. By dropping bit 9
* and setting bit 8, the scancode after 0x1d will access the
* second half of the table.
*/
hs->kbd.modifiers ^= (1 << 8) | (1 << 9);
return;
}
/* fall through to process Ctrl_L */
case 0xe1 ... 0xe7:
/* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R.
* Handle releases here, or fall through to process presses.
*/
if (keycode & (1 << 7)) {
hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
return;
}
/* fall through */
case 0xe8 ... 0xe9:
/* USB modifiers are just 1 byte long. Bits 8 and 9 of
* hs->kbd.modifiers implement a state machine that detects the
* 0xe0 and 0xe1/0x1d sequences. These bits do not follow the
* usual rules where bit 7 marks released keys; they are cleared
* elsewhere in the function as the state machine dictates.
*/
hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
return;
case 0xea ... 0xef:
abort();
default:
break;
}
if (keycode & (1 << 7)) {
for (i = hs->kbd.keys - 1; i >= 0; i--) {
if (hs->kbd.key[i] == hid_code) {
hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
hs->kbd.key[hs->kbd.keys] = 0x00;
break;
}
}
if (i < 0) {
return;
}
} else {
for (i = hs->kbd.keys - 1; i >= 0; i--) {
if (hs->kbd.key[i] == hid_code) {
break;
}
}
if (i < 0) {
if (hs->kbd.keys < sizeof(hs->kbd.key)) {
hs->kbd.key[hs->kbd.keys++] = hid_code;
}
} else {
return;
}
}
}
static inline int int_clamp(int val, int vmin, int vmax)
{
if (val < vmin) {
return vmin;
} else if (val > vmax) {
return vmax;
} else {
return val;
}
}
void hid_pointer_activate(HIDState *hs)
{
if (!hs->ptr.mouse_grabbed) {
qemu_input_handler_activate(hs->s);
hs->ptr.mouse_grabbed = 1;
}
}
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
{
int dx, dy, dz, l;
int index;
HIDPointerEvent *e;
hs->idle_pending = false;
hid_pointer_activate(hs);
/* When the buffer is empty, return the last event. Relative
movements will all be zero. */
index = (hs->n ? hs->head : hs->head - 1);
e = &hs->ptr.queue[index & QUEUE_MASK];
if (hs->kind == HID_MOUSE) {
dx = int_clamp(e->xdx, -127, 127);
dy = int_clamp(e->ydy, -127, 127);
e->xdx -= dx;
e->ydy -= dy;
} else {
dx = e->xdx;
dy = e->ydy;
}
dz = int_clamp(e->dz, -127, 127);
e->dz -= dz;
if (hs->n &&
!e->dz &&
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
/* that deals with this event */
QUEUE_INCR(hs->head);
hs->n--;
}
/* Appears we have to invert the wheel direction */
dz = 0 - dz;
l = 0;
switch (hs->kind) {
case HID_MOUSE:
if (len > l) {
buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx;
}
if (len > l) {
buf[l++] = dy;
}
if (len > l) {
buf[l++] = dz;
}
break;
case HID_TABLET:
if (len > l) {
buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx & 0xff;
}
if (len > l) {
buf[l++] = dx >> 8;
}
if (len > l) {
buf[l++] = dy & 0xff;
}
if (len > l) {
buf[l++] = dy >> 8;
}
if (len > l) {
buf[l++] = dz;
}
break;
default:
abort();
}
return l;
}
int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
{
hs->idle_pending = false;
if (len < 2) {
return 0;
}
hid_keyboard_process_keycode(hs);
buf[0] = hs->kbd.modifiers & 0xff;
buf[1] = 0;
if (hs->kbd.keys > 6) {
memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
} else {
memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
}
return MIN(8, len);
}
int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
{
if (len > 0) {
int ledstate = 0;
/* 0x01: Num Lock LED
* 0x02: Caps Lock LED
* 0x04: Scroll Lock LED
* 0x08: Compose LED
* 0x10: Kana LED */
hs->kbd.leds = buf[0];
if (hs->kbd.leds & 0x04) {
ledstate |= QEMU_SCROLL_LOCK_LED;
}
if (hs->kbd.leds & 0x01) {
ledstate |= QEMU_NUM_LOCK_LED;
}
if (hs->kbd.leds & 0x02) {
ledstate |= QEMU_CAPS_LOCK_LED;
}
kbd_put_ledstate(ledstate);
}
return 0;
}
void hid_reset(HIDState *hs)
{
switch (hs->kind) {
case HID_KEYBOARD:
memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
hs->kbd.keys = 0;
break;
case HID_MOUSE:
case HID_TABLET:
memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
break;
}
hs->head = 0;
hs->n = 0;
hs->protocol = 1;
hs->idle = 0;
hs->idle_pending = false;
hid_del_idle_timer(hs);
}
void hid_free(HIDState *hs)
{
qemu_input_handler_unregister(hs->s);
hid_del_idle_timer(hs);
}
static QemuInputHandler hid_keyboard_handler = {
.name = "QEMU HID Keyboard",
.mask = INPUT_EVENT_MASK_KEY,
.event = hid_keyboard_event,
};
static QemuInputHandler hid_mouse_handler = {
.name = "QEMU HID Mouse",
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
.event = hid_pointer_event,
.sync = hid_pointer_sync,
};
static QemuInputHandler hid_tablet_handler = {
.name = "QEMU HID Tablet",
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
.event = hid_pointer_event,
.sync = hid_pointer_sync,
};
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
{
hs->kind = kind;
hs->event = event;
if (hs->kind == HID_KEYBOARD) {
hs->s = qemu_input_handler_register((DeviceState *)hs,
&hid_keyboard_handler);
qemu_input_handler_activate(hs->s);
} else if (hs->kind == HID_MOUSE) {
hs->s = qemu_input_handler_register((DeviceState *)hs,
&hid_mouse_handler);
} else if (hs->kind == HID_TABLET) {
hs->s = qemu_input_handler_register((DeviceState *)hs,
&hid_tablet_handler);
}
}
static int hid_post_load(void *opaque, int version_id)
{
HIDState *s = opaque;
hid_set_next_idle(s);
if (s->n == QUEUE_LENGTH && (s->kind == HID_TABLET ||
s->kind == HID_MOUSE)) {
/*
* Handle ptr device migration from old qemu with full queue.
*
* Throw away everything but the last event, so we propagate
* at least the current button state to the guest. Also keep
* current position for the tablet, signal "no motion" for the
* mouse.
*/
HIDPointerEvent evt;
evt = s->ptr.queue[(s->head+s->n) & QUEUE_MASK];
if (s->kind == HID_MOUSE) {
evt.xdx = 0;
evt.ydy = 0;
}
s->ptr.queue[0] = evt;
s->head = 0;
s->n = 1;
}
return 0;
}
static const VMStateDescription vmstate_hid_ptr_queue = {
.name = "HIDPointerEventQueue",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32(xdx, HIDPointerEvent),
VMSTATE_INT32(ydy, HIDPointerEvent),
VMSTATE_INT32(dz, HIDPointerEvent),
VMSTATE_INT32(buttons_state, HIDPointerEvent),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_hid_ptr_device = {
.name = "HIDPointerDevice",
.version_id = 1,
.minimum_version_id = 1,
.post_load = hid_post_load,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
vmstate_hid_ptr_queue, HIDPointerEvent),
VMSTATE_UINT32(head, HIDState),
VMSTATE_UINT32(n, HIDState),
VMSTATE_INT32(protocol, HIDState),
VMSTATE_UINT8(idle, HIDState),
VMSTATE_END_OF_LIST(),
}
};
const VMStateDescription vmstate_hid_keyboard_device = {
.name = "HIDKeyboardDevice",
.version_id = 1,
.minimum_version_id = 1,
.post_load = hid_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
VMSTATE_UINT32(head, HIDState),
VMSTATE_UINT32(n, HIDState),
VMSTATE_UINT16(kbd.modifiers, HIDState),
VMSTATE_UINT8(kbd.leds, HIDState),
VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
VMSTATE_INT32(kbd.keys, HIDState),
VMSTATE_INT32(protocol, HIDState),
VMSTATE_UINT8(idle, HIDState),
VMSTATE_END_OF_LIST(),
}
};