Input: deactivate MT slots when inhibiting or suspending devices
authorAngela Czubak <acz@semihalf.com>
Wed, 20 Jul 2022 18:15:28 +0000 (11:15 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 20 Jul 2022 18:35:13 +0000 (11:35 -0700)
When inhibiting or suspending a device we are sending release events for
all currently held keys and buttons, however we retain active MT slot
state, which causes issues with gesture recognition when we resume or
uninhibit.

Let's fix it by introducing, in addition to input_dev_release_keys(),
nput_mt_release_slots() that will deactivate all currently active slots.

Signed-off-by: Angela Czubak <acz@semihalf.com>
Link: https://lore.kernel.org/r/20220718151715.1052842-3-acz@semihalf.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/input-core-private.h [new file with mode: 0644]
drivers/input/input-mt.c
drivers/input/input.c

diff --git a/drivers/input/input-core-private.h b/drivers/input/input-core-private.h
new file mode 100644 (file)
index 0000000..116834c
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _INPUT_CORE_PRIVATE_H
+#define _INPUT_CORE_PRIVATE_H
+
+/*
+ * Functions and definitions that are private to input core,
+ * should not be used by input drivers or handlers.
+ */
+
+struct input_dev;
+
+void input_mt_release_slots(struct input_dev *dev);
+void input_handle_event(struct input_dev *dev,
+                       unsigned int type, unsigned int code, int value);
+
+#endif /* _INPUT_CORE_PRIVATE_H */
index 44fe6f2f063ce45cc7d6830881e2cdf01d30495b..14b53dac1253bfed5ad2dbd9b1dc22cdb9b80c28 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/input/mt.h>
 #include <linux/export.h>
 #include <linux/slab.h>
+#include "input-core-private.h"
 
 #define TRKID_SGN      ((TRKID_MAX + 1) >> 1)
 
@@ -259,10 +260,13 @@ static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt)
 {
        int i;
 
+       lockdep_assert_held(&dev->event_lock);
+
        for (i = 0; i < mt->num_slots; i++) {
-               if (!input_mt_is_used(mt, &mt->slots[i])) {
-                       input_mt_slot(dev, i);
-                       input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+               if (input_mt_is_active(&mt->slots[i]) &&
+                   !input_mt_is_used(mt, &mt->slots[i])) {
+                       input_handle_event(dev, EV_ABS, ABS_MT_SLOT, i);
+                       input_handle_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
                }
        }
 }
@@ -278,12 +282,43 @@ void input_mt_drop_unused(struct input_dev *dev)
        struct input_mt *mt = dev->mt;
 
        if (mt) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&dev->event_lock, flags);
+
                __input_mt_drop_unused(dev, mt);
                mt->frame++;
+
+               spin_unlock_irqrestore(&dev->event_lock, flags);
        }
 }
 EXPORT_SYMBOL(input_mt_drop_unused);
 
+/**
+ * input_mt_release_slots() - Deactivate all slots
+ * @dev: input device with allocated MT slots
+ *
+ * Lift all active slots.
+ */
+void input_mt_release_slots(struct input_dev *dev)
+{
+       struct input_mt *mt = dev->mt;
+
+       lockdep_assert_held(&dev->event_lock);
+
+       if (mt) {
+               /* This will effectively mark all slots unused. */
+               mt->frame++;
+
+               __input_mt_drop_unused(dev, mt);
+
+               if (test_bit(ABS_PRESSURE, dev->absbit))
+                       input_handle_event(dev, EV_ABS, ABS_PRESSURE, 0);
+
+               mt->frame++;
+       }
+}
+
 /**
  * input_mt_sync_frame() - synchronize mt frame
  * @dev: input device with allocated MT slots
@@ -300,8 +335,13 @@ void input_mt_sync_frame(struct input_dev *dev)
        if (!mt)
                return;
 
-       if (mt->flags & INPUT_MT_DROP_UNUSED)
+       if (mt->flags & INPUT_MT_DROP_UNUSED) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&dev->event_lock, flags);
                __input_mt_drop_unused(dev, mt);
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
 
        if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT))
                use_count = true;
index 5e75b175b59497ee400752c81f63f17737bea3e6..ebb2b7f0f8ff46ec72943e05c94f442aea098e0a 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mutex.h>
 #include <linux/rcupdate.h>
 #include "input-compat.h"
+#include "input-core-private.h"
 #include "input-poller.h"
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
@@ -142,6 +143,8 @@ static void input_pass_values(struct input_dev *dev,
        struct input_handle *handle;
        struct input_value *v;
 
+       lockdep_assert_held(&dev->event_lock);
+
        if (!count)
                return;
 
@@ -384,8 +387,8 @@ static void input_event_dispose(struct input_dev *dev, int disposition,
        }
 }
 
-static void input_handle_event(struct input_dev *dev,
-                              unsigned int type, unsigned int code, int value)
+void input_handle_event(struct input_dev *dev,
+                       unsigned int type, unsigned int code, int value)
 {
        int disposition;
 
@@ -722,20 +725,21 @@ EXPORT_SYMBOL(input_close_device);
  * Simulate keyup events for all keys that are marked as pressed.
  * The function must be called with dev->event_lock held.
  */
-static void input_dev_release_keys(struct input_dev *dev)
+static bool input_dev_release_keys(struct input_dev *dev)
 {
        bool need_sync = false;
        int code;
 
+       lockdep_assert_held(&dev->event_lock);
+
        if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
                for_each_set_bit(code, dev->key, KEY_CNT) {
                        input_handle_event(dev, EV_KEY, code, 0);
                        need_sync = true;
                }
-
-               if (need_sync)
-                       input_handle_event(dev, EV_SYN, SYN_REPORT, 1);
        }
+
+       return need_sync;
 }
 
 /*
@@ -762,7 +766,8 @@ static void input_disconnect_device(struct input_dev *dev)
         * generate events even after we done here but they will not
         * reach any handlers.
         */
-       input_dev_release_keys(dev);
+       if (input_dev_release_keys(dev))
+               input_handle_event(dev, EV_SYN, SYN_REPORT, 1);
 
        list_for_each_entry(handle, &dev->h_list, d_node)
                handle->open = 0;
@@ -1757,7 +1762,8 @@ void input_reset_device(struct input_dev *dev)
        spin_lock_irqsave(&dev->event_lock, flags);
 
        input_dev_toggle(dev, true);
-       input_dev_release_keys(dev);
+       if (input_dev_release_keys(dev))
+               input_handle_event(dev, EV_SYN, SYN_REPORT, 1);
 
        spin_unlock_irqrestore(&dev->event_lock, flags);
        mutex_unlock(&dev->mutex);
@@ -1779,7 +1785,9 @@ static int input_inhibit_device(struct input_dev *dev)
        }
 
        spin_lock_irq(&dev->event_lock);
+       input_mt_release_slots(dev);
        input_dev_release_keys(dev);
+       input_handle_event(dev, EV_SYN, SYN_REPORT, 1);
        input_dev_toggle(dev, false);
        spin_unlock_irq(&dev->event_lock);
 
@@ -1830,7 +1838,8 @@ static int input_dev_suspend(struct device *dev)
         * Keys that are pressed now are unlikely to be
         * still pressed when we resume.
         */
-       input_dev_release_keys(input_dev);
+       if (input_dev_release_keys(input_dev))
+               input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1);
 
        /* Turn off LEDs and sounds, if any are active. */
        input_dev_toggle(input_dev, false);
@@ -1864,7 +1873,8 @@ static int input_dev_freeze(struct device *dev)
         * Keys that are pressed now are unlikely to be
         * still pressed when we resume.
         */
-       input_dev_release_keys(input_dev);
+       if (input_dev_release_keys(input_dev))
+               input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1);
 
        spin_unlock_irq(&input_dev->event_lock);