input: add a longpress device
Add an input device to take input key events as an input and generates short press or long press devices as output. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
parent
12b863067c
commit
5b36b4fa16
4 changed files with 216 additions and 0 deletions
68
dts/bindings/input/zephyr,input-longpress.yaml
Normal file
68
dts/bindings/input/zephyr,input-longpress.yaml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Copyright 2023 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Input longpress pseudo-device
|
||||
|
||||
Listens for key events as an input and produces key events as output
|
||||
corresponding to short and long press.
|
||||
|
||||
Can be optionally be associated to a specific device to listen for events
|
||||
only from that device. Example configuration:
|
||||
|
||||
longpress {
|
||||
input = <&buttons>;
|
||||
compatible = "zephyr,input-longpress";
|
||||
input-codes = <INPUT_KEY_0>, <INPUT_KEY_1>;
|
||||
short-codes = <INPUT_KEY_A>, <INPUT_KEY_B>;
|
||||
long-codes = <INPUT_KEY_X>, <INPUT_KEY_Y>;
|
||||
long-delay-ms = <1000>;
|
||||
};
|
||||
|
||||
Example output:
|
||||
|
||||
input event: dev=buttons SYN type= 1 code= 11 value=1 # INPUT_KEY_0 press
|
||||
# release before one second
|
||||
input event: dev=buttons SYN type= 1 code= 11 value=0 # INPUT_KEY_0 release
|
||||
input event: dev=longpress SYN type= 1 code= 30 value=1 # INPUT_KEY_A press
|
||||
input event: dev=longpress SYN type= 1 code= 30 value=0 # INPUT_KEY_A release
|
||||
|
||||
input event: dev=buttons SYN type= 1 code= 11 value=1 # INPUT_KEY_0 press
|
||||
# hold for more than one second
|
||||
input event: dev=longpress SYN type= 1 code= 45 value=1 # INPUT_KEY_A press
|
||||
# wait for release
|
||||
input event: dev=buttons SYN type= 1 code= 11 value=0 # INPUT_KEY_0 release
|
||||
input event: dev=longpress SYN type= 1 code= 45 value=0 # INPUT_KEY_A release
|
||||
|
||||
compatible: "zephyr,input-longpress"
|
||||
|
||||
properties:
|
||||
input:
|
||||
type: phandle
|
||||
description: |
|
||||
Input device phandle, if not specified listen for input from all devices.
|
||||
|
||||
input-codes:
|
||||
type: array
|
||||
required: true
|
||||
description: |
|
||||
Array of input event key codes (INPUT_KEY_* or INPUT_BTN_*).
|
||||
|
||||
short-codes:
|
||||
type: array
|
||||
required: true
|
||||
description: |
|
||||
Array of key codes to be generated for short press (INPUT_KEY_* or
|
||||
INPUT_BTN_*).
|
||||
|
||||
long-codes:
|
||||
type: array
|
||||
required: true
|
||||
description: |
|
||||
Array of key codes to be generated for long press (INPUT_KEY_* or
|
||||
INPUT_BTN_*).
|
||||
|
||||
long-delay-ms:
|
||||
type: int
|
||||
required: true
|
||||
description: Time delay to register a long press in milliseconds.
|
||||
|
|
@ -3,3 +3,5 @@
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(input.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_LONGPRESS input_longpress.c)
|
||||
|
|
|
|||
|
|
@ -67,4 +67,9 @@ config INPUT_THREAD_STACK_SIZE
|
|||
|
||||
endif # INPUT_MODE_THREAD
|
||||
|
||||
config INPUT_LONGPRESS
|
||||
bool "Input longpress"
|
||||
default y
|
||||
depends on DT_HAS_ZEPHYR_INPUT_LONGPRESS_ENABLED
|
||||
|
||||
endif # INPUT
|
||||
|
|
|
|||
141
subsys/input/input_longpress.c
Normal file
141
subsys/input/input_longpress.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephyr_input_longpress
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(input_longpress, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
struct longpress_config {
|
||||
const struct device *input_dev;
|
||||
const uint16_t *input_codes;
|
||||
const uint16_t *short_codes;
|
||||
const uint16_t *long_codes;
|
||||
uint32_t long_delays_ms;
|
||||
uint8_t num_codes;
|
||||
};
|
||||
|
||||
struct longpress_data_entry {
|
||||
const struct device *dev;
|
||||
struct k_work_delayable work;
|
||||
uint8_t index;
|
||||
bool long_fired;
|
||||
};
|
||||
|
||||
struct longpress_data {
|
||||
/* support data for every input code */
|
||||
struct longpress_data_entry *entries;
|
||||
};
|
||||
|
||||
static void longpress_deferred(struct k_work *work)
|
||||
{
|
||||
struct longpress_data_entry *entry = CONTAINER_OF(
|
||||
work, struct longpress_data_entry, work);
|
||||
const struct device *dev = entry->dev;
|
||||
const struct longpress_config *cfg = dev->config;
|
||||
uint16_t code;
|
||||
|
||||
code = cfg->long_codes[entry->index];
|
||||
|
||||
input_report_key(dev, code, 1, true, K_FOREVER);
|
||||
|
||||
entry->long_fired = true;
|
||||
}
|
||||
|
||||
static void longpress_cb(const struct device *dev, struct input_event *evt)
|
||||
{
|
||||
const struct longpress_config *cfg = dev->config;
|
||||
struct longpress_data *data = dev->data;
|
||||
struct longpress_data_entry *entry;
|
||||
int i;
|
||||
|
||||
if (evt->type != INPUT_EV_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg->num_codes; i++) {
|
||||
if (evt->code == cfg->input_codes[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == cfg->num_codes) {
|
||||
LOG_DBG("ignored code %d", evt->code);
|
||||
return;
|
||||
}
|
||||
|
||||
entry = &data->entries[i];
|
||||
|
||||
if (evt->value) {
|
||||
entry->long_fired = false;
|
||||
k_work_schedule(&entry->work, K_MSEC(cfg->long_delays_ms));
|
||||
} else {
|
||||
k_work_cancel_delayable(&entry->work);
|
||||
if (entry->long_fired) {
|
||||
input_report_key(dev, cfg->long_codes[i], 0, true, K_FOREVER);
|
||||
} else {
|
||||
input_report_key(dev, cfg->short_codes[i], 1, true, K_FOREVER);
|
||||
input_report_key(dev, cfg->short_codes[i], 0, true, K_FOREVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int longpress_init(const struct device *dev)
|
||||
{
|
||||
const struct longpress_config *cfg = dev->config;
|
||||
struct longpress_data *data = dev->data;
|
||||
|
||||
if (cfg->input_dev && !device_is_ready(cfg->input_dev)) {
|
||||
LOG_ERR("input device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cfg->num_codes; i++) {
|
||||
struct longpress_data_entry *entry = &data->entries[i];
|
||||
|
||||
entry->dev = dev;
|
||||
entry->index = i;
|
||||
k_work_init_delayable(&entry->work, longpress_deferred);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define INPUT_LONGPRESS_DEFINE(inst) \
|
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) == \
|
||||
DT_INST_PROP_LEN(inst, short_codes)); \
|
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) == \
|
||||
DT_INST_PROP_LEN(inst, long_codes)); \
|
||||
static void longpress_cb_##inst(struct input_event *evt) \
|
||||
{ \
|
||||
longpress_cb(DEVICE_DT_INST_GET(inst), evt); \
|
||||
} \
|
||||
INPUT_LISTENER_CB_DEFINE(DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \
|
||||
longpress_cb_##inst); \
|
||||
static const uint16_t longpress_input_codes_##inst[] = DT_INST_PROP(inst, input_codes); \
|
||||
static const uint16_t longpress_short_codes_##inst[] = DT_INST_PROP(inst, short_codes); \
|
||||
static const uint16_t longpress_long_codes_##inst[] = DT_INST_PROP(inst, long_codes); \
|
||||
static const struct longpress_config longpress_config_##inst = { \
|
||||
.input_dev = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \
|
||||
.input_codes = longpress_input_codes_##inst, \
|
||||
.short_codes = longpress_short_codes_##inst, \
|
||||
.long_codes = longpress_long_codes_##inst, \
|
||||
.num_codes = DT_INST_PROP_LEN(inst, input_codes), \
|
||||
.long_delays_ms = DT_INST_PROP(inst, long_delay_ms), \
|
||||
}; \
|
||||
static struct longpress_data_entry longpress_data_entries_##inst[DT_INST_PROP_LEN( \
|
||||
inst, input_codes)]; \
|
||||
static struct longpress_data longpress_data_##inst = { \
|
||||
.entries = longpress_data_entries_##inst, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, longpress_init, NULL, \
|
||||
&longpress_data_##inst, &longpress_config_##inst, \
|
||||
APPLICATION, CONFIG_INPUT_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(INPUT_LONGPRESS_DEFINE)
|
||||
Loading…
Reference in a new issue