-
Notifications
You must be signed in to change notification settings - Fork 8.2k
drivers: input: Input subsystem #48342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,4 +151,6 @@ source "drivers/xen/Kconfig" | |
|
|
||
| source "drivers/fuel_gauge/Kconfig" | ||
|
|
||
| source "drivers/input/Kconfig" | ||
|
|
||
| endmenu | ||
| 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) |
| 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 |
| 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) | ||
| { | ||
| 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> | ||
| 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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
||
| { | ||
| 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; | ||
| } | ||
There was a problem hiding this comment.
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