320 lines
8.9 KiB
Diff
320 lines
8.9 KiB
Diff
From caca5447fae9bfc87ab7d4af664d8bc95db6904d Mon Sep 17 00:00:00 2001
|
|
From: Kyle McMartin <kyle@treachery.i.jkkm.org>
|
|
Date: Mon, 11 Jan 2010 08:08:02 -0500
|
|
Subject: linux-2.6-dell-laptop-rfkill-fix.patch
|
|
|
|
---
|
|
drivers/input/input.c | 91 ++++++++++++++++++++++++++++-----
|
|
drivers/platform/x86/dell-laptop.c | 100 ++++++++++++++++++++++++++++++++++++
|
|
include/linux/input.h | 5 ++
|
|
3 files changed, 183 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/drivers/input/input.c b/drivers/input/input.c
|
|
index ab06071..1911c3a 100644
|
|
--- a/drivers/input/input.c
|
|
+++ b/drivers/input/input.c
|
|
@@ -90,19 +90,26 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
|
|
*/
|
|
static void input_pass_event(struct input_dev *dev,
|
|
unsigned int type, unsigned int code, int value)
|
|
-{
|
|
- struct input_handle *handle;
|
|
+
|
|
+{ struct input_handle *handle;
|
|
|
|
rcu_read_lock();
|
|
|
|
handle = rcu_dereference(dev->grab);
|
|
- if (handle)
|
|
+ if (handle) {
|
|
handle->handler->event(handle, type, code, value);
|
|
- else
|
|
- list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
|
- if (handle->open)
|
|
- handle->handler->event(handle,
|
|
- type, code, value);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ handle = rcu_dereference(dev->filter);
|
|
+ if (handle && handle->handler->filter(handle, type, code, value))
|
|
+ goto out;
|
|
+
|
|
+ list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
|
+ if (handle->open)
|
|
+ handle->handler->event(handle,
|
|
+ type, code, value);
|
|
+out:
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
@@ -383,12 +390,15 @@ int input_grab_device(struct input_handle *handle)
|
|
}
|
|
EXPORT_SYMBOL(input_grab_device);
|
|
|
|
-static void __input_release_device(struct input_handle *handle)
|
|
+static void __input_release_device(struct input_handle *handle, bool filter)
|
|
{
|
|
struct input_dev *dev = handle->dev;
|
|
|
|
- if (dev->grab == handle) {
|
|
- rcu_assign_pointer(dev->grab, NULL);
|
|
+ if (handle == (filter ? dev->filter : dev->grab)) {
|
|
+ if (filter)
|
|
+ rcu_assign_pointer(dev->filter, NULL);
|
|
+ else
|
|
+ rcu_assign_pointer(dev->grab, NULL);
|
|
/* Make sure input_pass_event() notices that grab is gone */
|
|
synchronize_rcu();
|
|
|
|
@@ -412,12 +422,65 @@ void input_release_device(struct input_handle *handle)
|
|
struct input_dev *dev = handle->dev;
|
|
|
|
mutex_lock(&dev->mutex);
|
|
- __input_release_device(handle);
|
|
+ __input_release_device(handle, false);
|
|
mutex_unlock(&dev->mutex);
|
|
}
|
|
EXPORT_SYMBOL(input_release_device);
|
|
|
|
/**
|
|
+ * input_filter_device - allow input events to be filtered from higher layers
|
|
+ * @handle: input handle that wants to filter the device
|
|
+ *
|
|
+ * When a device is filtered by an input handle all events generated by
|
|
+ * the device are to this handle. If the filter function returns true then
|
|
+ * the event is discarded rather than being passed to any other input handles,
|
|
+ * otherwise it is passed to them as normal. Grabs will be handled before
|
|
+ * filters, so a grabbed device will not deliver events to a filter function.
|
|
+ */
|
|
+int input_filter_device(struct input_handle *handle)
|
|
+{
|
|
+ struct input_dev *dev = handle->dev;
|
|
+ int retval;
|
|
+
|
|
+ retval = mutex_lock_interruptible(&dev->mutex);
|
|
+ if (retval)
|
|
+ return retval;
|
|
+
|
|
+ if (dev->filter) {
|
|
+ retval = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ rcu_assign_pointer(dev->filter, handle);
|
|
+ synchronize_rcu();
|
|
+
|
|
+ out:
|
|
+ mutex_unlock(&dev->mutex);
|
|
+ return retval;
|
|
+}
|
|
+EXPORT_SYMBOL(input_filter_device);
|
|
+
|
|
+/**
|
|
+ * input_unfilter_device - removes a filter from a device
|
|
+ * @handle: input handle that owns the device
|
|
+ *
|
|
+ * Removes the filter from a device so that other input handles can
|
|
+ * start receiving unfiltered input events. Upon release all handlers
|
|
+ * attached to the device have their start() method called so they
|
|
+ * have a change to synchronize device state with the rest of the
|
|
+ * system.
|
|
+ */
|
|
+void input_unfilter_device(struct input_handle *handle)
|
|
+{
|
|
+ struct input_dev *dev = handle->dev;
|
|
+
|
|
+ mutex_lock(&dev->mutex);
|
|
+ __input_release_device(handle, true);
|
|
+ mutex_unlock(&dev->mutex);
|
|
+}
|
|
+EXPORT_SYMBOL(input_unfilter_device);
|
|
+
|
|
+/**
|
|
* input_open_device - open input device
|
|
* @handle: handle through which device is being accessed
|
|
*
|
|
@@ -490,7 +553,9 @@ void input_close_device(struct input_handle *handle)
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
- __input_release_device(handle);
|
|
+ /* Release both grabs and filters */
|
|
+ __input_release_device(handle, false);
|
|
+ __input_release_device(handle, true);
|
|
|
|
if (!--dev->users && dev->close)
|
|
dev->close(dev);
|
|
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
|
|
index 3780994..25247be 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/input.h>
|
|
#include "../../firmware/dcdbas.h"
|
|
|
|
#define BRIGHTNESS_TOKEN 0x7d
|
|
@@ -214,6 +215,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 __init dell_setup_rfkill(void)
|
|
{
|
|
struct calling_interface_buffer buffer;
|
|
@@ -338,6 +349,90 @@ static struct backlight_ops dell_ops = {
|
|
.update_status = dell_send_intensity,
|
|
};
|
|
|
|
+static const struct input_device_id dell_input_ids[] = {
|
|
+ {
|
|
+ .bustype = 0x11,
|
|
+ .vendor = 0x01,
|
|
+ .product = 0x01,
|
|
+ .version = 0xab41,
|
|
+ .flags = INPUT_DEVICE_ID_MATCH_BUS |
|
|
+ INPUT_DEVICE_ID_MATCH_VENDOR |
|
|
+ INPUT_DEVICE_ID_MATCH_PRODUCT |
|
|
+ INPUT_DEVICE_ID_MATCH_VERSION
|
|
+ },
|
|
+ { },
|
|
+};
|
|
+
|
|
+static bool dell_input_filter(struct input_handle *handle, unsigned int type,
|
|
+ unsigned int code, int value)
|
|
+{
|
|
+ if (type == EV_KEY && code == KEY_WLAN && value == 1) {
|
|
+ dell_rfkill_update();
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dell_input_event(struct input_handle *handle, unsigned int type,
|
|
+ unsigned int code, int value)
|
|
+{
|
|
+}
|
|
+
|
|
+static int dell_input_connect(struct input_handler *handler,
|
|
+ struct input_dev *dev,
|
|
+ const struct input_device_id *id)
|
|
+{
|
|
+ struct input_handle *handle;
|
|
+ int error;
|
|
+
|
|
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
|
+ if (!handle)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ handle->dev = dev;
|
|
+ handle->handler = handler;
|
|
+ handle->name = "dell-laptop";
|
|
+
|
|
+ error = input_register_handle(handle);
|
|
+ if (error)
|
|
+ goto err_free_handle;
|
|
+
|
|
+ error = input_open_device(handle);
|
|
+ if (error)
|
|
+ goto err_unregister_handle;
|
|
+
|
|
+ error = input_filter_device(handle);
|
|
+ if (error)
|
|
+ goto err_close_handle;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_close_handle:
|
|
+ input_close_device(handle);
|
|
+err_unregister_handle:
|
|
+ input_unregister_handle(handle);
|
|
+err_free_handle:
|
|
+ kfree(handle);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static void dell_input_disconnect(struct input_handle *handle)
|
|
+{
|
|
+ input_close_device(handle);
|
|
+ input_unregister_handle(handle);
|
|
+ kfree(handle);
|
|
+}
|
|
+
|
|
+static struct input_handler dell_input_handler = {
|
|
+ .name = "dell-laptop",
|
|
+ .filter = dell_input_filter,
|
|
+ .event = dell_input_event,
|
|
+ .connect = dell_input_connect,
|
|
+ .disconnect = dell_input_disconnect,
|
|
+ .id_table = dell_input_ids,
|
|
+};
|
|
+
|
|
static int __init dell_init(void)
|
|
{
|
|
struct calling_interface_buffer buffer;
|
|
@@ -373,6 +468,10 @@ static int __init dell_init(void)
|
|
goto fail_rfkill;
|
|
}
|
|
|
|
+ if (input_register_handler(&dell_input_handler))
|
|
+ printk(KERN_INFO
|
|
+ "dell-laptop: Could not register input filter\n");
|
|
+
|
|
#ifdef CONFIG_ACPI
|
|
/* In the event of an ACPI backlight being available, don't
|
|
* register the platform controller.
|
|
@@ -426,6 +525,7 @@ static void __exit dell_exit(void)
|
|
{
|
|
backlight_device_unregister(dell_backlight_device);
|
|
dell_cleanup_rfkill();
|
|
+ input_unregister_handler(&dell_input_handler);
|
|
}
|
|
|
|
module_init(dell_init);
|
|
diff --git a/include/linux/input.h b/include/linux/input.h
|
|
index 7be8a65..7d49094 100644
|
|
--- a/include/linux/input.h
|
|
+++ b/include/linux/input.h
|
|
@@ -1127,6 +1127,7 @@ struct input_dev {
|
|
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
|
|
|
struct input_handle *grab;
|
|
+ struct input_handle *filter;
|
|
|
|
spinlock_t event_lock;
|
|
struct mutex mutex;
|
|
@@ -1227,6 +1228,7 @@ struct input_handler {
|
|
void *private;
|
|
|
|
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
|
+ bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
|
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
|
|
void (*disconnect)(struct input_handle *handle);
|
|
void (*start)(struct input_handle *handle);
|
|
@@ -1307,6 +1309,9 @@ void input_unregister_handle(struct input_handle *);
|
|
int input_grab_device(struct input_handle *);
|
|
void input_release_device(struct input_handle *);
|
|
|
|
+int input_filter_device(struct input_handle *);
|
|
+void input_unfilter_device(struct input_handle *);
|
|
+
|
|
int input_open_device(struct input_handle *);
|
|
void input_close_device(struct input_handle *);
|
|
|
|
--
|
|
1.6.5.2
|
|
|