[PATCH] gpio-keys debouncing support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Sometimes the gpio line can generates jitter while transitioning
from one state to another. Implement a way to filter such noise during
transitions.

Signed-off-by: Dmitry Baryshkov <dbaryshkov@xxxxxxxxx>
---
 drivers/input/keyboard/Kconfig     |    7 +++
 drivers/input/keyboard/gpio_keys.c |   96 ++++++++++++++++++++++++++++++++++--
 include/linux/gpio_keys.h          |    6 ++
 3 files changed, 104 insertions(+), 5 deletions(-)

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 8ea709b..0fb4592 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -295,6 +295,13 @@ config KEYBOARD_GPIO
 	  To compile this driver as a module, choose M here: the
 	  module will be called gpio-keys.
 
+config KEYBOARD_GPIO_DEBOUNCE
+	bool "GPIO Buttons debouncer"
+	depends on KEYBOARD_GPIO
+	help
+	  This driver will enable the debouncer in the gpio-keys
+	  driver for pins that take some time to come to stable state.
+
 config KEYBOARD_MAPLE
 	tristate "Maple bus keyboard"
 	depends on SH_DREAMCAST && MAPLE
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 6a9ca4b..577dfea 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -26,6 +26,39 @@
 
 #include <asm/gpio.h>
 
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+struct gpio_debounce_data {
+	struct platform_device *pdev;
+	struct gpio_keys_button *button;
+	int state;
+	int count;
+};
+
+static void gpio_debounce_timer(unsigned long _data)
+{
+	struct gpio_debounce_data *data = (struct gpio_debounce_data *)_data;
+	struct gpio_keys_button *button = data->button;
+	int gpio = button->gpio;
+	unsigned int type = button->type ?: EV_KEY;
+	int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;
+	struct input_dev *input = platform_get_drvdata(data->pdev);
+
+	if (state != data->state) {
+		data->count = 0;
+		data->state = state;
+		mod_timer(&button->timer, jiffies +
+					msecs_to_jiffies(button->interval));
+	} else if (data->count < button->count) {
+		data->count ++;
+		mod_timer(&button->timer, jiffies +
+					msecs_to_jiffies(button->interval));
+	} else {
+		input_event(input, type, button->code, !!state);
+		input_sync(input);
+	}
+}
+#endif
+
 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
 {
 	int i;
@@ -38,11 +71,21 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
 		int gpio = button->gpio;
 
 		if (irq == gpio_to_irq(gpio)) {
-			unsigned int type = button->type ?: EV_KEY;
-			int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+			if (button->count) {
+				if (!timer_pending(&button->timer))
+					mod_timer(&button->timer, jiffies +
+							msecs_to_jiffies(
+							button->interval));
+			} else
+#endif
+			{
+				unsigned int type = button->type ?: EV_KEY;
+				int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;
 
-			input_event(input, type, button->code, !!state);
-			input_sync(input);
+				input_event(input, type, button->code, !!state);
+				input_sync(input);
+			}
 		}
 	}
 
@@ -103,6 +146,31 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 			gpio_free(button->gpio);
 			goto fail;
 		}
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+		if (button->count && button->interval) {
+			struct gpio_debounce_data *data;
+			data = kzalloc(sizeof(struct gpio_debounce_data), GFP_KERNEL);
+			if (!data) {
+				error = -ENOMEM;
+				pr_err("gpio-keys: Unable to allocate debounce data"
+					" for GPIO %d, error %d\n",
+					button->gpio, error);
+				gpio_free(button->gpio);
+				goto fail;
+			}
+
+			data->pdev = pdev;
+			data->button = button;
+			data->state = -1;
+
+			init_timer(&button->timer);
+			button->timer.function = gpio_debounce_timer;
+			button->timer.data = (unsigned long)data;
+
+			mod_timer(&button->timer, jiffies +
+					msecs_to_jiffies(button->interval));
+		}
+#endif
 
 		error = request_irq(irq, gpio_keys_isr,
 				    IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
@@ -112,6 +180,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 		if (error) {
 			pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
 				irq, error);
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+			if (button->count) {
+				del_timer_sync(&button->timer);
+				kfree((void*)button->timer.data);
+			}
+#endif
 			gpio_free(button->gpio);
 			goto fail;
 		}
@@ -136,6 +210,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
  fail:
 	while (--i >= 0) {
 		free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+		if (pdata->buttons[i].count) {
+			del_timer_sync(&pdata->buttons[i].timer);
+			kfree((void*)pdata->buttons[i].timer.data);
+		}
+#endif
 		gpio_free(pdata->buttons[i].gpio);
 	}
 
@@ -156,6 +236,12 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
 	for (i = 0; i < pdata->nbuttons; i++) {
 		int irq = gpio_to_irq(pdata->buttons[i].gpio);
 		free_irq(irq, pdev);
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+		if (pdata->buttons[i].count) {
+			del_timer_sync(&pdata->buttons[i].timer);
+			kfree((void*)pdata->buttons[i].timer.data);
+		}
+#endif
 		gpio_free(pdata->buttons[i].gpio);
 	}
 
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index c6d3a9d..2432e62 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -9,6 +9,12 @@ struct gpio_keys_button {
 	char *desc;
 	int type;		/* input event type (EV_KEY, EV_SW) */
 	int wakeup;		/* configure the button as a wake-up source */
+#ifdef CONFIG_KEYBOARD_GPIO_DEBOUNCE
+	int count;
+	int interval;		/* in msecs */
+	/* private: */
+	struct timer_list	timer;
+#endif
 };
 
 struct gpio_keys_platform_data {
-- 
1.5.4.3


-- 
With best wishes
Dmitry

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux