Hello Christian,

thanks for your reworked patch. I think i found two bugs around the debounce
interval for both flavors. I'll have to admit, they are both edgecases ;)

On 06.07.19 23:57, Christian Lamparter wrote
> -static void gpio_keys_polled_check_state(struct gpio_keys_button_data *bdata)
> +static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata)
>   {
> +     unsigned int type = bdata->b->type ?: EV_KEY;
>       int state = gpio_button_get_value(bdata);
> +     unsigned long seen = jiffies;
>   
> -     if (state != bdata->last_state) {
> -             unsigned int type = bdata->b->type ?: EV_KEY;
> +     pr_debug(PFX "event type=%u, code=%u, pressed=%d\n",
> +              type, bdata->b->code, state);
> +
> +     /* is this the initialization state? */
> +     if (bdata->last_state == -1) {
> +             /*
> +              * Don't advertise unpressed buttons on initialization.
> +              * Just save their state and continue otherwise this
> +              * can cause OpenWrt to enter failsafe.
> +              */
> +             if (type == EV_KEY && state == 0)
> +                     goto set_state;

As we are calling gpio_keys_handle_button every poll-interval, we need 
to make sure we save the initial state AFTER the button has been stable 
for the debounce interval. For irq-based keys we get this "for free" as 
we modify the execution timer for the irq handling function.

However, we need to track the button state for the polled GPIO keys, so 
we cannot piggy-back on the last_state for deciding if the initial state 
is already set. We could use negative numbers for that, but i think this 
is more hacky than using a dedicated variable for keeping track.

This was the reason for my 'has_initial_state' variable. :)

> +             /*
> +              * But we are very interested in pressed buttons and
> +              * initial switch state. These will be reported to
> +              * userland.
> +              */
> +     } else if (bdata->last_state == state) {
> +             /* reset asserted counter (only relevant for polled keys) */

This is also needed for irq-driven keys. If the state changes two times 
within the debounce interval, gpio_keys_handle_button is still executed 
and we need to terminate here. Otherwise, we would skip a "release" or
"push" action.

I did rework those two parts a bit and tested it with the irq and
polled flavors without a problem. See below:


>From b751bb6f4088b4a16c378006fed4e071f905d9e0 Mon Sep 17 00:00:00 2001
From: David Bauer <m...@david-bauer.net>
Date: Wed, 10 Jul 2019 00:22:56 +0200
Subject: [PATCH] gpio-button-hotplug: unify polled and interrupt code

This patch unifies the polled and interrupt-driven gpio_keys code
paths as well implements consistent handling of the debounce
interval set for the GPIO buttons and switches.

Hotplug events will only be fired if

1. The input changes its state and remains stable for the duration
   of the debounce interval (default is 5 ms).

2. In the initial stable ((no state-change for duration of the
   debounce interval) state once the driver module gets loaded.

   Switch type inputs will always report their stable state.
   Unpressed buttons will not trigger an event for the initial
   stable state. Whereas pressed buttons will trigger an event.
   This is consistent with upstream's gpio-key driver that uses
   the input subsystem (and dont use autorepeat).

Prior to this patch, this was handled inconsistently for interrupt-based
an polled gpio-keys. Hence this patch unifies the shared logic into the
gpio_keys_handle_button() function and modify both implementations to
handle the initial state properly.

Run-tested for 'gpio-keys' and 'gpio-keys-polled' on

 - devolo WiFi pro 1200e
 - devolo WiFi pro 1750c
 - devolo WiFi pro 1750x
 - Netgear WNDR4700
 - Meraki MR24

Signed-off-by: David Bauer <m...@david-bauer.net>
[further cleanups, simplification and unification]
Signed-off-by: Christian Lamparter <chunk...@gmail.com>
[reworked initial state logic, fix initial debounce for polled keys]
Signed-off-by: David Bauer <m...@david-bauer.net>
---
 .../src/gpio-button-hotplug.c                 | 151 ++++++++++--------
 1 file changed, 86 insertions(+), 65 deletions(-)

diff --git a/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c 
b/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c
index e63d414284..a5a5ebed23 100644
--- a/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c
+++ b/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c
@@ -35,10 +35,6 @@
 #define DRV_NAME       "gpio-keys"
 #define PFX    DRV_NAME ": "
 
-struct bh_priv {
-       unsigned long           seen;
-};
-
 struct bh_event {
        const char              *name;
        unsigned int            type;
@@ -56,8 +52,10 @@ struct bh_map {
 
 struct gpio_keys_button_data {
        struct delayed_work work;
-       struct bh_priv bh;
+       unsigned long seen;
+       int map_entry;
        int last_state;
+       int has_initial_state;
        int count;
        int threshold;
        int can_sleep;
@@ -238,39 +236,6 @@ static int button_get_index(unsigned int code)
        return -1;
 }
 
-static void button_hotplug_event(struct gpio_keys_button_data *data,
-                          unsigned int type, int value)
-{
-       struct bh_priv *priv = &data->bh;
-       unsigned long seen = jiffies;
-       int btn;
-
-       pr_debug(PFX "event type=%u, code=%u, value=%d\n", type, data->b->code, 
value);
-
-       if ((type != EV_KEY) && (type != EV_SW))
-               return;
-
-       btn = button_get_index(data->b->code);
-       if (btn < 0)
-               return;
-
-       if (priv->seen == 0)
-               priv->seen = seen;
-
-       button_hotplug_create_event(button_map[btn].name, type,
-                       (seen - priv->seen) / HZ, value);
-       priv->seen = seen;
-}
-
-struct gpio_keys_button_dev {
-       int polled;
-       struct delayed_work work;
-
-       struct device *dev;
-       struct gpio_keys_platform_data *pdata;
-       struct gpio_keys_button_data data[0];
-};
-
 static int gpio_button_get_value(struct gpio_keys_button_data *bdata)
 {
        int val;
@@ -283,27 +248,69 @@ static int gpio_button_get_value(struct 
gpio_keys_button_data *bdata)
        return val ^ bdata->b->active_low;
 }
 
-static void gpio_keys_polled_check_state(struct gpio_keys_button_data *bdata)
+static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata)
 {
+       unsigned int type = bdata->b->type ?: EV_KEY;
        int state = gpio_button_get_value(bdata);
+       unsigned long seen = jiffies;
 
-       if (state != bdata->last_state) {
-               unsigned int type = bdata->b->type ?: EV_KEY;
-
-               if (bdata->count < bdata->threshold) {
-                       bdata->count++;
-                       return;
-               }
+       pr_debug(PFX "event type=%u, code=%u, pressed=%d\n",
+                type, bdata->b->code, state);
+
+       if ((bdata->has_initial_state && bdata->last_state == state) ||
+           (!bdata->has_initial_state && !bdata->irq && bdata->last_state != 
state)) {
+               /* Reset asserted counter when
+                *  1. we have a stable initial state and the current state 
matches the last
+                *  2. we do not have a stable state (yet),
+                *     we use polled GPIO keys,
+                *     the current state does not match the last.
+                */
+               goto set_state;
+       }
 
-               if (bdata->last_state != -1 || type == EV_SW)
-                       button_hotplug_event(bdata, type, state);
+       if (bdata->count < bdata->threshold) {
+               bdata->count++;
+               return;
+       }
 
-               bdata->last_state = state;
+       /* Is this the initialization state? */
+       if (!bdata->has_initial_state) {
+               /*
+                * Now the button was stable for the debounce interval.
+                * Mark the button as having an initial stable state
+                * and do not report a button press. Otherwise, this
+                * can cause OpenWrt to enter failsafe.
+                */
+               bdata->has_initial_state = 1;
+               if (type == EV_KEY)
+                       goto set_state;
+               /*
+                * But we are very interested in initial switch states.
+                * These will be reported to userland.
+                */
        }
 
+       if (bdata->seen == 0)
+               bdata->seen = seen;
+
+       button_hotplug_create_event(button_map[bdata->map_entry].name, type,
+                                   (seen - bdata->seen) / HZ, state);
+       bdata->seen = seen;
+
+set_state:
+       bdata->last_state = state;
        bdata->count = 0;
 }
 
+struct gpio_keys_button_dev {
+       int polled;
+       struct delayed_work work;
+
+       struct device *dev;
+       struct gpio_keys_platform_data *pdata;
+       struct gpio_keys_button_data data[0];
+};
+
 static void gpio_keys_polled_queue_work(struct gpio_keys_button_dev *bdev)
 {
        struct gpio_keys_platform_data *pdata = bdev->pdata;
@@ -322,7 +329,9 @@ static void gpio_keys_polled_poll(struct work_struct *work)
 
        for (i = 0; i < bdev->pdata->nbuttons; i++) {
                struct gpio_keys_button_data *bdata = &bdev->data[i];
-               gpio_keys_polled_check_state(bdata);
+
+               if (bdata->gpiod)
+                       gpio_keys_handle_button(bdata);
        }
        gpio_keys_polled_queue_work(bdev);
 }
@@ -342,8 +351,7 @@ static void gpio_keys_irq_work_func(struct work_struct 
*work)
        struct gpio_keys_button_data *bdata = container_of(work,
                struct gpio_keys_button_data, work.work);
 
-       button_hotplug_event(bdata, bdata->b->type ?: EV_KEY,
-                            gpio_button_get_value(bdata));
+       gpio_keys_handle_button(bdata);
 }
 
 static irqreturn_t button_handle_irq(int irq, void *_bdata)
@@ -351,8 +359,8 @@ static irqreturn_t button_handle_irq(int irq, void *_bdata)
        struct gpio_keys_button_data *bdata =
                (struct gpio_keys_button_data *) _bdata;
 
-       schedule_delayed_work(&bdata->work,
-                             msecs_to_jiffies(bdata->software_debounce));
+       mod_delayed_work(system_wq, &bdata->work,
+                        msecs_to_jiffies(bdata->software_debounce));
 
        return IRQ_HANDLED;
 }
@@ -413,7 +421,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
                                return ERR_PTR(error);
                        }
                } else {
-                       button->active_low = flags & OF_GPIO_ACTIVE_LOW;
+                       button->active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
                }
 
                if (of_property_read_u32(pp, "linux,code", &button->code)) {
@@ -520,6 +528,19 @@ static int gpio_keys_button_probe(struct platform_device 
*pdev,
                        return -EINVAL;
                }
 
+               bdata->map_entry = button_get_index(button->code);
+               if (bdata->map_entry < 0) {
+                       dev_warn(dev, DRV_NAME "does not support key code:%u\n",
+                               button->code);
+                       continue;
+               }
+
+               if (!(button->type == 0 || button->type == EV_KEY ||
+                     button->type == EV_SW)) {
+                       dev_warn(dev, DRV_NAME "only supports buttons or 
switches\n");
+                       continue;
+               }
+
                error = devm_gpio_request(dev, gpio,
                                     button->desc ? button->desc : DRV_NAME);
                if (error) {
@@ -540,13 +561,14 @@ static int gpio_keys_button_probe(struct platform_device 
*pdev,
                }
 
                bdata->can_sleep = gpio_cansleep(gpio);
-               bdata->last_state = -1;
+               bdata->last_state = -1; /* Unknown state on boot */
+               bdata->has_initial_state = 0;
 
                if (bdev->polled) {
                        bdata->threshold = 
DIV_ROUND_UP(button->debounce_interval,
                                                        pdata->poll_interval);
                } else {
-                       bdata->threshold = 1;
+                       /* bdata->threshold = 0; already initialized */
 
                        if (button->debounce_interval) {
                                error = gpiod_set_debounce(bdata->gpiod,
@@ -592,6 +614,11 @@ static int gpio_keys_probe(struct platform_device *pdev)
                struct gpio_keys_button_data *bdata = &bdev->data[i];
                unsigned long irqflags = IRQF_ONESHOT;
 
+               INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func);
+
+               if (!bdata->gpiod)
+                       continue;
+
                if (!button->irq) {
                        bdata->irq = gpio_to_irq(button->gpio);
 
@@ -606,7 +633,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
                        bdata->irq = button->irq;
                }
 
-               INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func);
+               schedule_delayed_work(&bdata->work,
+                                     
msecs_to_jiffies(bdata->software_debounce));
 
                ret = devm_request_threaded_irq(&pdev->dev,
                        bdata->irq, NULL, button_handle_irq,
@@ -621,9 +649,6 @@ static int gpio_keys_probe(struct platform_device *pdev)
                        dev_dbg(&pdev->dev, "gpio:%d has irq:%d\n",
                                button->gpio, bdata->irq);
                }
-
-               if (bdata->b->type == EV_SW)
-                       button_hotplug_event(bdata, EV_SW, 
gpio_button_get_value(bdata));
        }
 
        return 0;
@@ -634,7 +659,6 @@ static int gpio_keys_polled_probe(struct platform_device 
*pdev)
        struct gpio_keys_platform_data *pdata;
        struct gpio_keys_button_dev *bdev;
        int ret;
-       int i;
 
        ret = gpio_keys_button_probe(pdev, &bdev, 1);
 
@@ -648,9 +672,6 @@ static int gpio_keys_polled_probe(struct platform_device 
*pdev)
        if (pdata->enable)
                pdata->enable(bdev->dev);
 
-       for (i = 0; i < pdata->nbuttons; i++)
-               gpio_keys_polled_check_state(&bdev->data[i]);
-
        gpio_keys_polled_queue_work(bdev);
 
        return ret;
-- 
2.22.0

_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to