aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/input.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2010-02-28 23:55:20 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-02-28 23:55:20 -0800
commit35858adbfca13678af99fb31618ef4428d6dedb0 (patch)
tree3336feaa61324486945816cb52c347733e7c0821 /drivers/input/input.c
parent197d4db752e67160d79fed09968c2140376a80a3 (diff)
parent4b70858ba8d4537daf782defebe5f2ff80ccef2b (diff)
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/input/input.c')
-rw-r--r--drivers/input/input.c90
1 files changed, 72 insertions, 18 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 86cb2d2196f..41168d5f8c1 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -87,12 +87,14 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
}
/*
- * Pass event through all open handles. This function is called with
+ * Pass event first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
+ struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock();
@@ -100,11 +102,25 @@ static void input_pass_event(struct input_dev *dev,
handle = rcu_dereference(dev->grab);
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);
+ else {
+ bool filtered = false;
+
+ list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
+ if (!handle->open)
+ continue;
+
+ handler = handle->handler;
+ if (!handler->filter) {
+ if (filtered)
+ break;
+
+ handler->event(handle, type, code, value);
+
+ } else if (handler->filter(handle, type, code, value))
+ filtered = true;
+ }
+ }
+
rcu_read_unlock();
}
@@ -615,12 +631,12 @@ static int input_default_setkeycode(struct input_dev *dev,
}
}
- clear_bit(old_keycode, dev->keybit);
- set_bit(keycode, dev->keybit);
+ __clear_bit(old_keycode, dev->keybit);
+ __set_bit(keycode, dev->keybit);
for (i = 0; i < dev->keycodemax; i++) {
if (input_fetch_keycode(dev, i) == old_keycode) {
- set_bit(old_keycode, dev->keybit);
+ __set_bit(old_keycode, dev->keybit);
break; /* Setting the bit twice is useless, so break */
}
}
@@ -678,6 +694,9 @@ int input_set_keycode(struct input_dev *dev, int scancode, int keycode)
if (retval)
goto out;
+ /* Make sure KEY_RESERVED did not get enabled. */
+ __clear_bit(KEY_RESERVED, dev->keybit);
+
/*
* Simulate keyup event if keycode is not present
* in the keymap anymore
@@ -705,12 +724,13 @@ EXPORT_SYMBOL(input_set_keycode);
if (i != BITS_TO_LONGS(max)) \
continue;
-static const struct input_device_id *input_match_device(const struct input_device_id *id,
+static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
+ const struct input_device_id *id;
int i;
- for (; id->flags || id->driver_info; id++) {
+ for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
@@ -738,7 +758,8 @@ static const struct input_device_id *input_match_device(const struct input_devic
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
- return id;
+ if (!handler->match || handler->match(handler, dev))
+ return id;
}
return NULL;
@@ -749,10 +770,7 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han
const struct input_device_id *id;
int error;
- if (handler->blacklist && input_match_device(handler->blacklist, dev))
- return -ENODEV;
-
- id = input_match_device(handler->id_table, dev);
+ id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
@@ -988,6 +1006,8 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v)
union input_seq_state *state = (union input_seq_state *)&seq->private;
seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
+ if (handler->filter)
+ seq_puts(seq, " (filter)");
if (handler->fops)
seq_printf(seq, " Minor=%d", handler->minor);
seq_putc(seq, '\n');
@@ -1551,6 +1571,25 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
}
EXPORT_SYMBOL(input_set_capability);
+#define INPUT_CLEANSE_BITMASK(dev, type, bits) \
+ do { \
+ if (!test_bit(EV_##type, dev->evbit)) \
+ memset(dev->bits##bit, 0, \
+ sizeof(dev->bits##bit)); \
+ } while (0)
+
+static void input_cleanse_bitmasks(struct input_dev *dev)
+{
+ INPUT_CLEANSE_BITMASK(dev, KEY, key);
+ INPUT_CLEANSE_BITMASK(dev, REL, rel);
+ INPUT_CLEANSE_BITMASK(dev, ABS, abs);
+ INPUT_CLEANSE_BITMASK(dev, MSC, msc);
+ INPUT_CLEANSE_BITMASK(dev, LED, led);
+ INPUT_CLEANSE_BITMASK(dev, SND, snd);
+ INPUT_CLEANSE_BITMASK(dev, FF, ff);
+ INPUT_CLEANSE_BITMASK(dev, SW, sw);
+}
+
/**
* input_register_device - register device with input core
* @dev: device to be registered
@@ -1570,13 +1609,19 @@ int input_register_device(struct input_dev *dev)
const char *path;
int error;
+ /* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
+ /* KEY_RESERVED is not supposed to be transmitted to userspace. */
+ __clear_bit(KEY_RESERVED, dev->keybit);
+
+ /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
+ input_cleanse_bitmasks(dev);
+
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
-
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
@@ -1776,7 +1821,16 @@ int input_register_handle(struct input_handle *handle)
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
- list_add_tail_rcu(&handle->d_node, &dev->h_list);
+
+ /*
+ * Filters go to the head of the list, normal handlers
+ * to the tail.
+ */
+ if (handler->filter)
+ list_add_rcu(&handle->d_node, &dev->h_list);
+ else
+ list_add_tail_rcu(&handle->d_node, &dev->h_list);
+
mutex_unlock(&dev->mutex);
/*