1 /*
2  * Copyright (c) 2024 Chen Xingyu <hi@xingrz.me>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT adc_keys
7 
8 #include <stdlib.h>
9 #include <stdbool.h>
10 
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/adc.h>
13 #include <zephyr/input/input.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/sys/util.h>
17 
18 LOG_MODULE_REGISTER(adc_keys, CONFIG_INPUT_LOG_LEVEL);
19 
20 struct adc_keys_code_config {
21 	int32_t press_mv;
22 	uint8_t key_index;
23 };
24 
25 struct adc_keys_key_state {
26 	bool last_state;
27 	bool curr_state;
28 };
29 
30 struct adc_keys_config {
31 	struct adc_dt_spec channel;
32 	uint32_t sample_period_ms;
33 	int32_t keyup_mv;
34 	const struct adc_keys_code_config *code_cfg;
35 	const uint16_t *key_code;
36 	struct adc_keys_key_state *key_state;
37 	uint8_t code_cnt;
38 	uint8_t key_cnt;
39 };
40 
41 struct adc_keys_data {
42 	const struct device *self;
43 	struct k_work_delayable dwork;
44 	struct adc_sequence seq;
45 };
46 
adc_keys_read(const struct device * dev)47 static inline int32_t adc_keys_read(const struct device *dev)
48 {
49 	const struct adc_keys_config *cfg = dev->config;
50 	struct adc_keys_data *data = dev->data;
51 	uint16_t sample_raw;
52 	int32_t sample_mv;
53 	int ret;
54 
55 	data->seq.buffer = &sample_raw;
56 	data->seq.buffer_size = sizeof(sample_raw);
57 
58 	ret = adc_read(cfg->channel.dev, &data->seq);
59 	if (ret) {
60 		LOG_ERR("ADC read failed %d", ret);
61 		return cfg->keyup_mv;
62 	}
63 
64 	sample_mv = (int32_t)sample_raw;
65 	adc_raw_to_millivolts_dt(&cfg->channel, &sample_mv);
66 
67 	return sample_mv;
68 }
69 
adc_keys_process(const struct device * dev)70 static inline void adc_keys_process(const struct device *dev)
71 {
72 	const struct adc_keys_config *cfg = dev->config;
73 	int32_t sample_mv, closest_mv = 0;
74 	uint32_t diff, closest_diff = UINT32_MAX;
75 	const struct adc_keys_code_config *code_cfg;
76 	struct adc_keys_key_state *key_state;
77 	uint16_t key_code;
78 
79 	sample_mv = adc_keys_read(dev);
80 
81 	/*
82 	 * Find the closest key press threshold to the sample value.
83 	 */
84 
85 	for (uint8_t i = 0; i < cfg->code_cnt; i++) {
86 		diff = abs(sample_mv - cfg->code_cfg[i].press_mv);
87 		if (diff < closest_diff) {
88 			closest_diff = diff;
89 			closest_mv = cfg->code_cfg[i].press_mv;
90 		}
91 	}
92 
93 	diff = abs(sample_mv - cfg->keyup_mv);
94 	if (diff < closest_diff) {
95 		closest_diff = diff;
96 		closest_mv = cfg->keyup_mv;
97 	}
98 
99 	LOG_DBG("sample=%d mV, closest=%d mV, diff=%d mV", sample_mv, closest_mv, closest_diff);
100 
101 	/*
102 	 * Update cached key states according to the closest key press threshold.
103 	 *
104 	 * Note that multiple keys may have the same press threshold, which is
105 	 * the mixed voltage that these keys are simultaneously pressed.
106 	 */
107 
108 	for (uint8_t i = 0; i < cfg->code_cnt; i++) {
109 		code_cfg = &cfg->code_cfg[i];
110 		key_state = &cfg->key_state[code_cfg->key_index];
111 
112 		/*
113 		 * Only update curr_state if the key is pressed to prevent
114 		 * being overwritten by another threshold configuration.
115 		 */
116 		if (closest_mv == code_cfg->press_mv) {
117 			key_state->curr_state = true;
118 		}
119 	}
120 
121 	/*
122 	 * Report the key event if the key state has changed.
123 	 */
124 
125 	for (uint8_t i = 0; i < cfg->key_cnt; i++) {
126 		key_state = &cfg->key_state[i];
127 		key_code = cfg->key_code[i];
128 
129 		if (key_state->last_state != key_state->curr_state) {
130 			LOG_DBG("Report event %s %d, code=%d", dev->name, key_state->curr_state,
131 				key_code);
132 			input_report_key(dev, key_code, key_state->curr_state, true, K_FOREVER);
133 			key_state->last_state = key_state->curr_state;
134 		}
135 
136 		/*
137 		 * Reset the state so that it can be updated in the next
138 		 * iteration.
139 		 */
140 		key_state->curr_state = false;
141 	}
142 }
143 
adc_keys_work_handler(struct k_work * work)144 static void adc_keys_work_handler(struct k_work *work)
145 {
146 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
147 	struct adc_keys_data *data = CONTAINER_OF(dwork, struct adc_keys_data, dwork);
148 	const struct device *dev = data->self;
149 	const struct adc_keys_config *cfg = dev->config;
150 
151 	adc_keys_process(dev);
152 
153 	k_work_schedule(&data->dwork, K_MSEC(cfg->sample_period_ms));
154 }
155 
adc_keys_init(const struct device * dev)156 static int adc_keys_init(const struct device *dev)
157 {
158 	const struct adc_keys_config *cfg = dev->config;
159 	struct adc_keys_data *data = dev->data;
160 	int ret;
161 
162 	if (!adc_is_ready_dt(&cfg->channel)) {
163 		LOG_ERR("ADC controller device %s not ready", cfg->channel.dev->name);
164 		return -ENODEV;
165 	}
166 
167 	ret = adc_channel_setup_dt(&cfg->channel);
168 	if (ret) {
169 		LOG_ERR("ADC channel setup failed %d", ret);
170 		return ret;
171 	}
172 
173 	ret = adc_sequence_init_dt(&cfg->channel, &data->seq);
174 	if (ret) {
175 		LOG_ERR("ADC sequence init failed %d", ret);
176 		return ret;
177 	}
178 
179 	data->self = dev;
180 	k_work_init_delayable(&data->dwork, adc_keys_work_handler);
181 
182 	if (IS_ENABLED(CONFIG_INPUT_LOG_LEVEL_DBG)) {
183 		for (uint8_t i = 0; i < cfg->code_cnt; i++) {
184 			LOG_DBG("* code %d: key_index=%d threshold=%d mV code=%d", i,
185 				cfg->code_cfg[i].key_index, cfg->code_cfg[i].press_mv,
186 				cfg->key_code[cfg->code_cfg[i].key_index]);
187 		}
188 	}
189 
190 	k_work_schedule(&data->dwork, K_MSEC(cfg->sample_period_ms));
191 
192 	return 0;
193 }
194 
195 #define ADC_KEYS_CODE_CFG_ITEM(node_id, prop, idx)                                                 \
196 	{                                                                                          \
197 		.key_index = DT_NODE_CHILD_IDX(node_id) /* include disabled nodes */,              \
198 		.press_mv = DT_PROP_BY_IDX(node_id, prop, idx),                                    \
199 	}
200 
201 #define ADC_KEYS_CODE_CFG(node_id)                                                                 \
202 	DT_FOREACH_PROP_ELEM_SEP(node_id, press_thresholds_mv, ADC_KEYS_CODE_CFG_ITEM, (,))
203 
204 #define ADC_KEYS_KEY_CODE(node_id) DT_PROP(node_id, zephyr_code)
205 
206 #define ADC_KEYS_INST(n)                                                                           \
207 	static struct adc_keys_data adc_keys_data_##n;                                             \
208                                                                                                    \
209 	static const struct adc_keys_code_config adc_keys_code_cfg_##n[] = {                       \
210 		DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(n, ADC_KEYS_CODE_CFG, (,))};                 \
211                                                                                                    \
212 	static const uint16_t adc_keys_key_code_##n[] = {                                          \
213 		DT_INST_FOREACH_CHILD_SEP(n, ADC_KEYS_KEY_CODE, (,))};                             \
214                                                                                                    \
215 	static struct adc_keys_key_state                                                           \
216 		adc_keys_key_state_##n[ARRAY_SIZE(adc_keys_key_code_##n)];                         \
217                                                                                                    \
218 	static const struct adc_keys_config adc_keys_cfg_##n = {                                   \
219 		.channel = ADC_DT_SPEC_INST_GET(n),                                                \
220 		.sample_period_ms = DT_INST_PROP(n, sample_period_ms),                             \
221 		.keyup_mv = DT_INST_PROP(n, keyup_threshold_mv),                                   \
222 		.code_cfg = adc_keys_code_cfg_##n,                                                 \
223 		.key_code = adc_keys_key_code_##n,                                                 \
224 		.key_state = adc_keys_key_state_##n,                                               \
225 		.code_cnt = ARRAY_SIZE(adc_keys_code_cfg_##n),                                     \
226 		.key_cnt = ARRAY_SIZE(adc_keys_key_code_##n),                                      \
227 	};                                                                                         \
228                                                                                                    \
229 	DEVICE_DT_INST_DEFINE(n, adc_keys_init, NULL, &adc_keys_data_##n, &adc_keys_cfg_##n,       \
230 			      POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
231 
232 DT_INST_FOREACH_STATUS_OKAY(ADC_KEYS_INST)
233