--- /dev/null
- VirtQueueElement elem;
- VirtQueueElement *element = &elem;
-// preparing to merge qemu 2.6
-// element = virtqueue_pop(input->vq, sizeof(VirtQueueElement));
-// if (element) {
- if (virtqueue_pop(input->vq, element)) {
+ /*
+ * Maru Virtio Input Device
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ * Jinhyung Jo <Jinhyung Jo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+ #include "qemu/osdep.h"
+ #include "qemu/iov.h"
+ #include "hw/maru_device_ids.h"
+ #include "maru_virtio_input.h"
+
+ #include "util/new_debug_ch.h"
+ DECLARE_DEBUG_CHANNEL(input);
+
+
+ //
+ // INPUT COMMON
+ //
+
+ #define MAX_EVENT_CNT 128
+
+ #define TYPE_VIRTIO_MARU_INPUT "virtio-maru-input-device"
+
+ #if 0
+ // TODO
+ #define VIRTIO_MARU_INPUT(obj) \
+ OBJECT_CHECK(VirtIOMaruInput, (obj), TYPE_VIRTIO_MARU_INPUT)
+ #else
+ #define VIRTIO_MARU_INPUT(obj) ((VirtIOMaruInput *)(obj))
+ #endif
+
+ static void default_get_config(
+ VirtIODevice *vdev, uint8_t *config_data)
+ {
+ // do nothing
+ }
+
+ static void default_set_config(
+ VirtIODevice *vdev, const uint8_t *config_data)
+ {
+ // do nothing
+ }
+
+ static uint64_t default_get_features(
+ VirtIODevice *vdev, uint64_t request_features, Error **errp)
+ {
+ // do nothing
+ return request_features;
+ }
+
+
+ static void default_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+ {
+ // do nothing
+ }
+
+ static void send_event(void *opaque)
+ {
+ VirtIOMaruInput *input = VIRTIO_MARU_INPUT(opaque);
+ int i = 0;
+ int len = 0;
+
+ LOG_TRACE("%s\n", __func__);
+
+ qemu_mutex_lock(&input->event_mutex);
+
+ for (i = 0; i < input->buf_index; ++i) {
++ VirtQueueElement *element =
++ virtqueue_pop(input->vq, sizeof(VirtQueueElement));
++ if (element) {
+ len = iov_from_buf(element->in_sg, element->in_num,
+ 0, input->events + (input->event_size * i),
+ input->event_size);
+ virtqueue_push(input->vq, element, len);
+ } else {
+ LOG_SEVERE("virtqueue is not available, dropping event.\n");
+ break;
+ }
+ }
+ input->buf_index = 0;
+
+ qemu_mutex_unlock(&input->event_mutex);
+
+ virtio_notify(VIRTIO_DEVICE(input), input->vq);
+ }
+
+ static void input_common_realize(DeviceState *dev, const char* name,
+ uint16_t device_id, void *handle_output, void *bh,
+ void *events, size_t event_size)
+ {
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOMaruInput *input = VIRTIO_MARU_INPUT(dev);
+
+ if (!handle_output) {
+ handle_output = default_handle_output;
+ }
+
+ if (!bh) {
+ bh = send_event;
+ }
+
+ virtio_init(vdev, name, device_id, 4);
+
+ input->vq = virtio_add_queue(&input->vdev, MAX_EVENT_CNT, handle_output);
+ input->qdev = dev;
+
+ /* bottom halves */
+ input->bh = qemu_bh_new(bh, vdev);
+
+ input->events = (uint8_t *)events;
+ input->event_size = event_size;
+
+ qemu_mutex_init(&input->event_mutex);
+ }
+
+ static void input_common_unrealize(DeviceState *dev)
+ {
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOMaruInput *input = VIRTIO_MARU_INPUT(dev);
+
+ if (input->bh) {
+ qemu_bh_delete(input->bh);
+ }
+
+ qemu_mutex_destroy(&input->event_mutex);
+
+ virtio_cleanup(vdev);
+ }
+
+ static void input_common_reset(VirtIODevice *vdev)
+ {
+ VirtIOMaruInput *input = VIRTIO_MARU_INPUT(vdev);
+
+ qemu_mutex_lock(&input->event_mutex);
+ /* reset the counters */
+ input->event_cnt = 0;
+ input->buf_index = 0;
+ qemu_mutex_unlock(&input->event_mutex);
+ }
+
+ static bool check_ready(VirtIOMaruInput *input)
+ {
+ if (unlikely(!virtio_queue_ready(input->vq))) {
+ LOG_INFO("virtqueue is not ready, dropping event.\n");
+ return false;
+ }
+
+ ++(input->event_cnt);
+
+ if (input->buf_index >= MAX_EVENT_CNT ) {
+ LOG_SEVERE("event queue is not available, dropping event.\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ static void push_event(VirtIOMaruInput *input)
+ {
+ /* send */
+ qemu_bh_schedule(input->bh);
+ }
+
+
+ //
+ // TOUCHSCREEN
+ //
+
+ #define VIRTIO_MARU_TOUCHSCREEN(obj) \
+ OBJECT_CHECK(VirtIOTouchscreen, (obj), TYPE_VIRTIO_MARU_TOUCHSCREEN)
+
+ #define TOUCHSCREEN_OPTION_NAME "max_point"
+
+ static VirtIOTouchscreen *ts;
+ static touch_event touch_events[MAX_EVENT_CNT];
+
+ bool virtio_touchscreen_ready(void)
+ {
+ if (ts == NULL) {
+ return false;
+ }
+
+ return virtio_queue_ready(ts->input.vq) != 0;
+ }
+
+ int virtio_touchscreen_get_max_touch_point(void)
+ {
+ if (ts) {
+ return ts->max_finger;
+ } else {
+ return 1;
+ }
+ }
+
+ void virtio_touchscreen_event(int x, int y, int z, int buttons_state)
+ {
+ VirtIOMaruInput *input = &ts->input;
+ touch_event *event;
+
+ qemu_mutex_lock(&input->event_mutex);
+
+ if (!check_ready(input)) {
+ return;
+ }
+
+ event = &touch_events[input->buf_index++];
+
+ /* mouse event is copied into the queue */
+ event->x = x;
+ event->y = y;
+ event->z = z;
+ event->state = buttons_state;
+
+ qemu_mutex_unlock(&input->event_mutex);
+
+ push_event(input);
+
+
+ LOG_TRACE("touch event: x=%d, y=%d, z=%d, state=%d\n",
+ event->x, event->y,
+ event->z, event->state);
+ }
+
+ static void virtio_touchscreen_get_config(
+ VirtIODevice *vdev, uint8_t *config_data)
+ {
+ VirtIOTouchscreen *ts = VIRTIO_MARU_TOUCHSCREEN(vdev);
+ int max_trkid = 10;
+
+ LOG_TRACE("%s\n", __func__);
+
+ max_trkid = ts->max_finger;
+ memcpy(config_data, &max_trkid, 4);
+ }
+
+ static void virtio_touchscreen_device_realize(DeviceState *dev, Error **errp)
+ {
+ ts = VIRTIO_MARU_TOUCHSCREEN(dev);
+
+ LOG_INFO("%s: %d\n", __func__, ts->max_finger);
+
+ input_common_realize(dev, TYPE_VIRTIO_MARU_TOUCHSCREEN,
+ VIRTIO_ID_MARU_TOUCHSCREEN, NULL, NULL,
+ touch_events, sizeof(touch_event));
+ }
+
+ static void virtio_touchscreen_device_unrealize(DeviceState *dev, Error **errp)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_unrealize(dev);
+ }
+
+ static void virtio_touchscreen_device_reset(VirtIODevice *vdev)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_reset(vdev);
+ }
+
+ static Property virtio_touchscreen_properties[] = {
+ DEFINE_PROP_UINT32(TOUCHSCREEN_OPTION_NAME,
+ VirtIOTouchscreen, max_finger, DEFAULT_MAX_FINGER),
+ DEFINE_PROP_END_OF_LIST(),
+ };
+
+ static void virtio_touchscreen_class_init(ObjectClass *klass, void *data)
+ {
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ dc->props = virtio_touchscreen_properties;
+ vdc->realize = virtio_touchscreen_device_realize;
+ vdc->unrealize = virtio_touchscreen_device_unrealize;
+ vdc->reset = virtio_touchscreen_device_reset;
+ vdc->get_config = virtio_touchscreen_get_config;
+ vdc->set_config = default_set_config;
+ vdc->get_features = default_get_features;
+ }
+
+ static const TypeInfo virtio_touchscreen_info = {
+ .name = TYPE_VIRTIO_MARU_TOUCHSCREEN,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOTouchscreen),
+ .class_init = virtio_touchscreen_class_init,
+ };
+
+
+ //
+ // KEYBOARD
+ //
+
+ #define VIRTIO_MARU_KEYBOARD(obj) \
+ OBJECT_CHECK(VirtIOKeyboard, (obj), TYPE_VIRTIO_MARU_KEYBOARD)
+
+ static VirtIOKeyboard *kbd;
+ static keyboard_event keyboard_events[MAX_EVENT_CNT];
+
+ void virtio_keyboard_event(int keycode)
+ {
+ VirtIOMaruInput *input = &kbd->input;
+ keyboard_event *event;
+
+ qemu_mutex_lock(&input->event_mutex);
+
+ if (!check_ready(input)) {
+ return;
+ }
+
+ event = &keyboard_events[input->buf_index++];
+
+ if (keycode < 0xe0) {
+ if (kbd->extension_key) {
+ switch (keycode & 0x7f) {
+ case 28: /* KP_Enter */
+ event->code = 96;
+ break;
+ case 29: /* Right Ctrl */
+ event->code = 97;
+ break;
+ case 56: /* Right Alt */
+ event->code = 100;
+ break;
+ case 71: /* Home */
+ event->code = 102;
+ break;
+ case 72: /* Up */
+ event->code = 103;
+ break;
+ case 73: /* Page Up */
+ event->code = 104;
+ break;
+ case 75: /* Left */
+ event->code = 105;
+ break;
+ case 77: /* Right */
+ event->code = 106;
+ break;
+ case 79: /* End */
+ event->code = 107;
+ break;
+ case 80: /* Down */
+ event->code = 108;
+ break;
+ case 81: /* Page Down */
+ event->code = 109;
+ break;
+ case 82: /* Insert */
+ event->code = 110;
+ break;
+ case 83: /* Delete */
+ event->code = 111;
+ break;
+ default:
+ LOG_WARNING("There is no keymap for this keycode %d.\n", keycode);
+ }
+ kbd->extension_key = 0;
+ } else {
+ event->code = keycode & 0x7f;
+ }
+
+ if (!(keycode & 0x80)) {
+ event->type = 1; /* KEY_PRESSED */
+ } else {
+ event->type = 0; /* KEY_RELEASED */
+ }
+ } else {
+ LOG_TRACE("Extension key.\n");
+ event->code = keycode;
+ kbd->extension_key = 1;
+ }
+
+ qemu_mutex_unlock(&input->event_mutex);
+
+ push_event(input);
+
+ LOG_TRACE("keyboard event: code=%d, type=%d\n",
+ event->code, event->type);
+ }
+
+ static void virtio_keyboard_device_realize(DeviceState *dev, Error **errp)
+ {
+ kbd = VIRTIO_MARU_KEYBOARD(dev);
+
+ LOG_INFO("%s\n", __func__);
+
+ input_common_realize(dev, TYPE_VIRTIO_MARU_KEYBOARD,
+ VIRTIO_ID_MARU_KEYBOARD, NULL, NULL,
+ keyboard_events, sizeof(keyboard_event));
+ }
+
+ static void virtio_keyboard_device_unrealize(DeviceState *dev, Error **errp)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_unrealize(dev);
+ }
+
+ static void virtio_keyboard_device_reset(VirtIODevice *vdev)
+ {
+ VirtIOKeyboard *vkbd = (VirtIOKeyboard *)vdev;
+
+ LOG_INFO("%s\n", __func__);
+
+ vkbd->extension_key = 0;
+ input_common_reset(vdev);
+ }
+
+ static void virtio_keyboard_class_init(ObjectClass *klass, void *data)
+ {
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ vdc->unrealize = virtio_keyboard_device_unrealize;
+ vdc->realize = virtio_keyboard_device_realize;
+ vdc->reset = virtio_keyboard_device_reset;
+ vdc->get_config = default_get_config;
+ vdc->set_config = default_set_config;
+ vdc->get_features = default_get_features;
+ }
+
+ static const TypeInfo virtio_keyboard_info = {
+ .name = TYPE_VIRTIO_MARU_KEYBOARD,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOKeyboard),
+ .class_init = virtio_keyboard_class_init,
+ };
+
+
+ //
+ // HW KEY
+ //
+
+ #define VIRTIO_MARU_HWKEY(obj) \
+ OBJECT_CHECK(VirtIOHWKey, (obj), TYPE_VIRTIO_MARU_HWKEY)
+
+ static VirtIOHWKey *hwkey;
+ static hwkey_event hwkey_events[MAX_EVENT_CNT];
+
+ void maru_hwkey_event(int event_type, int keycode)
+ {
+ VirtIOMaruInput *input = &hwkey->input;
+ hwkey_event *event;
+
+ if (!check_ready(input)) {
+ return;
+ }
+
+ qemu_mutex_lock(&input->event_mutex);
+
+ event = &hwkey_events[input->buf_index++];
+
+ /* hwkey event is copied into the queue */
+ event->keycode = keycode;
+ event->event_type = event_type;
+
+ qemu_mutex_unlock(&input->event_mutex);
+
+ push_event(input);
+
+ LOG_TRACE("hwkey event: keycode=%d, event_type=%d\n",
+ event->keycode, event->event_type);
+ }
+
+ static void virtio_hwkey_device_realize(DeviceState *dev, Error **errp)
+ {
+ hwkey = VIRTIO_MARU_HWKEY(dev);
+
+ LOG_INFO("%s\n", __func__);
+
+ input_common_realize(dev, TYPE_VIRTIO_MARU_HWKEY,
+ VIRTIO_ID_MARU_HWKEY, NULL, NULL,
+ hwkey_events, sizeof(hwkey_event));
+ }
+
+ static void virtio_hwkey_device_unrealize(DeviceState *dev, Error **errp)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_unrealize(dev);
+ }
+
+ static void virtio_hwkey_device_reset(VirtIODevice *vdev)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_reset(vdev);
+ }
+
+ static void virtio_hwkey_class_init(ObjectClass *klass, void *data)
+ {
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ vdc->unrealize = virtio_hwkey_device_unrealize;
+ vdc->realize = virtio_hwkey_device_realize;
+ vdc->reset = virtio_hwkey_device_reset;
+ vdc->get_config = default_get_config;
+ vdc->set_config = default_set_config;
+ vdc->get_features = default_get_features;
+ }
+
+ static const TypeInfo virtio_hwkey_info = {
+ .name = TYPE_VIRTIO_MARU_HWKEY,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOHWKey),
+ .class_init = virtio_hwkey_class_init,
+ };
+
+
+ //
+ // ROTARY
+ //
+
+ #define VIRTIO_MARU_ROTARY(obj) \
+ OBJECT_CHECK(VirtIORotary, (obj), TYPE_VIRTIO_MARU_ROTARY)
+
+ static VirtIORotary *rotary;
+ static rotary_event rotary_events[MAX_EVENT_CNT];
+
+ void maru_rotary_event(int32_t delta, int32_t type)
+ {
+ VirtIOMaruInput *input = &rotary->input;
+ rotary_event *event;
+
+ /* skip useless data */
+ if (!delta && !type) {
+ return;
+ }
+
+ if (!check_ready(input)) {
+ return;
+ }
+
+ qemu_mutex_lock(&input->event_mutex);
+
+ event = &rotary_events[input->buf_index++];
+
+ event->delta = delta;
+ event->type = type;
+
+ qemu_mutex_unlock(&input->event_mutex);
+
+ push_event(input);
+
+ LOG_TRACE("[%s] delta(%d), type(%d)\n", __func__, delta, type);
+ }
+
+ static void virtio_rotary_device_realize(DeviceState *dev, Error **errp)
+ {
+ rotary = VIRTIO_MARU_ROTARY(dev);
+
+ LOG_INFO("%s\n", __func__);
+
+ input_common_realize(dev, TYPE_VIRTIO_MARU_ROTARY,
+ VIRTIO_ID_MARU_ROTARY, NULL, NULL,
+ rotary_events, sizeof(rotary_event));
+ }
+
+ static void virtio_rotary_device_unrealize(DeviceState *dev, Error **errp)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_unrealize(dev);
+ }
+
+ static void virtio_rotary_device_reset(VirtIODevice *vdev)
+ {
+ LOG_INFO("%s\n", __func__);
+
+ input_common_reset(vdev);
+ }
+
+ static void virtio_rotary_class_init(ObjectClass *klass, void *data)
+ {
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ vdc->unrealize = virtio_rotary_device_unrealize;
+ vdc->realize = virtio_rotary_device_realize;
+ vdc->reset = virtio_rotary_device_reset;
+ vdc->get_config = default_get_config;
+ vdc->set_config = default_set_config;
+ vdc->get_features = default_get_features;
+ }
+
+ static const TypeInfo virtio_rotary_info = {
+ .name = TYPE_VIRTIO_MARU_ROTARY,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIORotary),
+ .class_init = virtio_rotary_class_init,
+ };
+
+
+
+ //
+ // Register devices
+ //
+
+ static void virtio_register_types(void)
+ {
+ type_register_static(&virtio_touchscreen_info);
+ type_register_static(&virtio_keyboard_info);
+ type_register_static(&virtio_hwkey_info);
+ type_register_static(&virtio_rotary_info);
+ }
+
+ type_init(virtio_register_types)