mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
As analyzed, the NuttX initial keyboard API design uses event type KBD_SPECPRESS/KBD_SPECREL to deliver special keys and KBD_PRESS/KBD_RELEASE to deliver ASCII codes. But it seems that this design choice has not been followed in virtio-input, goldfish_events and sim_keyboard designs and result is that external keyboard special keys events are mapped to KEYCODE_xxx values which start from 0 and overlaps with ASCII keys. The issue is tracked under #19527 number. This set of changes correct events reporting for mentioned keyboards to report right event type for special keys. The solution is only partial at this phase. Virtual and more complex keyboards usually deliver key pressures as scancodes (key position on keyboard) and mapping to ASCII for keys which corresponds to letter and other similar keys lacks mapping of national alphabets, second row symbols and switch to capital letter according to modifiers. Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
468 lines
15 KiB
C
468 lines
15 KiB
C
/****************************************************************************
|
|
* drivers/virtio/virtio-input.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 <nuttx/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" */
|
|
spinlock_t lock; /* Lock */
|
|
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)
|
|
{
|
|
static const uint32_t types[2][2] =
|
|
{
|
|
{
|
|
KEYBOARD_RELEASE, KEYBOARD_PRESS
|
|
},
|
|
{
|
|
KBD_SPECREL, KBD_SPECPRESS
|
|
}
|
|
};
|
|
|
|
if (event->type == EV_KEY)
|
|
{
|
|
uint32_t trans_key;
|
|
bool is_special;
|
|
trans_key = keyboard_translate_virtio_code(event->code, &is_special);
|
|
priv->keyboardsample.code = trans_key;
|
|
priv->keyboardsample.type = types[is_special][event->value & 1];
|
|
}
|
|
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_lock(vq, &len, NULL, &priv->lock)) != 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_lock(vq, &vb, 0, 1, vb.buf, &priv->lock);
|
|
}
|
|
|
|
virtqueue_kick_lock(vq, &priv->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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_lock(vq, &vb, 0, 1, vb.buf, &priv->lock);
|
|
}
|
|
|
|
virtqueue_kick_lock(vq, &priv->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
}
|
|
|
|
spin_lock_init(&priv->lock);
|
|
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, NULL);
|
|
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);
|
|
}
|