qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-input.c (9818B)


      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 
     23 void 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 
     72 static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
     73 {
     74     /* nothing */
     75 }
     76 
     77 static 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 
    103 virtio_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 
    118 void 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 
    135 void 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 
    147 void 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 
    161 static 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 
    175 static 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 
    186 static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
    187                                           Error **errp)
    188 {
    189     return f;
    190 }
    191 
    192 static 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 
    207 static 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 
    220 static 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 
    233 static 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 
    265 static 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 
    278 static 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 
    292 static 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 
    303 static Property virtio_input_properties[] = {
    304     DEFINE_PROP_STRING("serial", VirtIOInput, serial),
    305     DEFINE_PROP_END_OF_LIST(),
    306 };
    307 
    308 static 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 
    325 static 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 
    337 static void virtio_register_types(void)
    338 {
    339     type_register_static(&virtio_input_info);
    340 }
    341 
    342 type_init(virtio_register_types)