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