1 /*
2  * Copyright 2024 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT input_keymap
8 
9 #include <zephyr/device.h>
10 #include <zephyr/dt-bindings/input/keymap.h>
11 #include <zephyr/input/input.h>
12 #include <zephyr/input/input_keymap.h>
13 #include <zephyr/kernel.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(input_keymap, CONFIG_INPUT_LOG_LEVEL);
17 
18 struct keymap_config {
19 	const struct device *input_dev;
20 	const uint16_t *codes;
21 	uint32_t num_codes;
22 	uint8_t row_size;
23 	uint8_t col_size;
24 };
25 
26 struct keymap_data {
27 	uint32_t row;
28 	uint32_t col;
29 	bool pressed;
30 };
31 
keymap_cb(struct input_event * evt,void * user_data)32 static void keymap_cb(struct input_event *evt, void *user_data)
33 {
34 	const struct device *dev = user_data;
35 	const struct keymap_config *cfg = dev->config;
36 	struct keymap_data *data = dev->data;
37 	const uint16_t *codes = cfg->codes;
38 	uint32_t offset;
39 
40 	switch (evt->code) {
41 	case INPUT_ABS_X:
42 		data->col = evt->value;
43 		break;
44 	case INPUT_ABS_Y:
45 		data->row = evt->value;
46 		break;
47 	case INPUT_BTN_TOUCH:
48 		data->pressed = evt->value;
49 		break;
50 	}
51 
52 	if (!evt->sync) {
53 		return;
54 	}
55 
56 	if (data->row >= cfg->row_size ||
57 	    data->col >= cfg->col_size) {
58 		LOG_WRN("keymap event out of range: row=%u col=%u", data->row, data->col);
59 		return;
60 	}
61 
62 	offset = (data->row * cfg->col_size) + data->col;
63 
64 	if (offset >= cfg->num_codes || codes[offset] == 0) {
65 		LOG_DBG("keymap event undefined: row=%u col=%u", data->row, data->col);
66 		return;
67 	}
68 
69 	LOG_DBG("input event: %3u %3u %d", data->row, data->col, data->pressed);
70 
71 	input_report_key(dev, codes[offset], data->pressed, true, K_FOREVER);
72 }
73 
keymap_init(const struct device * dev)74 static int keymap_init(const struct device *dev)
75 {
76 	const struct keymap_config *cfg = dev->config;
77 
78 	if (!device_is_ready(cfg->input_dev)) {
79 		LOG_ERR("input device not ready");
80 		return -ENODEV;
81 	}
82 
83 	return 0;
84 }
85 
86 #define KEYMAP_ENTRY_OFFSET(keymap_entry, col_size) \
87 	(MATRIX_ROW(keymap_entry) * col_size + MATRIX_COL(keymap_entry))
88 
89 #define KEYMAP_ENTRY_CODE(keymap_entry) (keymap_entry & 0xffff)
90 
91 #define KEYMAP_ENTRY_VALIDATE(node_id, prop, idx)			\
92 	BUILD_ASSERT(MATRIX_ROW(DT_PROP_BY_IDX(node_id, prop, idx)) <	\
93 		     DT_PROP(node_id, row_size), "invalid row");	\
94 	BUILD_ASSERT(MATRIX_COL(DT_PROP_BY_IDX(node_id, prop, idx)) <	\
95 		     DT_PROP(node_id, col_size), "invalid col");
96 
97 #define CODES_INIT(node_id, prop, idx) \
98 	[KEYMAP_ENTRY_OFFSET(DT_PROP_BY_IDX(node_id, prop, idx), DT_PROP(node_id, col_size))] = \
99 		KEYMAP_ENTRY_CODE(DT_PROP_BY_IDX(node_id, prop, idx)),
100 
101 #define INPUT_KEYMAP_DEFINE(inst)								\
102 	INPUT_CALLBACK_DEFINE_NAMED(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb,		\
103 				    (void *)DEVICE_DT_INST_GET(inst), keymap_cb_##inst);	\
104 												\
105 	DT_INST_FOREACH_PROP_ELEM(inst, keymap, KEYMAP_ENTRY_VALIDATE)				\
106 												\
107 	static const uint16_t keymap_codes_##inst[] = {						\
108 		DT_INST_FOREACH_PROP_ELEM(inst, keymap, CODES_INIT)				\
109 	};											\
110 												\
111 	static const struct keymap_config keymap_config_##inst = {				\
112 		.input_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)),				\
113 		.codes = keymap_codes_##inst,							\
114 		.num_codes = ARRAY_SIZE(keymap_codes_##inst),					\
115 		.row_size = DT_INST_PROP(inst, row_size),					\
116 		.col_size = DT_INST_PROP(inst, col_size),					\
117 	};											\
118 												\
119 	static struct keymap_data keymap_data_##inst;						\
120 												\
121 	DEVICE_DT_INST_DEFINE(inst, keymap_init, NULL,						\
122 			      &keymap_data_##inst, &keymap_config_##inst,			\
123 			      POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
124 
125 DT_INST_FOREACH_STATUS_OKAY(INPUT_KEYMAP_DEFINE)
126