mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-04-28 11:43:56 +00:00
Modified the copyright year range in code, and corrected "int32_tel" into "Intel" in two "hypervisor/include/debug/profiling.h" and "hypervisor/include/debug/profiling_internal.h". Tracked-On: #7559 Signed-off-by: Ziheng Li <ziheng.li@intel.com>
764 lines
17 KiB
C
764 lines
17 KiB
C
/*
|
|
* Copyright (C) 2018-2022 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
#include "dm.h"
|
|
#include "pci_core.h"
|
|
#include "virtio.h"
|
|
#include "mevent.h"
|
|
#include <linux/input.h>
|
|
|
|
static int virtio_input_debug;
|
|
#define DPRINTF(params) do { if (virtio_input_debug) pr_dbg params; } while (0)
|
|
#define WPRINTF(params) (pr_err params)
|
|
|
|
/*
|
|
* Queue definitions.
|
|
*/
|
|
#define VIRTIO_INPUT_EVENT_QUEUE 0
|
|
#define VIRTIO_INPUT_STATUS_QUEUE 1
|
|
#define VIRTIO_INPUT_MAXQ 2
|
|
|
|
/*
|
|
* Virtqueue size.
|
|
*/
|
|
#define VIRTIO_INPUT_RINGSZ 64
|
|
|
|
/*
|
|
* Default size of the buffer used to hold events between SYN
|
|
*/
|
|
#define VIRTIO_INPUT_PACKET_SIZE 10
|
|
|
|
/*
|
|
* Host capabilities
|
|
*/
|
|
#define VIRTIO_INPUT_S_HOSTCAPS (1UL << VIRTIO_F_VERSION_1)
|
|
|
|
enum virtio_input_config_select {
|
|
VIRTIO_INPUT_CFG_UNSET = 0x00,
|
|
VIRTIO_INPUT_CFG_ID_NAME = 0x01,
|
|
VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
|
|
VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
|
|
VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
|
|
VIRTIO_INPUT_CFG_EV_BITS = 0x11,
|
|
VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
|
|
};
|
|
|
|
struct virtio_input_absinfo {
|
|
uint32_t min;
|
|
uint32_t max;
|
|
uint32_t fuzz;
|
|
uint32_t flat;
|
|
uint32_t res;
|
|
};
|
|
|
|
struct virtio_input_devids {
|
|
uint16_t bustype;
|
|
uint16_t vendor;
|
|
uint16_t product;
|
|
uint16_t version;
|
|
};
|
|
|
|
struct virtio_input_event {
|
|
uint16_t type;
|
|
uint16_t code;
|
|
uint32_t value;
|
|
};
|
|
|
|
/*
|
|
* Device-specific configuration registers
|
|
* To query a specific piece of configuration information FE driver sets
|
|
* "select" and "subsel" accordingly, information size is returned in "size"
|
|
* and information data is returned in union "u"
|
|
*/
|
|
struct virtio_input_config {
|
|
uint8_t select;
|
|
uint8_t subsel;
|
|
uint8_t size;
|
|
uint8_t reserved[5];
|
|
union {
|
|
char string[128];
|
|
uint8_t bitmap[128];
|
|
struct virtio_input_absinfo abs;
|
|
struct virtio_input_devids ids;
|
|
} u;
|
|
};
|
|
|
|
struct virtio_input_event_elem {
|
|
struct virtio_input_event event;
|
|
struct iovec iov;
|
|
uint16_t idx;
|
|
};
|
|
|
|
/*
|
|
* Per-device struct
|
|
*/
|
|
struct virtio_input {
|
|
struct virtio_base base;
|
|
struct virtio_vq_info queues[VIRTIO_INPUT_MAXQ];
|
|
pthread_mutex_t mtx;
|
|
struct mevent *mevp;
|
|
uint64_t features;
|
|
struct virtio_input_config cfg;
|
|
char *evdev;
|
|
char *serial;
|
|
int fd;
|
|
bool ready;
|
|
|
|
struct virtio_input_event_elem *event_queue;
|
|
uint32_t event_qsize;
|
|
uint32_t event_qindex;
|
|
};
|
|
|
|
static void virtio_input_reset(void *);
|
|
static void virtio_input_neg_features(void *, uint64_t);
|
|
static void virtio_input_set_status(void *, uint64_t);
|
|
static int virtio_input_cfgread(void *, int, int, uint32_t *);
|
|
static int virtio_input_cfgwrite(void *, int, int, uint32_t);
|
|
static bool virtio_input_get_config(struct virtio_input *, uint8_t, uint8_t,
|
|
struct virtio_input_config *);
|
|
|
|
static struct virtio_ops virtio_input_ops = {
|
|
"virtio_input", /* our name */
|
|
VIRTIO_INPUT_MAXQ, /* we support VTCON_MAXQ virtqueues */
|
|
sizeof(struct virtio_input_config), /* config reg size */
|
|
virtio_input_reset, /* reset */
|
|
NULL, /* device-wide qnotify */
|
|
virtio_input_cfgread, /* read virtio config */
|
|
virtio_input_cfgwrite, /* write virtio config */
|
|
virtio_input_neg_features, /* apply negotiated features */
|
|
virtio_input_set_status, /* called on guest set status */
|
|
};
|
|
|
|
static void
|
|
virtio_input_reset(void *vdev)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = vdev;
|
|
|
|
DPRINTF(("vtinput: device reset requested!\n"));
|
|
vi->ready = false;
|
|
virtio_reset_dev(&vi->base);
|
|
}
|
|
|
|
static void
|
|
virtio_input_neg_features(void *vdev, uint64_t negotiated_features)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
vi->features = negotiated_features;
|
|
}
|
|
|
|
static void
|
|
virtio_input_set_status(void *vdev, uint64_t status)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
|
if (!vi->ready)
|
|
vi->ready = true;
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_cfgread(void *vdev, int offset, int size, uint32_t *retval)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
struct virtio_input_config cfg;
|
|
bool rc;
|
|
|
|
rc = virtio_input_get_config(vi, vi->cfg.select,
|
|
vi->cfg.subsel, &cfg);
|
|
if (rc)
|
|
memcpy(retval, (uint8_t *)&cfg + offset, size);
|
|
else
|
|
memset(retval, 0, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virtio_input_cfgwrite(void *vdev, int offset, int size, uint32_t val)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
if (offset == offsetof(struct virtio_input_config, select))
|
|
vi->cfg.select = (uint8_t)val;
|
|
else if (offset == offsetof(struct virtio_input_config, subsel))
|
|
vi->cfg.subsel = (uint8_t)val;
|
|
else
|
|
DPRINTF(("vtinput: write to readonly reg %d\n", offset));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_ignore_event(struct virtio_input_event *event)
|
|
{
|
|
if (!event)
|
|
return true;
|
|
|
|
/*
|
|
* EV_MSC is configured as INPUT_PASS_TO_ALL. In the use case of
|
|
* virtio-input, there is a loop as follows:
|
|
* - A mt frame with (EV_MSC,*,*) is passed to FE.
|
|
* - FE will call virtinput_status to pass (EV_MSC,*,*) back to BE.
|
|
* - BE writes this event to evdev. Because (EV_MSC,*,*)
|
|
* is configured as INPUT_PASS_TO_ALL, it will be written into
|
|
* the event buffer of evdev then be read out by BE without
|
|
* SYN followed.
|
|
* - Each mt frame will introduce one (EV_MSC,*,*).
|
|
* Later the frame becomes larger and larger...
|
|
*/
|
|
if (event->type == EV_MSC)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
virtio_input_notify_event_vq(void *vdev, struct virtio_vq_info *vq)
|
|
{
|
|
DPRINTF(("%s\n", __func__));
|
|
}
|
|
|
|
static void
|
|
virtio_input_notify_status_vq(void *vdev, struct virtio_vq_info *vq)
|
|
{
|
|
struct virtio_input *vi;
|
|
struct virtio_input_event event;
|
|
struct input_event host_event;
|
|
struct iovec iov;
|
|
int n, len;
|
|
uint16_t idx;
|
|
|
|
vi = vdev;
|
|
|
|
while (vq_has_descs(vq)) {
|
|
n = vq_getchain(vq, &idx, &iov, 1, NULL);
|
|
if (n < 0) {
|
|
WPRINTF(("virtio_input: invalid descriptors\n"));
|
|
return;
|
|
}
|
|
if (n == 0) {
|
|
WPRINTF(("virtio_input: get no available descriptors\n"));
|
|
return;
|
|
}
|
|
if (n != 1) {
|
|
WPRINTF(("virtio_input: get wrong number of available descriptors\n"));
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
return;
|
|
}
|
|
|
|
if (vi->fd > 0) {
|
|
memcpy(&event, iov.iov_base, sizeof(event));
|
|
if (!virtio_input_ignore_event(&event)) {
|
|
host_event.type = event.type;
|
|
host_event.code = event.code;
|
|
host_event.value = event.value;
|
|
if (gettimeofday(&host_event.time, NULL)) {
|
|
WPRINTF(("vtinput: gettimeofday failed\n"));
|
|
break;
|
|
}
|
|
len = write(vi->fd, &host_event, sizeof(host_event));
|
|
if (len == -1)
|
|
WPRINTF(("%s: write failed, len = %d, "
|
|
"errno = %d\n",
|
|
__func__, len, errno));
|
|
}
|
|
}
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
}
|
|
vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
|
|
}
|
|
|
|
static void
|
|
virtio_input_send_event(struct virtio_input *vi,
|
|
struct virtio_input_event *event)
|
|
{
|
|
struct virtio_vq_info *vq;
|
|
struct iovec iov;
|
|
int n, i;
|
|
uint16_t idx;
|
|
|
|
if (!vi->ready)
|
|
return;
|
|
|
|
if (vi->event_qindex == vi->event_qsize) {
|
|
vi->event_qsize++;
|
|
vi->event_queue = realloc(vi->event_queue,
|
|
vi->event_qsize *
|
|
sizeof(struct virtio_input_event_elem));
|
|
if (!vi->event_queue) {
|
|
WPRINTF(("virtio_input: realloc memory for vi->event_queue failed!\n"));
|
|
return;
|
|
}
|
|
}
|
|
vi->event_queue[vi->event_qindex].event = *event;
|
|
vi->event_qindex++;
|
|
|
|
if (event->type != EV_SYN || event->code != SYN_REPORT)
|
|
return;
|
|
|
|
vq = &vi->queues[VIRTIO_INPUT_EVENT_QUEUE];
|
|
for (i = 0; i < vi->event_qindex; i++) {
|
|
if (!vq_has_descs(vq)) {
|
|
while (i-- > 0)
|
|
vq_retchain(vq);
|
|
WPRINTF(("%s: not enough avail descs, dropped:%d\n",
|
|
__func__, vi->event_qindex));
|
|
goto out;
|
|
}
|
|
n = vq_getchain(vq, &idx, &iov, 1, NULL);
|
|
if (n < 0) {
|
|
WPRINTF(("virtio-input: invalid descriptors\n"));
|
|
return;
|
|
}
|
|
if (n == 0) {
|
|
WPRINTF(("virtio-input: get no available desciptors\n"));
|
|
return;
|
|
}
|
|
if (n != 1) {
|
|
WPRINTF(("virtio_input: get wrong number of available descriptors\n"));
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
return;
|
|
}
|
|
vi->event_queue[i].iov = iov;
|
|
vi->event_queue[i].idx = idx;
|
|
}
|
|
|
|
for (i = 0; i < vi->event_qindex; i++) {
|
|
memcpy(vi->event_queue[i].iov.iov_base,
|
|
&vi->event_queue[i].event,
|
|
sizeof(struct virtio_input_event));
|
|
vq_relchain(vq, vi->event_queue[i].idx,
|
|
sizeof(struct virtio_input_event));
|
|
}
|
|
|
|
out:
|
|
vi->event_qindex = 0;
|
|
vq_endchains(vq, 1);
|
|
}
|
|
|
|
static void
|
|
virtio_input_read_event(int fd __attribute__((unused)),
|
|
enum ev_type t __attribute__((unused)),
|
|
void *arg)
|
|
{
|
|
struct virtio_input *vi = arg;
|
|
struct virtio_input_event event;
|
|
struct input_event host_event;
|
|
int len;
|
|
|
|
while (1) {
|
|
len = read(vi->fd, &host_event, sizeof(host_event));
|
|
if (len != sizeof(host_event)) {
|
|
if (len == -1 && errno != EAGAIN)
|
|
WPRINTF(("vtinput: host read failed! "
|
|
"len = %d, errno = %d\n",
|
|
len, errno));
|
|
break;
|
|
}
|
|
|
|
event.type = host_event.type;
|
|
event.code = host_event.code;
|
|
event.value = host_event.value;
|
|
virtio_input_send_event(vi, &event);
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_get_bitmap(struct virtio_input *vi, unsigned int cmd, int count,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
int i, size = -1;
|
|
int rc;
|
|
|
|
if (count <= 0)
|
|
return -1;
|
|
|
|
if (!cfg)
|
|
return -1;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
rc = ioctl(vi->fd, cmd, cfg->u.bitmap);
|
|
if (rc < 0)
|
|
return -1;
|
|
|
|
count = count / 8;
|
|
for (i = count - 1; i >= 0; i--) {
|
|
if (cfg->u.bitmap[i]) {
|
|
size = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_propbits(struct virtio_input *vi,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
unsigned int cmd;
|
|
int size;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
cmd = EVIOCGPROP(INPUT_PROP_CNT / 8);
|
|
size = virtio_input_get_bitmap(vi, cmd, INPUT_PROP_CNT, cfg);
|
|
if (size > 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_PROP_BITS;
|
|
cfg->subsel = 0;
|
|
cfg->size = size;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_evbits(struct virtio_input *vi, int type,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
unsigned int cmd;
|
|
int count, size;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
switch (type) {
|
|
case EV_KEY:
|
|
count = KEY_CNT;
|
|
break;
|
|
case EV_REL:
|
|
count = REL_CNT;
|
|
break;
|
|
case EV_ABS:
|
|
count = ABS_CNT;
|
|
break;
|
|
case EV_MSC:
|
|
count = MSC_CNT;
|
|
break;
|
|
case EV_SW:
|
|
count = SW_CNT;
|
|
break;
|
|
case EV_LED:
|
|
count = LED_CNT;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
cmd = EVIOCGBIT(type, count / 8);
|
|
size = virtio_input_get_bitmap(vi, cmd, count, cfg);
|
|
if (size > 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_EV_BITS;
|
|
cfg->subsel = type;
|
|
cfg->size = size;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_absinfo(struct virtio_input *vi, int axis,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
struct virtio_input_config ev_cfg;
|
|
struct input_absinfo abs;
|
|
bool has_ev_abs;
|
|
int rc;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
has_ev_abs = virtio_input_get_evbits(vi, EV_ABS, &ev_cfg);
|
|
if (!has_ev_abs)
|
|
return false;
|
|
|
|
rc = ioctl(vi->fd, EVIOCGABS(axis), &abs);
|
|
if (rc < 0)
|
|
return false;
|
|
|
|
cfg->u.abs.min = abs.minimum;
|
|
cfg->u.abs.max = abs.maximum;
|
|
cfg->u.abs.fuzz = abs.fuzz;
|
|
cfg->u.abs.flat = abs.flat;
|
|
cfg->u.abs.res = abs.resolution;
|
|
|
|
cfg->select = VIRTIO_INPUT_CFG_ABS_INFO;
|
|
cfg->subsel = axis;
|
|
cfg->size = sizeof(struct virtio_input_absinfo);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_config(struct virtio_input *vi, uint8_t select,
|
|
uint8_t subsel, struct virtio_input_config *cfg)
|
|
{
|
|
struct input_id dev_ids;
|
|
bool found = false;
|
|
int rc;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
|
|
switch (select) {
|
|
case VIRTIO_INPUT_CFG_ID_NAME:
|
|
rc = ioctl(vi->fd, EVIOCGNAME(sizeof(cfg->u.string) - 1),
|
|
cfg->u.string);
|
|
if (rc >= 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_NAME;
|
|
cfg->size = strnlen(cfg->u.string,
|
|
sizeof(cfg->u.string));
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ID_SERIAL:
|
|
if (vi->serial) {
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_SERIAL;
|
|
cfg->size = snprintf(cfg->u.string,
|
|
sizeof(cfg->u.string), "%s", vi->serial);
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ID_DEVIDS:
|
|
rc = ioctl(vi->fd, EVIOCGID, &dev_ids);
|
|
if (!rc) {
|
|
cfg->u.ids.bustype = dev_ids.bustype;
|
|
cfg->u.ids.vendor = dev_ids.vendor;
|
|
cfg->u.ids.product = dev_ids.product;
|
|
cfg->u.ids.version = dev_ids.version;
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_DEVIDS;
|
|
cfg->size = sizeof(struct virtio_input_devids);
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_PROP_BITS:
|
|
found = virtio_input_get_propbits(vi, cfg);
|
|
break;
|
|
case VIRTIO_INPUT_CFG_EV_BITS:
|
|
found = virtio_input_get_evbits(vi, subsel, cfg);
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ABS_INFO:
|
|
found = virtio_input_get_absinfo(vi, subsel, cfg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
virtio_input_teardown(void *param)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = (struct virtio_input *)param;
|
|
if (vi) {
|
|
pthread_mutex_destroy(&vi->mtx);
|
|
if (vi->event_queue)
|
|
free(vi->event_queue);
|
|
if (vi->fd > 0)
|
|
close(vi->fd);
|
|
if (vi->evdev)
|
|
free(vi->evdev);
|
|
if (vi->serial)
|
|
free(vi->serial);
|
|
|
|
virtio_input_reset(vi);
|
|
free(vi);
|
|
vi = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
struct virtio_input *vi;
|
|
pthread_mutexattr_t attr;
|
|
char *opt;
|
|
int flags, ver;
|
|
int rc;
|
|
|
|
/* get evdev path from opts
|
|
* -s n,virtio-input,/dev/input/eventX[,serial]
|
|
*/
|
|
if (!opts) {
|
|
WPRINTF(("%s: evdev path is NULL\n", __func__));
|
|
return -1;
|
|
}
|
|
|
|
vi = calloc(1, sizeof(struct virtio_input));
|
|
if (!vi) {
|
|
WPRINTF(("%s: out of memory\n", __func__));
|
|
return -1;
|
|
}
|
|
|
|
opt = strsep(&opts, ",");
|
|
if (!opt) {
|
|
WPRINTF(("%s: evdev path is NULL\n", __func__));
|
|
goto opt_fail;
|
|
}
|
|
|
|
vi->evdev = strdup(opt);
|
|
if (!vi->evdev) {
|
|
WPRINTF(("%s: strdup failed\n", __func__));
|
|
goto opt_fail;
|
|
}
|
|
|
|
if (opts) {
|
|
vi->serial = strdup(opts);
|
|
if (!vi->serial) {
|
|
WPRINTF(("%s: strdup serial failed\n", __func__));
|
|
goto serial_fail;
|
|
}
|
|
}
|
|
|
|
vi->fd = open(vi->evdev, O_RDWR);
|
|
if (vi->fd < 0) {
|
|
WPRINTF(("open %s failed %d\n", vi->evdev, errno));
|
|
goto open_fail;
|
|
}
|
|
flags = fcntl(vi->fd, F_GETFL);
|
|
fcntl(vi->fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
rc = ioctl(vi->fd, EVIOCGVERSION, &ver); /* is it a evdev device? */
|
|
if (rc < 0) {
|
|
WPRINTF(("%s: get version failed\n", vi->evdev));
|
|
goto evdev_fail;
|
|
}
|
|
|
|
rc = ioctl(vi->fd, EVIOCGRAB, 1); /* exclusive access */
|
|
if (rc < 0) {
|
|
WPRINTF(("%s: grab device failed %d\n", vi->evdev, errno));
|
|
goto evdev_fail;
|
|
}
|
|
|
|
/* init mutex attribute properly to avoid deadlock */
|
|
rc = pthread_mutexattr_init(&attr);
|
|
if (rc)
|
|
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
|
|
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
if (rc)
|
|
DPRINTF(("vtinput: mutexattr_settype failed with "
|
|
"error %d!\n", rc));
|
|
rc = pthread_mutex_init(&vi->mtx, &attr);
|
|
if (rc)
|
|
DPRINTF(("vtinput: pthread_mutex_init failed with "
|
|
"error %d!\n", rc));
|
|
|
|
vi->event_qsize = VIRTIO_INPUT_PACKET_SIZE;
|
|
vi->event_qindex = 0;
|
|
vi->event_queue = calloc(vi->event_qsize,
|
|
sizeof(struct virtio_input_event_elem));
|
|
if (!vi->event_queue) {
|
|
WPRINTF(("vtinput: could not alloc event queue buf\n"));
|
|
goto evqueue_fail;
|
|
}
|
|
|
|
vi->mevp = mevent_add(vi->fd, EVF_READ, virtio_input_read_event, vi,
|
|
virtio_input_teardown, vi);
|
|
if (vi->mevp == NULL) {
|
|
WPRINTF(("vtinput: could not register event\n"));
|
|
goto mevent_fail;
|
|
}
|
|
|
|
virtio_linkup(&vi->base, &virtio_input_ops, vi, dev, vi->queues, BACKEND_VBSU);
|
|
vi->base.mtx = &vi->mtx;
|
|
vi->base.device_caps = VIRTIO_INPUT_S_HOSTCAPS;
|
|
|
|
vi->queues[VIRTIO_INPUT_EVENT_QUEUE].qsize = VIRTIO_INPUT_RINGSZ;
|
|
vi->queues[VIRTIO_INPUT_EVENT_QUEUE].notify =
|
|
virtio_input_notify_event_vq;
|
|
|
|
vi->queues[VIRTIO_INPUT_STATUS_QUEUE].qsize = VIRTIO_INPUT_RINGSZ;
|
|
vi->queues[VIRTIO_INPUT_STATUS_QUEUE].notify =
|
|
virtio_input_notify_status_vq;
|
|
|
|
/* initialize config space */
|
|
pci_set_cfgdata16(dev, PCIR_DEVICE, 0x1040 + VIRTIO_TYPE_INPUT);
|
|
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
|
|
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_INPUTDEV);
|
|
pci_set_cfgdata8(dev, PCIR_SUBCLASS, PCIS_INPUTDEV_OTHER);
|
|
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, 0x1100);
|
|
if (is_winvm == true)
|
|
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, ORACLE_VENDOR_ID);
|
|
else
|
|
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
|
|
pci_set_cfgdata16(dev, PCIR_REVID, 1);
|
|
|
|
if (virtio_interrupt_init(&vi->base, virtio_uses_msix())) {
|
|
DPRINTF(("%s, interrupt_init failed!\n", __func__));
|
|
goto fail;
|
|
}
|
|
rc = virtio_set_modern_bar(&vi->base, true);
|
|
|
|
return rc;
|
|
|
|
fail:
|
|
/* all resources will be freed in the teardown callback */
|
|
mevent_delete(vi->mevp);
|
|
return -1;
|
|
|
|
mevent_fail:
|
|
free(vi->event_queue);
|
|
vi->event_queue = NULL;
|
|
evqueue_fail:
|
|
pthread_mutex_destroy(&vi->mtx);
|
|
evdev_fail:
|
|
close(vi->fd);
|
|
vi->fd = -1;
|
|
open_fail:
|
|
if (vi->serial) {
|
|
free(vi->serial);
|
|
vi->serial = NULL;
|
|
}
|
|
serial_fail:
|
|
free(vi->evdev);
|
|
vi->evdev = NULL;
|
|
opt_fail:
|
|
free(vi);
|
|
vi = NULL;
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
virtio_input_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = (struct virtio_input *)dev->arg;
|
|
if (vi && vi->mevp)
|
|
mevent_delete(vi->mevp);
|
|
}
|
|
|
|
struct pci_vdev_ops pci_ops_virtio_input = {
|
|
.class_name = "virtio-input",
|
|
.vdev_init = virtio_input_init,
|
|
.vdev_deinit = virtio_input_deinit,
|
|
.vdev_barwrite = virtio_pci_write,
|
|
.vdev_barread = virtio_pci_read
|
|
};
|
|
DEFINE_PCI_DEVTYPE(pci_ops_virtio_input);
|