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_ID_INPUT, vinput->cfg_size);
 261    vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
 262    vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
 263}
 264
 265static void virtio_input_finalize(Object *obj)
 266{
 267    VirtIOInput *vinput = VIRTIO_INPUT(obj);
 268    VirtIOInputConfig *cfg, *next;
 269
 270    QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) {
 271        QTAILQ_REMOVE(&vinput->cfg_list, cfg, node);
 272        g_free(cfg);
 273    }
 274
 275    g_free(vinput->queue);
 276}
 277
 278static void virtio_input_device_unrealize(DeviceState *dev)
 279{
 280    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 281    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 282    VirtIOInput *vinput = VIRTIO_INPUT(dev);
 283
 284    if (vic->unrealize) {
 285        vic->unrealize(dev);
 286    }
 287    virtio_delete_queue(vinput->evt);
 288    virtio_delete_queue(vinput->sts);
 289    virtio_cleanup(vdev);
 290}
 291
 292static const VMStateDescription vmstate_virtio_input = {
 293    .name = "virtio-input",
 294    .minimum_version_id = VIRTIO_INPUT_VM_VERSION,
 295    .version_id = VIRTIO_INPUT_VM_VERSION,
 296    .fields = (VMStateField[]) {
 297        VMSTATE_VIRTIO_DEVICE,
 298        VMSTATE_END_OF_LIST()
 299    },
 300    .post_load = virtio_input_post_load,
 301};
 302
 303static Property virtio_input_properties[] = {
 304    DEFINE_PROP_STRING("serial", VirtIOInput, serial),
 305    DEFINE_PROP_END_OF_LIST(),
 306};
 307
 308static void virtio_input_class_init(ObjectClass *klass, void *data)
 309{
 310    DeviceClass *dc = DEVICE_CLASS(klass);
 311    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 312
 313    device_class_set_props(dc, virtio_input_properties);
 314    dc->vmsd           = &vmstate_virtio_input;
 315    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 316    vdc->realize      = virtio_input_device_realize;
 317    vdc->unrealize    = virtio_input_device_unrealize;
 318    vdc->get_config   = virtio_input_get_config;
 319    vdc->set_config   = virtio_input_set_config;
 320    vdc->get_features = virtio_input_get_features;
 321    vdc->set_status   = virtio_input_set_status;
 322    vdc->reset        = virtio_input_reset;
 323}
 324
 325static const TypeInfo virtio_input_info = {
 326    .name          = TYPE_VIRTIO_INPUT,
 327    .parent        = TYPE_VIRTIO_DEVICE,
 328    .instance_size = sizeof(VirtIOInput),
 329    .class_size    = sizeof(VirtIOInputClass),
 330    .class_init    = virtio_input_class_init,
 331    .abstract      = true,
 332    .instance_finalize = virtio_input_finalize,
 333};
 334
 335/* ----------------------------------------------------------------- */
 336
 337static void virtio_register_types(void)
 338{
 339    type_register_static(&virtio_input_info);
 340}
 341
 342type_init(virtio_register_types)
 343