Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f4f37c8ec7d2491c8885c890ba74254b9adfbeee
Commit:     f4f37c8ec7d2491c8885c890ba74254b9adfbeee
Parent:     554101e3e5f396b987c846332863a3fcdc87b1d6
Author:     Dmitry Torokhov <[EMAIL PROTECTED]>
AuthorDate: Sun Nov 4 00:41:12 2007 -0400
Committer:  Dmitry Torokhov <[EMAIL PROTECTED]>
CommitDate: Mon Jan 21 01:11:06 2008 -0500

    Input: Add proper locking when changing device's keymap
    
    Take dev->event_lock to make sure that we don't race with input_event() and
    also force key up event when removing a key from keymap table.
    
    Signed-off-by: Dmitry Torokhov <[EMAIL PROTECTED]>
---
 drivers/char/keyboard.c |    4 +-
 drivers/input/evdev.c   |    6 ++--
 drivers/input/input.c   |   78 +++++++++++++++++++++++++++++++++++++++++++---
 include/linux/input.h   |    3 ++
 4 files changed, 80 insertions(+), 11 deletions(-)

diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index fc54d23..5218d0d 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -194,7 +194,7 @@ int getkeycode(unsigned int scancode)
        int error = -ENODEV;
 
        list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
-               error = handle->dev->getkeycode(handle->dev, scancode, 
&keycode);
+               error = input_get_keycode(handle->dev, scancode, &keycode);
                if (!error)
                        return keycode;
        }
@@ -208,7 +208,7 @@ int setkeycode(unsigned int scancode, unsigned int keycode)
        int error = -ENODEV;
 
        list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
-               error = handle->dev->setkeycode(handle->dev, scancode, keycode);
+               error = input_set_keycode(handle->dev, scancode, keycode);
                if (!error)
                        break;
        }
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e5b4e9b..0727b0a 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -617,7 +617,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int 
cmd,
                if (get_user(t, ip))
                        return -EFAULT;
 
-               error = dev->getkeycode(dev, t, &v);
+               error = input_get_keycode(dev, t, &v);
                if (error)
                        return error;
 
@@ -630,7 +630,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int 
cmd,
                if (get_user(t, ip) || get_user(v, ip + 1))
                        return -EFAULT;
 
-               return dev->setkeycode(dev, t, v);
+               return input_set_keycode(dev, t, v);
 
        case EVIOCSFF:
                if (copy_from_user(&effect, p, sizeof(effect)))
@@ -683,7 +683,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int 
cmd,
                                case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  
break;
                                case EV_SW:  bits = dev->swbit;  len = SW_MAX;  
break;
                                default: return -EINVAL;
-                       }
+                               }
                                return bits_to_user(bits, len, _IOC_SIZE(cmd), 
p, compat_mode);
                        }
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index a0be978..e1729e1 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -493,7 +493,7 @@ static void input_disconnect_device(struct input_dev *dev)
        if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
                for (code = 0; code <= KEY_MAX; code++) {
                        if (is_event_supported(code, dev->keybit, KEY_MAX) &&
-                           test_bit(code, dev->key)) {
+                           __test_and_clear_bit(code, dev->key)) {
                                input_pass_event(dev, EV_KEY, code, 0);
                        }
                }
@@ -526,7 +526,7 @@ static int input_default_getkeycode(struct input_dev *dev,
        if (!dev->keycodesize)
                return -EINVAL;
 
-       if (scancode < 0 || scancode >= dev->keycodemax)
+       if (scancode >= dev->keycodemax)
                return -EINVAL;
 
        *keycode = input_fetch_keycode(dev, scancode);
@@ -540,10 +540,7 @@ static int input_default_setkeycode(struct input_dev *dev,
        int old_keycode;
        int i;
 
-       if (scancode < 0 || scancode >= dev->keycodemax)
-               return -EINVAL;
-
-       if (keycode < 0 || keycode > KEY_MAX)
+       if (scancode >= dev->keycodemax)
                return -EINVAL;
 
        if (!dev->keycodesize)
@@ -586,6 +583,75 @@ static int input_default_setkeycode(struct input_dev *dev,
        return 0;
 }
 
+/**
+ * input_get_keycode - retrieve keycode currently mapped to a given scancode
+ * @dev: input device which keymap is being queried
+ * @scancode: scancode (or its equivalent for device in question) for which
+ *     keycode is needed
+ * @keycode: result
+ *
+ * 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)
+{
+       if (scancode < 0)
+               return -EINVAL;
+
+       return dev->getkeycode(dev, scancode, keycode);
+}
+EXPORT_SYMBOL(input_get_keycode);
+
+/**
+ * input_get_keycode - assign new keycode to a given scancode
+ * @dev: input device which keymap is being updated
+ * @scancode: scancode (or its equivalent for device in question)
+ * @keycode: new keycode to be assigned to the scancode
+ *
+ * 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)
+{
+       unsigned long flags;
+       int old_keycode;
+       int retval;
+
+       if (scancode < 0)
+               return -EINVAL;
+
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       retval = dev->getkeycode(dev, scancode, &old_keycode);
+       if (retval)
+               goto out;
+
+       retval = dev->setkeycode(dev, scancode, keycode);
+       if (retval)
+               goto out;
+
+       /*
+        * Simulate keyup event if keycode is not present
+        * in the keymap anymore
+        */
+       if (test_bit(EV_KEY, dev->evbit) &&
+           !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
+           __test_and_clear_bit(old_keycode, dev->key)) {
+
+               input_pass_event(dev, EV_KEY, old_keycode, 0);
+               if (dev->sync)
+                       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+       }
+
+ out:
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return retval;
+}
+EXPORT_SYMBOL(input_set_keycode);
 
 #define MATCH_BIT(bit, max) \
                for (i = 0; i < BITS_TO_LONGS(max); i++) \
diff --git a/include/linux/input.h b/include/linux/input.h
index 2075d6d..9a963fe 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1309,6 +1309,9 @@ static inline void input_set_abs_params(struct input_dev 
*dev, int axis, int min
        dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
 }
 
+int input_get_keycode(struct input_dev *dev, int scancode, int *keycode);
+int input_set_keycode(struct input_dev *dev, int scancode, int keycode);
+
 extern struct class input_class;
 
 /**
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to