diff options
Diffstat (limited to 'components/drivers/input/input.c')
| -rw-r--r-- | components/drivers/input/input.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/components/drivers/input/input.c b/components/drivers/input/input.c new file mode 100644 index 0000000000..deec1fb594 --- /dev/null +++ b/components/drivers/input/input.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <rtdevice.h> + +#define DBG_TAG "rtdm.input" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static RT_DEFINE_SPINLOCK(input_device_lock); +static rt_list_t input_device_nodes = RT_LIST_OBJECT_INIT(input_device_nodes); + +static struct rt_dm_ida input_ida = RT_DM_IDA_INIT(INPUT); + +#ifdef RT_INPUT_TOUCHSCREEN +extern void input_touch_register(struct rt_input_device *idev); +extern void input_touch_unregister(struct rt_input_device *idev); +#endif + +#ifdef RT_INPUT_UAPI +extern void input_uapi_init(struct rt_input_device *idev); +extern void input_uapi_finit(struct rt_input_device *idev); +extern void input_uapi_event(struct rt_input_device *idev, struct rt_input_event *event); +#endif + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops _input_ops = +{ +}; +#endif + +rt_err_t rt_input_device_register(struct rt_input_device *idev) +{ + rt_err_t err; + int device_id; + const char *dev_name; + + if (!idev) + { + return -RT_EINVAL; + } + + if ((device_id = rt_dm_ida_alloc(&input_ida)) < 0) + { + err = -RT_EFULL; + goto _remove_config; + } + + rt_dm_dev_set_name(&idev->parent, "input%u", device_id); + dev_name = rt_dm_dev_get_name(&idev->parent); + + rt_list_init(&idev->list); + rt_list_init(&idev->handler_nodes); + rt_spin_lock_init(&idev->lock); + + /* Just make a search interface */ + idev->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + idev->parent.ops = idev->parent.ops ? : &_input_ops; +#endif + idev->parent.master_id = input_ida.master_id; + idev->parent.device_id = device_id; + + if ((err = rt_device_register(&idev->parent, dev_name, RT_DEVICE_FLAG_DEACTIVATE))) + { + goto _fail; + } + +#ifdef RT_INPUT_UAPI + input_uapi_init(idev); +#endif + + rt_spin_lock(&input_device_lock); + rt_list_insert_before(&input_device_nodes, &idev->list); + rt_spin_unlock(&input_device_lock); + +#ifdef RT_INPUT_TOUCHSCREEN + /* MUST be registered after the list is inserted */ + input_touch_register(idev); +#endif + + if (idev->poller) + { + rt_timer_start(&idev->poller->timer); + } + + return RT_EOK; + +_fail: + rt_dm_ida_free(&input_ida, device_id); + +_remove_config: + rt_input_remove_config(idev); + + return err; +} + +rt_err_t rt_input_device_unregister(struct rt_input_device *idev) +{ + const char *dev_name; + + if (!idev) + { + return -RT_EINVAL; + } + + dev_name = rt_dm_dev_get_name(&idev->parent); + + if (idev->parent.ref_count) + { + LOG_E("%s: there is %u user", dev_name, idev->parent.ref_count); + return -RT_EINVAL; + } + +#ifdef RT_INPUT_UAPI + input_uapi_finit(idev); +#endif +#ifdef RT_INPUT_TOUCHSCREEN + input_touch_unregister(idev); +#endif + rt_input_remove_config(idev); + + rt_dm_ida_free(&input_ida, idev->parent.device_id); + + rt_device_unregister(&idev->parent); + + rt_spin_lock(&input_device_lock); + rt_list_remove(&idev->list); + rt_spin_unlock(&input_device_lock); + + return RT_EOK; +} + +rt_err_t rt_input_set_capability(struct rt_input_device *idev, + rt_uint16_t type, rt_uint16_t code) +{ + if (!idev) + { + return -RT_EINVAL; + } + + switch (type) + { + case EV_KEY: + rt_bitmap_set_bit(idev->key_map, code); + break; + + case EV_REL: + rt_bitmap_set_bit(idev->rel_map, code); + break; + + case EV_ABS: + if (!idev->absinfo) + { + idev->absinfo = rt_calloc(ABS_CNT, sizeof(*idev->absinfo)); + + if (!idev->absinfo) + { + return -RT_ENOMEM; + } + } + rt_bitmap_set_bit(idev->abs_map, code); + break; + + case EV_MSC: + case EV_SW: + case EV_LED: + case EV_SND: + case EV_REP: + case EV_FF: + case EV_PWR: + case EV_FF_STATUS: + return -RT_ENOSYS; + + default: + return -RT_EINVAL; + } + + rt_bitmap_set_bit(idev->cap, type); + + return RT_EOK; +} + +rt_err_t rt_input_set_absinfo(struct rt_input_device *idev, rt_uint32_t axis, + rt_int32_t min, rt_int32_t max, rt_int32_t fuzz, rt_int32_t flat) +{ + struct rt_input_absinfo *absinfo; + + if (!idev || !idev->absinfo) + { + return -RT_EINVAL; + } + + rt_bitmap_set_bit(idev->abs_map, axis); + + absinfo = &idev->absinfo[axis]; + absinfo->minimum = min; + absinfo->maximum = max; + absinfo->fuzz = fuzz; + absinfo->flat = flat; + + return RT_EOK; +} + +static void input_poll(void *param) +{ + struct rt_input_device *idev = param; + + idev->poller->poll(idev); +} + +rt_err_t rt_input_setup_polling(struct rt_input_device *idev, + void (*poll)(struct rt_input_device *idev)) +{ + const char *dev_name; + + if (!idev || !poll) + { + return -RT_EINVAL; + } + + dev_name = rt_dm_dev_get_name(&idev->parent); + + idev->poller = rt_malloc(sizeof(*idev->poller)); + + if (!idev->poller) + { + return -RT_ENOMEM; + } + + idev->poller->poll = poll; + + rt_timer_init(&idev->poller->timer, dev_name, input_poll, idev, + rt_tick_from_millisecond(RT_INPUT_POLL_INTERVAL_DEFAULT), + RT_TIMER_FLAG_PERIODIC); + + return RT_EOK; +} + +void rt_input_remove_config(struct rt_input_device *idev) +{ + if (!idev) + { + return; + } + + if (idev->poller) + { + rt_timer_stop(&idev->poller->timer); + rt_timer_detach(&idev->poller->timer); + + rt_free(idev->poller); + idev->poller = RT_NULL; + } + + if (idev->absinfo) + { + rt_free(idev->absinfo); + idev->absinfo = RT_NULL; + } + +#ifdef RT_INPUT_TOUCHSCREEN + if (idev->touch) + { + rt_free(idev->touch); + idev->touch = RT_NULL; + } +#endif /* RT_INPUT_TOUCHSCREEN */ +} + +rt_err_t rt_input_set_poll_interval(struct rt_input_device *idev, + rt_uint32_t interval_ms) +{ + rt_tick_t tick; + + if (!idev || !idev->poller) + { + return -RT_EINVAL; + } + + tick = rt_tick_from_millisecond(interval_ms); + + return rt_timer_control(&idev->poller->timer, RT_TIMER_CTRL_SET_TIME, &tick); +} + +rt_err_t rt_input_trigger(struct rt_input_device *idev, + rt_uint16_t type, rt_uint16_t code, rt_int32_t value) +{ + RT_ASSERT(idev != RT_NULL); + + if (idev->trigger) + { + return idev->trigger(idev, type, code, value); + } + + return -RT_ENOSYS; +} + +void rt_input_event(struct rt_input_device *idev, + rt_uint16_t type, rt_uint16_t code, rt_int32_t value) +{ + struct rt_input_event event; + struct rt_input_handler *handler, *handler_next; + + RT_ASSERT(idev != RT_NULL); + RT_ASSERT(type < EV_MAX); + + event.tick = rt_tick_get(); + event.type = type; + event.code = code; + event.value = value; + + rt_spin_lock(&idev->lock); + +#ifdef RT_INPUT_UAPI + input_uapi_event(idev, &event); +#endif + + rt_list_for_each_entry_safe(handler, handler_next, &idev->handler_nodes, list) + { + if (handler->callback(handler, &event)) + { + break; + } + } + + rt_spin_unlock(&idev->lock); +} + +rt_err_t rt_input_add_handler(struct rt_input_handler *handler) +{ + struct rt_input_device *idev = RT_NULL, *idev_tmp; + + if (!handler || !handler->callback) + { + return -RT_EINVAL; + } + + if (!handler->idev && !handler->identify) + { + return -RT_EINVAL; + } + + rt_spin_lock(&input_device_lock); + + rt_list_for_each_entry(idev_tmp, &input_device_nodes, list) + { + if (handler->idev) + { + if (handler->idev == idev_tmp) + { + idev = idev_tmp; + break; + } + } + else if (handler->identify(handler, idev_tmp)) + { + idev = idev_tmp; + break; + } + } + + rt_spin_unlock(&input_device_lock); + + if (!idev) + { + return -RT_ENOSYS; + } + + handler->idev = idev; + rt_list_init(&handler->list); + + rt_spin_lock(&idev->lock); + rt_list_insert_before(&idev->handler_nodes, &handler->list); + rt_spin_unlock(&idev->lock); + + return RT_EOK; +} + +rt_err_t rt_input_del_handler(struct rt_input_handler *handler) +{ + struct rt_input_device *idev; + + if (!handler) + { + return -RT_EINVAL; + } + + idev = handler->idev; + + rt_spin_lock(&idev->lock); + rt_list_remove(&handler->list); + rt_spin_unlock(&idev->lock); + + return RT_EOK; +} |
