diff mbox

Input - mt: Fix input_mt_get_slot_by_key

Message ID 1422982530-4906-1-git-send-email-benjamin.tissoires@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Benjamin Tissoires Feb. 3, 2015, 4:55 p.m. UTC
The case occured recently with a touchscreen using twice a slot during a
single EV_SYN event:

E: 0.288415 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
E: 0.296207 0003 002f 0000      # EV_ABS / ABS_MT_SLOT          0
E: 0.296207 0003 0039 -001      # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.296207 0003 002f 0001      # EV_ABS / ABS_MT_SLOT          1
E: 0.296207 0003 0035 0908      # EV_ABS / ABS_MT_POSITION_X    908
E: 0.296207 0003 0036 1062      # EV_ABS / ABS_MT_POSITION_Y    1062
E: 0.296207 0003 002f 0000      # EV_ABS / ABS_MT_SLOT          0
E: 0.296207 0003 0039 8787      # EV_ABS / ABS_MT_TRACKING_ID   8787
E: 0.296207 0003 0035 1566      # EV_ABS / ABS_MT_POSITION_X    1566
E: 0.296207 0003 0036 0861      # EV_ABS / ABS_MT_POSITION_Y    861
E: 0.296207 0003 0000 0908      # EV_ABS / ABS_X                908
E: 0.296207 0003 0001 1062      # EV_ABS / ABS_Y                1062
E: 0.296207 0000 0000 0000      # ------------ SYN_REPORT (0) ----------

This occured because while having already slots 0 and 1 assigned, the
touchscreen sent:

0.293377 Tip Switch: 0 | Contact Id: 0 | X:  539 | Y: 1960 | Contact Count: 3
0.294783 Tip Switch: 1 | Contact Id: 1 | X:  908 | Y: 1062 | Contact Count: 0
0.296187 Tip Switch: 1 | Contact Id: 2 | X: 1566 | Y:  861 | Contact Count: 0

Slot 0 is released correclty, but when we look for Contact ID 2, the slot
0 is then picked up again because it is marked as inactive (trackingID < 0).

This is wrong, and we should not reuse a slot in the same frame.
The test should also check for input_mt_is_used().

In addition, we need to initialize mt->frame to an other value than 0.
With mt->frame being 0, all slots are tagged as being used, and so
input_mt_get_slot_by_key() would return -1 for all requests.

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=88903

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---

Hi Dmitry,

I wonder if this one should go to stable as well.

Cheers,
Benjamin

 drivers/input/input-mt.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

Comments

Henrik Rydberg Feb. 3, 2015, 7:30 p.m. UTC | #1
Hi Benjamin,

> Slot 0 is released correclty, but when we look for Contact ID 2, the slot
> 0 is then picked up again because it is marked as inactive (trackingID < 0).
> 
> This is wrong, and we should not reuse a slot in the same frame.
> The test should also check for input_mt_is_used().

Good catch! However...

> @@ -453,7 +456,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key)
>  			return s - mt->slots;
>  
>  	for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
> -		if (!input_mt_is_active(s)) {
> +		if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
>  			s->key = key;
>  			return s - mt->slots;
>  		}
> 

Here, you are changing the preconditions of the function without explicit
reference to all its users. For one, it is now assumed that input_mt_is_used()
is up-to-date, which requires either input_mt_drop_unused() or
input_mt_sync_frame(), which does not seem to be true for all users of
input_mt_get_slot_by_key(). After a couple of iterations with
input_mt_report_slot_state() in those drivers, input_mt_is_used() will be true
for all slots, and the driver will stop working.

How about defering the deassignments until the end of the loop instead? That
would remove possible reuse.

Thanks,
Henrik

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Benjamin Tissoires Feb. 4, 2015, 3:15 p.m. UTC | #2
On Feb 03 2015 or thereabouts, Henrik Rydberg wrote:
> Hi Benjamin,
> 
> > Slot 0 is released correclty, but when we look for Contact ID 2, the slot
> > 0 is then picked up again because it is marked as inactive (trackingID < 0).
> > 
> > This is wrong, and we should not reuse a slot in the same frame.
> > The test should also check for input_mt_is_used().
> 
> Good catch! However...
> 
> > @@ -453,7 +456,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key)
> >  			return s - mt->slots;
> >  
> >  	for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
> > -		if (!input_mt_is_active(s)) {
> > +		if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
> >  			s->key = key;
> >  			return s - mt->slots;
> >  		}
> > 
> 
> Here, you are changing the preconditions of the function without explicit
> reference to all its users. For one, it is now assumed that input_mt_is_used()
> is up-to-date, which requires either input_mt_drop_unused() or
> input_mt_sync_frame(), which does not seem to be true for all users of
> input_mt_get_slot_by_key(). After a couple of iterations with
> input_mt_report_slot_state() in those drivers, input_mt_is_used() will be true
> for all slots, and the driver will stop working.

Hmm... You are right. Thanks for pointing this out. This is something I
should have definitively thought.

So, on the kernel tree, I have only 5 drivers matching
input_mt_get_slot_by_key:

$ grep -lIr input_mt_get_slot_by_key | grep -E "\.[ch]$"
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-multitouch.c
drivers/hid/wacom_wac.c
drivers/input/input-mt.c
drivers/input/touchscreen/sur40.c
drivers/input/touchscreen/pixcir_i2c_ts.c
include/linux/input/mt.h

On these five drivers, 4 are using properly input_mt_sync_frame, and
only one is not. This one is the Wacom one :)

For most of the touch devices (except those that are auto-detected and
handled), the code just calls input_mt_report_pointer_emulation()
instead of input_mt_sync_frame().

Changing the 5 calls in this driver and adding a requirement to call
input_mt_sync_frame() when using input_mt_get_slot_by_key() does not
seem terrible.

> 
> How about defering the deassignments until the end of the loop instead? That
> would remove possible reuse.
> 

I don't think this is feasible. There is no loop in the slot assignment
case. The state is stored by the input subsystem, and when we process
a touch, all the previous touches have already been processed and forwarded
to the user space.

We *could* defer the slot release before sending the EV_SYN event, but
that would force users to call input_mt_sync_frame() anyway to release
the slots. So we would get an even greater requirement, because now, all
users of the slotted protocol would have to call input_mt_sync_frame().

Cheers,
Benjamin
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index cb150a1..e02fd92 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -88,10 +88,13 @@  int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
 			goto err_mem;
 	}
 
-	/* Mark slots as 'unused' */
+	/* Mark slots as 'inactive' */
 	for (i = 0; i < num_slots; i++)
 		input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
 
+	/* Mark slots as 'unused' */
+	mt->frame = 1;
+
 	dev->mt = mt;
 	return 0;
 err_mem:
@@ -453,7 +456,7 @@  int input_mt_get_slot_by_key(struct input_dev *dev, int key)
 			return s - mt->slots;
 
 	for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
-		if (!input_mt_is_active(s)) {
+		if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
 			s->key = key;
 			return s - mt->slots;
 		}