nuttx/drivers/input/goldfish_events.c
Pavel Pisa ab4890df16
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
drivers/input: partial fix of indistinguishable ASCII and special keycodes
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>
2026-07-29 22:51:15 -03:00

393 lines
12 KiB
C

/****************************************************************************
* drivers/input/goldfish_events.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 <nuttx/debug.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/queue.h>
#include <nuttx/kmalloc.h>
#include <nuttx/input/goldfish_events.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>
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
#ifndef putreg32
#define putreg32(v, x) (*(FAR volatile uint32_t *)(x) = (v))
#endif
#ifndef getreg32
#define getreg32(x) (*(FAR volatile uint32_t *)(x))
#endif
/****************************************************************************
* Private Types
****************************************************************************/
enum
{
GOLDFISH_EVENTS_READ = 0x00, /* Read next event type, code or value. */
GOLDFISH_EVENTS_SET_PAGE = 0x00, /* Set page index. */
GOLDFISH_EVENTS_LEN = 0x04, /* Read length of page data. */
GOLDFISH_EVENTS_DATA = 0x08, /* Read page data. */
GOLDFISH_EVENTS_PAGE_NAME = 0x0000, /* Keyboard charmap name. */
GOLDFISH_EVENTS_PAGE_EVBITS = 0x10000, /* Event code supported sets. */
GOLDFISH_EVENTS_PAGE_ABSDATA = 0x20003 /* (really 0x20000 + EV_ABS) EV_ABS min/max values. */
};
struct goldfish_input_event
{
uint32_t type;
uint32_t code;
uint32_t value;
};
struct goldfish_events_s
{
FAR void *base;
int irq;
struct work_s work; /* Supports the interrupt handling "bottom half" */
struct mouse_lowerhalf_s mouselower; /* Mouse device lowerhalf instance */
struct mouse_report_s mousesample; /* Mouse event */
struct keyboard_lowerhalf_s keyboardlower; /* Keyboard device lowerhalf instance */
struct touch_lowerhalf_s touchlower; /* Touchpad device lowerhalf instance */
struct touch_sample_s touchsample; /* Touchpad event */
};
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int
goldfish_events_interrupt(int irq, FAR void *dev_id, FAR void *arg);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: goldfish_events_send_keyboard_event
****************************************************************************/
static bool
goldfish_events_send_keyboard_event(FAR struct goldfish_events_s *events,
FAR struct goldfish_input_event *evt)
{
uint32_t trans_key;
bool is_special;
static const uint32_t types[2][2] =
{
{
KEYBOARD_RELEASE, KEYBOARD_PRESS
},
{
KBD_SPECREL, KBD_SPECPRESS
}
};
if (evt->type == EV_KEY)
{
trans_key = keyboard_translate_virtio_code(evt->code, &is_special);
keyboard_event(&(events->keyboardlower),
trans_key,
types[is_special][evt->value & 1]);
return true;
}
return false;
}
/****************************************************************************
* Name: goldfish_events_send_mouse_event
****************************************************************************/
static bool
goldfish_events_send_mouse_event(FAR struct goldfish_events_s *events,
FAR struct goldfish_input_event *evt)
{
if (evt->type == EV_REL)
{
switch (evt->code)
{
case REL_X:
events->mousesample.x = evt->value;
return true;
case REL_Y:
events->mousesample.y = evt->value;
return true;
#ifdef CONFIG_INPUT_MOUSE_WHEEL
case REL_WHEEL:
events->mousesample.wheel = evt->value;
return true;
#endif
}
}
else if (evt->type == EV_KEY && evt->value != 0)
{
switch (evt->code)
{
case BTN_LEFT:
events->mousesample.buttons |= MOUSE_BUTTON_1;
return true;
case BTN_RIGHT:
events->mousesample.buttons |= MOUSE_BUTTON_2;
return true;
case BTN_MIDDLE:
events->mousesample.buttons |= MOUSE_BUTTON_3;
return true;
}
}
else if (evt->type == EV_SYN && evt->code == SYN_REPORT &&
((events->mousesample.x != 0) ||
(events->mousesample.y != 0)
#ifdef CONFIG_INPUT_MOUSE_WHEEL
|| (events->mousesample.wheel != 0)
#endif
))
{
mouse_event(events->mouselower.priv, &events->mousesample);
memset(&events->mousesample, 0, sizeof(events->mousesample));
}
return false;
}
/****************************************************************************
* Name: goldfish_events_send_touch_event
****************************************************************************/
static bool
goldfish_events_send_touch_event(FAR struct goldfish_events_s *events,
FAR struct goldfish_input_event *evt)
{
if (evt->type == EV_ABS)
{
switch (evt->code)
{
case ABS_PRESSURE:
events->touchsample.point[0].flags |= TOUCH_PRESSURE_VALID;
events->touchsample.point[0].pressure = evt->value;
return true;
case ABS_X:
events->touchsample.point[0].flags |= TOUCH_POS_VALID;
events->touchsample.point[0].x = evt->value;
return true;
case ABS_Y:
events->touchsample.point[0].flags |= TOUCH_POS_VALID;
events->touchsample.point[0].y = evt->value;
return true;
}
}
else if (evt->type == EV_KEY)
{
if (evt->code == BTN_TOUCH)
{
if (evt->value)
{
events->touchsample.point[0].flags |= TOUCH_DOWN;
}
else
{
events->touchsample.point[0].flags |= TOUCH_UP;
}
return true;
}
}
else if (evt->type == EV_SYN && evt->code == SYN_REPORT &&
((events->touchsample.point[0].pressure != 0) ||
(events->touchsample.point[0].x != 0) ||
(events->touchsample.point[0].y != 0)))
{
events->touchsample.npoints = 1;
events->touchsample.point[0].timestamp = touch_get_time();
touch_event(events->touchlower.priv, &events->touchsample);
memset(&events->touchsample, 0, sizeof(events->touchsample));
}
return false;
}
/****************************************************************************
* Name: goldfish_events_worker
****************************************************************************/
static void goldfish_events_worker(FAR void *arg)
{
FAR struct goldfish_events_s *events = (FAR struct goldfish_events_s *)arg;
struct goldfish_input_event evt;
up_enable_irq(events->irq);
evt.type = getreg32(events->base + GOLDFISH_EVENTS_READ);
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
evt.code = getreg32(events->base + GOLDFISH_EVENTS_READ);
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
evt.value = getreg32(events->base + GOLDFISH_EVENTS_READ);
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
iinfo("goldfish_events_interrupt events(%" PRIu32 ", %" PRIu32 ", \
%" PRIu32 ").\n", evt.type, evt.code, evt.value);
if (goldfish_events_send_touch_event(events, &evt))
{
return;
}
if (goldfish_events_send_mouse_event(events, &evt))
{
return;
}
goldfish_events_send_keyboard_event(events, &evt);
}
/****************************************************************************
* Name: goldfish_events_interrupt
****************************************************************************/
static int
goldfish_events_interrupt(int irq, FAR void *dev_id, FAR void *arg)
{
FAR struct goldfish_events_s *events = arg;
work_queue(HPWORK, &events->work, goldfish_events_worker, events, 0);
up_disable_irq(events->irq);
return 0;
}
/****************************************************************************
* Name: goldfish_drivers_register
****************************************************************************/
static void goldfish_drivers_register(FAR struct goldfish_events_s *events)
{
putreg32(GOLDFISH_EVENTS_PAGE_EVBITS | EV_KEY,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
keyboard_register(&(events->keyboardlower),
"/dev/kbd0",
CONFIG_INPUT_GOLDFISH_NBUFFER);
}
}
putreg32(GOLDFISH_EVENTS_PAGE_EVBITS | EV_REL,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
mouse_register(&(events->mouselower),
"/dev/mouse0",
CONFIG_INPUT_GOLDFISH_NBUFFER);
}
}
putreg32(GOLDFISH_EVENTS_PAGE_EVBITS | EV_ABS,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
putreg32(GOLDFISH_EVENTS_PAGE_ABSDATA,
events->base + GOLDFISH_EVENTS_SET_PAGE);
if (getreg32(events->base + GOLDFISH_EVENTS_LEN))
{
touch_register(&(events->touchlower),
"/dev/input0",
CONFIG_INPUT_GOLDFISH_NBUFFER);
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: goldfish_events_register
****************************************************************************/
int goldfish_events_register(FAR void *base, int irq)
{
FAR struct goldfish_events_s *events;
int ret;
events = (FAR struct goldfish_events_s *)kmm_zalloc(sizeof(*events));
if (events == NULL)
{
return -ENOMEM;
}
events->base = base;
events->irq = irq;
ret = irq_attach(events->irq, goldfish_events_interrupt, events);
if (ret < 0)
{
goto err_free_events;
}
/* Register lower half drivers */
goldfish_drivers_register(events);
up_enable_irq(events->irq);
return 0;
err_free_events:
kmm_free(events);
return ret;
}