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    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 = g_realloc(vinput->queue, vinput->qsize *
  35                                  sizeof(vinput->queue[0]));
  36    }
  37    vinput->queue[vinput->qindex++].event = *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    for (i = 0; i < vinput->qindex; i++) {
  47        elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
  48        if (!elem) {
  49            while (--i >= 0) {
  50                virtqueue_unpop(vinput->evt, vinput->queue[i].elem, 0);
  51            }
  52            vinput->qindex = 0;
  53            trace_virtio_input_queue_full();
  54            return;
  55        }
  56        vinput->queue[i].elem = elem;
  57    }
  58
  59    /* ... and finally pass them to the guest */
  60    for (i = 0; i < vinput->qindex; i++) {
  61        elem = vinput->queue[i].elem;
  62        len = iov_from_buf(elem->in_sg, elem->in_num,
  63                           0, &vinput->queue[i].event, 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 int virtio_input_post_load(void *opaque, int version_id)
 220{
 221    VirtIOInput *vinput = opaque;
 222    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
 223    VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
 224
 225    vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
 226    if (vic->change_active) {
 227        vic->change_active(vinput);
 228    }
 229    return 0;
 230}
 231
 232static void virtio_input_device_realize(DeviceState *dev, Error **errp)
 233{
 234    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 235    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 236    VirtIOInput *vinput = VIRTIO_INPUT(dev);
 237    VirtIOInputConfig *cfg;
 238    Error *local_err = NULL;
 239
 240    if (vic->realize) {
 241        vic->realize(dev, &local_err);
 242        if (local_err) {
 243            error_propagate(errp, local_err);
 244            return;
 245        }
 246    }
 247
 248    virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
 249                              vinput->serial);
 250
 251    QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
 252        if (vinput->cfg_size < cfg->config.size) {
 253            vinput->cfg_size = cfg->config.size;
 254        }
 255    }
 256    vinput->cfg_size += 8;
 257    assert(vinput->cfg_size <= sizeof(virtio_input_config));
 258
 259    virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
 260                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}
 277static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
 278{
 279    VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
 280    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 281    Error *local_err = NULL;
 282
 283    if (vic->unrealize) {
 284        vic->unrealize(dev, &local_err);
 285        if (local_err) {
 286            error_propagate(errp, local_err);
 287            return;
 288        }
 289    }
 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    dc->props          = 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