forked from kofal.net/zmk
Merge remote-tracking branch 'upstream/main' into bluetooth/battery-reporting
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Peter Johanson
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -18,136 +18,131 @@
|
||||
|
||||
LOG_MODULE_REGISTER(EC11, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int ec11_get_ab_state(struct device *dev)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
static int ec11_get_ab_state(struct device *dev) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
|
||||
return (gpio_pin_get(drv_data->a, drv_cfg->a_pin) << 1) | gpio_pin_get(drv_data->b, drv_cfg->b_pin);
|
||||
return (gpio_pin_get(drv_data->a, drv_cfg->a_pin) << 1) |
|
||||
gpio_pin_get(drv_data->b, drv_cfg->b_pin);
|
||||
}
|
||||
|
||||
static int ec11_sample_fetch(struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
u8_t val;
|
||||
s8_t delta;
|
||||
static int ec11_sample_fetch(struct device *dev, enum sensor_channel chan) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
u8_t val;
|
||||
s8_t delta;
|
||||
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_ROTATION);
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_ROTATION);
|
||||
|
||||
val = ec11_get_ab_state(dev);
|
||||
val = ec11_get_ab_state(dev);
|
||||
|
||||
LOG_DBG("prev: %d, new: %d", drv_data->ab_state, val);
|
||||
LOG_DBG("prev: %d, new: %d", drv_data->ab_state, val);
|
||||
|
||||
switch(val | (drv_data->ab_state << 2)) {
|
||||
case 0b0010: case 0b0100: case 0b1101: case 0b1011:
|
||||
delta = -1;
|
||||
break;
|
||||
case 0b0001: case 0b0111: case 0b1110: case 0b1000:
|
||||
delta = 1;
|
||||
break;
|
||||
default:
|
||||
delta = 0;
|
||||
break;
|
||||
}
|
||||
switch (val | (drv_data->ab_state << 2)) {
|
||||
case 0b0010:
|
||||
case 0b0100:
|
||||
case 0b1101:
|
||||
case 0b1011:
|
||||
delta = -1;
|
||||
break;
|
||||
case 0b0001:
|
||||
case 0b0111:
|
||||
case 0b1110:
|
||||
case 0b1000:
|
||||
delta = 1;
|
||||
break;
|
||||
default:
|
||||
delta = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DBG("Delta: %d", delta);
|
||||
LOG_DBG("Delta: %d", delta);
|
||||
|
||||
drv_data->pulses += delta;
|
||||
drv_data->ab_state = val;
|
||||
drv_data->pulses += delta;
|
||||
drv_data->ab_state = val;
|
||||
|
||||
drv_data->ticks = drv_data->pulses / drv_cfg->resolution;
|
||||
drv_data->delta = delta;
|
||||
drv_data->pulses %= drv_cfg->resolution;
|
||||
|
||||
return 0;
|
||||
drv_data->ticks = drv_data->pulses / drv_cfg->resolution;
|
||||
drv_data->delta = delta;
|
||||
drv_data->pulses %= drv_cfg->resolution;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ec11_channel_get(struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
|
||||
if (chan != SENSOR_CHAN_ROTATION) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
static int ec11_channel_get(struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
|
||||
val->val1 = drv_data->ticks;
|
||||
val->val2 = drv_data->delta;
|
||||
|
||||
return 0;
|
||||
if (chan != SENSOR_CHAN_ROTATION) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
val->val1 = drv_data->ticks;
|
||||
val->val2 = drv_data->delta;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api ec11_driver_api = {
|
||||
#ifdef CONFIG_EC11_TRIGGER
|
||||
.trigger_set = ec11_trigger_set,
|
||||
.trigger_set = ec11_trigger_set,
|
||||
#endif
|
||||
.sample_fetch = ec11_sample_fetch,
|
||||
.channel_get = ec11_channel_get,
|
||||
.sample_fetch = ec11_sample_fetch,
|
||||
.channel_get = ec11_channel_get,
|
||||
};
|
||||
|
||||
int ec11_init(struct device *dev)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
int ec11_init(struct device *dev) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
|
||||
LOG_DBG("A: %s %d B: %s %d resolution %d", drv_cfg->a_label, drv_cfg->a_pin, drv_cfg->b_label, drv_cfg->b_pin, drv_cfg->resolution);
|
||||
LOG_DBG("A: %s %d B: %s %d resolution %d", drv_cfg->a_label, drv_cfg->a_pin, drv_cfg->b_label,
|
||||
drv_cfg->b_pin, drv_cfg->resolution);
|
||||
|
||||
drv_data->a = device_get_binding(drv_cfg->a_label);
|
||||
if (drv_data->a == NULL) {
|
||||
LOG_ERR("Failed to get pointer to A GPIO device");
|
||||
return -EINVAL;
|
||||
}
|
||||
drv_data->a = device_get_binding(drv_cfg->a_label);
|
||||
if (drv_data->a == NULL) {
|
||||
LOG_ERR("Failed to get pointer to A GPIO device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drv_data->b = device_get_binding(drv_cfg->b_label);
|
||||
if (drv_data->b == NULL) {
|
||||
LOG_ERR("Failed to get pointer to B GPIO device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(drv_data->a, drv_cfg->a_pin,
|
||||
drv_cfg->a_flags
|
||||
| GPIO_INPUT)) {
|
||||
LOG_DBG("Failed to configure A pin");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(drv_data->b, drv_cfg->b_pin,
|
||||
drv_cfg->b_flags
|
||||
| GPIO_INPUT)) {
|
||||
LOG_DBG("Failed to configure B pin");
|
||||
return -EIO;
|
||||
}
|
||||
drv_data->b = device_get_binding(drv_cfg->b_label);
|
||||
if (drv_data->b == NULL) {
|
||||
LOG_ERR("Failed to get pointer to B GPIO device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(drv_data->a, drv_cfg->a_pin, drv_cfg->a_flags | GPIO_INPUT)) {
|
||||
LOG_DBG("Failed to configure A pin");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(drv_data->b, drv_cfg->b_pin, drv_cfg->b_flags | GPIO_INPUT)) {
|
||||
LOG_DBG("Failed to configure B pin");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EC11_TRIGGER
|
||||
if (ec11_init_interrupt(dev) < 0) {
|
||||
LOG_DBG("Failed to initialize interrupt!");
|
||||
return -EIO;
|
||||
}
|
||||
if (ec11_init_interrupt(dev) < 0) {
|
||||
LOG_DBG("Failed to initialize interrupt!");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
drv_data->ab_state = ec11_get_ab_state(dev);
|
||||
drv_data->ab_state = ec11_get_ab_state(dev);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EC11_INST(n) \
|
||||
struct ec11_data ec11_data_##n; \
|
||||
const struct ec11_config ec11_cfg_##n = { \
|
||||
.a_label = DT_INST_GPIO_LABEL(n, a_gpios), \
|
||||
.a_pin = DT_INST_GPIO_PIN(n, a_gpios), \
|
||||
.a_flags = DT_INST_GPIO_FLAGS(n, a_gpios), \
|
||||
.b_label = DT_INST_GPIO_LABEL(n, b_gpios), \
|
||||
.b_pin = DT_INST_GPIO_PIN(n, b_gpios), \
|
||||
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios), \
|
||||
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(ec11, DT_INST_LABEL(n), ec11_init, \
|
||||
&ec11_data_##n, \
|
||||
&ec11_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&ec11_driver_api);
|
||||
#define EC11_INST(n) \
|
||||
struct ec11_data ec11_data_##n; \
|
||||
const struct ec11_config ec11_cfg_##n = { \
|
||||
.a_label = DT_INST_GPIO_LABEL(n, a_gpios), \
|
||||
.a_pin = DT_INST_GPIO_PIN(n, a_gpios), \
|
||||
.a_flags = DT_INST_GPIO_FLAGS(n, a_gpios), \
|
||||
.b_label = DT_INST_GPIO_LABEL(n, b_gpios), \
|
||||
.b_pin = DT_INST_GPIO_PIN(n, b_gpios), \
|
||||
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios), \
|
||||
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(ec11, DT_INST_LABEL(n), ec11_init, &ec11_data_##n, &ec11_cfg_##n, \
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &ec11_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(EC11_INST)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Peter Johanson
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -11,39 +11,39 @@
|
||||
#include <sys/util.h>
|
||||
|
||||
struct ec11_config {
|
||||
const char *a_label;
|
||||
const u8_t a_pin;
|
||||
const u8_t a_flags;
|
||||
const char *a_label;
|
||||
const u8_t a_pin;
|
||||
const u8_t a_flags;
|
||||
|
||||
const char *b_label;
|
||||
const u8_t b_pin;
|
||||
const u8_t b_flags;
|
||||
const char *b_label;
|
||||
const u8_t b_pin;
|
||||
const u8_t b_flags;
|
||||
|
||||
const u8_t resolution;
|
||||
const u8_t resolution;
|
||||
};
|
||||
|
||||
struct ec11_data {
|
||||
struct device *a;
|
||||
struct device *b;
|
||||
u8_t ab_state;
|
||||
s8_t pulses;
|
||||
s8_t ticks;
|
||||
s8_t delta;
|
||||
struct device *a;
|
||||
struct device *b;
|
||||
u8_t ab_state;
|
||||
s8_t pulses;
|
||||
s8_t ticks;
|
||||
s8_t delta;
|
||||
|
||||
#ifdef CONFIG_EC11_TRIGGER
|
||||
struct gpio_callback a_gpio_cb;
|
||||
struct gpio_callback b_gpio_cb;
|
||||
struct device *dev;
|
||||
struct gpio_callback a_gpio_cb;
|
||||
struct gpio_callback b_gpio_cb;
|
||||
struct device *dev;
|
||||
|
||||
sensor_trigger_handler_t handler;
|
||||
const struct sensor_trigger *trigger;
|
||||
sensor_trigger_handler_t handler;
|
||||
const struct sensor_trigger *trigger;
|
||||
|
||||
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
|
||||
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_EC11_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_EC11_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
struct k_work work;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_EC11_TRIGGER */
|
||||
@@ -51,9 +51,8 @@ struct ec11_data {
|
||||
|
||||
#ifdef CONFIG_EC11_TRIGGER
|
||||
|
||||
int ec11_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
int ec11_trigger_set(struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
int ec11_init_interrupt(struct device *dev);
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -19,158 +19,130 @@ extern struct ec11_data ec11_driver;
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(EC11, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static inline void setup_int(struct device *dev,
|
||||
bool enable)
|
||||
{
|
||||
struct ec11_data *data = dev->driver_data;
|
||||
const struct ec11_config *cfg = dev->config_info;
|
||||
static inline void setup_int(struct device *dev, bool enable) {
|
||||
struct ec11_data *data = dev->driver_data;
|
||||
const struct ec11_config *cfg = dev->config_info;
|
||||
|
||||
LOG_DBG("enabled %s", (enable ? "true" : "false"));
|
||||
LOG_DBG("enabled %s", (enable ? "true" : "false"));
|
||||
|
||||
if (gpio_pin_interrupt_configure(data->a,
|
||||
cfg->a_pin,
|
||||
enable
|
||||
? GPIO_INT_EDGE_BOTH
|
||||
: GPIO_INT_DISABLE)) {
|
||||
LOG_WRN("Unable to set A pin GPIO interrupt");
|
||||
}
|
||||
if (gpio_pin_interrupt_configure(data->a, cfg->a_pin,
|
||||
enable ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE)) {
|
||||
LOG_WRN("Unable to set A pin GPIO interrupt");
|
||||
}
|
||||
|
||||
if (gpio_pin_interrupt_configure(data->b,
|
||||
cfg->b_pin,
|
||||
enable
|
||||
? GPIO_INT_EDGE_BOTH
|
||||
: GPIO_INT_DISABLE)) {
|
||||
LOG_WRN("Unable to set A pin GPIO interrupt");
|
||||
}
|
||||
if (gpio_pin_interrupt_configure(data->b, cfg->b_pin,
|
||||
enable ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE)) {
|
||||
LOG_WRN("Unable to set A pin GPIO interrupt");
|
||||
}
|
||||
}
|
||||
|
||||
static void ec11_a_gpio_callback(struct device *dev,
|
||||
struct gpio_callback *cb, u32_t pins)
|
||||
{
|
||||
struct ec11_data *drv_data =
|
||||
CONTAINER_OF(cb, struct ec11_data, a_gpio_cb);
|
||||
static void ec11_a_gpio_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) {
|
||||
struct ec11_data *drv_data = CONTAINER_OF(cb, struct ec11_data, a_gpio_cb);
|
||||
|
||||
LOG_DBG("");
|
||||
LOG_DBG("");
|
||||
|
||||
setup_int(drv_data->dev, false);
|
||||
setup_int(drv_data->dev, false);
|
||||
|
||||
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&drv_data->gpio_sem);
|
||||
k_sem_give(&drv_data->gpio_sem);
|
||||
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&drv_data->work);
|
||||
k_work_submit(&drv_data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ec11_b_gpio_callback(struct device *dev,
|
||||
struct gpio_callback *cb, u32_t pins)
|
||||
{
|
||||
struct ec11_data *drv_data =
|
||||
CONTAINER_OF(cb, struct ec11_data, b_gpio_cb);
|
||||
static void ec11_b_gpio_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) {
|
||||
struct ec11_data *drv_data = CONTAINER_OF(cb, struct ec11_data, b_gpio_cb);
|
||||
|
||||
LOG_DBG("");
|
||||
LOG_DBG("");
|
||||
|
||||
setup_int(drv_data->dev, false);
|
||||
setup_int(drv_data->dev, false);
|
||||
|
||||
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&drv_data->gpio_sem);
|
||||
k_sem_give(&drv_data->gpio_sem);
|
||||
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&drv_data->work);
|
||||
k_work_submit(&drv_data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ec11_thread_cb(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
static void ec11_thread_cb(void *arg) {
|
||||
struct device *dev = arg;
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
|
||||
drv_data->handler(dev, drv_data->trigger);
|
||||
drv_data->handler(dev, drv_data->trigger);
|
||||
|
||||
setup_int(dev, true);
|
||||
setup_int(dev, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EC11_TRIGGER_OWN_THREAD
|
||||
static void ec11_thread(int dev_ptr, int unused)
|
||||
{
|
||||
struct device *dev = INT_TO_POINTER(dev_ptr);
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
static void ec11_thread(int dev_ptr, int unused) {
|
||||
struct device *dev = INT_TO_POINTER(dev_ptr);
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
|
||||
ARG_UNUSED(unused);
|
||||
ARG_UNUSED(unused);
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
|
||||
ec11_thread_cb(dev);
|
||||
}
|
||||
while (1) {
|
||||
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
|
||||
ec11_thread_cb(dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EC11_TRIGGER_GLOBAL_THREAD
|
||||
static void ec11_work_cb(struct k_work *work)
|
||||
{
|
||||
struct ec11_data *drv_data =
|
||||
CONTAINER_OF(work, struct ec11_data, work);
|
||||
static void ec11_work_cb(struct k_work *work) {
|
||||
struct ec11_data *drv_data = CONTAINER_OF(work, struct ec11_data, work);
|
||||
|
||||
LOG_DBG("");
|
||||
LOG_DBG("");
|
||||
|
||||
ec11_thread_cb(drv_data->dev);
|
||||
ec11_thread_cb(drv_data->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
int ec11_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
int ec11_trigger_set(struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
|
||||
setup_int(dev, false);
|
||||
setup_int(dev, false);
|
||||
|
||||
k_msleep(5);
|
||||
k_msleep(5);
|
||||
|
||||
drv_data->trigger = trig;
|
||||
drv_data->handler = handler;
|
||||
drv_data->trigger = trig;
|
||||
drv_data->handler = handler;
|
||||
|
||||
setup_int(dev, true);
|
||||
setup_int(dev, true);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ec11_init_interrupt(struct device *dev)
|
||||
{
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
int ec11_init_interrupt(struct device *dev) {
|
||||
struct ec11_data *drv_data = dev->driver_data;
|
||||
const struct ec11_config *drv_cfg = dev->config_info;
|
||||
|
||||
drv_data->dev = dev;
|
||||
/* setup gpio interrupt */
|
||||
drv_data->dev = dev;
|
||||
/* setup gpio interrupt */
|
||||
|
||||
gpio_init_callback(&drv_data->a_gpio_cb, ec11_a_gpio_callback, BIT(drv_cfg->a_pin));
|
||||
|
||||
gpio_init_callback(&drv_data->a_gpio_cb,
|
||||
ec11_a_gpio_callback,
|
||||
BIT(drv_cfg->a_pin));
|
||||
if (gpio_add_callback(drv_data->a, &drv_data->a_gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set A callback!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (gpio_add_callback(drv_data->a, &drv_data->a_gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set A callback!");
|
||||
return -EIO;
|
||||
}
|
||||
gpio_init_callback(&drv_data->b_gpio_cb, ec11_b_gpio_callback, BIT(drv_cfg->b_pin));
|
||||
|
||||
gpio_init_callback(&drv_data->b_gpio_cb,
|
||||
ec11_b_gpio_callback,
|
||||
BIT(drv_cfg->b_pin));
|
||||
|
||||
if (gpio_add_callback(drv_data->b, &drv_data->b_gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set B callback!");
|
||||
return -EIO;
|
||||
}
|
||||
if (gpio_add_callback(drv_data->b, &drv_data->b_gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set B callback!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_EC11_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
|
||||
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
|
||||
|
||||
k_thread_create(&drv_data->thread, drv_data->thread_stack,
|
||||
CONFIG_EC11_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)ec11_thread, dev,
|
||||
0, NULL, K_PRIO_COOP(CONFIG_EC11_THREAD_PRIORITY),
|
||||
0, K_NO_WAIT);
|
||||
k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_EC11_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)ec11_thread, dev, 0, NULL,
|
||||
K_PRIO_COOP(CONFIG_EC11_THREAD_PRIORITY), 0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_EC11_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_init(&drv_data->work, ec11_work_cb);
|
||||
k_work_init(&drv_data->work, ec11_work_cb);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -15,248 +15,215 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct kscan_gpio_item_config
|
||||
{
|
||||
char *label;
|
||||
gpio_pin_t pin;
|
||||
gpio_flags_t flags;
|
||||
struct kscan_gpio_item_config {
|
||||
char *label;
|
||||
gpio_pin_t pin;
|
||||
gpio_flags_t flags;
|
||||
};
|
||||
|
||||
union work_reference {
|
||||
struct k_delayed_work delayed;
|
||||
struct k_work direct;
|
||||
struct k_delayed_work delayed;
|
||||
struct k_work direct;
|
||||
};
|
||||
|
||||
struct kscan_gpio_config
|
||||
{
|
||||
u8_t num_of_inputs;
|
||||
u8_t debounce_period;
|
||||
struct kscan_gpio_item_config inputs[];
|
||||
struct kscan_gpio_config {
|
||||
u8_t num_of_inputs;
|
||||
u8_t debounce_period;
|
||||
struct kscan_gpio_item_config inputs[];
|
||||
};
|
||||
|
||||
struct kscan_gpio_data
|
||||
{
|
||||
struct kscan_gpio_data {
|
||||
#if defined(CONFIG_ZMK_KSCAN_GPIO_POLLING)
|
||||
struct k_timer poll_timer;
|
||||
struct k_timer poll_timer;
|
||||
#endif /* defined(CONFIG_ZMK_KSCAN_GPIO_POLLING) */
|
||||
kscan_callback_t callback;
|
||||
union work_reference work;
|
||||
struct device *dev;
|
||||
u32_t pin_state;
|
||||
struct device *inputs[];
|
||||
kscan_callback_t callback;
|
||||
union work_reference work;
|
||||
struct device *dev;
|
||||
u32_t pin_state;
|
||||
struct device *inputs[];
|
||||
};
|
||||
|
||||
static struct device **kscan_gpio_input_devices(struct device *dev)
|
||||
{
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
return data->inputs;
|
||||
static struct device **kscan_gpio_input_devices(struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
return data->inputs;
|
||||
}
|
||||
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs(struct device *dev)
|
||||
{
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
return cfg->inputs;
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs(struct device *dev) {
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
return cfg->inputs;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_ZMK_KSCAN_GPIO_POLLING)
|
||||
|
||||
struct kscan_gpio_irq_callback
|
||||
{
|
||||
union work_reference *work;
|
||||
u8_t debounce_period;
|
||||
struct gpio_callback callback;
|
||||
struct kscan_gpio_irq_callback {
|
||||
union work_reference *work;
|
||||
u8_t debounce_period;
|
||||
struct gpio_callback callback;
|
||||
};
|
||||
|
||||
static int kscan_gpio_config_interrupts(struct device *dev, gpio_flags_t flags)
|
||||
{
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
struct device **devices = kscan_gpio_input_devices(dev);
|
||||
const struct kscan_gpio_item_config *configs = kscan_gpio_input_configs(dev);
|
||||
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++)
|
||||
{
|
||||
struct device *dev = devices[i];
|
||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
||||
static int kscan_gpio_config_interrupts(struct device *dev, gpio_flags_t flags) {
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
struct device **devices = kscan_gpio_input_devices(dev);
|
||||
const struct kscan_gpio_item_config *configs = kscan_gpio_input_configs(dev);
|
||||
|
||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
||||
struct device *dev = devices[i];
|
||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
||||
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Unable to enable matrix GPIO interrupt");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
||||
|
||||
return 0;
|
||||
if (err) {
|
||||
LOG_ERR("Unable to enable matrix GPIO interrupt");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_gpio_direct_enable(struct device *dev)
|
||||
{
|
||||
return kscan_gpio_config_interrupts(dev,
|
||||
GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH);
|
||||
static int kscan_gpio_direct_enable(struct device *dev) {
|
||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH);
|
||||
}
|
||||
static int kscan_gpio_direct_disable(struct device *dev)
|
||||
{
|
||||
return kscan_gpio_config_interrupts(dev,
|
||||
GPIO_INT_DISABLE);
|
||||
static int kscan_gpio_direct_disable(struct device *dev) {
|
||||
return kscan_gpio_config_interrupts(dev, GPIO_INT_DISABLE);
|
||||
}
|
||||
|
||||
static void kscan_gpio_irq_callback_handler(struct device *dev,
|
||||
struct gpio_callback *cb, gpio_port_pins_t pin)
|
||||
{
|
||||
struct kscan_gpio_irq_callback *data =
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback, callback);
|
||||
|
||||
if (data->debounce_period > 0) {
|
||||
k_delayed_work_cancel(&data->work->delayed);
|
||||
k_delayed_work_submit(&data->work->delayed, K_MSEC(data->debounce_period));
|
||||
} else {
|
||||
k_work_submit(&data->work->direct);
|
||||
}
|
||||
static void kscan_gpio_irq_callback_handler(struct device *dev, struct gpio_callback *cb,
|
||||
gpio_port_pins_t pin) {
|
||||
struct kscan_gpio_irq_callback *data =
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback, callback);
|
||||
|
||||
if (data->debounce_period > 0) {
|
||||
k_delayed_work_cancel(&data->work->delayed);
|
||||
k_delayed_work_submit(&data->work->delayed, K_MSEC(data->debounce_period));
|
||||
} else {
|
||||
k_work_submit(&data->work->direct);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !defined(CONFIG_ZMK_KSCAN_GPIO_POLLING) */
|
||||
#else /* !defined(CONFIG_ZMK_KSCAN_GPIO_POLLING) */
|
||||
|
||||
static void kscan_gpio_timer_handler(struct k_timer *timer)
|
||||
{
|
||||
struct kscan_gpio_data *data =
|
||||
CONTAINER_OF(timer, struct kscan_gpio_data, poll_timer);
|
||||
static void kscan_gpio_timer_handler(struct k_timer *timer) {
|
||||
struct kscan_gpio_data *data = CONTAINER_OF(timer, struct kscan_gpio_data, poll_timer);
|
||||
|
||||
k_work_submit(&data->work.direct);
|
||||
k_work_submit(&data->work.direct);
|
||||
}
|
||||
|
||||
static int kscan_gpio_direct_enable(struct device *dev)
|
||||
{
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10));
|
||||
return 0;
|
||||
static int kscan_gpio_direct_enable(struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10));
|
||||
return 0;
|
||||
}
|
||||
static int kscan_gpio_direct_disable(struct device *dev)
|
||||
{
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
k_timer_stop(&data->poll_timer);
|
||||
return 0;
|
||||
static int kscan_gpio_direct_disable(struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
k_timer_stop(&data->poll_timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_ZMK_KSCAN_GPIO_POLLING) */
|
||||
|
||||
static int kscan_gpio_direct_configure(struct device *dev, kscan_callback_t callback)
|
||||
{
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
if (!callback)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
data->callback = callback;
|
||||
return 0;
|
||||
static int kscan_gpio_direct_configure(struct device *dev, kscan_callback_t callback) {
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
data->callback = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_gpio_read(struct device *dev)
|
||||
{
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
u32_t read_state = data->pin_state;
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
static int kscan_gpio_read(struct device *dev) {
|
||||
struct kscan_gpio_data *data = dev->driver_data;
|
||||
const struct kscan_gpio_config *cfg = dev->config_info;
|
||||
u32_t read_state = data->pin_state;
|
||||
for (int i = 0; i < cfg->num_of_inputs; i++) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kscan_gpio_work_handler(struct k_work *work)
|
||||
{
|
||||
struct kscan_gpio_data *data =
|
||||
CONTAINER_OF(work, struct kscan_gpio_data, work);
|
||||
kscan_gpio_read(data->dev);
|
||||
static void kscan_gpio_work_handler(struct k_work *work) {
|
||||
struct kscan_gpio_data *data = CONTAINER_OF(work, struct kscan_gpio_data, work);
|
||||
kscan_gpio_read(data->dev);
|
||||
}
|
||||
|
||||
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,
|
||||
.config = kscan_gpio_direct_configure,
|
||||
.enable_callback = kscan_gpio_direct_enable,
|
||||
.disable_callback = kscan_gpio_direct_disable,
|
||||
};
|
||||
|
||||
#define KSCAN_DIRECT_INPUT_ITEM(i,n) \
|
||||
{ \
|
||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, input_gpios, i), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, input_gpios, i), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, input_gpios, i), \
|
||||
},
|
||||
#define KSCAN_DIRECT_INPUT_ITEM(i, n) \
|
||||
{ \
|
||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, input_gpios, i), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, input_gpios, i), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, input_gpios, i), \
|
||||
},
|
||||
|
||||
#define INST_INPUT_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
||||
|
||||
#define GPIO_INST_INIT(n) \
|
||||
COND_CODE_0(CONFIG_ZMK_KSCAN_GPIO_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(struct device *dev) \
|
||||
{ \
|
||||
struct kscan_gpio_data *data = dev->driver_data; \
|
||||
const struct kscan_gpio_config *cfg = dev->config_info; \
|
||||
int err; \
|
||||
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(CONFIG_ZMK_KSCAN_GPIO_POLLING, \
|
||||
( \
|
||||
irq_callbacks_##n[i].work = &data->work; \
|
||||
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(CONFIG_ZMK_KSCAN_GPIO_POLLING, (k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ( )) \
|
||||
if (cfg->debounce_period > 0) { \
|
||||
k_delayed_work_init(&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_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, \
|
||||
POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
|
||||
&gpio_driver_api);
|
||||
#define GPIO_INST_INIT(n) \
|
||||
COND_CODE_0(CONFIG_ZMK_KSCAN_GPIO_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(struct device *dev) { \
|
||||
struct kscan_gpio_data *data = dev->driver_data; \
|
||||
const struct kscan_gpio_config *cfg = dev->config_info; \
|
||||
int err; \
|
||||
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( \
|
||||
CONFIG_ZMK_KSCAN_GPIO_POLLING, \
|
||||
(irq_callbacks_##n[i].work = &data->work; \
|
||||
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(CONFIG_ZMK_KSCAN_GPIO_POLLING, \
|
||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
|
||||
if (cfg->debounce_period > 0) { \
|
||||
k_delayed_work_init(&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_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
||||
&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)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -15,266 +15,248 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct kscan_gpio_item_config
|
||||
{
|
||||
char *label;
|
||||
gpio_pin_t pin;
|
||||
gpio_flags_t flags;
|
||||
struct kscan_gpio_item_config {
|
||||
char *label;
|
||||
gpio_pin_t pin;
|
||||
gpio_flags_t flags;
|
||||
};
|
||||
|
||||
|
||||
#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
|
||||
{ \
|
||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
|
||||
},
|
||||
#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
|
||||
{ \
|
||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
|
||||
},
|
||||
|
||||
#define _KSCAN_GPIO_ROW_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, idx)
|
||||
#define _KSCAN_GPIO_COL_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, idx)
|
||||
|
||||
|
||||
static int kscan_gpio_config_interrupts(struct device **devices,
|
||||
const struct kscan_gpio_item_config *configs,
|
||||
size_t len, gpio_flags_t flags)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
struct device *dev = devices[i];
|
||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
||||
const struct kscan_gpio_item_config *configs, size_t len,
|
||||
gpio_flags_t flags) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
struct device *dev = devices[i];
|
||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
||||
|
||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
||||
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Unable to enable matrix GPIO interrupt");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
LOG_ERR("Unable to enable matrix GPIO interrupt");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
|
||||
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
|
||||
#define INST_OUTPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), (INST_MATRIX_COLS(n)))
|
||||
#define INST_INPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), (INST_MATRIX_ROWS(n)))
|
||||
#define INST_OUTPUT_LEN(n) \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), \
|
||||
(INST_MATRIX_COLS(n)))
|
||||
#define INST_INPUT_LEN(n) \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), \
|
||||
(INST_MATRIX_ROWS(n)))
|
||||
|
||||
#define GPIO_INST_INIT(n) \
|
||||
struct kscan_gpio_irq_callback_##n \
|
||||
{ \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \
|
||||
struct gpio_callback callback; \
|
||||
}; \
|
||||
static struct kscan_gpio_irq_callback_##n \
|
||||
irq_callbacks_##n[INST_INPUT_LEN(n)]; \
|
||||
struct kscan_gpio_config_##n \
|
||||
{ \
|
||||
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
|
||||
}; \
|
||||
struct kscan_gpio_data_##n \
|
||||
{ \
|
||||
kscan_callback_t callback; \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
|
||||
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
struct device *rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct device *cols[INST_MATRIX_COLS(n)]; \
|
||||
struct device *dev; \
|
||||
}; \
|
||||
static struct device **kscan_gpio_input_devices_##n(struct device *dev) \
|
||||
{ \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), (data->rows))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) \
|
||||
{ \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
|
||||
return ((COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \
|
||||
} \
|
||||
static struct device **kscan_gpio_output_devices_##n(struct device *dev) \
|
||||
{ \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), (data->cols))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n(struct device *dev) \
|
||||
{ \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
|
||||
} \
|
||||
static int kscan_gpio_enable_interrupts_##n(struct device *dev) \
|
||||
{ \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
|
||||
GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH); \
|
||||
} \
|
||||
static int kscan_gpio_disable_interrupts_##n(struct device *dev) \
|
||||
{ \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
|
||||
GPIO_INT_DISABLE); \
|
||||
} \
|
||||
static void kscan_gpio_set_output_state_##n(struct device *dev, int value) \
|
||||
{ \
|
||||
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) \
|
||||
{ \
|
||||
struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \
|
||||
gpio_pin_set(in_dev, cfg->pin, value); \
|
||||
} \
|
||||
} \
|
||||
static void kscan_gpio_set_matrix_state_##n(bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], u32_t input_index, u32_t output_index, bool value) \
|
||||
{ \
|
||||
state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), (input_index))][COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), (output_index))] = value; \
|
||||
} \
|
||||
static int kscan_gpio_read_##n(struct device *dev) \
|
||||
{ \
|
||||
bool submit_follow_up_read = false; \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
/* Disable our interrupts temporarily while we scan, to avoid */ \
|
||||
/* re-entry while we iterate columns and set them active one by one */ \
|
||||
/* to get pressed state for each matrix cell. */ \
|
||||
kscan_gpio_disable_interrupts_##n(dev); \
|
||||
kscan_gpio_set_output_state_##n(dev, 0); \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \
|
||||
{ \
|
||||
struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) \
|
||||
{ \
|
||||
struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \
|
||||
kscan_gpio_set_matrix_state_##n(read_state, i, o, gpio_pin_get(in_dev, in_cfg->pin) > 0); \
|
||||
} \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
||||
} \
|
||||
/* Set all our outputs as active again, then re-enable interrupts, */ \
|
||||
/* so we can trigger interrupts again for future press/release */ \
|
||||
kscan_gpio_set_output_state_##n(dev, 1); \
|
||||
kscan_gpio_enable_interrupts_##n(dev); \
|
||||
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) \
|
||||
{ \
|
||||
for (int c = 0; c < INST_MATRIX_COLS(n); c++) \
|
||||
{ \
|
||||
bool pressed = read_state[r][c]; \
|
||||
/* Follow up reads needed because further interrupts won't fire on already tripped input GPIO pins */ \
|
||||
submit_follow_up_read = (submit_follow_up_read || pressed); \
|
||||
if (pressed != data->matrix_state[r][c]) \
|
||||
{ \
|
||||
LOG_DBG("Sending event at %d,%d state %s", \
|
||||
r, c, (pressed ? "on" : "off")); \
|
||||
data->matrix_state[r][c] = pressed; \
|
||||
data->callback(dev, r, c, pressed); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (submit_follow_up_read) { \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), \
|
||||
({ k_work_submit(&data->work); }), \
|
||||
({ \
|
||||
k_delayed_work_cancel(&data->work); \
|
||||
k_delayed_work_submit(&data->work, K_MSEC(5)); })) \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
static void kscan_gpio_work_handler_##n(struct k_work *work) \
|
||||
{ \
|
||||
struct kscan_gpio_data_##n *data = \
|
||||
CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
|
||||
kscan_gpio_read_##n(data->dev); \
|
||||
} \
|
||||
static void kscan_gpio_irq_callback_handler_##n(struct device *dev, \
|
||||
struct gpio_callback *cb, gpio_port_pins_t pin) \
|
||||
{ \
|
||||
struct kscan_gpio_irq_callback_##n *data = \
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), \
|
||||
({ k_work_submit(data->work); }), \
|
||||
({ \
|
||||
k_delayed_work_cancel(data->work); \
|
||||
k_delayed_work_submit(data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); })) \
|
||||
} \
|
||||
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
|
||||
.rows = { [INST_MATRIX_ROWS(n)-1] = NULL}, \
|
||||
.cols = { [INST_MATRIX_COLS(n)-1] = NULL }\
|
||||
}; \
|
||||
static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) \
|
||||
{ \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
if (!callback) \
|
||||
{ \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
data->callback = callback; \
|
||||
return 0; \
|
||||
}; \
|
||||
static int kscan_gpio_enable_##n(struct device *dev) \
|
||||
{ \
|
||||
int err = kscan_gpio_enable_interrupts_##n(dev); \
|
||||
if (err) { return err; } \
|
||||
return kscan_gpio_read_##n(dev); \
|
||||
}; \
|
||||
static int kscan_gpio_init_##n(struct device *dev) \
|
||||
{ \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
int err; \
|
||||
struct device **input_devices = kscan_gpio_input_devices_##n(dev); \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) \
|
||||
{ \
|
||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(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; \
|
||||
} \
|
||||
irq_callbacks_##n[i].work = &data->work; \
|
||||
gpio_init_callback(&irq_callbacks_##n[i].callback, kscan_gpio_irq_callback_handler_##n, 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; \
|
||||
} \
|
||||
} \
|
||||
struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \
|
||||
{ \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
output_devices[o] = device_get_binding(out_cfg->label); \
|
||||
if (!output_devices[o]) \
|
||||
{ \
|
||||
LOG_ERR("Unable to find output GPIO device"); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
err = gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT_ACTIVE | out_cfg->flags); \
|
||||
if (err) \
|
||||
{ \
|
||||
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, out_cfg->label); \
|
||||
return err; \
|
||||
} \
|
||||
} \
|
||||
data->dev = dev; \
|
||||
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))(&data->work, kscan_gpio_work_handler_##n); \
|
||||
return 0; \
|
||||
} \
|
||||
static const struct kscan_driver_api gpio_driver_api_##n = { \
|
||||
.config = kscan_gpio_configure_##n, \
|
||||
.enable_callback = kscan_gpio_enable_##n, \
|
||||
.disable_callback = kscan_gpio_disable_interrupts_##n, \
|
||||
}; \
|
||||
static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \
|
||||
.rows = { UTIL_LISTIFY(INST_MATRIX_ROWS(n), _KSCAN_GPIO_ROW_CFG_INIT, n) }, \
|
||||
.cols = { UTIL_LISTIFY(INST_MATRIX_COLS(n), _KSCAN_GPIO_COL_CFG_INIT, n) }, \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, \
|
||||
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||
&gpio_driver_api_##n);
|
||||
#define GPIO_INST_INIT(n) \
|
||||
struct kscan_gpio_irq_callback_##n { \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \
|
||||
struct gpio_callback callback; \
|
||||
}; \
|
||||
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)]; \
|
||||
struct kscan_gpio_config_##n { \
|
||||
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
|
||||
}; \
|
||||
struct kscan_gpio_data_##n { \
|
||||
kscan_callback_t callback; \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
|
||||
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
struct device *rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct device *cols[INST_MATRIX_COLS(n)]; \
|
||||
struct device *dev; \
|
||||
}; \
|
||||
static struct device **kscan_gpio_input_devices_##n(struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), \
|
||||
(data->rows))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) { \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
|
||||
return (( \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \
|
||||
} \
|
||||
static struct device **kscan_gpio_output_devices_##n(struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), \
|
||||
(data->cols))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n( \
|
||||
struct device *dev) { \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
|
||||
return ( \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
|
||||
} \
|
||||
static int kscan_gpio_enable_interrupts_##n(struct device *dev) { \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
||||
kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
|
||||
GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH); \
|
||||
} \
|
||||
static int kscan_gpio_disable_interrupts_##n(struct device *dev) { \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
||||
kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \
|
||||
GPIO_INT_DISABLE); \
|
||||
} \
|
||||
static void kscan_gpio_set_output_state_##n(struct device *dev, int value) { \
|
||||
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) { \
|
||||
struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \
|
||||
gpio_pin_set(in_dev, cfg->pin, value); \
|
||||
} \
|
||||
} \
|
||||
static void kscan_gpio_set_matrix_state_##n( \
|
||||
bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], u32_t input_index, \
|
||||
u32_t output_index, bool value) { \
|
||||
state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), \
|
||||
(input_index))] \
|
||||
[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), \
|
||||
(output_index))] = value; \
|
||||
} \
|
||||
static int kscan_gpio_read_##n(struct device *dev) { \
|
||||
bool submit_follow_up_read = false; \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
/* Disable our interrupts temporarily while we scan, to avoid */ \
|
||||
/* re-entry while we iterate columns and set them active one by one */ \
|
||||
/* to get pressed state for each matrix cell. */ \
|
||||
kscan_gpio_disable_interrupts_##n(dev); \
|
||||
kscan_gpio_set_output_state_##n(dev, 0); \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
||||
struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
||||
struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *in_cfg = \
|
||||
&kscan_gpio_input_configs_##n(dev)[i]; \
|
||||
kscan_gpio_set_matrix_state_##n(read_state, i, o, \
|
||||
gpio_pin_get(in_dev, in_cfg->pin) > 0); \
|
||||
} \
|
||||
gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
||||
} \
|
||||
/* Set all our outputs as active again, then re-enable interrupts, */ \
|
||||
/* so we can trigger interrupts again for future press/release */ \
|
||||
kscan_gpio_set_output_state_##n(dev, 1); \
|
||||
kscan_gpio_enable_interrupts_##n(dev); \
|
||||
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \
|
||||
for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \
|
||||
bool pressed = read_state[r][c]; \
|
||||
/* Follow up reads needed because further interrupts won't fire on already tripped \
|
||||
* input GPIO pins */ \
|
||||
submit_follow_up_read = (submit_follow_up_read || pressed); \
|
||||
if (pressed != data->matrix_state[r][c]) { \
|
||||
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
|
||||
data->matrix_state[r][c] = pressed; \
|
||||
data->callback(dev, r, c, pressed); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (submit_follow_up_read) { \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), ({ \
|
||||
k_delayed_work_cancel(&data->work); \
|
||||
k_delayed_work_submit(&data->work, K_MSEC(5)); \
|
||||
})) \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
static void kscan_gpio_work_handler_##n(struct k_work *work) { \
|
||||
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
|
||||
kscan_gpio_read_##n(data->dev); \
|
||||
} \
|
||||
static void kscan_gpio_irq_callback_handler_##n(struct device *dev, struct gpio_callback *cb, \
|
||||
gpio_port_pins_t pin) { \
|
||||
struct kscan_gpio_irq_callback_##n *data = \
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
|
||||
k_delayed_work_cancel(data->work); \
|
||||
k_delayed_work_submit(data->work, \
|
||||
K_MSEC(DT_INST_PROP(n, debounce_period))); \
|
||||
})) \
|
||||
} \
|
||||
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
|
||||
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \
|
||||
static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) { \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
if (!callback) { \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
data->callback = callback; \
|
||||
return 0; \
|
||||
}; \
|
||||
static int kscan_gpio_enable_##n(struct device *dev) { \
|
||||
int err = kscan_gpio_enable_interrupts_##n(dev); \
|
||||
if (err) { \
|
||||
return err; \
|
||||
} \
|
||||
return kscan_gpio_read_##n(dev); \
|
||||
}; \
|
||||
static int kscan_gpio_init_##n(struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->driver_data; \
|
||||
int err; \
|
||||
struct device **input_devices = kscan_gpio_input_devices_##n(dev); \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(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; \
|
||||
} \
|
||||
irq_callbacks_##n[i].work = &data->work; \
|
||||
gpio_init_callback(&irq_callbacks_##n[i].callback, \
|
||||
kscan_gpio_irq_callback_handler_##n, 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; \
|
||||
} \
|
||||
} \
|
||||
struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
output_devices[o] = device_get_binding(out_cfg->label); \
|
||||
if (!output_devices[o]) { \
|
||||
LOG_ERR("Unable to find output GPIO device"); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
err = gpio_pin_configure(output_devices[o], out_cfg->pin, \
|
||||
GPIO_OUTPUT_ACTIVE | out_cfg->flags); \
|
||||
if (err) { \
|
||||
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \
|
||||
out_cfg->label); \
|
||||
return err; \
|
||||
} \
|
||||
} \
|
||||
data->dev = dev; \
|
||||
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))( \
|
||||
&data->work, kscan_gpio_work_handler_##n); \
|
||||
return 0; \
|
||||
} \
|
||||
static const struct kscan_driver_api gpio_driver_api_##n = { \
|
||||
.config = kscan_gpio_configure_##n, \
|
||||
.enable_callback = kscan_gpio_enable_##n, \
|
||||
.disable_callback = kscan_gpio_disable_interrupts_##n, \
|
||||
}; \
|
||||
static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \
|
||||
.rows = {UTIL_LISTIFY(INST_MATRIX_ROWS(n), _KSCAN_GPIO_ROW_CFG_INIT, n)}, \
|
||||
.cols = {UTIL_LISTIFY(INST_MATRIX_COLS(n), _KSCAN_GPIO_COL_CFG_INIT, n)}, \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, APPLICATION, \
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, &gpio_driver_api_##n);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user