qemu/hw/input/virtio-input.c
<<
>>
Prefs
   1/*
   2 * This work is licensed under the terms of the GNU GPL, version 2 or
   3 * (at your option) any later version.  See the COPYING file in the
   4 * top-level directory.
   5 */
   6
   7#include "qemu/osdep.h"
   8#include "qapi/error.h"
   9#include "qemu/iov.h"
  10#include "qemu/module.h"
  11#include "trace.h"
  12
  13#include "hw/virtio/virtio.h"
  14#include "hw/qdev-properties.h"
  15#include "hw/virtio/virtio-input.h"
  16
  17#include "standard-headers/linux/input.h"
  18
  19#define VIRTIO_INPUT_VM_VERSION 1
  20
  21/* ----------------------------------------------------------------- */
  22
  23void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
  24{
  25    VirtQueueElement *elem;
  26    int i, len;
  27
  28    if (!vinput->active) {
  29        return;
  30    }
  31
  32    /* queue up events ... */
  33    if (vinput->qindex == vinput->qsize) {
  34        vinput->qsize++;
  35        vinput->queue = g_realloc(vinput->queue, vinput->qsize *
  36                                  sizeof(vinput->queue[0]));
  37    }
  38    vinput->queue[vinput->qindex++].event = *event;
  39
  40    /* ... until we see a report sync ... */
  41    if (event->type != cpu_to_le16(EV_SYN) ||
  42        event->code != cpu_to_le16(SYN_REPORT)) {
  43        return;
  44    }
  45
  46    /* ... then check available space ... */
  47    for (i = 0; i < vinput->qindex; i++) {
  48        elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
  49        if (!elem) {
  50            while (--i >= 0) {
  51                virtqueue_unpop(vinput->evt, vinput->queue[i].elem, 0);
  52            }
  53            vinput->qindex = 0;
  54            trace_virtio_input_queue_full();
  55            return;
  56        }
  57        vinput->queue[i].elem = elem;
  58    }
  59
  60    /* ... and finally pass them to the guest */
  61    for (i = 0; i < vinput->qindex; i++) {
  62        elem = vinput->queue[i].elem;
  63        len = iov_from_buf(elem->in_sg, elem->in_num,
  64                           0, &vinput->queue[i].event, sizeof(virtio_input_event));
  65        virtqueue_push(vinput->evt, elem, len);
  66        g_free(elem);
  67    }
  68    virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
  69    vinput->qindex = 0;
  70}
  71
  72static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
  73{
  74    /* nothing */
  75}
  76
  77static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
  78{
  79    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
  80    VirtIOInput *vinput = VIRTIO_INPUT(vdev);
  81    virtio_input_event event;
  82    VirtQueueElement *elem;
  83    int len;
  84
  85    for (;;) {
  86        elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
  87        if (!elem) {
  88            break;
  89        }
  90
  91        memset(&event, 0, sizeof(event));
  92        len = iov_to_buf(elem->out_sg, elem->out_num,
  93                         0, &event, sizeof(event));
  94        if (vic->handle_status) {
  95            vic->handle_status(vinput, &event);
  96        }
  97        virtqueue_push(vinput->sts, elem, len);
  98        g_free(elem);
  99    }
 100    virtio_notify(vdev, vinput->sts);
 101}
 102
 103virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
 104                                              uint8_t select,
 105                                              uint8_t subsel)
 106{
 107    VirtIOInputConfig *cfg;
 108
 109    QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
 110        if (select == cfg->config.select &&
 111            subsel == cfg->config.subsel) {
 112            return &cfg->config;
 113        }
 114    }
 115    return NULL;
 116}
 117
 118void virtio_input_add_config(VirtIOInput *vinput,
 119                             virtio_input_config *config)
 120{
 121    VirtIOInputConfig *cfg;
 122
 123    if (virtio_input_find_config(vinput, config->select, config->subsel)) {
 124        /* should not happen */
 125        fprintf(stderr, "%s: duplicate config: %d/%d\n",
 126                __func__, config->select, config->subsel);
 127        abort();
 128    }
 129
 130    cfg = g_new0(VirtIOInputConfig, 1);
 131    cfg->config = *config;
 132    QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
 133}
 134
 135void virtio_input_init_config(VirtIOInput *vinput,
 136                              virtio_input_config *config)
 137{
 138    int i = 0;
 139
 140    QTAILQ_INIT(&vinput->cfg_list);
 141    while (config[i].select) {
 142        virtio_input_add_config(vinput, config + i);
 143        i++;
 144    }
 145}
 146
 147void virtio_input_idstr_config(VirtIOInput *vinput,
 148                               uint8_t select, const char *string)
 149{
 150    virtio_input_config id;
 151
 152    if (!string) {
 153        return;
 154    }
 155    memset(&id, 0, sizeof(id));
 156    id.select = select;
 157    id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
 158    virtio_input_add_config(vinput, &id);
 159}
 160
 161static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
 162{
 163    VirtIOInput *vinput = VIRTIO_INPUT(vdev);
 164    virtio_input_config *config;
 165
 166    config = virtio_input_find_config(vinput, vinput->cfg_select,
 167                                      vinput->cfg_subsel);
 168    if (config) {
 169        memcpy(config_data, config, vinput->cfg_size);
 170    } else {
 171        memset(config_data, 0, vinput->cfg_size);
 172    }
 173}
 174
 175static void virtio_input_set_config(VirtIODevice *vdev,
 176                                    const uint8_t *config_data)
 177{
 178    VirtIOInput *vinput = VIRTIO_INPUT(vdev);
 179    virtio_input_config *config = (virtio_input_config *)config_data;
 180
 181    vinput->cfg_select = config->select;
 182    vinput->cfg_subsel = config->subsel;
 183    virtio_notify_config(vdev);
 184}
 185
 186static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
 187                                          Error **errp)
 188{
 189    return f;
 190}
 191
 192static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
 193{
 194    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
 195    VirtIOInput *vinput = VIRTIO_INPUT(vdev);
 196
 197    if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
 198        if (!vinput->active) {
 199            vinput->active = true;
 200            if (vic->change_active) {
 201                vic->change_active(vinput);
 202            }
 203        }
 204    }
 205}
 206
 207static void virtio_input_reset(VirtIODevice *vdev)
 208{
 209    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
 210    VirtIOInput *vinput = VIRTIO_INPUT(vdev);
 211
 212    if (vinput->active) {
 213        vinput->active = false;
 214        if (vic->change_active) {
 215            vic->change_active(vinput);
 216        }
 217    }
 218}
 219
 220static int virtio_input_post_load(void *opaque, int version_id)
 221{
 222    VirtIOInput *vinput = opaque;
 223    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
 224    VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
 225
 226    vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
 227    if (vic->change_active) {
 228        vic->change_active(vinput);
 229    }
 230    return 0;
 231}
 232
 233static void virtio_input_device_realize(DeviceState *dev, Error **errp)
 234{
 235    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 236    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 237    VirtIOInput *vinput = VIRTIO_INPUT(dev);
 238    VirtIOInputConfig *cfg;
 239    Error *local_err = NULL;
 240
 241    if (vic->realize) {
 242        vic->realize(dev, &local_err);
 243        if (local_err) {
 244            error_propagate(errp, local_err);
 245            return;
 246        }
 247    }
 248
 249    virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
 250                              vinput->serial);
 251
 252    QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
 253        if (vinput->cfg_size < cfg->config.size) {
 254            vinput->cfg_size = cfg->config.size;
 255        }
 256    }
 257    vinput->cfg_size += 8;
 258    assert(vinput->cfg_size <= sizeof(virtio_input_config));
 259
 260    virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
 261                vinput->cfg_size);
 262    vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
 263    vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
 264}
 265
 266static void virtio_input_finalize(Object *obj)
 267{
 268    VirtIOInput *vinput = VIRTIO_INPUT(obj);
 269    VirtIOInputConfig *cfg, *next;
 270
 271    QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) {
 272        QTAILQ_REMOVE(&vinput->cfg_list, cfg, node);
 273        g_free(cfg);
 274    }
 275
 276    g_free(vinput->queue);
 277}
 278
 279static void virtio_input_device_unrealize(DeviceState *dev)
 280{
 281    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 282    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 283    VirtIOInput *vinput = VIRTIO_INPUT(dev);
 284
 285    if (vic->unrealize) {
 286        vic->unrealize(dev);
 287    }
 288    virtio_delete_queue(vinput->evt);
 289    virtio_delete_queue(vinput->sts);
 290    virtio_cleanup(vdev);
 291}
 292
 293static const VMStateDescription vmstate_virtio_input = {
 294    .name = "virtio-input",
 295    .minimum_version_id = VIRTIO_INPUT_VM_VERSION,
 296    .version_id = VIRTIO_INPUT_VM_VERSION,
 297    .fields = (VMStateField[]) {
 298        VMSTATE_VIRTIO_DEVICE,
 299        VMSTATE_END_OF_LIST()
 300    },
 301    .post_load = virtio_input_post_load,
 302};
 303
 304static Property virtio_input_properties[] = {
 305    DEFINE_PROP_STRING("serial", VirtIOInput, serial),
 306    DEFINE_PROP_END_OF_LIST(),
 307};
 308
 309static void virtio_input_class_init(ObjectClass *klass, void *data)
 310{
 311    DeviceClass *dc = DEVICE_CLASS(klass);
 312    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 313
 314    device_class_set_props(dc, virtio_input_properties);
 315    dc->vmsd           = &vmstate_virtio_input;
 316    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 317    vdc->realize      = virtio_input_device_realize;
 318    vdc->unrealize    = virtio_input_device_unrealize;
 319    vdc->get_config   = virtio_input_get_config;
 320    vdc->set_config   = virtio_input_set_config;
 321    vdc->get_features = virtio_input_get_features;
 322    vdc->set_status   = virtio_input_set_status;
 323    vdc->reset        = virtio_input_reset;
 324}
 325
 326static const TypeInfo virtio_input_info = {
 327    .name          = TYPE_VIRTIO_INPUT,
 328    .parent        = TYPE_VIRTIO_DEVICE,
 329    .instance_size = sizeof(VirtIOInput),
 330    .class_size    = sizeof(VirtIOInputClass),
 331    .class_init    = virtio_input_class_init,
 332    .abstract      = true,
 333    .instance_finalize = virtio_input_finalize,
 334};
 335
 336/* ----------------------------------------------------------------- */
 337
 338static void virtio_register_types(void)
 339{
 340    type_register_static(&virtio_input_info);
 341}
 342
 343type_init(virtio_register_types)
 344