Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / input / input.c
index 6c161e2..9c79bd5 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/input.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/random.h>
 #include <linux/major.h>
 #include <linux/proc_fs.h>
@@ -46,6 +47,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)];
@@ -86,12 +88,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();
@@ -99,11 +103,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();
 }
 
@@ -565,7 +583,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;
@@ -579,7 +598,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;
@@ -637,12 +657,17 @@ 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;
+       unsigned long flags;
+       int retval;
 
-       return dev->getkeycode(dev, scancode, keycode);
+       spin_lock_irqsave(&dev->event_lock, flags);
+       retval = dev->getkeycode(dev, scancode, keycode);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return retval;
 }
 EXPORT_SYMBOL(input_get_keycode);
 
@@ -655,16 +680,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);
@@ -707,12 +730,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)
@@ -740,7 +764,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;
@@ -751,10 +776,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;
 
@@ -990,6 +1012,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');
@@ -1803,7 +1827,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);
 
        /*
@@ -1854,35 +1887,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;
 }