@@ -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);
@@ -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 *);