qemu/contrib/vhost-user-input/main.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
   9#include <glib.h>
  10#include <linux/input.h>
  11
  12#include "qemu/iov.h"
  13#include "qemu/bswap.h"
  14#include "qemu/sockets.h"
  15#include "contrib/libvhost-user/libvhost-user.h"
  16#include "contrib/libvhost-user/libvhost-user-glib.h"
  17#include "standard-headers/linux/virtio_input.h"
  18#include "qapi/error.h"
  19
  20enum {
  21    VHOST_USER_INPUT_MAX_QUEUES = 2,
  22};
  23
  24typedef struct virtio_input_event virtio_input_event;
  25typedef struct virtio_input_config virtio_input_config;
  26
  27typedef struct VuInput {
  28    VugDev dev;
  29    GSource *evsrc;
  30    int evdevfd;
  31    GArray *config;
  32    virtio_input_config *sel_config;
  33    struct {
  34        virtio_input_event event;
  35        VuVirtqElement *elem;
  36    } *queue;
  37    uint32_t qindex, qsize;
  38} VuInput;
  39
  40static void vi_input_send(VuInput *vi, struct virtio_input_event *event)
  41{
  42    VuDev *dev = &vi->dev.parent;
  43    VuVirtq *vq = vu_get_queue(dev, 0);
  44    VuVirtqElement *elem;
  45    int i, len;
  46
  47    /* queue up events ... */
  48    if (vi->qindex == vi->qsize) {
  49        vi->qsize++;
  50        vi->queue = g_realloc_n(vi->queue, vi->qsize, sizeof(vi->queue[0]));
  51    }
  52    vi->queue[vi->qindex++].event = *event;
  53
  54    /* ... until we see a report sync ... */
  55    if (event->type != htole16(EV_SYN) ||
  56        event->code != htole16(SYN_REPORT)) {
  57        return;
  58    }
  59
  60    /* ... then check available space ... */
  61    for (i = 0; i < vi->qindex; i++) {
  62        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
  63        if (!elem) {
  64            while (--i >= 0) {
  65                vu_queue_unpop(dev, vq, vi->queue[i].elem, 0);
  66            }
  67            vi->qindex = 0;
  68            g_warning("virtio-input queue full");
  69            return;
  70        }
  71        vi->queue[i].elem = elem;
  72    }
  73
  74    /* ... and finally pass them to the guest */
  75    for (i = 0; i < vi->qindex; i++) {
  76        elem = vi->queue[i].elem;
  77        len = iov_from_buf(elem->in_sg, elem->in_num,
  78                           0, &vi->queue[i].event, sizeof(virtio_input_event));
  79        vu_queue_push(dev, vq, elem, len);
  80        g_free(elem);
  81    }
  82
  83    vu_queue_notify(&vi->dev.parent, vq);
  84    vi->qindex = 0;
  85}
  86
  87static void
  88vi_evdev_watch(VuDev *dev, int condition, void *data)
  89{
  90    VuInput *vi = data;
  91    int fd = vi->evdevfd;
  92
  93    g_debug("Got evdev condition %x", condition);
  94
  95    struct virtio_input_event virtio;
  96    struct input_event evdev;
  97    int rc;
  98
  99    for (;;) {
 100        rc = read(fd, &evdev, sizeof(evdev));
 101        if (rc != sizeof(evdev)) {
 102            break;
 103        }
 104
 105        g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value);
 106
 107        virtio.type  = htole16(evdev.type);
 108        virtio.code  = htole16(evdev.code);
 109        virtio.value = htole32(evdev.value);
 110        vi_input_send(vi, &virtio);
 111    }
 112}
 113
 114
 115static void vi_handle_status(VuInput *vi, virtio_input_event *event)
 116{
 117    struct input_event evdev;
 118    int rc;
 119
 120    if (gettimeofday(&evdev.time, NULL)) {
 121        perror("vi_handle_status: gettimeofday");
 122        return;
 123    }
 124
 125    evdev.type = le16toh(event->type);
 126    evdev.code = le16toh(event->code);
 127    evdev.value = le32toh(event->value);
 128
 129    rc = write(vi->evdevfd, &evdev, sizeof(evdev));
 130    if (rc == -1) {
 131        perror("vi_host_handle_status: write");
 132    }
 133}
 134
 135static void vi_handle_sts(VuDev *dev, int qidx)
 136{
 137    VuInput *vi = container_of(dev, VuInput, dev.parent);
 138    VuVirtq *vq = vu_get_queue(dev, qidx);
 139    virtio_input_event event;
 140    VuVirtqElement *elem;
 141    int len;
 142
 143    g_debug("%s", G_STRFUNC);
 144
 145    for (;;) {
 146        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
 147        if (!elem) {
 148            break;
 149        }
 150
 151        memset(&event, 0, sizeof(event));
 152        len = iov_to_buf(elem->out_sg, elem->out_num,
 153                         0, &event, sizeof(event));
 154        vi_handle_status(vi, &event);
 155        vu_queue_push(dev, vq, elem, len);
 156        g_free(elem);
 157    }
 158
 159    vu_queue_notify(&vi->dev.parent, vq);
 160}
 161
 162static void
 163vi_panic(VuDev *dev, const char *msg)
 164{
 165    g_critical("%s\n", msg);
 166    exit(EXIT_FAILURE);
 167}
 168
 169static void
 170vi_queue_set_started(VuDev *dev, int qidx, bool started)
 171{
 172    VuInput *vi = container_of(dev, VuInput, dev.parent);
 173    VuVirtq *vq = vu_get_queue(dev, qidx);
 174
 175    g_debug("queue started %d:%d", qidx, started);
 176
 177    if (qidx == 1) {
 178        vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL);
 179    }
 180
 181    started = vu_queue_started(dev, vu_get_queue(dev, 0)) &&
 182        vu_queue_started(dev, vu_get_queue(dev, 1));
 183
 184    if (started && !vi->evsrc) {
 185        vi->evsrc = vug_source_new(&vi->dev, vi->evdevfd,
 186                                   G_IO_IN, vi_evdev_watch, vi);
 187    }
 188
 189    if (!started && vi->evsrc) {
 190        g_source_destroy(vi->evsrc);
 191        vi->evsrc = NULL;
 192    }
 193}
 194
 195static virtio_input_config *
 196vi_find_config(VuInput *vi, uint8_t select, uint8_t subsel)
 197{
 198    virtio_input_config *cfg;
 199    int i;
 200
 201    for (i = 0; i < vi->config->len; i++) {
 202        cfg = &g_array_index(vi->config, virtio_input_config, i);
 203        if (select == cfg->select && subsel == cfg->subsel) {
 204            return cfg;
 205        }
 206    }
 207
 208    return NULL;
 209}
 210
 211static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len)
 212{
 213    VuInput *vi = container_of(dev, VuInput, dev.parent);
 214
 215    g_return_val_if_fail(len <= sizeof(*vi->sel_config), -1);
 216
 217    if (vi->sel_config) {
 218        memcpy(config, vi->sel_config, len);
 219    } else {
 220        memset(config, 0, len);
 221    }
 222
 223    return 0;
 224}
 225
 226static int vi_set_config(VuDev *dev, const uint8_t *data,
 227                         uint32_t offset, uint32_t size,
 228                         uint32_t flags)
 229{
 230    VuInput *vi = container_of(dev, VuInput, dev.parent);
 231    virtio_input_config *config = (virtio_input_config *)data;
 232
 233    vi->sel_config = vi_find_config(vi, config->select, config->subsel);
 234
 235    return 0;
 236}
 237
 238static const VuDevIface vuiface = {
 239    .queue_set_started = vi_queue_set_started,
 240    .get_config = vi_get_config,
 241    .set_config = vi_set_config,
 242};
 243
 244static void
 245vi_bits_config(VuInput *vi, int type, int count)
 246{
 247    virtio_input_config bits;
 248    int rc, i, size = 0;
 249
 250    memset(&bits, 0, sizeof(bits));
 251    rc = ioctl(vi->evdevfd, EVIOCGBIT(type, count / 8), bits.u.bitmap);
 252    if (rc < 0) {
 253        return;
 254    }
 255
 256    for (i = 0; i < count / 8; i++) {
 257        if (bits.u.bitmap[i]) {
 258            size = i + 1;
 259        }
 260    }
 261    if (size == 0) {
 262        return;
 263    }
 264
 265    bits.select = VIRTIO_INPUT_CFG_EV_BITS;
 266    bits.subsel = type;
 267    bits.size   = size;
 268    g_array_append_val(vi->config, bits);
 269}
 270
 271static char *opt_evdev;
 272static int opt_fdnum = -1;
 273static char *opt_socket_path;
 274static gboolean opt_nograb;
 275static gboolean opt_print_caps;
 276
 277static GOptionEntry entries[] = {
 278    { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps,
 279      "Print capabilities", NULL },
 280    { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &opt_nograb,
 281      "Don't grab device", NULL },
 282    { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum,
 283      "Use inherited fd socket", "FDNUM" },
 284    { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path,
 285      "Use UNIX socket path", "PATH" },
 286    { "evdev-path", 'p', 0, G_OPTION_ARG_FILENAME, &opt_evdev,
 287      "evdev input device path", "PATH" },
 288    { NULL, }
 289};
 290
 291int
 292main(int argc, char *argv[])
 293{
 294    GMainLoop *loop = NULL;
 295    VuInput vi = { 0, };
 296    int rc, ver, fd;
 297    virtio_input_config id;
 298    struct input_id ids;
 299    GError *error = NULL;
 300    GOptionContext *context;
 301
 302    context = g_option_context_new(NULL);
 303    g_option_context_add_main_entries(context, entries, NULL);
 304    if (!g_option_context_parse(context, &argc, &argv, &error)) {
 305        g_printerr("Option parsing failed: %s\n", error->message);
 306        exit(EXIT_FAILURE);
 307    }
 308    if (opt_print_caps) {
 309        g_print("{\n");
 310        g_print("  \"type\": \"input\",\n");
 311        g_print("  \"features\": [\n");
 312        g_print("    \"evdev-path\",\n");
 313        g_print("    \"no-grab\"\n");
 314        g_print("  ]\n");
 315        g_print("}\n");
 316        exit(EXIT_SUCCESS);
 317    }
 318    if (!opt_evdev) {
 319        g_printerr("Please specify an evdev path\n");
 320        exit(EXIT_FAILURE);
 321    }
 322    if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) {
 323        g_printerr("Please specify either --fd or --socket-path\n");
 324        exit(EXIT_FAILURE);
 325    }
 326
 327    vi.evdevfd = open(opt_evdev, O_RDWR);
 328    if (vi.evdevfd < 0) {
 329        g_printerr("Failed to open evdev: %s\n", g_strerror(errno));
 330        exit(EXIT_FAILURE);
 331    }
 332
 333    rc = ioctl(vi.evdevfd, EVIOCGVERSION, &ver);
 334    if (rc < 0) {
 335        g_printerr("%s: is not an evdev device\n", argv[1]);
 336        exit(EXIT_FAILURE);
 337    }
 338
 339    if (!opt_nograb) {
 340        rc = ioctl(vi.evdevfd, EVIOCGRAB, 1);
 341        if (rc < 0) {
 342            g_printerr("Failed to grab device\n");
 343            exit(EXIT_FAILURE);
 344        }
 345    }
 346
 347    vi.config = g_array_new(false, false, sizeof(virtio_input_config));
 348    memset(&id, 0, sizeof(id));
 349    if (ioctl(vi.evdevfd, EVIOCGNAME(sizeof(id.u.string) - 1),
 350              id.u.string) < 0) {
 351        g_printerr("Failed to get evdev name: %s\n", g_strerror(errno));
 352        exit(EXIT_FAILURE);
 353    }
 354    id.select = VIRTIO_INPUT_CFG_ID_NAME;
 355    id.size = strlen(id.u.string);
 356    g_array_append_val(vi.config, id);
 357
 358    if (ioctl(vi.evdevfd, EVIOCGID, &ids) == 0) {
 359        memset(&id, 0, sizeof(id));
 360        id.select = VIRTIO_INPUT_CFG_ID_DEVIDS;
 361        id.size = sizeof(struct virtio_input_devids);
 362        id.u.ids.bustype = cpu_to_le16(ids.bustype);
 363        id.u.ids.vendor  = cpu_to_le16(ids.vendor);
 364        id.u.ids.product = cpu_to_le16(ids.product);
 365        id.u.ids.version = cpu_to_le16(ids.version);
 366        g_array_append_val(vi.config, id);
 367    }
 368
 369    vi_bits_config(&vi, EV_KEY, KEY_CNT);
 370    vi_bits_config(&vi, EV_REL, REL_CNT);
 371    vi_bits_config(&vi, EV_ABS, ABS_CNT);
 372    vi_bits_config(&vi, EV_MSC, MSC_CNT);
 373    vi_bits_config(&vi, EV_SW,  SW_CNT);
 374    g_debug("config length: %u", vi.config->len);
 375
 376    if (opt_socket_path) {
 377        int lsock = unix_listen(opt_socket_path, &error_fatal);
 378        if (lsock < 0) {
 379            g_printerr("Failed to listen on %s.\n", opt_socket_path);
 380            exit(EXIT_FAILURE);
 381        }
 382        fd = accept(lsock, NULL, NULL);
 383        close(lsock);
 384    } else {
 385        fd = opt_fdnum;
 386    }
 387    if (fd == -1) {
 388        g_printerr("Invalid vhost-user socket.\n");
 389        exit(EXIT_FAILURE);
 390    }
 391
 392    if (!vug_init(&vi.dev, VHOST_USER_INPUT_MAX_QUEUES, fd, vi_panic,
 393                  &vuiface)) {
 394        g_printerr("Failed to initialize libvhost-user-glib.\n");
 395        exit(EXIT_FAILURE);
 396    }
 397
 398    loop = g_main_loop_new(NULL, FALSE);
 399    g_main_loop_run(loop);
 400    g_main_loop_unref(loop);
 401
 402    vug_deinit(&vi.dev);
 403
 404    if (vi.evsrc) {
 405        g_source_unref(vi.evsrc);
 406    }
 407    g_array_free(vi.config, TRUE);
 408    g_free(vi.queue);
 409    return 0;
 410}
 411