linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] input: Allow filtering of i8042 events
@ 2009-12-09 18:33 Matthew Garrett
  2009-12-09 18:33 ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
  2009-12-09 20:06 ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
  0 siblings, 2 replies; 11+ messages in thread
From: Matthew Garrett @ 2009-12-09 18:33 UTC (permalink / raw)
  To: linux-input; +Cc: linux-acpi, dtor, lenb, Matthew Garrett

Some hardware (such as Dell laptops) signal a variety of events through the
i8042 controller, even if these don't map to keyboard events. Add support
for drivers to filter the i8042 event stream in order to respond to these
events and (if appropriate) block them from entering the input stream.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/input/serio/i8042.c |   73 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/i8042.h       |   24 ++++++++++++++
 2 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 1df02d2..2d02fe2 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -108,11 +108,10 @@ struct i8042_port {
 	int irq;
 	bool exists;
 	signed char mux;
+	bool (*filter)(unsigned char data, unsigned int flags,
+		       struct serio *port);
 };
 
-#define I8042_KBD_PORT_NO	0
-#define I8042_AUX_PORT_NO	1
-#define I8042_MUX_PORT_NO	2
 #define I8042_NUM_PORTS		(I8042_NUM_MUX_PORTS + 2)
 
 static struct i8042_port i8042_ports[I8042_NUM_PORTS];
@@ -139,6 +138,66 @@ void i8042_unlock_chip(void)
 }
 EXPORT_SYMBOL(i8042_unlock_chip);
 
+int i8042_install_filter(int port, bool (*filter)(unsigned char data,
+						  unsigned int flags,
+						  struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	if (port >= I8042_NUM_PORTS) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i8042_ports[port].exists) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (i8042_ports[port].filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_ports[port].filter = filter;
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_install_filter);
+
+int i8042_remove_filter(int port, bool (*filter)(unsigned char data,
+						 unsigned int flags,
+						 struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	if (port >= I8042_NUM_PORTS) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!i8042_ports[port].exists) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!i8042_ports[port].filter || i8042_ports[port].filter != filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_ports[port].filter = NULL;
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_remove_filter);
+
 /*
  * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
  * be ready for reading values from it / writing values to it.
@@ -454,8 +513,12 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 			goto out;
 		}
 
-	if (likely(port->exists))
-		serio_interrupt(port->serio, data, dfl);
+	if (likely(port->exists)) {
+		if (port->filter && port->filter(data, dfl, port->serio))
+			goto out;
+		else
+			serio_interrupt(port->serio, data, dfl);
+	}
 
  out:
 	return IRQ_RETVAL(ret);
diff --git a/include/linux/i8042.h b/include/linux/i8042.h
index 60c3360..02f823b 100644
--- a/include/linux/i8042.h
+++ b/include/linux/i8042.h
@@ -33,12 +33,22 @@
 
 struct serio;
 
+#define I8042_KBD_PORT_NO       0
+#define I8042_AUX_PORT_NO       1
+#define I8042_MUX_PORT_NO       2
+
 #if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE)
 
 void i8042_lock_chip(void);
 void i8042_unlock_chip(void);
 int i8042_command(unsigned char *param, int command);
 bool i8042_check_port_owner(const struct serio *);
+int i8042_install_filter(int port, bool (*filter)(unsigned char data,
+						  unsigned int flags,
+						  struct serio *port));
+int i8042_remove_filter(int port, bool (*filter)(unsigned char data,
+						 unsigned int flags,
+						 struct serio *port));
 
 #else
 
@@ -60,6 +70,20 @@ bool i8042_check_port_owner(const struct serio *serio)
 	return false;
 }
 
+int i8042_install_filter(int port, bool (*filter)(unsigned char data,
+                                                  unsigned int flags,
+						  struct serio *port))
+{
+	return -ENODEV;
+}
+
+int i8042_remove_filter(int port, bool (*filter)(unsigned char data,
+						 unsigned int flags,
+						 struct serio *port))
+{
+	return -ENODEV;
+}
+
 #endif
 
 #endif
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/2] dell-laptop: Update rfkill state on kill switch
  2009-12-09 18:33 [PATCH 1/2] input: Allow filtering of i8042 events Matthew Garrett
@ 2009-12-09 18:33 ` Matthew Garrett
  2009-12-09 20:07   ` Dmitry Torokhov
  2009-12-09 20:06 ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
  1 sibling, 1 reply; 11+ messages in thread
From: Matthew Garrett @ 2009-12-09 18:33 UTC (permalink / raw)
  To: linux-input; +Cc: linux-acpi, dtor, lenb, Matthew Garrett

The rfkill interface on Dells only sends a notification that the switch
has been changed via the keyboard controller. Add a filter so we can
pick these notifications up and update the rfkill state appropriately.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/platform/x86/dell-laptop.c |   40 ++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 9061111..9dbec0f 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -22,6 +22,7 @@
 #include <linux/rfkill.h>
 #include <linux/power_supply.h>
 #include <linux/acpi.h>
+#include <linux/i8042.h>
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
@@ -206,6 +207,16 @@ static const struct rfkill_ops dell_rfkill_ops = {
 	.query = dell_rfkill_query,
 };
 
+static void dell_rfkill_update(void)
+{
+	if (wifi_rfkill)
+		dell_rfkill_query(wifi_rfkill, (void *)1);
+	if (bluetooth_rfkill)
+		dell_rfkill_query(bluetooth_rfkill, (void *)2);
+	if (wwan_rfkill)
+		dell_rfkill_query(wwan_rfkill, (void *)3);
+}
+
 static int dell_setup_rfkill(void)
 {
 	struct calling_interface_buffer buffer;
@@ -310,6 +321,26 @@ static struct backlight_ops dell_ops = {
 	.update_status  = dell_send_intensity,
 };
 
+bool dell_laptop_i8042_filter(unsigned char data, unsigned int flags,
+			      struct serio *port)
+{
+	static bool extended;
+
+	if (unlikely(data == 0xe0)) {
+		extended = true;
+		return false;
+	} else if (unlikely(extended)) {
+		switch (data) {
+		case 0x8:
+			dell_rfkill_update();
+			break;
+		}
+		extended = false;
+	}
+
+	return false;
+}
+
 static int __init dell_init(void)
 {
 	struct calling_interface_buffer buffer;
@@ -333,6 +364,13 @@ static int __init dell_init(void)
 		goto out;
 	}
 
+	ret = i8042_install_filter(I8042_KBD_PORT_NO, dell_laptop_i8042_filter);
+	if (ret) {
+		printk(KERN_WARNING
+		       "dell-laptop: Unable to install key filter\n");
+		goto out;
+	}
+
 #ifdef CONFIG_ACPI
 	/* In the event of an ACPI backlight being available, don't
 	 * register the platform controller.
@@ -369,6 +407,7 @@ static int __init dell_init(void)
 
 	return 0;
 out:
+	i8042_remove_filter(I8042_KBD_PORT_NO, dell_laptop_i8042_filter);
 	if (wifi_rfkill)
 		rfkill_unregister(wifi_rfkill);
 	if (bluetooth_rfkill)
@@ -381,6 +420,7 @@ out:
 
 static void __exit dell_exit(void)
 {
+	i8042_remove_filter(I8042_KBD_PORT_NO, dell_laptop_i8042_filter);
 	backlight_device_unregister(dell_backlight_device);
 	if (wifi_rfkill)
 		rfkill_unregister(wifi_rfkill);
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-09 18:33 [PATCH 1/2] input: Allow filtering of i8042 events Matthew Garrett
  2009-12-09 18:33 ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
@ 2009-12-09 20:06 ` Dmitry Torokhov
  2009-12-09 20:16   ` Matthew Garrett
  1 sibling, 1 reply; 11+ messages in thread
From: Dmitry Torokhov @ 2009-12-09 20:06 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-input, linux-acpi, lenb

Hi Matthew,

On Wed, Dec 09, 2009 at 01:33:28PM -0500, Matthew Garrett wrote:
> Some hardware (such as Dell laptops) signal a variety of events through the
> i8042 controller, even if these don't map to keyboard events. Add support
> for drivers to filter the i8042 event stream in order to respond to these
> events and (if appropriate) block them from entering the input stream.
> 

This is certainly going in the direction I wanted but it looks like
it is more complex than it needs to be. Why don't you just allow
installing global filter for entire i8042 (pass the status register
there along with data byte)? The data you are interested in goes kind of
"ouside" KBD/AUX port abstraction anyway...  Plus currently you are not
handling MUX case properly.

Also I don't think you need to handle the case when we install the same
filter twice? Just always return -EBUSY if there is a filter already and
let drivers keep track of their own stuff and not register tehir filters
twice.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] dell-laptop: Update rfkill state on kill switch
  2009-12-09 18:33 ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
@ 2009-12-09 20:07   ` Dmitry Torokhov
  0 siblings, 0 replies; 11+ messages in thread
From: Dmitry Torokhov @ 2009-12-09 20:07 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-input, linux-acpi, lenb

On Wed, Dec 09, 2009 at 01:33:29PM -0500, Matthew Garrett wrote:
> The rfkill interface on Dells only sends a notification that the switch
> has been changed via the keyboard controller. Add a filter so we can
> pick these notifications up and update the rfkill state appropriately.
> 

This needs Kconfig love for the case i8042=m dell-laptop=y

-- 
Dmitry

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-09 20:06 ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
@ 2009-12-09 20:16   ` Matthew Garrett
  2009-12-09 20:39     ` Dmitry Torokhov
  0 siblings, 1 reply; 11+ messages in thread
From: Matthew Garrett @ 2009-12-09 20:16 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-acpi, lenb

On Wed, Dec 09, 2009 at 12:06:15PM -0800, Dmitry Torokhov wrote:

> This is certainly going in the direction I wanted but it looks like
> it is more complex than it needs to be. Why don't you just allow
> installing global filter for entire i8042 (pass the status register
> there along with data byte)? The data you are interested in goes kind of
> "ouside" KBD/AUX port abstraction anyway...  Plus currently you are not
> handling MUX case properly.

Wouldn't that require me to be able to process any conceivable mouse 
protocol as well?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-09 20:16   ` Matthew Garrett
@ 2009-12-09 20:39     ` Dmitry Torokhov
  2009-12-10 23:25       ` Matthew Garrett
  0 siblings, 1 reply; 11+ messages in thread
From: Dmitry Torokhov @ 2009-12-09 20:39 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-input, linux-acpi, lenb

On Wed, Dec 09, 2009 at 08:16:20PM +0000, Matthew Garrett wrote:
> On Wed, Dec 09, 2009 at 12:06:15PM -0800, Dmitry Torokhov wrote:
> 
> > This is certainly going in the direction I wanted but it looks like
> > it is more complex than it needs to be. Why don't you just allow
> > installing global filter for entire i8042 (pass the status register
> > there along with data byte)? The data you are interested in goes kind of
> > "ouside" KBD/AUX port abstraction anyway...  Plus currently you are not
> > handling MUX case properly.
> 
> Wouldn't that require me to be able to process any conceivable mouse 
> protocol as well?

No, just have your filter function check STR to make sure the byte you got
is not coming from AUX. Or we could say that filter only works for data
pretending to be coming from KBD (because in reality the data we are
trying to filter out comes only this way) and move the check into i8042
itself.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-09 20:39     ` Dmitry Torokhov
@ 2009-12-10 23:25       ` Matthew Garrett
  2009-12-10 23:25         ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
  2009-12-11  7:59         ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
  0 siblings, 2 replies; 11+ messages in thread
From: Matthew Garrett @ 2009-12-10 23:25 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, linux-acpi, lenb, Matthew Garrett

Some hardware (such as Dell laptops) signal a variety of events through the
i8042 controller, even if these don't map to keyboard events. Add support
for drivers to filter the i8042 event stream in order to respond to these
events and (if appropriate) block them from entering the input stream.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---

This version makes the filter global and just lets the filter driver handle
making sure it's only looking at the correct data.

 drivers/input/serio/i8042.c |   57 +++++++++++++++++++++++++++++++++++++++----
 include/linux/i8042.h       |   16 ++++++++++++
 2 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 1df02d2..a16d813 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -110,9 +110,9 @@ struct i8042_port {
 	signed char mux;
 };
 
-#define I8042_KBD_PORT_NO	0
-#define I8042_AUX_PORT_NO	1
-#define I8042_MUX_PORT_NO	2
+#define I8042_KBD_PORT_NO       0
+#define I8042_AUX_PORT_NO       1
+#define I8042_MUX_PORT_NO       2
 #define I8042_NUM_PORTS		(I8042_NUM_MUX_PORTS + 2)
 
 static struct i8042_port i8042_ports[I8042_NUM_PORTS];
@@ -127,6 +127,9 @@ static struct platform_device *i8042_platform_device;
 
 static irqreturn_t i8042_interrupt(int irq, void *dev_id);
 
+static 	bool (*i8042_filter)(unsigned char data, unsigned char str,
+			     struct serio *port);
+
 void i8042_lock_chip(void)
 {
 	mutex_lock(&i8042_mutex);
@@ -139,6 +142,46 @@ void i8042_unlock_chip(void)
 }
 EXPORT_SYMBOL(i8042_unlock_chip);
 
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (i8042_filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_filter = filter;
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_install_filter);
+
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (!i8042_filter || i8042_filter != filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_filter = NULL;
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_remove_filter);
+
 /*
  * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
  * be ready for reading values from it / writing values to it.
@@ -454,8 +497,12 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 			goto out;
 		}
 
-	if (likely(port->exists))
-		serio_interrupt(port->serio, data, dfl);
+	if (likely(port->exists)) {
+		if (i8042_filter && i8042_filter(data, str, port->serio))
+			goto out;
+		else
+			serio_interrupt(port->serio, data, dfl);
+	}
 
  out:
 	return IRQ_RETVAL(ret);
diff --git a/include/linux/i8042.h b/include/linux/i8042.h
index 60c3360..bf2ccaf 100644
--- a/include/linux/i8042.h
+++ b/include/linux/i8042.h
@@ -39,6 +39,10 @@ void i8042_lock_chip(void);
 void i8042_unlock_chip(void);
 int i8042_command(unsigned char *param, int command);
 bool i8042_check_port_owner(const struct serio *);
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *port));
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *port));
 
 #else
 
@@ -60,6 +64,18 @@ bool i8042_check_port_owner(const struct serio *serio)
 	return false;
 }
 
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *port))
+{
+	return -ENODEV;
+}
+
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *port))
+{
+	return -ENODEV;
+}
+
 #endif
 
 #endif
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/2] dell-laptop: Update rfkill state on kill switch
  2009-12-10 23:25       ` Matthew Garrett
@ 2009-12-10 23:25         ` Matthew Garrett
  2009-12-29 10:01           ` Dmitry Torokhov
  2009-12-11  7:59         ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
  1 sibling, 1 reply; 11+ messages in thread
From: Matthew Garrett @ 2009-12-10 23:25 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, linux-acpi, lenb, Matthew Garrett

The rfkill interface on Dells only sends a notification that the switch
has been changed via the keyboard controller. Add a filter so we can
pick these notifications up and update the rfkill state appropriately.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/platform/x86/Kconfig       |    1 +
 drivers/platform/x86/dell-laptop.c |   43 ++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 55ca39d..4d42e95 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -79,6 +79,7 @@ config DELL_LAPTOP
 	depends on BACKLIGHT_CLASS_DEVICE
 	depends on RFKILL || RFKILL = n
 	depends on POWER_SUPPLY
+	depends on SERIO_I8042
 	default n
 	---help---
 	This driver adds support for rfkill and backlight control to Dell
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 9061111..0071b56 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -22,6 +22,7 @@
 #include <linux/rfkill.h>
 #include <linux/power_supply.h>
 #include <linux/acpi.h>
+#include <linux/i8042.h>
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
@@ -206,6 +207,16 @@ static const struct rfkill_ops dell_rfkill_ops = {
 	.query = dell_rfkill_query,
 };
 
+static void dell_rfkill_update(void)
+{
+	if (wifi_rfkill)
+		dell_rfkill_query(wifi_rfkill, (void *)1);
+	if (bluetooth_rfkill)
+		dell_rfkill_query(bluetooth_rfkill, (void *)2);
+	if (wwan_rfkill)
+		dell_rfkill_query(wwan_rfkill, (void *)3);
+}
+
 static int dell_setup_rfkill(void)
 {
 	struct calling_interface_buffer buffer;
@@ -310,6 +321,29 @@ static struct backlight_ops dell_ops = {
 	.update_status  = dell_send_intensity,
 };
 
+bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
+			      struct serio *port)
+{
+	static bool extended;
+
+	if (str & 0x20)
+		return false;
+
+	if (unlikely(data == 0xe0)) {
+		extended = true;
+		return false;
+	} else if (unlikely(extended)) {
+		switch (data) {
+		case 0x8:
+			dell_rfkill_update();
+			break;
+		}
+		extended = false;
+	}
+
+	return false;
+}
+
 static int __init dell_init(void)
 {
 	struct calling_interface_buffer buffer;
@@ -333,6 +367,13 @@ static int __init dell_init(void)
 		goto out;
 	}
 
+	ret = i8042_install_filter(dell_laptop_i8042_filter);
+	if (ret) {
+		printk(KERN_WARNING
+		       "dell-laptop: Unable to install key filter\n");
+		goto out;
+	}
+
 #ifdef CONFIG_ACPI
 	/* In the event of an ACPI backlight being available, don't
 	 * register the platform controller.
@@ -369,6 +410,7 @@ static int __init dell_init(void)
 
 	return 0;
 out:
+	i8042_remove_filter(dell_laptop_i8042_filter);
 	if (wifi_rfkill)
 		rfkill_unregister(wifi_rfkill);
 	if (bluetooth_rfkill)
@@ -381,6 +423,7 @@ out:
 
 static void __exit dell_exit(void)
 {
+	i8042_remove_filter(dell_laptop_i8042_filter);
 	backlight_device_unregister(dell_backlight_device);
 	if (wifi_rfkill)
 		rfkill_unregister(wifi_rfkill);
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-10 23:25       ` Matthew Garrett
  2009-12-10 23:25         ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
@ 2009-12-11  7:59         ` Dmitry Torokhov
  2009-12-11 12:46           ` Matthew Garrett
  1 sibling, 1 reply; 11+ messages in thread
From: Dmitry Torokhov @ 2009-12-11  7:59 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-input, linux-acpi, lenb

On Thu, Dec 10, 2009 at 06:25:32PM -0500, Matthew Garrett wrote:
> Some hardware (such as Dell laptops) signal a variety of events through the
> i8042 controller, even if these don't map to keyboard events. Add support
> for drivers to filter the i8042 event stream in order to respond to these
> events and (if appropriate) block them from entering the input stream.
> 

Locking is screwed (sorry, did not notice it forst time around). In fact
it was screwed in i8042 itself as well. What do you think about the
following 2 patchs (one prepares i8042 and the other is your vesion,
adapted).

Thanks.

-- 
Dmitry


Input: i8042 - fix locking in interrupt routine

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>

We need to protect not only i8042 status and data register from concurrent
access from IRQ 1 and 12 but the rest of the shared state as well, so let's
move release of i8042_lock in i8042_interrupt() a little bit further down.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/serio/i8042.c |   34 ++++++++++++++++++++++++++--------
 1 files changed, 26 insertions(+), 8 deletions(-)


diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 1df02d2..773ff11 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -369,6 +369,25 @@ static void i8042_stop(struct serio *serio)
 }
 
 /*
+ * i8042_filter() filters out unwanted bytes from the input data stream.
+ * It is called from i8042_interrupt and thus is running with interrupts
+ * off and i8042_lock held.
+ */
+static bool i8042_filter(unsigned char data, unsigned char str)
+{
+	if (unlikely(i8042_suppress_kbd_ack)) {
+		if ((~str & I8042_STR_AUXDATA) &&
+		    (data == 0xfa || data == 0xfe)) {
+			i8042_suppress_kbd_ack--;
+			dbg("Filtering extra keyboard ACK\n");
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
  * i8042_interrupt() is the most important function in this driver -
  * it handles the interrupts from the i8042, and sends incoming bytes
  * to the upper layers.
@@ -381,9 +400,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 	unsigned char str, data;
 	unsigned int dfl;
 	unsigned int port_no;
+	bool filtered;
 	int ret = 1;
 
 	spin_lock_irqsave(&i8042_lock, flags);
+
 	str = i8042_read_status();
 	if (unlikely(~str & I8042_STR_OBF)) {
 		spin_unlock_irqrestore(&i8042_lock, flags);
@@ -391,8 +412,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 		ret = 0;
 		goto out;
 	}
+
 	data = i8042_read_data();
-	spin_unlock_irqrestore(&i8042_lock, flags);
 
 	if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
 		static unsigned long last_transmit;
@@ -447,14 +468,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 	    dfl & SERIO_PARITY ? ", bad parity" : "",
 	    dfl & SERIO_TIMEOUT ? ", timeout" : "");
 
-	if (unlikely(i8042_suppress_kbd_ack))
-		if (port_no == I8042_KBD_PORT_NO &&
-		    (data == 0xfa || data == 0xfe)) {
-			i8042_suppress_kbd_ack--;
-			goto out;
-		}
+	filtered = i8042_filter(data, str);
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
 
-	if (likely(port->exists))
+	if (likely(port->exists && !filtered))
 		serio_interrupt(port->serio, data, dfl);
 
  out:


Input: i8042 - allow installing platform filters for incoming data

From: Matthew Garrett <mjg@redhat.com>

Some hardware (such as Dell laptops) signal a variety of events through
the i8042 controller, even if these don't map to keyboard events. Add
support for drivers to filter the i8042 event stream in order to respond
to these events and (if appropriate) block them from entering the input
stream.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/serio/i8042.c |   60 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/i8042.h       |   18 ++++++++++++-
 2 files changed, 73 insertions(+), 5 deletions(-)


diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 773ff11..d84a36e 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -126,6 +126,8 @@ static unsigned char i8042_suppress_kbd_ack;
 static struct platform_device *i8042_platform_device;
 
 static irqreturn_t i8042_interrupt(int irq, void *dev_id);
+static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
+				     struct serio *serio);
 
 void i8042_lock_chip(void)
 {
@@ -139,6 +141,48 @@ void i8042_unlock_chip(void)
 }
 EXPORT_SYMBOL(i8042_unlock_chip);
 
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *serio))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (i8042_platform_filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_platform_filter = filter;
+
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_install_filter);
+
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (i8042_platform_filter != filter) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	i8042_platform_filter = NULL;
+
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_remove_filter);
+
 /*
  * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
  * be ready for reading values from it / writing values to it.
@@ -373,17 +417,23 @@ static void i8042_stop(struct serio *serio)
  * It is called from i8042_interrupt and thus is running with interrupts
  * off and i8042_lock held.
  */
-static bool i8042_filter(unsigned char data, unsigned char str)
+static bool i8042_filter(unsigned char data, unsigned char str,
+			 struct serio *serio)
 {
 	if (unlikely(i8042_suppress_kbd_ack)) {
 		if ((~str & I8042_STR_AUXDATA) &&
 		    (data == 0xfa || data == 0xfe)) {
 			i8042_suppress_kbd_ack--;
-			dbg("Filtering extra keyboard ACK\n");
+			dbg("Extra keyboard ACK - filtered out\n");
 			return true;
 		}
 	}
 
+	if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
+		dbg("Filtered out by platfrom filter\n");
+		return true;
+	}
+
 	return false;
 }
 
@@ -396,6 +446,7 @@ static bool i8042_filter(unsigned char data, unsigned char str)
 static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 {
 	struct i8042_port *port;
+	struct serio *serio;
 	unsigned long flags;
 	unsigned char str, data;
 	unsigned int dfl;
@@ -462,18 +513,19 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 	}
 
 	port = &i8042_ports[port_no];
+	serio = port->exists ? port->serio : NULL;
 
 	dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
 	    data, port_no, irq,
 	    dfl & SERIO_PARITY ? ", bad parity" : "",
 	    dfl & SERIO_TIMEOUT ? ", timeout" : "");
 
-	filtered = i8042_filter(data, str);
+	filtered = i8042_filter(data, str, serio);
 
 	spin_unlock_irqrestore(&i8042_lock, flags);
 
 	if (likely(port->exists && !filtered))
-		serio_interrupt(port->serio, data, dfl);
+		serio_interrupt(serio, data, dfl);
 
  out:
 	return IRQ_RETVAL(ret);
diff --git a/include/linux/i8042.h b/include/linux/i8042.h
index 60c3360..9bf6870 100644
--- a/include/linux/i8042.h
+++ b/include/linux/i8042.h
@@ -39,6 +39,10 @@ void i8042_lock_chip(void);
 void i8042_unlock_chip(void);
 int i8042_command(unsigned char *param, int command);
 bool i8042_check_port_owner(const struct serio *);
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *serio));
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *serio));
 
 #else
 
@@ -52,7 +56,7 @@ void i8042_unlock_chip(void)
 
 int i8042_command(unsigned char *param, int command)
 {
-	return -ENOSYS;
+	return -ENODEV;
 }
 
 bool i8042_check_port_owner(const struct serio *serio)
@@ -60,6 +64,18 @@ bool i8042_check_port_owner(const struct serio *serio)
 	return false;
 }
 
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *serio))
+{
+	return -ENODEV;
+}
+
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *serio))
+{
+	return -ENODEV;
+}
+
 #endif
 
 #endif

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] input: Allow filtering of i8042 events
  2009-12-11  7:59         ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
@ 2009-12-11 12:46           ` Matthew Garrett
  0 siblings, 0 replies; 11+ messages in thread
From: Matthew Garrett @ 2009-12-11 12:46 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-acpi, lenb

On Thu, Dec 10, 2009 at 11:59:10PM -0800, Dmitry Torokhov wrote:

> Locking is screwed (sorry, did not notice it forst time around). In fact
> it was screwed in i8042 itself as well. What do you think about the
> following 2 patchs (one prepares i8042 and the other is your vesion,
> adapted).

Looks good to me. Thanks!

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] dell-laptop: Update rfkill state on kill switch
  2009-12-10 23:25         ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
@ 2009-12-29 10:01           ` Dmitry Torokhov
  0 siblings, 0 replies; 11+ messages in thread
From: Dmitry Torokhov @ 2009-12-29 10:01 UTC (permalink / raw)
  To: Len Brown; +Cc: linux-input, linux-acpi, Matthew Garrett

On Thu, Dec 10, 2009 at 06:25:33PM -0500, Matthew Garrett wrote:
> The rfkill interface on Dells only sends a notification that the switch
> has been changed via the keyboard controller. Add a filter so we can
> pick these notifications up and update the rfkill state appropriately.
> 
> Signed-off-by: Matthew Garrett <mjg@redhat.com>

Len,

the i8042 changes are in Linus's tree now so this patch can now be
applied.

Acked-by: Dmitry Torokhov <dtor@mail.ru>

> ---
>  drivers/platform/x86/Kconfig       |    1 +
>  drivers/platform/x86/dell-laptop.c |   43 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 44 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 55ca39d..4d42e95 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -79,6 +79,7 @@ config DELL_LAPTOP
>  	depends on BACKLIGHT_CLASS_DEVICE
>  	depends on RFKILL || RFKILL = n
>  	depends on POWER_SUPPLY
> +	depends on SERIO_I8042
>  	default n
>  	---help---
>  	This driver adds support for rfkill and backlight control to Dell
> diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
> index 9061111..0071b56 100644
> --- a/drivers/platform/x86/dell-laptop.c
> +++ b/drivers/platform/x86/dell-laptop.c
> @@ -22,6 +22,7 @@
>  #include <linux/rfkill.h>
>  #include <linux/power_supply.h>
>  #include <linux/acpi.h>
> +#include <linux/i8042.h>
>  #include "../../firmware/dcdbas.h"
>  
>  #define BRIGHTNESS_TOKEN 0x7d
> @@ -206,6 +207,16 @@ static const struct rfkill_ops dell_rfkill_ops = {
>  	.query = dell_rfkill_query,
>  };
>  
> +static void dell_rfkill_update(void)
> +{
> +	if (wifi_rfkill)
> +		dell_rfkill_query(wifi_rfkill, (void *)1);
> +	if (bluetooth_rfkill)
> +		dell_rfkill_query(bluetooth_rfkill, (void *)2);
> +	if (wwan_rfkill)
> +		dell_rfkill_query(wwan_rfkill, (void *)3);
> +}
> +
>  static int dell_setup_rfkill(void)
>  {
>  	struct calling_interface_buffer buffer;
> @@ -310,6 +321,29 @@ static struct backlight_ops dell_ops = {
>  	.update_status  = dell_send_intensity,
>  };
>  
> +bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
> +			      struct serio *port)
> +{
> +	static bool extended;
> +
> +	if (str & 0x20)
> +		return false;
> +
> +	if (unlikely(data == 0xe0)) {
> +		extended = true;
> +		return false;
> +	} else if (unlikely(extended)) {
> +		switch (data) {
> +		case 0x8:
> +			dell_rfkill_update();
> +			break;
> +		}
> +		extended = false;
> +	}
> +
> +	return false;
> +}
> +
>  static int __init dell_init(void)
>  {
>  	struct calling_interface_buffer buffer;
> @@ -333,6 +367,13 @@ static int __init dell_init(void)
>  		goto out;
>  	}
>  
> +	ret = i8042_install_filter(dell_laptop_i8042_filter);
> +	if (ret) {
> +		printk(KERN_WARNING
> +		       "dell-laptop: Unable to install key filter\n");
> +		goto out;
> +	}
> +
>  #ifdef CONFIG_ACPI
>  	/* In the event of an ACPI backlight being available, don't
>  	 * register the platform controller.
> @@ -369,6 +410,7 @@ static int __init dell_init(void)
>  
>  	return 0;
>  out:
> +	i8042_remove_filter(dell_laptop_i8042_filter);
>  	if (wifi_rfkill)
>  		rfkill_unregister(wifi_rfkill);
>  	if (bluetooth_rfkill)
> @@ -381,6 +423,7 @@ out:
>  
>  static void __exit dell_exit(void)
>  {
> +	i8042_remove_filter(dell_laptop_i8042_filter);
>  	backlight_device_unregister(dell_backlight_device);
>  	if (wifi_rfkill)
>  		rfkill_unregister(wifi_rfkill);
> -- 
> 1.6.5.2
> 

-- 
Dmitry

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2009-12-29 10:01 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-09 18:33 [PATCH 1/2] input: Allow filtering of i8042 events Matthew Garrett
2009-12-09 18:33 ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
2009-12-09 20:07   ` Dmitry Torokhov
2009-12-09 20:06 ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
2009-12-09 20:16   ` Matthew Garrett
2009-12-09 20:39     ` Dmitry Torokhov
2009-12-10 23:25       ` Matthew Garrett
2009-12-10 23:25         ` [PATCH 2/2] dell-laptop: Update rfkill state on kill switch Matthew Garrett
2009-12-29 10:01           ` Dmitry Torokhov
2009-12-11  7:59         ` [PATCH 1/2] input: Allow filtering of i8042 events Dmitry Torokhov
2009-12-11 12:46           ` Matthew Garrett

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).