Skip to content

Commit

Permalink
Input: deactivate MT slots when inhibiting or suspending devices
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Angela Czubak authored and Dmitry Torokhov committed Jul 20, 2022
1 parent 59b7a5a commit ebfa004
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 14 deletions.
16 changes: 16 additions & 0 deletions drivers/input/input-core-private.h
Original file line number Diff line number Diff line change
@@ -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 */
48 changes: 44 additions & 4 deletions drivers/input/input-mt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -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
Expand All @@ -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;
Expand Down
30 changes: 20 additions & 10 deletions drivers/input/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>");
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

/*
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit ebfa004

Please sign in to comment.