From: Matthew Garrett <mjg@redhat.com>
To: linux-acpi@vger.kernel.org
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com,
Matthew Garrett <mjg@redhat.com>
Subject: [PATCH 1/2] input: Add support for filtering input events
Date: Fri, 31 Jul 2009 03:26:46 +0100 [thread overview]
Message-ID: <1249007207-27392-1-git-send-email-mjg@redhat.com> (raw)
Devices occasionally use the keyboard controller to send hardware
notifications that should be handled by the kernel rather than userspace.
These can be handled in kernel by registering an input handler, but the
event will still bubble up to userspace where it may cause confusion. This
patch adds support for the kernel to register filters, allowing it to
indicate that the event should be consumed by the hardware-specific driver
rather than passed to other input handlers. The assumption is that, as this
is hardware specific, there should be no need to have more than one filter -
similarly, if an input device is grabbed, we assume that people know what
they're doing and don't pass it to the filter.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
drivers/input/input.c | 91 ++++++++++++++++++++++++++++++++++++++++++-------
include/linux/input.h | 5 +++
2 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 7c237e6..80f1e48 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -88,19 +88,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();
}
@@ -375,12 +382,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();
@@ -404,12 +414,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
*
@@ -482,7 +545,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/include/linux/input.h b/include/linux/input.h
index 8b3bc3e..e28f116 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1118,6 +1118,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;
@@ -1218,6 +1219,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);
@@ -1295,6 +1297,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.2.5
next reply other threads:[~2009-07-31 2:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-07-31 2:26 Matthew Garrett [this message]
2009-07-31 2:26 ` [PATCH 2/2] dell-laptop: Trigger rfkill updates on wifi toggle switch press Matthew Garrett
2009-08-03 17:04 ` [PATCH 1/2] input: Add support for filtering input events Dmitry Torokhov
2009-08-04 10:01 ` Matthew Garrett
2009-08-05 4:49 ` Dmitry Torokhov
2009-08-06 12:49 ` Henrique de Moraes Holschuh
2009-08-06 14:48 ` Dmitry Torokhov
2009-08-06 19:18 ` Henrique de Moraes Holschuh
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1249007207-27392-1-git-send-email-mjg@redhat.com \
--to=mjg@redhat.com \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-input@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).