1 /*
2  * Copyright 2025 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT arduino_modulino_buttons
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/input/input.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 
15 LOG_MODULE_REGISTER(modulino_buttons, CONFIG_INPUT_LOG_LEVEL);
16 
17 #define MODULINO_NUM_BUTTONS 3
18 
19 struct modulino_buttons_config {
20 	struct i2c_dt_spec bus;
21 	uint32_t poll_period_ms;
22 	uint32_t zephyr_code[MODULINO_NUM_BUTTONS];
23 };
24 
25 struct modulino_buttons_data {
26 	const struct device *dev;
27 	struct k_work_delayable poll_work;
28 	uint8_t prev_state[MODULINO_NUM_BUTTONS];
29 };
30 
modulino_buttons_handler(struct k_work * work)31 static void modulino_buttons_handler(struct k_work *work)
32 {
33 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
34 	struct modulino_buttons_data *data = CONTAINER_OF(
35 			dwork, struct modulino_buttons_data, poll_work);
36 	const struct device *dev = data->dev;
37 	const struct modulino_buttons_config *cfg = dev->config;
38 	int ret;
39 	uint8_t buf[MODULINO_NUM_BUTTONS + 1];
40 
41 	ret = i2c_read_dt(&cfg->bus, buf, sizeof(buf));
42 	if (ret < 0) {
43 		LOG_ERR("i2c read error: %d", ret);
44 		goto out;
45 	}
46 
47 	for (uint8_t i = 0; i < MODULINO_NUM_BUTTONS; i++) {
48 		uint8_t state = buf[i + 1];
49 
50 		if (data->prev_state[i] != state) {
51 			input_report_key(dev, cfg->zephyr_code[i], state, true, K_FOREVER);
52 		}
53 	}
54 
55 	memcpy(data->prev_state, &buf[1], sizeof(data->prev_state));
56 
57 out:
58 	k_work_reschedule(dwork, K_MSEC(cfg->poll_period_ms));
59 }
60 
modulino_buttons_init(const struct device * dev)61 static int modulino_buttons_init(const struct device *dev)
62 {
63 	const struct modulino_buttons_config *cfg = dev->config;
64 	struct modulino_buttons_data *data = dev->data;
65 
66 	data->dev = dev;
67 
68 	if (!i2c_is_ready_dt(&cfg->bus)) {
69 		LOG_ERR("Bus device is not ready");
70 		return -ENODEV;
71 	}
72 
73 	k_work_init_delayable(&data->poll_work, modulino_buttons_handler);
74 	k_work_reschedule(&data->poll_work, K_MSEC(cfg->poll_period_ms));
75 
76 	return 0;
77 }
78 
79 #define MODULINO_BUTTONS_INIT(inst)							\
80 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, zephyr_codes) == MODULINO_NUM_BUTTONS,	\
81 		     "zephyr,codes must specify three key codes");			\
82 											\
83 	static const struct modulino_buttons_config modulino_buttons_cfg_##inst = {	\
84 		.bus = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)),				\
85 		.poll_period_ms = DT_INST_PROP(inst, poll_period_ms),			\
86 		.zephyr_code = DT_INST_PROP(inst, zephyr_codes),			\
87 	};										\
88 											\
89 	static struct modulino_buttons_data modulino_buttons_data_##inst;		\
90 											\
91 	DEVICE_DT_INST_DEFINE(inst, modulino_buttons_init, NULL,			\
92 			      &modulino_buttons_data_##inst,				\
93 			      &modulino_buttons_cfg_##inst,				\
94 			      POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
95 
96 
97 DT_INST_FOREACH_STATUS_OKAY(MODULINO_BUTTONS_INIT)
98