Skip to content

Commit 49eff65

Browse files
miggazElquezkartben
authored andcommitted
drivers: input: Add driver for the Nintendo Nunchuk through I2C.
Add a driver for the Nintendo Nunchuk, accessed through the I2C bus. This driver only supports the joystick and the buttons, not the accelerometer. Signed-off-by: Miguel Gazquez <[email protected]>
1 parent d0785cc commit 49eff65

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed

drivers/input/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ zephyr_library_sources_ifdef(CONFIG_INPUT_ITE_IT8801_KBD input_ite_it8801_kbd.c)
2222
zephyr_library_sources_ifdef(CONFIG_INPUT_ITE_IT8XXX2_KBD input_ite_it8xxx2_kbd.c)
2323
zephyr_library_sources_ifdef(CONFIG_INPUT_KBD_MATRIX input_kbd_matrix.c)
2424
zephyr_library_sources_ifdef(CONFIG_INPUT_NPCX_KBD input_npcx_kbd.c)
25+
zephyr_library_sources_ifdef(CONFIG_INPUT_NUNCHUK input_nunchuk.c)
2526
zephyr_library_sources_ifdef(CONFIG_INPUT_PAT912X input_pat912x.c)
2627
zephyr_library_sources_ifdef(CONFIG_INPUT_PAW32XX input_paw32xx.c)
2728
zephyr_library_sources_ifdef(CONFIG_INPUT_PINNACLE input_pinnacle.c)

drivers/input/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ source "drivers/input/Kconfig.it8801"
2424
source "drivers/input/Kconfig.it8xxx2"
2525
source "drivers/input/Kconfig.kbd_matrix"
2626
source "drivers/input/Kconfig.npcx"
27+
source "drivers/input/Kconfig.nunchuk"
2728
source "drivers/input/Kconfig.pat912x"
2829
source "drivers/input/Kconfig.paw32xx"
2930
source "drivers/input/Kconfig.pinnacle"

drivers/input/Kconfig.nunchuk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2024 Bootlin
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config INPUT_NUNCHUK
5+
bool "Nintendo Nunchuk joystick"
6+
default y
7+
depends on DT_HAS_NINTENDO_NUNCHUK_ENABLED
8+
select I2C
9+
help
10+
This option enable the driver for the Nintendo Nunchuk joystick.

drivers/input/input_nunchuk.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright (c) 2024 Bootlin
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nintendo_nunchuk
8+
9+
#include <zephyr/device.h>
10+
#include <zephyr/drivers/i2c.h>
11+
#include <zephyr/logging/log.h>
12+
#include <zephyr/input/input.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/timing/timing.h>
15+
16+
LOG_MODULE_REGISTER(input_nunchuk, CONFIG_INPUT_LOG_LEVEL);
17+
18+
#define NUNCHUK_DELAY_MS 10
19+
#define NUNCHUK_READ_SIZE 6
20+
21+
struct nunchuk_config {
22+
struct i2c_dt_spec i2c_bus;
23+
int polling_interval_ms;
24+
};
25+
26+
struct nunchuk_data {
27+
const struct device *dev;
28+
uint8_t joystick_x;
29+
uint8_t joystick_y;
30+
bool button_c;
31+
bool button_z;
32+
struct k_work_delayable work;
33+
k_timeout_t interval_ms;
34+
};
35+
36+
static int nunchuk_read_registers(const struct device *dev, uint8_t *buffer)
37+
{
38+
const struct nunchuk_config *cfg = dev->config;
39+
int ret;
40+
uint8_t value = 0;
41+
42+
ret = i2c_write_dt(&cfg->i2c_bus, &value, sizeof(value));
43+
if (ret < 0) {
44+
return ret;
45+
}
46+
47+
k_msleep(NUNCHUK_DELAY_MS);
48+
ret = i2c_read_dt(&cfg->i2c_bus, buffer, NUNCHUK_READ_SIZE);
49+
if (ret < 0) {
50+
return ret;
51+
}
52+
53+
return 0;
54+
}
55+
56+
static void nunchuk_poll(struct k_work *work)
57+
{
58+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
59+
struct nunchuk_data *data = CONTAINER_OF(dwork, struct nunchuk_data, work);
60+
const struct device *dev = data->dev;
61+
uint8_t buffer[NUNCHUK_READ_SIZE];
62+
uint8_t joystick_x, joystick_y;
63+
bool button_c, button_z;
64+
bool y_changed;
65+
bool sync_flag;
66+
int ret;
67+
68+
nunchuk_read_registers(dev, buffer);
69+
70+
joystick_x = buffer[0];
71+
joystick_y = buffer[1];
72+
y_changed = (joystick_y != data->joystick_y);
73+
74+
if (joystick_x != data->joystick_x) {
75+
data->joystick_x = joystick_x;
76+
sync_flag = !y_changed;
77+
ret = input_report_abs(dev, INPUT_ABS_X, data->joystick_x, sync_flag, K_FOREVER);
78+
}
79+
80+
if (y_changed) {
81+
data->joystick_y = joystick_y;
82+
ret = input_report_abs(dev, INPUT_ABS_Y, data->joystick_y, true, K_FOREVER);
83+
}
84+
85+
button_z = buffer[5] & BIT(0);
86+
if (button_z != data->button_z) {
87+
data->button_z = button_z;
88+
ret = input_report_key(dev, INPUT_KEY_Z, !data->button_z, true, K_FOREVER);
89+
}
90+
91+
button_c = buffer[5] & BIT(1);
92+
if (button_c != data->button_c) {
93+
data->button_c = button_c;
94+
ret = input_report_key(dev, INPUT_KEY_C, !data->button_c, true, K_FOREVER);
95+
}
96+
97+
k_work_reschedule(dwork, data->interval_ms);
98+
}
99+
100+
static int nunchuk_init(const struct device *dev)
101+
{
102+
const struct nunchuk_config *cfg = dev->config;
103+
struct nunchuk_data *data = dev->data;
104+
int ret;
105+
106+
uint8_t init_seq_1[2] = {0xf0, 0x55};
107+
uint8_t init_seq_2[2] = {0xfb, 0x00};
108+
uint8_t buffer[NUNCHUK_READ_SIZE];
109+
110+
data->dev = dev;
111+
data->interval_ms = K_MSEC(cfg->polling_interval_ms - 11);
112+
113+
if (!i2c_is_ready_dt(&cfg->i2c_bus)) {
114+
LOG_ERR("Bus device is not ready");
115+
return -ENODEV;
116+
}
117+
118+
/* Send the unencrypted init sequence */
119+
ret = i2c_write_dt(&cfg->i2c_bus, init_seq_1, sizeof(init_seq_1));
120+
if (ret < 0) {
121+
LOG_ERR("I2C write failed (%d).", ret);
122+
return ret;
123+
}
124+
125+
k_msleep(1);
126+
ret = i2c_write_dt(&cfg->i2c_bus, init_seq_2, sizeof(init_seq_2));
127+
if (ret < 0) {
128+
return ret;
129+
}
130+
131+
k_msleep(1);
132+
ret = nunchuk_read_registers(dev, buffer);
133+
if (ret < 0) {
134+
return ret;
135+
}
136+
137+
/* Sometimes, the first read gives unexpected results, so we make another one. */
138+
k_msleep(1);
139+
ret = nunchuk_read_registers(dev, buffer);
140+
if (ret < 0) {
141+
return ret;
142+
}
143+
144+
data->joystick_x = buffer[0];
145+
data->joystick_y = buffer[1];
146+
data->button_z = buffer[5] & BIT(0);
147+
data->button_c = buffer[5] & BIT(1);
148+
149+
k_work_init_delayable(&data->work, nunchuk_poll);
150+
ret = k_work_reschedule(&data->work, data->interval_ms);
151+
152+
return ret;
153+
}
154+
155+
#define NUNCHUK_INIT(inst) \
156+
static const struct nunchuk_config nunchuk_config_##inst = { \
157+
.i2c_bus = I2C_DT_SPEC_INST_GET(inst), \
158+
.polling_interval_ms = DT_INST_PROP(inst, polling_interval_ms), \
159+
}; \
160+
BUILD_ASSERT(DT_INST_PROP(inst, polling_interval_ms) > 20); \
161+
\
162+
static struct nunchuk_data nunchuk_data_##inst; \
163+
\
164+
DEVICE_DT_INST_DEFINE(inst, &nunchuk_init, NULL, &nunchuk_data_##inst, \
165+
&nunchuk_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
166+
NULL);
167+
168+
DT_INST_FOREACH_STATUS_OKAY(NUNCHUK_INIT)

tests/drivers/build_all/input/app.overlay

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@
293293
};
294294
};
295295
};
296+
297+
nunchuk@a {
298+
reg = <0xa>;
299+
compatible= "nintendo,nunchuk";
300+
};
296301
};
297302

298303
spi@2 {

0 commit comments

Comments
 (0)