Input: deactivate MT slots when inhibiting or suspending devices

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>
This commit is contained in:
Angela Czubak 2022-07-20 11:15:28 -07:00 committed by Dmitry Torokhov
parent 59b7a5af8e
commit ebfa0043c9
3 changed files with 80 additions and 14 deletions

View File

@ -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 */

View 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;

View 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);