fb37391ea6
Signed-off-by: liuhongchao <liuhongchao@xiaomi.com>
453 lines
15 KiB
C
453 lines
15 KiB
C
/****************************************************************************
|
|
* drivers/virtio/virtio-input.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <nuttx/queue.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/virtio/virtio.h>
|
|
#include <nuttx/input/mouse.h>
|
|
#include <nuttx/input/touchscreen.h>
|
|
#include <nuttx/input/keyboard.h>
|
|
#include <nuttx/input/kbd_codec.h>
|
|
#include <nuttx/input/virtio-input-event-codes.h>
|
|
|
|
#include "virtio-input.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define VIRTIO_INPUT_EVENT 0
|
|
#define VIRTIO_INPUT_NUM 1
|
|
#define VIRTIO_INPUT_EVT_NUM 8
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct virtio_input_priv;
|
|
typedef void (*virtio_send_event_handler)(FAR struct virtio_input_priv *,
|
|
FAR struct virtio_input_event *);
|
|
|
|
struct virtio_input_priv
|
|
{
|
|
FAR struct virtio_device *vdev;
|
|
char name[NAME_MAX]; /* Device name */
|
|
struct virtio_input_event evt[VIRTIO_INPUT_EVT_NUM];
|
|
size_t evtnum; /* Input event number */
|
|
struct work_s work; /* Supports the interrupt handling "bottom half" */
|
|
virtio_send_event_handler eventhandler;
|
|
|
|
union
|
|
{
|
|
struct mouse_lowerhalf_s mouselower; /* Mouse device lowerhalf instance */
|
|
struct keyboard_lowerhalf_s keyboardlower; /* Keyboard device lowerhalf instance */
|
|
struct touch_lowerhalf_s touchlower; /* Touchpad device lowerhalf instance */
|
|
};
|
|
|
|
union
|
|
{
|
|
struct mouse_report_s mousesample; /* Mouse event */
|
|
struct keyboard_event_s keyboardsample; /* Keyboard event */
|
|
struct touch_sample_s touchsample; /* Touchpad event */
|
|
};
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int virtio_input_probe(FAR struct virtio_device *vdev);
|
|
static void virtio_input_remove(FAR struct virtio_device *vdev);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static struct virtio_driver g_virtio_input_driver =
|
|
{
|
|
.node = LIST_INITIAL_VALUE(g_virtio_input_driver.node), /* node */
|
|
.device = VIRTIO_ID_INPUT, /* device id */
|
|
.probe = virtio_input_probe, /* probe */
|
|
.remove = virtio_input_remove, /* remove */
|
|
};
|
|
|
|
static int g_virtio_mouse_idx = 0;
|
|
static int g_virtio_touch_idx = 0;
|
|
static int g_virtio_keyboard_idx = 0;
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_send_keyboard_event
|
|
****************************************************************************/
|
|
|
|
static void
|
|
virtio_input_send_keyboard_event(FAR struct virtio_input_priv *priv,
|
|
FAR struct virtio_input_event *event)
|
|
{
|
|
if (event->type == EV_KEY)
|
|
{
|
|
priv->keyboardsample.code =
|
|
keyboard_translate_virtio_code(event->code);
|
|
priv->keyboardsample.type = event->value;
|
|
}
|
|
else if (event->type == EV_SYN && event->code == SYN_REPORT)
|
|
{
|
|
keyboard_event(&(priv->keyboardlower),
|
|
priv->keyboardsample.code,
|
|
priv->keyboardsample.type);
|
|
memset(&priv->keyboardsample, 0, sizeof(priv->keyboardsample));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_send_mouse_event
|
|
****************************************************************************/
|
|
|
|
static void
|
|
virtio_input_send_mouse_event(FAR struct virtio_input_priv *priv,
|
|
FAR struct virtio_input_event *event)
|
|
{
|
|
if (event->type == EV_REL)
|
|
{
|
|
switch (event->code)
|
|
{
|
|
case REL_X:
|
|
priv->mousesample.x = event->value;
|
|
break;
|
|
|
|
case REL_Y:
|
|
priv->mousesample.y = event->value;
|
|
break;
|
|
|
|
#ifdef CONFIG_INPUT_MOUSE_WHEEL
|
|
case REL_WHEEL:
|
|
priv->mousesample.wheel = event->value;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
else if (event->type == EV_KEY)
|
|
{
|
|
switch (event->code)
|
|
{
|
|
case BTN_LEFT:
|
|
if (event->value)
|
|
{
|
|
priv->mousesample.buttons |= MOUSE_BUTTON_1;
|
|
}
|
|
else
|
|
{
|
|
priv->mousesample.buttons &= ~MOUSE_BUTTON_1;
|
|
}
|
|
break;
|
|
|
|
case BTN_RIGHT:
|
|
if (event->value)
|
|
{
|
|
priv->mousesample.buttons |= MOUSE_BUTTON_2;
|
|
}
|
|
else
|
|
{
|
|
priv->mousesample.buttons &= ~MOUSE_BUTTON_2;
|
|
}
|
|
break;
|
|
|
|
case BTN_MIDDLE:
|
|
if (event->value)
|
|
{
|
|
priv->mousesample.buttons |= MOUSE_BUTTON_3;
|
|
}
|
|
else
|
|
{
|
|
priv->mousesample.buttons &= ~MOUSE_BUTTON_3;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (event->type == EV_SYN && event->code == SYN_REPORT)
|
|
{
|
|
mouse_event(priv->mouselower.priv, &priv->mousesample);
|
|
priv->mousesample.x = 0;
|
|
priv->mousesample.y = 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_send_touch_event
|
|
****************************************************************************/
|
|
|
|
static void
|
|
virtio_input_send_touch_event(FAR struct virtio_input_priv *priv,
|
|
FAR struct virtio_input_event *event)
|
|
{
|
|
if (event->type == EV_ABS)
|
|
{
|
|
switch (event->code)
|
|
{
|
|
case ABS_PRESSURE:
|
|
priv->touchsample.point[0].flags |= TOUCH_PRESSURE_VALID;
|
|
priv->touchsample.point[0].pressure = event->value;
|
|
break;
|
|
|
|
case ABS_X:
|
|
priv->touchsample.point[0].flags |= TOUCH_POS_VALID;
|
|
priv->touchsample.point[0].x = event->value;
|
|
break;
|
|
|
|
case ABS_Y:
|
|
priv->touchsample.point[0].flags |= TOUCH_POS_VALID;
|
|
priv->touchsample.point[0].y = event->value;
|
|
break;
|
|
}
|
|
}
|
|
else if (event->type == EV_SYN && event->code == SYN_REPORT)
|
|
{
|
|
priv->touchsample.npoints = 1;
|
|
priv->touchsample.point[0].timestamp = touch_get_time();
|
|
|
|
touch_event(priv->touchlower.priv, &priv->touchsample);
|
|
memset(&priv->touchsample, 0, sizeof(priv->touchsample));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_worker
|
|
****************************************************************************/
|
|
|
|
static void virtio_input_worker(FAR void *arg)
|
|
{
|
|
FAR struct virtio_input_priv *priv = (FAR struct virtio_input_priv *)arg;
|
|
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_INPUT_EVENT].vq;
|
|
FAR struct virtio_input_event *evt;
|
|
FAR struct virtqueue_buf vb;
|
|
uint32_t len;
|
|
|
|
while ((evt = (FAR struct virtio_input_event *)
|
|
virtqueue_get_buffer(vq, &len, NULL)) != NULL)
|
|
{
|
|
vrtinfo("virtio_input_worker (type,code,value)-(%d,%d,%" PRIu32 ").\n",
|
|
evt->type, evt->code, evt->value);
|
|
|
|
priv->eventhandler(priv, evt);
|
|
|
|
vb.buf = evt;
|
|
vb.len = len;
|
|
virtqueue_add_buffer(vq, &vb, 0, 1, vb.buf);
|
|
}
|
|
|
|
virtqueue_kick(vq);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_recv_events
|
|
****************************************************************************/
|
|
|
|
static void virtio_input_recv_events(FAR struct virtqueue *vq)
|
|
{
|
|
FAR struct virtio_input_priv *priv = vq->vq_dev->priv;
|
|
int ret;
|
|
|
|
ret = work_queue(HPWORK, &priv->work, virtio_input_worker, priv, 0);
|
|
if (ret != 0)
|
|
{
|
|
vrterr("ERROR: Failed to queue work: %d\n", ret);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_fill_event
|
|
****************************************************************************/
|
|
|
|
static void virtio_input_fill_event(FAR struct virtio_input_priv *priv)
|
|
{
|
|
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_INPUT_EVENT].vq;
|
|
FAR struct virtqueue_buf vb;
|
|
int i;
|
|
|
|
for (i = 0; i < priv->evtnum; i++)
|
|
{
|
|
vb.buf = &priv->evt[i];
|
|
vb.len = sizeof(struct virtio_input_event);
|
|
virtqueue_add_buffer(vq, &vb, 0, 1, vb.buf);
|
|
}
|
|
|
|
virtqueue_kick(vq);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_select_cfg
|
|
****************************************************************************/
|
|
|
|
static uint8_t virtio_input_select_cfg(FAR struct virtio_input_priv *priv,
|
|
uint8_t select, uint8_t subsel)
|
|
{
|
|
uint8_t cfg_size = 0;
|
|
virtio_write_config(priv->vdev, offsetof(struct virtio_input_config,
|
|
select), &select, sizeof(uint8_t));
|
|
virtio_write_config(priv->vdev, offsetof(struct virtio_input_config,
|
|
subsel), &subsel, sizeof(uint8_t));
|
|
virtio_read_config(priv->vdev, offsetof(struct virtio_input_config,
|
|
size), &cfg_size, sizeof(uint8_t));
|
|
return cfg_size;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_register
|
|
****************************************************************************/
|
|
|
|
static void virtio_input_register(FAR struct virtio_input_priv *priv)
|
|
{
|
|
if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS))
|
|
{
|
|
priv->touchlower.maxpoint = 1;
|
|
snprintf(priv->name, NAME_MAX, "/dev/input%d",
|
|
g_virtio_touch_idx++);
|
|
touch_register(&(priv->touchlower),
|
|
priv->name,
|
|
priv->evtnum);
|
|
priv->eventhandler = virtio_input_send_touch_event;
|
|
}
|
|
else if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_REL))
|
|
{
|
|
snprintf(priv->name, NAME_MAX, "/dev/mouse%d",
|
|
g_virtio_mouse_idx++);
|
|
mouse_register(&(priv->mouselower),
|
|
priv->name,
|
|
priv->evtnum);
|
|
priv->eventhandler = virtio_input_send_mouse_event;
|
|
}
|
|
else if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY))
|
|
{
|
|
snprintf(priv->name, NAME_MAX, "/dev/kbd%d",
|
|
g_virtio_keyboard_idx++);
|
|
keyboard_register(&(priv->keyboardlower),
|
|
priv->name,
|
|
priv->evtnum);
|
|
priv->eventhandler = virtio_input_send_keyboard_event;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_probe
|
|
****************************************************************************/
|
|
|
|
static int virtio_input_probe(FAR struct virtio_device *vdev)
|
|
{
|
|
FAR struct virtio_input_priv *priv;
|
|
FAR const char *vqnames[VIRTIO_INPUT_NUM];
|
|
vq_callback callbacks[VIRTIO_INPUT_NUM];
|
|
int ret;
|
|
|
|
priv = kmm_zalloc(sizeof(*priv));
|
|
if (priv == NULL)
|
|
{
|
|
vrterr("No enough memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->vdev = vdev;
|
|
vdev->priv = priv;
|
|
|
|
/* Initialize the virtio device */
|
|
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
|
|
virtio_set_features(vdev, 0);
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
|
|
|
|
vqnames[VIRTIO_INPUT_EVENT] = "virtio_input_event";
|
|
callbacks[VIRTIO_INPUT_EVENT] = virtio_input_recv_events;
|
|
ret = virtio_create_virtqueues(vdev, 0, VIRTIO_INPUT_NUM, vqnames,
|
|
callbacks);
|
|
if (ret < 0)
|
|
{
|
|
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
|
|
virtio_reset_device(vdev);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
|
|
virtqueue_enable_cb(vdev->vrings_info[VIRTIO_INPUT_EVENT].vq);
|
|
priv->evtnum = MIN(vdev->vrings_info[VIRTIO_INPUT_EVENT].info.num_descs,
|
|
VIRTIO_INPUT_EVT_NUM);
|
|
|
|
/* register lower half drivers */
|
|
|
|
virtio_input_register(priv);
|
|
|
|
virtio_input_fill_event(priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_input_remove
|
|
****************************************************************************/
|
|
|
|
static void virtio_input_remove(FAR struct virtio_device *vdev)
|
|
{
|
|
FAR struct virtio_input_priv *priv = vdev->priv;
|
|
|
|
if (priv->eventhandler == virtio_input_send_keyboard_event)
|
|
{
|
|
keyboard_unregister(&(priv->keyboardlower), priv->name);
|
|
}
|
|
else if (priv->eventhandler == virtio_input_send_mouse_event)
|
|
{
|
|
mouse_unregister(&(priv->mouselower), priv->name);
|
|
}
|
|
else if (priv->eventhandler == virtio_input_send_touch_event)
|
|
{
|
|
touch_unregister(&(priv->touchlower), priv->name);
|
|
}
|
|
|
|
virtio_reset_device(vdev);
|
|
virtio_delete_virtqueues(vdev);
|
|
kmm_free(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_register_input_driver
|
|
****************************************************************************/
|
|
|
|
int virtio_register_input_driver(void)
|
|
{
|
|
return virtio_register_driver(&g_virtio_input_driver);
|
|
}
|