refactor(kscan): Match direct GPIO driver to matrix driver
Rewrote the direct GPIO kscan driver to match the improvements made to the matrix driver in82cb762698
andf946dc6893
. It now uses the same debouncing system as the matrix driver.
This commit is contained in:
parent
54747a52f9
commit
0738b4b2b0
3 changed files with 331 additions and 229 deletions
|
@ -4,83 +4,268 @@
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
|
#include "debounce.h"
|
||||||
|
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <drivers/kscan.h>
|
#include <devicetree.h>
|
||||||
#include <drivers/gpio.h>
|
#include <drivers/gpio.h>
|
||||||
|
#include <drivers/kscan.h>
|
||||||
|
#include <kernel.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
struct kscan_gpio_item_config {
|
#define DT_DRV_COMPAT zmk_kscan_gpio_direct
|
||||||
char *label;
|
|
||||||
gpio_pin_t pin;
|
|
||||||
gpio_flags_t flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
union work_reference {
|
#if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0
|
||||||
struct k_work_delayable delayed;
|
#define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS
|
||||||
struct k_work direct;
|
#else
|
||||||
};
|
#define INST_DEBOUNCE_PRESS_MS(n) \
|
||||||
|
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_press_ms))
|
||||||
|
#endif
|
||||||
|
|
||||||
struct kscan_gpio_config {
|
#if CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS >= 0
|
||||||
uint8_t num_of_inputs;
|
#define INST_DEBOUNCE_RELEASE_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS
|
||||||
uint8_t debounce_period;
|
#else
|
||||||
struct kscan_gpio_item_config inputs[];
|
#define INST_DEBOUNCE_RELEASE_MS(n) \
|
||||||
};
|
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms))
|
||||||
|
#endif
|
||||||
|
|
||||||
struct kscan_gpio_data {
|
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
||||||
#if defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
#define USE_INTERRUPTS (!USE_POLLING)
|
||||||
struct k_timer poll_timer;
|
|
||||||
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, (), code)
|
||||||
kscan_callback_t callback;
|
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
||||||
union work_reference work;
|
COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, pollcode, intcode)
|
||||||
|
|
||||||
|
#define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
||||||
|
#define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
|
||||||
|
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx),
|
||||||
|
|
||||||
|
struct kscan_direct_irq_callback {
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
uint32_t pin_state;
|
|
||||||
const struct device *inputs[];
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct device **kscan_gpio_input_devices(const struct device *dev) {
|
|
||||||
struct kscan_gpio_data *data = dev->data;
|
|
||||||
return data->inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs(const struct device *dev) {
|
|
||||||
const struct kscan_gpio_config *cfg = dev->config;
|
|
||||||
return cfg->inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kscan_gpio_direct_queue_read(union work_reference *work, uint8_t debounce_period) {
|
|
||||||
if (debounce_period > 0) {
|
|
||||||
k_work_reschedule(&work->delayed, K_MSEC(debounce_period));
|
|
||||||
} else {
|
|
||||||
k_work_submit(&work->direct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
|
||||||
|
|
||||||
struct kscan_gpio_irq_callback {
|
|
||||||
const struct device *dev;
|
|
||||||
union work_reference *work;
|
|
||||||
uint8_t debounce_period;
|
|
||||||
struct gpio_callback callback;
|
struct gpio_callback callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t flags) {
|
struct kscan_direct_data {
|
||||||
const struct kscan_gpio_config *cfg = dev->config;
|
const struct device *dev;
|
||||||
const struct device **devices = kscan_gpio_input_devices(dev);
|
kscan_callback_t callback;
|
||||||
const struct kscan_gpio_item_config *configs = kscan_gpio_input_configs(dev);
|
struct k_work_delayable work;
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
/** Array of length config->inputs.len */
|
||||||
|
struct kscan_direct_irq_callback *irqs;
|
||||||
|
#endif
|
||||||
|
/** Timestamp of the current or scheduled scan. */
|
||||||
|
int64_t scan_time;
|
||||||
|
/** Current state of the inputs as an array of length config->inputs.len */
|
||||||
|
struct debounce_state *pin_state;
|
||||||
|
};
|
||||||
|
|
||||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
struct kscan_gpio_list {
|
||||||
const struct device *dev = devices[i];
|
const struct gpio_dt_spec *gpios;
|
||||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
/** Define a kscan_gpio_list from a compile-time GPIO array. */
|
||||||
|
#define KSCAN_GPIO_LIST(gpio_array) \
|
||||||
|
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
|
||||||
|
|
||||||
|
struct kscan_direct_config {
|
||||||
|
struct kscan_gpio_list inputs;
|
||||||
|
struct debounce_config debounce_config;
|
||||||
|
int32_t debounce_scan_period_ms;
|
||||||
|
int32_t poll_period_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
static int kscan_direct_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
|
||||||
|
const struct kscan_direct_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||||
|
|
||||||
|
int err = gpio_pin_interrupt_configure_dt(gpio, flags);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to configure interrupt for pin %u on %s", gpio->pin, gpio->port->name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
static int kscan_direct_interrupt_enable(const struct device *dev) {
|
||||||
|
return kscan_direct_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
static int kscan_direct_interrupt_disable(const struct device *dev) {
|
||||||
|
return kscan_direct_interrupt_configure(dev, GPIO_INT_DISABLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
static void kscan_direct_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
|
||||||
|
const gpio_port_pins_t pin) {
|
||||||
|
struct kscan_direct_irq_callback *irq_data =
|
||||||
|
CONTAINER_OF(cb, struct kscan_direct_irq_callback, callback);
|
||||||
|
struct kscan_direct_data *data = irq_data->dev->data;
|
||||||
|
|
||||||
|
// Disable our interrupts temporarily to avoid re-entry while we scan.
|
||||||
|
kscan_direct_interrupt_disable(data->dev);
|
||||||
|
|
||||||
|
data->scan_time = k_uptime_get();
|
||||||
|
|
||||||
|
k_work_reschedule(&data->work, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void kscan_direct_read_continue(const struct device *dev) {
|
||||||
|
const struct kscan_direct_config *config = dev->config;
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
|
||||||
|
data->scan_time += config->debounce_scan_period_ms;
|
||||||
|
|
||||||
|
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kscan_direct_read_end(const struct device *dev) {
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
// Return to waiting for an interrupt.
|
||||||
|
kscan_direct_interrupt_enable(dev);
|
||||||
|
#else
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
const struct kscan_direct_config *config = dev->config;
|
||||||
|
|
||||||
|
data->scan_time += config->poll_period_ms;
|
||||||
|
|
||||||
|
// Return to polling slowly.
|
||||||
|
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_read(const struct device *dev) {
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
const struct kscan_direct_config *config = dev->config;
|
||||||
|
|
||||||
|
// Read the inputs.
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||||
|
|
||||||
|
const bool active = gpio_pin_get_dt(gpio);
|
||||||
|
|
||||||
|
debounce_update(&data->pin_state[i], active, config->debounce_scan_period_ms,
|
||||||
|
&config->debounce_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the new state.
|
||||||
|
bool continue_scan = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
struct debounce_state *state = &data->pin_state[i];
|
||||||
|
|
||||||
|
if (debounce_get_changed(state)) {
|
||||||
|
const bool pressed = debounce_is_pressed(state);
|
||||||
|
|
||||||
|
LOG_DBG("Sending event at 0,%i state %s", i, pressed ? "on" : "off");
|
||||||
|
data->callback(dev, 0, i, pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue_scan = continue_scan || debounce_is_active(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (continue_scan) {
|
||||||
|
// At least one key is pressed or the debouncer has not yet decided if
|
||||||
|
// it is pressed. Poll quickly until everything is released.
|
||||||
|
kscan_direct_read_continue(dev);
|
||||||
|
} else {
|
||||||
|
// All keys are released. Return to normal.
|
||||||
|
kscan_direct_read_end(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kscan_direct_work_handler(struct k_work *work) {
|
||||||
|
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
|
||||||
|
struct kscan_direct_data *data = CONTAINER_OF(dwork, struct kscan_direct_data, work);
|
||||||
|
kscan_direct_read(data->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_configure(const struct device *dev, kscan_callback_t callback) {
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->callback = callback;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_enable(const struct device *dev) {
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
|
||||||
|
data->scan_time = k_uptime_get();
|
||||||
|
|
||||||
|
// Read will automatically start interrupts/polling once done.
|
||||||
|
return kscan_direct_read(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_disable(const struct device *dev) {
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
|
||||||
|
k_work_cancel_delayable(&data->work);
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
return kscan_direct_interrupt_disable(dev);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_init_input_inst(const struct device *dev, const struct gpio_dt_spec *gpio,
|
||||||
|
const int index) {
|
||||||
|
if (!device_is_ready(gpio->port)) {
|
||||||
|
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = gpio_pin_configure_dt(gpio, GPIO_INPUT);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Configured pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
struct kscan_direct_data *data = dev->data;
|
||||||
|
struct kscan_direct_irq_callback *irq = &data->irqs[index];
|
||||||
|
|
||||||
|
irq->dev = dev;
|
||||||
|
gpio_init_callback(&irq->callback, kscan_direct_irq_callback_handler, BIT(gpio->pin));
|
||||||
|
err = gpio_add_callback(gpio->port, &irq->callback);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Error adding the callback to the input device: %i", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_direct_init_inputs(const struct device *dev) {
|
||||||
|
const struct kscan_direct_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||||
|
int err = kscan_direct_init_input_inst(dev, gpio, i);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Unable to enable direct GPIO interrupt");
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,155 +273,54 @@ static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t f
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kscan_gpio_direct_enable(const struct device *dev) {
|
static int kscan_direct_init(const struct device *dev) {
|
||||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_LEVEL_ACTIVE);
|
struct kscan_direct_data *data = dev->data;
|
||||||
}
|
|
||||||
static int kscan_gpio_direct_disable(const struct device *dev) {
|
|
||||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_DISABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kscan_gpio_irq_callback_handler(const struct device *dev, struct gpio_callback *cb,
|
data->dev = dev;
|
||||||
gpio_port_pins_t pin) {
|
|
||||||
struct kscan_gpio_irq_callback *data =
|
|
||||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback, callback);
|
|
||||||
|
|
||||||
kscan_gpio_direct_disable(data->dev);
|
kscan_direct_init_inputs(dev);
|
||||||
kscan_gpio_direct_queue_read(data->work, data->debounce_period);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
k_work_init_delayable(&data->work, kscan_direct_work_handler);
|
||||||
|
|
||||||
static void kscan_gpio_timer_handler(struct k_timer *timer) {
|
|
||||||
struct kscan_gpio_data *data = CONTAINER_OF(timer, struct kscan_gpio_data, poll_timer);
|
|
||||||
|
|
||||||
kscan_gpio_direct_queue_read(&data->work, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kscan_gpio_direct_enable(const struct device *dev) {
|
|
||||||
struct kscan_gpio_data *data = dev->data;
|
|
||||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int kscan_gpio_direct_disable(const struct device *dev) {
|
|
||||||
struct kscan_gpio_data *data = dev->data;
|
|
||||||
k_timer_stop(&data->poll_timer);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING) */
|
|
||||||
|
|
||||||
static int kscan_gpio_direct_configure(const struct device *dev, kscan_callback_t callback) {
|
|
||||||
struct kscan_gpio_data *data = dev->data;
|
|
||||||
if (!callback) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
data->callback = callback;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kscan_gpio_read(const struct device *dev) {
|
|
||||||
struct kscan_gpio_data *data = dev->data;
|
|
||||||
const struct kscan_gpio_config *cfg = dev->config;
|
|
||||||
uint32_t read_state = data->pin_state;
|
|
||||||
bool submit_follow_up_read = false;
|
|
||||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
|
||||||
const struct device *in_dev = kscan_gpio_input_devices(dev)[i];
|
|
||||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(dev)[i];
|
|
||||||
WRITE_BIT(read_state, i, gpio_pin_get(in_dev, in_cfg->pin) > 0);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
|
||||||
bool prev_pressed = BIT(i) & data->pin_state;
|
|
||||||
bool pressed = (BIT(i) & read_state) != 0;
|
|
||||||
submit_follow_up_read = (submit_follow_up_read || pressed);
|
|
||||||
if (pressed != prev_pressed) {
|
|
||||||
LOG_DBG("Sending event at %d,%d state %s", 0, i, (pressed ? "on" : "off"));
|
|
||||||
WRITE_BIT(data->pin_state, i, pressed);
|
|
||||||
data->callback(dev, 0, i, pressed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(CONFIG_ZMK_KSCAN_DIRECT_POLLING)
|
|
||||||
if (submit_follow_up_read) {
|
|
||||||
kscan_gpio_direct_queue_read(&data->work, cfg->debounce_period);
|
|
||||||
} else {
|
|
||||||
kscan_gpio_direct_enable(dev);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kscan_gpio_work_handler(struct k_work *work) {
|
static const struct kscan_driver_api kscan_direct_api = {
|
||||||
struct kscan_gpio_data *data = CONTAINER_OF(work, struct kscan_gpio_data, work);
|
.config = kscan_direct_configure,
|
||||||
kscan_gpio_read(data->dev);
|
.enable_callback = kscan_direct_enable,
|
||||||
}
|
.disable_callback = kscan_direct_disable,
|
||||||
|
|
||||||
static const struct kscan_driver_api gpio_driver_api = {
|
|
||||||
.config = kscan_gpio_direct_configure,
|
|
||||||
.enable_callback = kscan_gpio_direct_enable,
|
|
||||||
.disable_callback = kscan_gpio_direct_disable,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KSCAN_DIRECT_INPUT_ITEM(i, n) \
|
#define KSCAN_DIRECT_INIT(n) \
|
||||||
|
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||||
|
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
|
||||||
|
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||||
|
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
||||||
|
\
|
||||||
|
static const struct gpio_dt_spec kscan_direct_inputs_##n[] = { \
|
||||||
|
UTIL_LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, n)}; \
|
||||||
|
\
|
||||||
|
static struct debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \
|
||||||
|
\
|
||||||
|
COND_INTERRUPTS( \
|
||||||
|
(static struct kscan_direct_irq_callback kscan_direct_irqs_##n[INST_INPUTS_LEN(n)];)) \
|
||||||
|
\
|
||||||
|
static struct kscan_direct_data kscan_direct_data_##n = { \
|
||||||
|
.pin_state = kscan_direct_state_##n, COND_INTERRUPTS((.irqs = kscan_direct_irqs_##n, ))}; \
|
||||||
|
\
|
||||||
|
static struct kscan_direct_config kscan_direct_config_##n = { \
|
||||||
|
.inputs = KSCAN_GPIO_LIST(kscan_direct_inputs_##n), \
|
||||||
|
.debounce_config = \
|
||||||
{ \
|
{ \
|
||||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, input_gpios, i), \
|
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
|
||||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, input_gpios, i), \
|
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
|
||||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, input_gpios, i), \
|
}, \
|
||||||
},
|
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
|
||||||
|
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, NULL, &kscan_direct_data_##n, \
|
||||||
|
&kscan_direct_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||||
|
&kscan_direct_api);
|
||||||
|
|
||||||
#define INST_INPUT_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
DT_INST_FOREACH_STATUS_OKAY(KSCAN_DIRECT_INIT);
|
||||||
|
|
||||||
#define GPIO_INST_INIT(n) \
|
|
||||||
COND_CODE_0(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
|
||||||
(static struct kscan_gpio_irq_callback irq_callbacks_##n[INST_INPUT_LEN(n)];), ()) \
|
|
||||||
static struct kscan_gpio_data kscan_gpio_data_##n = { \
|
|
||||||
.inputs = {[INST_INPUT_LEN(n) - 1] = NULL}}; \
|
|
||||||
static int kscan_gpio_init_##n(const struct device *dev) { \
|
|
||||||
struct kscan_gpio_data *data = dev->data; \
|
|
||||||
const struct kscan_gpio_config *cfg = dev->config; \
|
|
||||||
int err; \
|
|
||||||
const struct device **input_devices = kscan_gpio_input_devices(dev); \
|
|
||||||
for (int i = 0; i < cfg->num_of_inputs; i++) { \
|
|
||||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs(dev)[i]; \
|
|
||||||
input_devices[i] = device_get_binding(in_cfg->label); \
|
|
||||||
if (!input_devices[i]) { \
|
|
||||||
LOG_ERR("Unable to find input GPIO device"); \
|
|
||||||
return -EINVAL; \
|
|
||||||
} \
|
|
||||||
err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \
|
|
||||||
if (err) { \
|
|
||||||
LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \
|
|
||||||
return err; \
|
|
||||||
} \
|
|
||||||
COND_CODE_0( \
|
|
||||||
IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
|
||||||
(irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
|
|
||||||
irq_callbacks_##n[i].debounce_period = cfg->debounce_period; \
|
|
||||||
gpio_init_callback(&irq_callbacks_##n[i].callback, \
|
|
||||||
kscan_gpio_irq_callback_handler, BIT(in_cfg->pin)); \
|
|
||||||
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
|
|
||||||
if (err) { \
|
|
||||||
LOG_ERR("Error adding the callback to the column device"); \
|
|
||||||
return err; \
|
|
||||||
}), \
|
|
||||||
()) \
|
|
||||||
} \
|
|
||||||
data->dev = dev; \
|
|
||||||
COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KSCAN_DIRECT_POLLING), \
|
|
||||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
|
|
||||||
if (cfg->debounce_period > 0) { \
|
|
||||||
k_work_init_delayable(&data->work.delayed, kscan_gpio_work_handler); \
|
|
||||||
} else { \
|
|
||||||
k_work_init(&data->work.direct, kscan_gpio_work_handler); \
|
|
||||||
} \
|
|
||||||
return 0; \
|
|
||||||
} \
|
|
||||||
static const struct kscan_gpio_config kscan_gpio_config_##n = { \
|
|
||||||
.inputs = {UTIL_LISTIFY(INST_INPUT_LEN(n), KSCAN_DIRECT_INPUT_ITEM, n)}, \
|
|
||||||
.num_of_inputs = INST_INPUT_LEN(n), \
|
|
||||||
.debounce_period = DT_INST_PROP(n, debounce_period)}; \
|
|
||||||
DEVICE_DT_INST_DEFINE(n, kscan_gpio_init_##n, NULL, &kscan_gpio_data_##n, \
|
|
||||||
&kscan_gpio_config_##n, POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
|
|
||||||
&gpio_driver_api);
|
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
|
||||||
|
|
|
@ -418,46 +418,46 @@ static const struct kscan_driver_api kscan_matrix_api = {
|
||||||
.disable_callback = kscan_matrix_disable,
|
.disable_callback = kscan_matrix_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KSCAN_MATRIX_INIT(index) \
|
#define KSCAN_MATRIX_INIT(n) \
|
||||||
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(index) <= DEBOUNCE_COUNTER_MAX, \
|
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||||
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
|
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
|
||||||
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(index) <= DEBOUNCE_COUNTER_MAX, \
|
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
|
||||||
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
||||||
\
|
\
|
||||||
static const struct gpio_dt_spec kscan_matrix_rows_##index[] = { \
|
static const struct gpio_dt_spec kscan_matrix_rows_##n[] = { \
|
||||||
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
|
UTIL_LISTIFY(INST_ROWS_LEN(n), KSCAN_GPIO_ROW_CFG_INIT, n)}; \
|
||||||
\
|
\
|
||||||
static const struct gpio_dt_spec kscan_matrix_cols_##index[] = { \
|
static const struct gpio_dt_spec kscan_matrix_cols_##n[] = { \
|
||||||
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
|
UTIL_LISTIFY(INST_COLS_LEN(n), KSCAN_GPIO_COL_CFG_INIT, n)}; \
|
||||||
\
|
\
|
||||||
static struct debounce_state kscan_matrix_state_##index[INST_MATRIX_LEN(index)]; \
|
static struct debounce_state kscan_matrix_state_##n[INST_MATRIX_LEN(n)]; \
|
||||||
\
|
\
|
||||||
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
|
COND_INTERRUPTS( \
|
||||||
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
|
(static struct kscan_matrix_irq_callback kscan_matrix_irqs_##n[INST_INPUTS_LEN(n)];)) \
|
||||||
\
|
\
|
||||||
static struct kscan_matrix_data kscan_matrix_data_##index = { \
|
static struct kscan_matrix_data kscan_matrix_data_##n = { \
|
||||||
.matrix_state = kscan_matrix_state_##index, \
|
.matrix_state = kscan_matrix_state_##n, \
|
||||||
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \
|
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##n, ))}; \
|
||||||
\
|
\
|
||||||
static struct kscan_matrix_config kscan_matrix_config_##index = { \
|
static struct kscan_matrix_config kscan_matrix_config_##n = { \
|
||||||
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##index), \
|
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##n), \
|
||||||
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##index), \
|
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##n), \
|
||||||
.inputs = KSCAN_GPIO_LIST( \
|
.inputs = \
|
||||||
COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \
|
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_cols_##n), (kscan_matrix_rows_##n))), \
|
||||||
.outputs = KSCAN_GPIO_LIST( \
|
.outputs = \
|
||||||
COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \
|
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_rows_##n), (kscan_matrix_cols_##n))), \
|
||||||
.debounce_config = \
|
.debounce_config = \
|
||||||
{ \
|
{ \
|
||||||
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(index), \
|
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
|
||||||
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(index), \
|
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(n), \
|
||||||
}, \
|
}, \
|
||||||
.debounce_scan_period_ms = DT_INST_PROP(index, debounce_scan_period_ms), \
|
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
|
||||||
.poll_period_ms = DT_INST_PROP(index, poll_period_ms), \
|
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
|
||||||
.diode_direction = INST_DIODE_DIR(index), \
|
.diode_direction = INST_DIODE_DIR(n), \
|
||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
DEVICE_DT_INST_DEFINE(index, &kscan_matrix_init, NULL, &kscan_matrix_data_##index, \
|
DEVICE_DT_INST_DEFINE(n, &kscan_matrix_init, NULL, &kscan_matrix_data_##n, \
|
||||||
&kscan_matrix_config_##index, APPLICATION, \
|
&kscan_matrix_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||||
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_matrix_api);
|
&kscan_matrix_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MATRIX_INIT);
|
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MATRIX_INIT);
|
||||||
|
|
|
@ -12,5 +12,23 @@ properties:
|
||||||
type: phandle-array
|
type: phandle-array
|
||||||
required: true
|
required: true
|
||||||
debounce-period:
|
debounce-period:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
deprecated: true
|
||||||
|
description: Deprecated. Use debounce-press-ms and debounce-release-ms instead.
|
||||||
|
debounce-press-ms:
|
||||||
type: int
|
type: int
|
||||||
default: 5
|
default: 5
|
||||||
|
description: Debounce time for key press in milliseconds. Use 0 for eager debouncing.
|
||||||
|
debounce-release-ms:
|
||||||
|
type: int
|
||||||
|
default: 5
|
||||||
|
description: Debounce time for key release in milliseconds.
|
||||||
|
debounce-scan-period-ms:
|
||||||
|
type: int
|
||||||
|
default: 1
|
||||||
|
description: Time between reads in milliseconds when any key is pressed.
|
||||||
|
poll-period-ms:
|
||||||
|
type: int
|
||||||
|
default: 10
|
||||||
|
description: Time between reads in milliseconds when no key is pressed and ZMK_KSCAN_DIRECT_POLLING is enabled.
|
||||||
|
|
Loading…
Reference in a new issue