1 /*
2  * Copyright 2024 Kelly Helmut Lord
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_input_double_tap
8 
9 #include <zephyr/device.h>
10 #include <zephyr/input/input.h>
11 #include <zephyr/kernel.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(input_double_tap, CONFIG_INPUT_LOG_LEVEL);
15 
16 struct double_tap_config {
17 	const struct device *input_dev;
18 	struct double_tap_data_entry *entries;
19 	const uint16_t *input_codes;
20 	const uint16_t *double_tap_codes;
21 	uint32_t double_tap_delay_ms;
22 	uint8_t num_codes;
23 };
24 
25 struct double_tap_data_entry {
26 	const struct device *dev;
27 	struct k_work_delayable work;
28 	uint8_t index;
29 	bool first_tap;
30 };
31 
double_tap_deferred(struct k_work * work)32 static void double_tap_deferred(struct k_work *work)
33 {
34 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
35 	struct double_tap_data_entry *entry =
36 		CONTAINER_OF(dwork, struct double_tap_data_entry, work);
37 
38 	entry->first_tap = false;
39 }
40 
double_tap_cb(struct input_event * evt,void * user_data)41 static void double_tap_cb(struct input_event *evt, void *user_data)
42 {
43 	const struct device *dev = user_data;
44 	const struct double_tap_config *cfg = dev->config;
45 	struct double_tap_data_entry *entry;
46 	int i;
47 
48 	if (evt->type != INPUT_EV_KEY) {
49 		return;
50 	}
51 
52 	for (i = 0; i < cfg->num_codes; i++) {
53 		if (evt->code == cfg->input_codes[i]) {
54 			break;
55 		}
56 	}
57 	if (i == cfg->num_codes) {
58 		LOG_DBG("ignored code %d", evt->code);
59 		return;
60 	}
61 
62 	entry = &cfg->entries[i];
63 
64 	if (evt->value) {
65 		if (entry->first_tap) {
66 			k_work_cancel_delayable(&entry->work);
67 			input_report_key(dev, cfg->double_tap_codes[i], 1, true, K_FOREVER);
68 			input_report_key(dev, cfg->double_tap_codes[i], 0, true, K_FOREVER);
69 			entry->first_tap = false;
70 		} else {
71 			entry->first_tap = true;
72 			k_work_schedule(&entry->work, K_MSEC(cfg->double_tap_delay_ms));
73 		}
74 	}
75 }
76 
double_tap_init(const struct device * dev)77 static int double_tap_init(const struct device *dev)
78 {
79 	const struct double_tap_config *cfg = dev->config;
80 
81 	if (cfg->input_dev && !device_is_ready(cfg->input_dev)) {
82 		LOG_ERR("input device not ready");
83 		return -ENODEV;
84 	}
85 
86 	for (int i = 0; i < cfg->num_codes; i++) {
87 		struct double_tap_data_entry *entry = &cfg->entries[i];
88 
89 		entry->dev = dev;
90 		entry->index = i;
91 		entry->first_tap = false;
92 		k_work_init_delayable(&entry->work, double_tap_deferred);
93 	}
94 
95 	return 0;
96 }
97 
98 #define INPUT_DOUBLE_TAP_DEFINE(inst)                                                              \
99 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) ==                                        \
100 		     DT_INST_PROP_LEN(inst, double_tap_codes));                                    \
101                                                                                                    \
102 	INPUT_CALLBACK_DEFINE_NAMED(DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)),           \
103 				    double_tap_cb, (void *)DEVICE_DT_INST_GET(inst),               \
104 				    double_tap_cb_##inst);                                         \
105                                                                                                    \
106 	static const uint16_t double_tap_input_codes_##inst[] = DT_INST_PROP(inst, input_codes);   \
107                                                                                                    \
108 	static const uint16_t double_tap_codes_##inst[] = DT_INST_PROP(inst, double_tap_codes);    \
109                                                                                                    \
110 	static struct double_tap_data_entry                                                        \
111 		double_tap_data_entries_##inst[DT_INST_PROP_LEN(inst, input_codes)];               \
112                                                                                                    \
113 	static const struct double_tap_config double_tap_config_##inst = {                         \
114 		.input_dev = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)),                  \
115 		.entries = double_tap_data_entries_##inst,                                         \
116 		.input_codes = double_tap_input_codes_##inst,                                      \
117 		.double_tap_codes = double_tap_codes_##inst,                                       \
118 		.num_codes = DT_INST_PROP_LEN(inst, input_codes),                                  \
119 		.double_tap_delay_ms = DT_INST_PROP(inst, double_tap_delay_ms),                    \
120 	};                                                                                         \
121                                                                                                    \
122 	DEVICE_DT_INST_DEFINE(inst, double_tap_init, NULL, NULL, &double_tap_config_##inst,        \
123 			      POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
124 
125 DT_INST_FOREACH_STATUS_OKAY(INPUT_DOUBLE_TAP_DEFINE)
126