| /* |
| * Copyright 2024 Google LLC |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT input_keymap |
| |
| #include <zephyr/device.h> |
| #include <zephyr/dt-bindings/input/keymap.h> |
| #include <zephyr/input/input.h> |
| #include <zephyr/input/input_keymap.h> |
| #include <zephyr/kernel.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(input_keymap, CONFIG_INPUT_LOG_LEVEL); |
| |
| struct keymap_config { |
| const struct device *input_dev; |
| const uint16_t *codes; |
| uint32_t num_codes; |
| uint8_t row_size; |
| uint8_t col_size; |
| }; |
| |
| struct keymap_data { |
| uint32_t row; |
| uint32_t col; |
| bool pressed; |
| }; |
| |
| static void keymap_cb(const struct device *dev, struct input_event *evt) |
| { |
| const struct keymap_config *cfg = dev->config; |
| struct keymap_data *data = dev->data; |
| const uint16_t *codes = cfg->codes; |
| uint32_t offset; |
| |
| switch (evt->code) { |
| case INPUT_ABS_X: |
| data->col = evt->value; |
| break; |
| case INPUT_ABS_Y: |
| data->row = evt->value; |
| break; |
| case INPUT_BTN_TOUCH: |
| data->pressed = evt->value; |
| break; |
| } |
| |
| if (!evt->sync) { |
| return; |
| } |
| |
| if (data->row >= cfg->row_size || |
| data->col >= cfg->col_size) { |
| LOG_WRN("keymap event out of range: row=%u col=%u", data->row, data->col); |
| return; |
| } |
| |
| offset = (data->row * cfg->col_size) + data->col; |
| |
| if (offset >= cfg->num_codes || codes[offset] == 0) { |
| LOG_DBG("keymap event undefined: row=%u col=%u", data->row, data->col); |
| return; |
| } |
| |
| LOG_DBG("input event: %3u %3u %d", data->row, data->col, data->pressed); |
| |
| input_report_key(dev, codes[offset], data->pressed, true, K_FOREVER); |
| } |
| |
| static int keymap_init(const struct device *dev) |
| { |
| const struct keymap_config *cfg = dev->config; |
| |
| if (!device_is_ready(cfg->input_dev)) { |
| LOG_ERR("input device not ready"); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| #define KEYMAP_ENTRY_OFFSET(keymap_entry, col_size) \ |
| (MATRIX_ROW(keymap_entry) * col_size + MATRIX_COL(keymap_entry)) |
| |
| #define KEYMAP_ENTRY_CODE(keymap_entry) (keymap_entry & 0xffff) |
| |
| #define KEYMAP_ENTRY_VALIDATE(node_id, prop, idx) \ |
| BUILD_ASSERT(MATRIX_ROW(DT_PROP_BY_IDX(node_id, prop, idx)) < \ |
| DT_PROP(node_id, row_size), "invalid row"); \ |
| BUILD_ASSERT(MATRIX_COL(DT_PROP_BY_IDX(node_id, prop, idx)) < \ |
| DT_PROP(node_id, col_size), "invalid col"); |
| |
| #define CODES_INIT(node_id, prop, idx) \ |
| [KEYMAP_ENTRY_OFFSET(DT_PROP_BY_IDX(node_id, prop, idx), DT_PROP(node_id, col_size))] = \ |
| KEYMAP_ENTRY_CODE(DT_PROP_BY_IDX(node_id, prop, idx)), |
| |
| #define INPUT_KEYMAP_DEFINE(inst) \ |
| static void keymap_cb_##inst(struct input_event *evt) \ |
| { \ |
| keymap_cb(DEVICE_DT_INST_GET(inst), evt); \ |
| } \ |
| INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb_##inst); \ |
| \ |
| DT_INST_FOREACH_PROP_ELEM(inst, keymap, KEYMAP_ENTRY_VALIDATE) \ |
| \ |
| static const uint16_t keymap_codes_##inst[] = { \ |
| DT_INST_FOREACH_PROP_ELEM(inst, keymap, CODES_INIT) \ |
| }; \ |
| \ |
| static const struct keymap_config keymap_config_##inst = { \ |
| .input_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ |
| .codes = keymap_codes_##inst, \ |
| .num_codes = ARRAY_SIZE(keymap_codes_##inst), \ |
| .row_size = DT_INST_PROP(inst, row_size), \ |
| .col_size = DT_INST_PROP(inst, col_size), \ |
| }; \ |
| \ |
| static struct keymap_data keymap_data_##inst; \ |
| \ |
| DEVICE_DT_INST_DEFINE(inst, keymap_init, NULL, \ |
| &keymap_data_##inst, &keymap_config_##inst, \ |
| POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL); |
| |
| DT_INST_FOREACH_STATUS_OKAY(INPUT_KEYMAP_DEFINE) |