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