1 /*
2  * Copyright (c) 2022, Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_gpio_emul_sdl
8 
9 #include <zephyr/drivers/gpio/gpio_emul.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log.h>
12 
13 #include "gpio_emul_sdl_bottom.h"
14 
15 LOG_MODULE_REGISTER(gpio_emul_sdl, CONFIG_GPIO_LOG_LEVEL);
16 
17 struct gpio_sdl_config {
18 	const struct device *emul;
19 
20 	const int *codes;
21 	uint8_t num_codes;
22 	struct gpio_sdl_data *data;
23 };
24 
sdl_filter_top(struct gpio_sdl_data * bottom_data)25 static int sdl_filter_top(struct gpio_sdl_data *bottom_data)
26 {
27 	const struct device *port = bottom_data->dev;
28 	const struct gpio_sdl_config *config = port->config;
29 	int ret;
30 
31 	gpio_pin_t pin = 0;
32 
33 	/* Search for the corresponding scancode */
34 	while (pin < config->num_codes) {
35 		if (config->codes[pin] == bottom_data->event_scan_code) {
36 			break;
37 		}
38 		pin++;
39 	}
40 
41 	if (pin == config->num_codes) {
42 		/* Not tracked */
43 		return 1;
44 	}
45 
46 	/* Lock the scheduler so we can't be preempted,
47 	 * as the gpio_emul driver keeps a mutex locked
48 	 * for as long as there are pending interrupts
49 	 */
50 	k_sched_lock();
51 
52 	/* Update the pin state */
53 	ret = gpio_emul_input_set(config->emul, pin, bottom_data->key_down);
54 
55 	k_sched_unlock();
56 	if (ret < 0) {
57 		LOG_WRN("Failed to emulate input (%d)", ret);
58 	}
59 
60 	return 0;
61 }
62 
gpio_sdl_init(const struct device * dev)63 static int gpio_sdl_init(const struct device *dev)
64 {
65 	const struct gpio_sdl_config *config = dev->config;
66 
67 	for (uint8_t pin = 0; pin < config->num_codes; ++pin) {
68 		if (config->codes[pin] != GPIOEMULSDL_SCANCODE_UNKNOWN) {
69 			LOG_INF("GPIO %s:%u = %u", dev->name, pin, config->codes[pin]);
70 		}
71 	}
72 
73 	config->data->dev = (void *)dev;
74 	config->data->callback = sdl_filter_top;
75 	gpio_sdl_init_bottom(config->data);
76 	return 0;
77 }
78 
79 #define GPIO_SDL_DEFINE(inst)								\
80 	BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(DT_INST_PARENT(inst),			\
81 					       zephyr_gpio_emul, okay),			\
82 		     "Enabled parent zephyr,gpio-emul node is required");		\
83 											\
84 	static const int gpio_sdl_##inst##_codes[]					\
85 		= DT_INST_PROP(inst, scancodes);					\
86 											\
87 	static struct gpio_sdl_data data_##inst;				\
88 											\
89 	static const struct gpio_sdl_config gpio_sdl_##inst##_config = {		\
90 		.emul = DEVICE_DT_GET(DT_INST_PARENT(inst)),				\
91 		.codes = gpio_sdl_##inst##_codes,					\
92 		.num_codes = DT_INST_PROP_LEN(inst, scancodes),				\
93 		.data = &data_##inst,					\
94 	};										\
95 												\
96 	DEVICE_DT_INST_DEFINE(inst, gpio_sdl_init, NULL, NULL,				\
97 			      &gpio_sdl_##inst##_config, POST_KERNEL,			\
98 			      CONFIG_GPIO_INIT_PRIORITY, NULL);
99 
100 DT_INST_FOREACH_STATUS_OKAY(GPIO_SDL_DEFINE)
101