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 "trace.h"
  11
  12#include "hw/qdev.h"
  13#include "hw/virtio/virtio.h"
  14#include "hw/virtio/virtio-input.h"
  15
  16#include "standard-headers/linux/input.h"
  17
  18#define VIRTIO_INPUT_VM_VERSION 1
  19
  20/* ----------------------------------------------------------------- */
  21
  22void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
  23{
  24    VirtQueueElement *elem;
  25    unsigned have, need;
  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 = realloc(vinput->queue, vinput->qsize *
  36                                sizeof(virtio_input_event));
  37    }
  38    vinput->queue[vinput->qindex++] = *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    need = sizeof(virtio_input_event) * vinput->qindex;
  48    virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
  49    if (have < need) {
  50        vinput->qindex = 0;
  51        trace_virtio_input_queue_full();
  52        return;
  53    }
  54
  55    /* ... and finally pass them to the guest */
  56    for (i = 0; i < vinput->qindex; i++) {
  57        elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
  58        if (!elem) {
  59            /* should not happen, we've checked for space beforehand */
  60            fprintf(stderr, "%s: Huh?  No vq elem available ...\n", __func__);
  61            return;
  62        }
  63        len = iov_from_buf(elem->in_sg, elem->in_num,
  64                           0, vinput->queue+i, 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_load(QEMUFile *f, void *opaque, size_t size)
 221{
 222    VirtIOInput *vinput = opaque;
 223    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
 224    VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
 225    int ret;
 226
 227    ret = virtio_load(vdev, f, VIRTIO_INPUT_VM_VERSION);
 228    if (ret) {
 229        return ret;
 230    }
 231
 232    /* post_load() */
 233    vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
 234    if (vic->change_active) {
 235        vic->change_active(vinput);
 236    }
 237    return 0;
 238}
 239
 240static void virtio_input_device_realize(DeviceState *dev, Error **errp)
 241{
 242    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 243    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 244    VirtIOInput *vinput = VIRTIO_INPUT(dev);
 245    VirtIOInputConfig *cfg;
 246    Error *local_err = NULL;
 247
 248    if (vic->realize) {
 249        vic->realize(dev, &local_err);
 250        if (local_err) {
 251            error_propagate(errp, local_err);
 252            return;
 253        }
 254    }
 255
 256    virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
 257                              vinput->serial);
 258
 259    QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
 260        if (vinput->cfg_size < cfg->config.size) {
 261            vinput->cfg_size = cfg->config.size;
 262        }
 263    }
 264    vinput->cfg_size += 8;
 265    assert(vinput->cfg_size <= sizeof(virtio_input_config));
 266
 267    virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
 268                vinput->cfg_size);
 269    vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
 270    vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
 271}
 272
 273static void virtio_input_finalize(Object *obj)
 274{
 275    VirtIOInput *vinput = VIRTIO_INPUT(obj);
 276    VirtIOInputConfig *cfg, *next;
 277
 278    QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) {
 279        QTAILQ_REMOVE(&vinput->cfg_list, cfg, node);
 280        g_free(cfg);
 281    }
 282}
 283static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
 284{
 285    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 286    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 287    Error *local_err = NULL;
 288
 289    if (vic->unrealize) {
 290        vic->unrealize(dev, &local_err);
 291        if (local_err) {
 292            error_propagate(errp, local_err);
 293            return;
 294        }
 295    }
 296    virtio_cleanup(vdev);
 297}
 298
 299VMSTATE_VIRTIO_DEVICE(input, VIRTIO_INPUT_VM_VERSION, virtio_input_load,
 300                      virtio_vmstate_save);
 301
 302static Property virtio_input_properties[] = {
 303    DEFINE_PROP_STRING("serial", VirtIOInput, serial),
 304    DEFINE_PROP_END_OF_LIST(),
 305};
 306
 307static void virtio_input_class_init(ObjectClass *klass, void *data)
 308{
 309    DeviceClass *dc = DEVICE_CLASS(klass);
 310    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 311
 312    dc->props          = virtio_input_properties;
 313    dc->vmsd           = &vmstate_virtio_input;
 314    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 315    vdc->realize      = virtio_input_device_realize;
 316    vdc->unrealize    = virtio_input_device_unrealize;
 317    vdc->get_config   = virtio_input_get_config;
 318    vdc->set_config   = virtio_input_set_config;
 319    vdc->get_features = virtio_input_get_features;
 320    vdc->set_status   = virtio_input_set_status;
 321    vdc->reset        = virtio_input_reset;
 322}
 323
 324static const TypeInfo virtio_input_info = {
 325    .name          = TYPE_VIRTIO_INPUT,
 326    .parent        = TYPE_VIRTIO_DEVICE,
 327    .instance_size = sizeof(VirtIOInput),
 328    .class_size    = sizeof(VirtIOInputClass),
 329    .class_init    = virtio_input_class_init,
 330    .abstract      = true,
 331    .instance_finalize = virtio_input_finalize,
 332};
 333
 334/* ----------------------------------------------------------------- */
 335
 336static void virtio_register_types(void)
 337{
 338    type_register_static(&virtio_input_info);
 339}
 340
 341type_init(virtio_register_types)
 342