lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 5 Feb 2009 17:38:26 +0100 (CET)
From:	Guennadi Liakhovetski <lg@...x.de>
To:	linux-input@...r.kernel.org
cc:	linux-kernel@...r.kernel.org, dmitry.torokhov@...il.com
Subject: [PATCH] gpio-keys: poll for release if GPIO cannot interrupt on both
 edges

There are GPIO controllers, that cannot be configured to produce 
interrupts on both edges. To be able to meaningfully use these for 
gpio-keys, add a polling option.

Signed-off-by: Guennadi Liakhovetski <lg@...x.de>
---

I previously tried to solve this problem by reporting a release event 
immediately after press, but this proved to be not very useful. This patch 
adds a timer to poll for release events on such buttons. I so far set the 
polling period to 50ms, don't know how good or bad this is, and whether we 
want to have it configurable.

diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ad67d76..2203fd0 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -29,10 +29,13 @@ struct gpio_button_data {
 	struct gpio_keys_button *button;
 	struct input_dev *input;
 	struct timer_list timer;
+	struct timer_list *release;
+	int state;
 };
 
 struct gpio_keys_drvdata {
 	struct input_dev *input;
+	struct timer_list release;
 	struct gpio_button_data data[0];
 };
 
@@ -43,10 +46,41 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
 	unsigned int type = button->type ?: EV_KEY;
 	int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
 
-	input_event(input, type, button->code, !!state);
+	bdata->state = !!state;
+	input_event(input, type, button->code, bdata->state);
+	/* If the button only supports press events, poll for release */
+	if (state && button->irq_trigger != (IRQF_TRIGGER_RISING |
+					     IRQF_TRIGGER_FALLING))
+		mod_timer(bdata->release, jiffies + msecs_to_jiffies(50));
+
 	input_sync(input);
 }
 
+static void gpio_poll_button(unsigned long _data)
+{
+	struct platform_device *pdev = (struct platform_device *)_data;
+	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+		struct gpio_keys_button *button = bdata->button;
+
+		if (bdata->state && button->irq_trigger !=
+		    (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+			int state = (gpio_get_value(button->gpio) ? 1 : 0) ^
+				button->active_low;
+
+			if (!state)
+				gpio_keys_report_event(bdata);
+			else
+				mod_timer(bdata->release, jiffies +
+					  msecs_to_jiffies(50));
+		}
+	}
+}
+
 static void gpio_check_button(unsigned long _data)
 {
 	struct gpio_button_data *data = (struct gpio_button_data *)_data;
@@ -104,6 +138,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 
 	ddata->input = input;
 
+	setup_timer(&ddata->release,
+		    gpio_poll_button, (unsigned long)pdev);
+
 	for (i = 0; i < pdata->nbuttons; i++) {
 		struct gpio_keys_button *button = &pdata->buttons[i];
 		struct gpio_button_data *bdata = &ddata->data[i];
@@ -141,9 +178,19 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 			goto fail2;
 		}
 
+		button->irq_trigger &= IRQF_TRIGGER_RISING |
+			IRQF_TRIGGER_FALLING;
+
+		if (!button->irq_trigger)
+			button->irq_trigger = IRQF_TRIGGER_RISING |
+				IRQF_TRIGGER_FALLING;
+
+		if (button->irq_trigger != (IRQF_TRIGGER_RISING |
+					    IRQF_TRIGGER_FALLING))
+			bdata->release = &ddata->release;
+
 		error = request_irq(irq, gpio_keys_isr,
-				    IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
-					IRQF_TRIGGER_FALLING,
+				    IRQF_SAMPLE_RANDOM | button->irq_trigger,
 				    button->desc ? button->desc : "gpio_keys",
 				    bdata);
 		if (error) {
@@ -171,6 +218,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 	return 0;
 
  fail2:
+	del_timer_sync(&ddata->release);
 	while (--i >= 0) {
 		free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
 		if (pdata->buttons[i].debounce_interval)
@@ -195,6 +243,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
 
 	device_init_wakeup(&pdev->dev, 0);
 
+	del_timer_sync(&ddata->release);
+
 	for (i = 0; i < pdata->nbuttons; i++) {
 		int irq = gpio_to_irq(pdata->buttons[i].gpio);
 		free_irq(irq, &ddata->data[i]);
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 1289fa7..d0bd8e2 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -10,6 +10,7 @@ struct gpio_keys_button {
 	int type;		/* input event type (EV_KEY, EV_SW) */
 	int wakeup;		/* configure the button as a wake-up source */
 	int debounce_interval;	/* debounce ticks interval in msecs */
+	unsigned long irq_trigger; /* IRQF_TRIGGER_{RISING,FALLING} */
 };
 
 struct gpio_keys_platform_data {
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ