input: add input subsystem
Initial commit introducing the input subsystem into Zephyr. Includes the input_event data structure, the input_report_* APIs, an iterables sections based subscription API and two operation modes: synchronous, where the listeners are called directly, and asynchronous, where the listeners are called in a dedicated thread. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
parent
e073210ec2
commit
3386e96515
142
include/zephyr/dt-bindings/input/input-event-codes.h
Normal file
142
include/zephyr/dt-bindings/input/input-event-codes.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Input event codes, for codes available in Linux, use the same values as in
|
||||
* https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/input-event-codes.h
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_INPUT_INPUT_EVENT_CODES_H_
|
||||
#define ZEPHYR_INCLUDE_DT_BINDINGS_INPUT_INPUT_EVENT_CODES_H_
|
||||
|
||||
/**
|
||||
* @defgroup input_events Input Event Definitions
|
||||
* @ingroup input_interface
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @name Input event types.
|
||||
* @anchor INPUT_EV_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_EV_KEY 0x01
|
||||
#define INPUT_EV_REL 0x02
|
||||
#define INPUT_EV_ABS 0x03
|
||||
#define INPUT_EV_MSC 0x04
|
||||
#define INPUT_EV_VENDOR_START 0xf0
|
||||
#define INPUT_EV_VENDOR_STOP 0xff
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Input event KEY codes.
|
||||
* @anchor INPUT_KEY_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_KEY_0 11
|
||||
#define INPUT_KEY_1 2
|
||||
#define INPUT_KEY_2 3
|
||||
#define INPUT_KEY_3 4
|
||||
#define INPUT_KEY_4 5
|
||||
#define INPUT_KEY_5 6
|
||||
#define INPUT_KEY_6 7
|
||||
#define INPUT_KEY_7 8
|
||||
#define INPUT_KEY_8 9
|
||||
#define INPUT_KEY_9 10
|
||||
#define INPUT_KEY_A 30
|
||||
#define INPUT_KEY_B 48
|
||||
#define INPUT_KEY_C 46
|
||||
#define INPUT_KEY_D 32
|
||||
#define INPUT_KEY_E 18
|
||||
#define INPUT_KEY_F 33
|
||||
#define INPUT_KEY_G 34
|
||||
#define INPUT_KEY_H 35
|
||||
#define INPUT_KEY_I 23
|
||||
#define INPUT_KEY_J 36
|
||||
#define INPUT_KEY_K 37
|
||||
#define INPUT_KEY_L 38
|
||||
#define INPUT_KEY_M 50
|
||||
#define INPUT_KEY_N 49
|
||||
#define INPUT_KEY_O 24
|
||||
#define INPUT_KEY_P 25
|
||||
#define INPUT_KEY_Q 16
|
||||
#define INPUT_KEY_R 19
|
||||
#define INPUT_KEY_S 31
|
||||
#define INPUT_KEY_T 20
|
||||
#define INPUT_KEY_U 22
|
||||
#define INPUT_KEY_V 47
|
||||
#define INPUT_KEY_VOLUMEDOWN 114
|
||||
#define INPUT_KEY_VOLUMEUP 115
|
||||
#define INPUT_KEY_W 17
|
||||
#define INPUT_KEY_X 45
|
||||
#define INPUT_KEY_Y 21
|
||||
#define INPUT_KEY_Z 44
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Input event BTN codes.
|
||||
* @anchor INPUT_BTN_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_BTN_DPAD_DOWN 0x221
|
||||
#define INPUT_BTN_DPAD_LEFT 0x222
|
||||
#define INPUT_BTN_DPAD_RIGHT 0x223
|
||||
#define INPUT_BTN_DPAD_UP 0x220
|
||||
#define INPUT_BTN_EAST 0x131
|
||||
#define INPUT_BTN_LEFT 0x110
|
||||
#define INPUT_BTN_MIDDLE 0x112
|
||||
#define INPUT_BTN_MODE 0x13c
|
||||
#define INPUT_BTN_NORTH 0x133
|
||||
#define INPUT_BTN_RIGHT 0x111
|
||||
#define INPUT_BTN_SELECT 0x13a
|
||||
#define INPUT_BTN_SOUTH 0x130
|
||||
#define INPUT_BTN_START 0x13b
|
||||
#define INPUT_BTN_THUMBL 0x13d
|
||||
#define INPUT_BTN_THUMBR 0x13e
|
||||
#define INPUT_BTN_TL 0x136
|
||||
#define INPUT_BTN_TL2 0x138
|
||||
#define INPUT_BTN_TOUCH 0x14a
|
||||
#define INPUT_BTN_TR 0x137
|
||||
#define INPUT_BTN_TR2 0x139
|
||||
#define INPUT_BTN_WEST 0x134
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Input event ABS codes.
|
||||
* @anchor INPUT_ABS_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_ABS_RX 0x03
|
||||
#define INPUT_ABS_RY 0x04
|
||||
#define INPUT_ABS_RZ 0x05
|
||||
#define INPUT_ABS_X 0x00
|
||||
#define INPUT_ABS_Y 0x01
|
||||
#define INPUT_ABS_Z 0x02
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Input event REL codes.
|
||||
* @anchor INPUT_REL_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_REL_RX 0x03
|
||||
#define INPUT_REL_RY 0x04
|
||||
#define INPUT_REL_RZ 0x05
|
||||
#define INPUT_REL_X 0x00
|
||||
#define INPUT_REL_Y 0x01
|
||||
#define INPUT_REL_Z 0x02
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Input event MSC codes.
|
||||
* @anchor INPUT_MSC_CODES
|
||||
* @{
|
||||
*/
|
||||
#define INPUT_MSC_SCAN 0x04
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_INPUT_INPUT_EVENT_CODES_H_ */
|
150
include/zephyr/input/input.h
Normal file
150
include/zephyr/input/input.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_INPUT_H_
|
||||
#define ZEPHYR_INCLUDE_INPUT_H_
|
||||
|
||||
/**
|
||||
* @brief Input Interface
|
||||
* @defgroup input_interface Input Interface
|
||||
* @ingroup io_interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Input event structure.
|
||||
*
|
||||
* This structure represents a single input event, for example a key or button
|
||||
* press for a single button, or an absolute or relative coordinate for a
|
||||
* single axis.
|
||||
*/
|
||||
struct input_event {
|
||||
/** Device generating the event or NULL. */
|
||||
const struct device *dev;
|
||||
/** Sync flag. */
|
||||
uint8_t sync;
|
||||
/** Event type (see @ref INPUT_EV_CODES). */
|
||||
uint8_t type;
|
||||
/**
|
||||
* Event code (see @ref INPUT_KEY_CODES, @ref INPUT_BTN_CODES,
|
||||
* @ref INPUT_ABS_CODES, @ref INPUT_REL_CODES, @ref INPUT_MSC_CODES).
|
||||
*/
|
||||
uint16_t code;
|
||||
/** Event value. */
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Report a new input event.
|
||||
*
|
||||
* This causes all the listeners for the specified device to be triggered,
|
||||
* either synchronously or through the input thread if utilized.
|
||||
*
|
||||
* @param dev Device generating the event or NULL.
|
||||
* @param type Event type (see @ref INPUT_EV_CODES).
|
||||
* @param code Event code (see @ref INPUT_KEY_CODES, @ref INPUT_BTN_CODES,
|
||||
* @ref INPUT_ABS_CODES, @ref INPUT_REL_CODES, @ref INPUT_MSC_CODES).
|
||||
* @param value Event value.
|
||||
* @param sync Set the synchronization bit for the event.
|
||||
* @param timeout Timeout for reporting the event, ignored if
|
||||
* @kconfig{CONFIG_INPUT_MODE_SYNCHRONOUS} is used.
|
||||
* @retval 0 if the message has been processed.
|
||||
* @retval negative if @kconfig{CONFIG_INPUT_MODE_THREAD} is enabled and the
|
||||
* message failed to be enqueued.
|
||||
*/
|
||||
int input_report(const struct device *dev,
|
||||
uint8_t type, uint16_t code, int32_t value, bool sync,
|
||||
k_timeout_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Report a new @ref INPUT_EV_KEY input event, note that value is
|
||||
* converted to either 0 or 1.
|
||||
*
|
||||
* @see input_report() for more details.
|
||||
*/
|
||||
static inline int input_report_key(const struct device *dev,
|
||||
uint16_t code, int32_t value, bool sync,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
return input_report(dev, INPUT_EV_KEY, code, !!value, sync, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report a new @ref INPUT_EV_REL input event.
|
||||
*
|
||||
* @see input_report() for more details.
|
||||
*/
|
||||
static inline int input_report_rel(const struct device *dev,
|
||||
uint16_t code, int32_t value, bool sync,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
return input_report(dev, INPUT_EV_REL, code, value, sync, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report a new @ref INPUT_EV_ABS input event.
|
||||
*
|
||||
* @see input_report() for more details.
|
||||
*/
|
||||
static inline int input_report_abs(const struct device *dev,
|
||||
uint16_t code, int32_t value, bool sync,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
return input_report(dev, INPUT_EV_ABS, code, value, sync, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the input queue is empty.
|
||||
*
|
||||
* This can be used to batch input event processing until the whole queue has
|
||||
* been emptied. Always returns true if @kconfig{CONFIG_INPUT_MODE_SYNCHRONOUS}
|
||||
* is enabled.
|
||||
*/
|
||||
bool input_queue_empty(void);
|
||||
|
||||
/**
|
||||
* @brief Input listener callback structure.
|
||||
*/
|
||||
struct input_listener {
|
||||
/** @ref device pointer or NULL. */
|
||||
const struct device *dev;
|
||||
/** The callback function. */
|
||||
void (*callback)(struct input_event *evt);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Register a callback structure for input events.
|
||||
*
|
||||
* The @p _dev field can be used to only invoke callback for events generated
|
||||
* by a specific device. Setting dev to NULL causes callback to be invoked for
|
||||
* every event.
|
||||
*
|
||||
* @param _dev @ref device pointer or NULL.
|
||||
* @param _callback The callback function.
|
||||
*/
|
||||
#define INPUT_LISTENER_CB_DEFINE(_dev, _callback) \
|
||||
static const STRUCT_SECTION_ITERABLE(input_listener, \
|
||||
_input_listener__##_callback) = { \
|
||||
.dev = _dev, \
|
||||
.callback = _callback, \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_INPUT_H_ */
|
|
@ -27,6 +27,7 @@ add_subdirectory_ifdef(CONFIG_DISK_ACCESS disk)
|
|||
add_subdirectory_ifdef(CONFIG_DSP dsp)
|
||||
add_subdirectory_ifdef(CONFIG_EMUL emul)
|
||||
add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu)
|
||||
add_subdirectory_ifdef(CONFIG_INPUT input)
|
||||
add_subdirectory_ifdef(CONFIG_JWT jwt)
|
||||
add_subdirectory_ifdef(CONFIG_LORAWAN lorawan)
|
||||
add_subdirectory_ifdef(CONFIG_NET_BUF net)
|
||||
|
|
|
@ -17,6 +17,7 @@ source "subsys/dsp/Kconfig"
|
|||
source "subsys/emul/Kconfig"
|
||||
source "subsys/fb/Kconfig"
|
||||
source "subsys/fs/Kconfig"
|
||||
source "subsys/input/Kconfig"
|
||||
source "subsys/ipc/Kconfig"
|
||||
source "subsys/jwt/Kconfig"
|
||||
source "subsys/logging/Kconfig"
|
||||
|
|
7
subsys/input/CMakeLists.txt
Normal file
7
subsys/input/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(input.c)
|
||||
|
||||
zephyr_linker_sources(SECTIONS input.ld)
|
70
subsys/input/Kconfig
Normal file
70
subsys/input/Kconfig
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Copyright 2023 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig INPUT
|
||||
bool "Input"
|
||||
help
|
||||
Include input subsystem and drivers in the system config.
|
||||
|
||||
if INPUT
|
||||
|
||||
module = INPUT
|
||||
module-str = input
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config INPUT_INIT_PRIORITY
|
||||
int "Input subsystem and drivers init priority"
|
||||
default 90
|
||||
help
|
||||
Input subsystem and drivers initialization priority.
|
||||
|
||||
choice INPUT_MODE
|
||||
prompt "Input event processing mode"
|
||||
default INPUT_MODE_THREAD
|
||||
|
||||
config INPUT_MODE_SYNCHRONOUS
|
||||
bool "Process input events synchronously"
|
||||
help
|
||||
Input events callbacks are processed synchronously in the context of
|
||||
the code that is reporting the event.
|
||||
|
||||
config INPUT_MODE_THREAD
|
||||
bool "Process input events in a dedicated thread"
|
||||
depends on MULTITHREADING
|
||||
help
|
||||
Input events are added to a message queue and the callbacks are
|
||||
processed asynchronously in a dedicated thread.
|
||||
|
||||
endchoice
|
||||
|
||||
if INPUT_MODE_THREAD
|
||||
|
||||
config INPUT_THREAD_PRIORITY_OVERRIDE
|
||||
bool "Override default input thread priority"
|
||||
help
|
||||
Option to change the default value of input thread priority.
|
||||
|
||||
if INPUT_THREAD_PRIORITY_OVERRIDE
|
||||
config INPUT_THREAD_PRIORITY
|
||||
int "Input thread priority"
|
||||
default 0
|
||||
help
|
||||
Set thread priority of the input
|
||||
endif
|
||||
|
||||
config INPUT_QUEUE_MAX_MSGS
|
||||
int "Input queue max messages"
|
||||
default 16
|
||||
help
|
||||
Maximum number of messages in the input event queue.
|
||||
|
||||
config INPUT_THREAD_STACK_SIZE
|
||||
int "Input thread stack size"
|
||||
default 512
|
||||
help
|
||||
Stack size for the thread processing the input events, must have
|
||||
enough space for executing the registered callbacks.
|
||||
|
||||
endif # INPUT_MODE_THREAD
|
||||
|
||||
endif # INPUT
|
87
subsys/input/input.c
Normal file
87
subsys/input/input.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(input, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
#ifdef CONFIG_INPUT_MODE_THREAD
|
||||
|
||||
K_MSGQ_DEFINE(input_msgq, sizeof(struct input_event),
|
||||
CONFIG_INPUT_QUEUE_MAX_MSGS, 4);
|
||||
|
||||
#endif
|
||||
|
||||
static void input_process(struct input_event *evt)
|
||||
{
|
||||
STRUCT_SECTION_FOREACH(input_listener, listener) {
|
||||
if (listener->dev == NULL || listener->dev == evt->dev) {
|
||||
listener->callback(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool input_queue_empty(void)
|
||||
{
|
||||
#ifdef CONFIG_INPUT_MODE_THREAD
|
||||
if (k_msgq_num_used_get(&input_msgq) > 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int input_report(const struct device *dev,
|
||||
uint8_t type, uint16_t code, int32_t value, bool sync,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
struct input_event evt = {
|
||||
.dev = dev,
|
||||
.sync = sync,
|
||||
.type = type,
|
||||
.code = code,
|
||||
.value = value,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_INPUT_MODE_THREAD
|
||||
return k_msgq_put(&input_msgq, &evt, timeout);
|
||||
#else
|
||||
input_process(&evt);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INPUT_MODE_THREAD
|
||||
|
||||
static void input_thread(void)
|
||||
{
|
||||
struct input_event evt;
|
||||
int ret;
|
||||
|
||||
while (true) {
|
||||
ret = k_msgq_get(&input_msgq, &evt, K_FOREVER);
|
||||
if (ret) {
|
||||
LOG_ERR("k_msgq_get error: %d", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
input_process(&evt);
|
||||
}
|
||||
}
|
||||
|
||||
#define INPUT_THREAD_PRIORITY \
|
||||
COND_CODE_1(CONFIG_INPUT_THREAD_PRIORITY_OVERRIDE, \
|
||||
(CONFIG_INPUT_THREAD_PRIORITY), (K_LOWEST_APPLICATION_THREAD_PRIO))
|
||||
|
||||
K_THREAD_DEFINE(input,
|
||||
CONFIG_INPUT_THREAD_STACK_SIZE,
|
||||
input_thread,
|
||||
NULL, NULL, NULL,
|
||||
INPUT_THREAD_PRIORITY, 0, 0);
|
||||
|
||||
#endif /* CONFIG_INPUT_MODE_THREAD */
|
1
subsys/input/input.ld
Normal file
1
subsys/input/input.ld
Normal file
|
@ -0,0 +1 @@
|
|||
ITERABLE_SECTION_ROM(input_listener, 4)
|
Loading…
Reference in a new issue