summaryrefslogtreecommitdiffstats
path: root/drivers/input/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/input.c')
-rw-r--r--drivers/input/input.c225
1 files changed, 171 insertions, 54 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 5c16001959c..e2aad0a5182 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -24,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include <linux/smp_lock.h>
+#include "input-compat.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
@@ -45,6 +46,7 @@ static unsigned int input_abs_bypass_init_data[] __initdata = {
ABS_MT_TOOL_TYPE,
ABS_MT_BLOB_ID,
ABS_MT_TRACKING_ID,
+ ABS_MT_PRESSURE,
0
};
static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)];
@@ -85,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();
@@ -98,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();
}
@@ -296,9 +314,15 @@ static void input_handle_event(struct input_dev *dev,
* @value: value of the event
*
* This function should be used by drivers implementing various input
- * devices. See also input_inject_event().
+ * devices to report input events. See also input_inject_event().
+ *
+ * NOTE: input_event() may be safely used right after input device was
+ * allocated with input_allocate_device(), even before it is registered
+ * with input_register_device(), but the event will not reach any of the
+ * input handlers. Such early invocation of input_event() may be used
+ * to 'seed' initial state of a switch or initial position of absolute
+ * axis, etc.
*/
-
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
@@ -558,7 +582,8 @@ static int input_fetch_keycode(struct input_dev *dev, int scancode)
}
static int input_default_getkeycode(struct input_dev *dev,
- int scancode, int *keycode)
+ unsigned int scancode,
+ unsigned int *keycode)
{
if (!dev->keycodesize)
return -EINVAL;
@@ -572,7 +597,8 @@ static int input_default_getkeycode(struct input_dev *dev,
}
static int input_default_setkeycode(struct input_dev *dev,
- int scancode, int keycode)
+ unsigned int scancode,
+ unsigned int keycode)
{
int old_keycode;
int i;
@@ -607,12 +633,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 */
}
}
@@ -630,11 +656,9 @@ static int input_default_setkeycode(struct input_dev *dev,
* This function should be called by anyone interested in retrieving current
* keymap. Presently keyboard and evdev handlers use it.
*/
-int input_get_keycode(struct input_dev *dev, int scancode, int *keycode)
+int input_get_keycode(struct input_dev *dev,
+ unsigned int scancode, unsigned int *keycode)
{
- if (scancode < 0)
- return -EINVAL;
-
return dev->getkeycode(dev, scancode, keycode);
}
EXPORT_SYMBOL(input_get_keycode);
@@ -648,16 +672,14 @@ EXPORT_SYMBOL(input_get_keycode);
* This function should be called by anyone needing to update current
* keymap. Presently keyboard and evdev handlers use it.
*/
-int input_set_keycode(struct input_dev *dev, int scancode, int keycode)
+int input_set_keycode(struct input_dev *dev,
+ unsigned int scancode, unsigned int keycode)
{
unsigned long flags;
int old_keycode;
int retval;
- if (scancode < 0)
- return -EINVAL;
-
- if (keycode < 0 || keycode > KEY_MAX)
+ if (keycode > KEY_MAX)
return -EINVAL;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -670,6 +692,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
@@ -697,12 +722,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)
@@ -730,7 +756,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;
@@ -741,10 +768,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;
@@ -758,6 +782,40 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han
return error;
}
+#ifdef CONFIG_COMPAT
+
+static int input_bits_to_string(char *buf, int buf_size,
+ unsigned long bits, bool skip_empty)
+{
+ int len = 0;
+
+ if (INPUT_COMPAT_TEST) {
+ u32 dword = bits >> 32;
+ if (dword || !skip_empty)
+ len += snprintf(buf, buf_size, "%x ", dword);
+
+ dword = bits & 0xffffffffUL;
+ if (dword || !skip_empty || len)
+ len += snprintf(buf + len, max(buf_size - len, 0),
+ "%x", dword);
+ } else {
+ if (bits || !skip_empty)
+ len += snprintf(buf, buf_size, "%lx", bits);
+ }
+
+ return len;
+}
+
+#else /* !CONFIG_COMPAT */
+
+static int input_bits_to_string(char *buf, int buf_size,
+ unsigned long bits, bool skip_empty)
+{
+ return bits || !skip_empty ?
+ snprintf(buf, buf_size, "%lx", bits) : 0;
+}
+
+#endif
#ifdef CONFIG_PROC_FS
@@ -826,14 +884,25 @@ static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
unsigned long *bitmap, int max)
{
int i;
-
- for (i = BITS_TO_LONGS(max) - 1; i > 0; i--)
- if (bitmap[i])
- break;
+ bool skip_empty = true;
+ char buf[18];
seq_printf(seq, "B: %s=", name);
- for (; i >= 0; i--)
- seq_printf(seq, "%lx%s", bitmap[i], i > 0 ? " " : "");
+
+ for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) {
+ if (input_bits_to_string(buf, sizeof(buf),
+ bitmap[i], skip_empty)) {
+ skip_empty = false;
+ seq_printf(seq, "%s%s", buf, i > 0 ? " " : "");
+ }
+ }
+
+ /*
+ * If no output was produced print a single 0.
+ */
+ if (skip_empty)
+ seq_puts(seq, "0");
+
seq_putc(seq, '\n');
}
@@ -935,6 +1004,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');
@@ -1122,14 +1193,23 @@ static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
{
int i;
int len = 0;
+ bool skip_empty = true;
+
+ for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) {
+ len += input_bits_to_string(buf + len, max(buf_size - len, 0),
+ bitmap[i], skip_empty);
+ if (len) {
+ skip_empty = false;
+ if (i > 0)
+ len += snprintf(buf + len, max(buf_size - len, 0), " ");
+ }
+ }
- for (i = BITS_TO_LONGS(max) - 1; i > 0; i--)
- if (bitmap[i])
- break;
-
- for (; i >= 0; i--)
- len += snprintf(buf + len, max(buf_size - len, 0),
- "%lx%s", bitmap[i], i > 0 ? " " : "");
+ /*
+ * If no output was produced print a single 0.
+ */
+ if (len == 0)
+ len = snprintf(buf, buf_size, "%d", 0);
if (add_cr)
len += snprintf(buf + len, max(buf_size - len, 0), "\n");
@@ -1144,7 +1224,8 @@ static ssize_t input_dev_show_cap_##bm(struct device *dev, \
{ \
struct input_dev *input_dev = to_input_dev(dev); \
int len = input_print_bitmap(buf, PAGE_SIZE, \
- input_dev->bm##bit, ev##_MAX, 1); \
+ input_dev->bm##bit, ev##_MAX, \
+ true); \
return min_t(int, len, PAGE_SIZE); \
} \
static DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL)
@@ -1208,7 +1289,7 @@ static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
len = input_print_bitmap(&env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen,
- bitmap, max, 0);
+ bitmap, max, false);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
@@ -1488,6 +1569,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
@@ -1507,13 +1607,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;
@@ -1713,7 +1819,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);
/*
@@ -1764,35 +1879,37 @@ static int input_open_file(struct inode *inode, struct file *file)
const struct file_operations *old_fops, *new_fops = NULL;
int err;
- lock_kernel();
+ err = mutex_lock_interruptible(&input_mutex);
+ if (err)
+ return err;
+
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];
- if (!handler || !(new_fops = fops_get(handler->fops))) {
- err = -ENODEV;
- goto out;
- }
+ if (handler)
+ new_fops = fops_get(handler->fops);
+
+ mutex_unlock(&input_mutex);
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
- if (!new_fops->open) {
+ if (!new_fops || !new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
+
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
-
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
- unlock_kernel();
return err;
}