blob: 9e98fd5f5148b56091ba3f79af4c76b1ec366ff4 [file] [log] [blame]
/*
* 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(struct input_event *evt, void *user_data)
{
const struct device *dev = user_data;
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) \
INPUT_CALLBACK_DEFINE_NAMED(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb, \
(void *)DEVICE_DT_INST_GET(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)