Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/develop/api/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,7 @@ between major releases are available in the :ref:`zephyr_release_notes`.
* - :ref:`zdsp_api`
- Experimental
- 3.3

* - :ref:`input_api`
- Experimental
- 3.3
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ add_subdirectory_ifdef(CONFIG_MM_DRV mm)
add_subdirectory_ifdef(CONFIG_RESET reset)
add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump)
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE fuel_gauge)
add_subdirectory_ifdef(CONFIG_INPUT input)
2 changes: 2 additions & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,6 @@ source "drivers/xen/Kconfig"

source "drivers/fuel_gauge/Kconfig"

source "drivers/input/Kconfig"

endmenu
9 changes: 9 additions & 0 deletions drivers/input/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2022, [email protected]
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources_ifdef(CONFIG_INPUT input_internal.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE input_handlers.c)

add_subdirectory_ifdef(CONFIG_KEYBOARD keyboard)
36 changes: 36 additions & 0 deletions drivers/input/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Input subsystem configuration options

# Copyright (c) 2022, [email protected]
# SPDX-License-Identifier: Apache-2.0

menuconfig INPUT
bool "Input Subsystem Drivers"
select RING_BUFFER
help
Include input subsystem drivers in system config.

if INPUT

config ENABLE_INPUT_ISR_LOCK
bool "Input event interrupt lock"
help
Use k_spin_lock() and k_spin_unlock() to control ring buffer exclusive access.
This is required to be able to report input event in isr context.

source "drivers/input/keyboard/Kconfig"

module = INPUT
module-str = input
source "subsys/logging/Kconfig.template.log_config"

config INPUT_INIT_PRIORITY
int "Input subsystem driver init priority"
default 90
help
Input subsystem driver initialization priority.

config INPUT_ATTR_DATA_SIZE
int "Input attribute data max size"
default 64

endif # INPUT
85 changes: 85 additions & 0 deletions drivers/input/input_handlers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2022, [email protected]
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/drivers/input.h>
#include <zephyr/syscall_handler.h>

static inline int z_vrfy_input_setup(const struct device *dev)
{
Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, setup));

return z_impl_input_setup((const struct device *)dev);
}
#include <syscalls/input_setup_mrsh.c>

static inline int z_vrfy_input_release(const struct device *dev)
{
Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, release));

return z_impl_input_release((const struct device *)dev);
}
#include <syscalls/input_release_mrsh.c>

static inline int z_vrfy_input_attr_get(const struct device *dev,
enum input_attr_type type, union input_attr_data *data)
{
Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, attr_get));

if (data) {
union input_attr_data _data;
int retval = 0;

retval = z_impl_input_attr_get((const struct device *)dev, type, &_data);
Z_OOPS(z_user_to_copy(data, &_data, sizeof(union input_attr_data)));

return retval;
}

return z_impl_input_attr_get((const struct device *)dev, type, NULL);
}
#include <syscalls/input_attr_get_mrsh.c>

static inline int z_vrfy_input_attr_set(const struct device *dev,
enum input_attr_type type, union input_attr_data *data)
{
Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, attr_set));

if (data) {
union input_attr_data _data;

Z_OOPS(z_user_from_copy(&_data, data, sizeof(union input_attr_data)));
return z_impl_input_attr_set((const struct device *)dev, type, &_data);
}

return z_impl_input_attr_set((const struct device *)dev, type, NULL);
}

#include <syscalls/input_attr_set_mrsh.c>

static inline int z_vrfy_input_event_read(const struct device *dev, struct input_event *event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async api would be very useful
blocking api forces you to use a dedicated thread to handle read.

I suggest creating a pollable sync object that can be used similarly as in linux poll.
Here is the example from NCS how it can be done https://github.com/nrfconnect/sdk-nrf/blob/main/samples/nfc/tag_reader/src/main.c#L703

{
struct input_event _event;
int retval = 0;

Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, event_read));

retval = z_impl_input_event_read((const struct device *)dev, &_event);
Z_OOPS(z_user_to_copy(event, &_event, sizeof(struct input_event)));

return retval;
}
#include <syscalls/input_event_read_mrsh.c>

static inline int z_vrfy_input_event_write(const struct device *dev, struct input_event *event)
{
struct input_event _event;

Z_OOPS(Z_SYSCALL_DRIVER_INPUT(dev, event_write));
Z_OOPS(z_user_from_copy(&_event, event, sizeof(struct input_event)));

return z_impl_input_event_write((const struct device *)dev, &_event);
}
#include <syscalls/input_event_write_mrsh.c>
214 changes: 214 additions & 0 deletions drivers/input/input_internal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright (c) 2022, [email protected]
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "input_internal.h"

int input_internal_setup(struct input_dev *dev)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From one input device stand-point: are you going to call this more than once at runtime?

If not, then all of this can be integrated into a macro with relevant static intialization for each objects (rb, mutex, sem...)

{
int retval = 0;

if (dev->buf == NULL) {
return -EFAULT;
}

ring_buf_reset(dev->buf);

#ifndef CONFIG_ENABLE_INPUT_ISR_LOCK
retval = k_mutex_init(&(dev->mutex));
if (retval < 0) {
return retval;
}
#endif

retval = k_sem_init(&(dev->sem), 0, 1);
if (retval < 0) {
return retval;
}

dev->readtimeo = K_FOREVER;

return 0;
}

int input_internal_release(struct input_dev *dev)
{
k_sem_reset(&(dev->sem));

return 0;
}

int input_internal_attr_get(struct input_dev *dev,
enum input_attr_type type, union input_attr_data *data)
{
int retval = 0;

switch (type) {
case INPUT_ATTR_EVENT_READ_TIMEOUT:
{
data->timeout = dev->readtimeo;
break;
}

default:
retval = -ENOTSUP;
break;
}

return retval;
}

int input_internal_attr_set(struct input_dev *dev,
enum input_attr_type type, union input_attr_data *data)
{
int retval = 0;

switch (type) {
case INPUT_ATTR_EVENT_READ_TIMEOUT:
{
dev->readtimeo = data->timeout;

k_sem_give(&(dev->sem));

break;
}

default:
retval = -ENOTSUP;
break;
}

return retval;
}

#ifdef CONFIG_ENABLE_INPUT_ISR_LOCK

#define INPUT_LOCK() ({ \
key = k_spin_lock(&(dev->lock)); \
0; \
})

#define INPUT_UNLOCK() ({ \
k_spin_unlock(&(dev->lock), key); \
0; \
})

#else

#define INPUT_LOCK() \
k_mutex_lock(&(dev->mutex), K_FOREVER);

#define INPUT_UNLOCK() \
k_mutex_unlock(&(dev->mutex));

#endif

int input_internal_event_read(struct input_dev *dev, struct input_event *event)
{
#ifdef CONFIG_ENABLE_INPUT_ISR_LOCK
k_spinlock_key_t key;
#endif
uint32_t size = 0;
int retval = 0;

if (dev->buf == NULL) {
return -EFAULT;
}

INPUT_ASSERT(!k_is_in_isr(), "Can't read event in isr context.");

retval = INPUT_LOCK();
if (retval < 0) {
return retval;
}

size = ring_buf_size_get(dev->buf);
if (size > 0) {
/* Ring buf size must be a multiple of input event size */
INPUT_ASSERT(!(size % input_event_size), "Invalid ring buf size");

size = ring_buf_get(dev->buf, (uint8_t *)event, input_event_size);
} else if (!K_TIMEOUT_EQ(dev->readtimeo, K_NO_WAIT)) {
do {

INPUT_UNLOCK();

retval = k_sem_take(&(dev->sem), dev->readtimeo);
if (retval < 0) {
return retval;
}

retval = INPUT_LOCK();
if (retval < 0) {
return retval;
}

size = ring_buf_size_get(dev->buf);
if (size > 0) {
/* Ring buf size must be a multiple of input event size */
INPUT_ASSERT(!(size % input_event_size), "Invalid ring buf size");

size = ring_buf_get(dev->buf, (uint8_t *)event, input_event_size);
break;
}
} while (1);
} else {
retval = -EAGAIN;
}

INPUT_UNLOCK();

return retval;
}

int input_event(struct input_dev *dev, uint16_t type, uint16_t code, int32_t value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

creating an event system only for input may be a resource waste.
I would suggest integrating this with system wide event manager. We have the code ready for that (handling input events for both usb and ble) in NCS.

{
struct input_event event;
int64_t time_stamp;
#ifdef CONFIG_ENABLE_INPUT_ISR_LOCK
k_spinlock_key_t key;
#endif
uint32_t size = 0;
int retval = 0;

#ifndef CONFIG_ENABLE_INPUT_ISR_LOCK
INPUT_ASSERT(!k_is_in_isr(), "Can't report event in isr context.");
#endif

if (dev->buf == NULL) {
return -EFAULT;
}

/* capture initial time stamp */
time_stamp = k_uptime_get();

event.time.tv_sec = (time_stamp / 1000);
event.time.tv_usec = (time_stamp % 1000) * 1000;

event.type = type;
event.code = code;
event.value = value;

retval = INPUT_LOCK();
if (retval < 0) {
return retval;
}

size = ring_buf_space_get(dev->buf);
if (size > 0) {
/* Ring buf space must be a multiple of input event size */
INPUT_ASSERT(!(size % input_event_size), "Invalid ring buf space");

size = ring_buf_put(dev->buf, (const uint8_t *)&event, input_event_size);

k_sem_give(&(dev->sem));
} else {
retval = -EAGAIN;
}

INPUT_UNLOCK();

return retval;
}
Loading