[libevdev/f20] Backport the SYN_DROPPED fixes from 1.1, too important not to have
Peter Hutterer
whot at fedoraproject.org
Wed Mar 19 06:29:58 UTC 2014
commit 229f1a97a594e4f25a948c7eaa141223d27c07c3
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date: Wed Mar 19 16:08:33 2014 +1000
Backport the SYN_DROPPED fixes from 1.1, too important not to have
0001-Revert-Drop-deprecated-functions.patch | 10 +-
...t-Drop-some-leftover-deprecated-constants.patch | 9 +-
0003-Plug-a-memory-leak-for-name-uniq-phys.patch | 43 +++
...leaks-when-failing-to-create-a-uinput-dev.patch | 61 ++++
0005-Don-t-sync-past-MAX_SLOTS-slots.patch | 52 ++++
0006-Add-unlikely-macro.patch | 30 ++
0007-Dynamically-allocate-the-slot-values.patch | 164 ++++++++++
...-Reduce-memory-requirement-for-MT-syncing.patch | 144 +++++++++
0009-Increase-MAX_SLOTS-to-60.patch | 64 ++++
...-Cap-slot-values-to-the-announced-maximum.patch | 196 ++++++++++++
...unctions-macros-between-libevdev-int.h-an.patch | 209 +++++++++++++
...n-t-sync-the-MT-state-for-fake-MT-devices.patch | 170 +++++++++++
...ra-ABS_MT_SLOT-event-to-sync-the-client-u.patch | 312 ++++++++++++++++++++
...king-ID-changes-during-SYN_DROPPED-termin.patch | 254 ++++++++++++++++
libevdev.spec | 38 ++-
15 files changed, 1742 insertions(+), 14 deletions(-)
---
diff --git a/0001-Revert-Drop-deprecated-functions.patch b/0001-Revert-Drop-deprecated-functions.patch
index e4857e7..3303d0e 100644
--- a/0001-Revert-Drop-deprecated-functions.patch
+++ b/0001-Revert-Drop-deprecated-functions.patch
@@ -1,7 +1,7 @@
-From 51bf0ead89df5c1ddaa22d2f7fc0e04cedbdc590 Mon Sep 17 00:00:00 2001
+From c1db26fba41dbcffe6b392da85f8f015d4340e3d Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam at redhat.com>
Date: Thu, 26 Dec 2013 18:58:23 -0800
-Subject: [PATCH] Revert "Drop deprecated functions"
+Subject: [PATCH libevdev 01/14] Revert "Drop deprecated functions"
This reverts commit 7a38f4abc3ed1df368d5bad32a1d72559acdc234. It is incorrect and dangerous to drop functions without an soname bump. This broke GNOME.
---
@@ -118,10 +118,10 @@ index 8a37204..02e4f78 100644
libevdev_event_type_get_max(unsigned int type)
{
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
-index 37ca2f4..c89f4ad 100644
+index 44aa3a6..1c91c82 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
-@@ -1642,6 +1642,36 @@ int libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period);
+@@ -1639,6 +1639,36 @@ int libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period);
#define LIBEVDEV_DEPRECATED
#endif
@@ -159,5 +159,5 @@ index 37ca2f4..c89f4ad 100644
}
#endif
--
-1.8.5.2
+1.8.5.3
diff --git a/0001-Revert-Drop-some-leftover-deprecated-constants.patch b/0002-Revert-Drop-some-leftover-deprecated-constants.patch
similarity index 82%
rename from 0001-Revert-Drop-some-leftover-deprecated-constants.patch
rename to 0002-Revert-Drop-some-leftover-deprecated-constants.patch
index 250be85..03d1a92 100644
--- a/0001-Revert-Drop-some-leftover-deprecated-constants.patch
+++ b/0002-Revert-Drop-some-leftover-deprecated-constants.patch
@@ -1,7 +1,8 @@
-From 9ee61912a356bb8c1995cf262428196e5e4f179d Mon Sep 17 00:00:00 2001
+From e245f427f54e5b4e91b9a0c21c19eb069e9e09dc Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer at who-t.net>
Date: Fri, 3 Jan 2014 08:30:04 +1000
-Subject: [PATCH libevdev] Revert "Drop some leftover deprecated constants."
+Subject: [PATCH libevdev 02/14] Revert "Drop some leftover deprecated
+ constants."
This reverts commit a612ee753e8c40d2d1fff6da516d278064e27d86.
---
@@ -9,7 +10,7 @@ This reverts commit a612ee753e8c40d2d1fff6da516d278064e27d86.
1 file changed, 6 insertions(+)
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
-index 8a37204..432aeff 100644
+index 02e4f78..98eed41 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -36,6 +36,12 @@
@@ -26,5 +27,5 @@ index 8a37204..432aeff 100644
static int
--
-1.8.4.2
+1.8.5.3
diff --git a/0003-Plug-a-memory-leak-for-name-uniq-phys.patch b/0003-Plug-a-memory-leak-for-name-uniq-phys.patch
new file mode 100644
index 0000000..a067adc
--- /dev/null
+++ b/0003-Plug-a-memory-leak-for-name-uniq-phys.patch
@@ -0,0 +1,43 @@
+From 7a75c77b6ed90f638b821fbf159dffc2c3f24608 Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 11:22:27 +1000
+Subject: [PATCH libevdev 03/14] Plug a memory leak for name, uniq, phys
+
+If a device is assigned a name, uniq and/or phys before calling
+libevdev_set_fd(), those values would leak.
+
+Change the default alloc to calloc, so name, uniq, and phys are initialized to
+zero before we call libevdev_reset
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+(cherry picked from commit f162f00f1db2cd0141958a4d8ed4a665bfc888e3)
+---
+ libevdev/libevdev.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index 98eed41..b1cd7ea 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -113,6 +113,9 @@ log_msg(enum libevdev_log_priority priority,
+ static void
+ libevdev_reset(struct libevdev *dev)
+ {
++ free(dev->name);
++ free(dev->phys);
++ free(dev->uniq);
+ memset(dev, 0, sizeof(*dev));
+ dev->fd = -1;
+ dev->initialized = false;
+@@ -128,7 +131,7 @@ libevdev_new(void)
+ {
+ struct libevdev *dev;
+
+- dev = malloc(sizeof(*dev));
++ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+--
+1.8.5.3
+
diff --git a/0004-Fix-memory-leaks-when-failing-to-create-a-uinput-dev.patch b/0004-Fix-memory-leaks-when-failing-to-create-a-uinput-dev.patch
new file mode 100644
index 0000000..b7f043a
--- /dev/null
+++ b/0004-Fix-memory-leaks-when-failing-to-create-a-uinput-dev.patch
@@ -0,0 +1,61 @@
+From f72a734a2a247d7b47e73fe2155d8c9d18ae761d Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 11:29:19 +1000
+Subject: [PATCH libevdev 04/14] Fix memory leaks when failing to create a
+ uinput device
+
+For an invalid fd, or a failure to open the device, the pre-allocated uinput
+device struct would leak.
+
+We can drop the open_uinput() function now, since skipping to the error
+handling means we'll return -errno anyway.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+(cherry picked from commit fdf737494ed940d68ce68c52ee029417bb68c8ff)
+---
+ libevdev/libevdev-uinput.c | 17 ++++-------------
+ 1 file changed, 4 insertions(+), 13 deletions(-)
+
+diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c
+index ea9cf78..09b7044 100644
+--- a/libevdev/libevdev-uinput.c
++++ b/libevdev/libevdev-uinput.c
+@@ -155,16 +155,6 @@ set_props(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
+ return rc;
+ }
+
+-static int
+-open_uinput(void)
+-{
+- int fd = open("/dev/uinput", O_RDWR|O_CLOEXEC);
+- if (fd < 0)
+- return -errno;
+-
+- return fd;
+-}
+-
+ LIBEVDEV_EXPORT int
+ libevdev_uinput_get_fd(const struct libevdev_uinput *uinput_dev)
+ {
+@@ -277,14 +267,15 @@ libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct li
+ return -ENOMEM;
+
+ if (fd == LIBEVDEV_UINPUT_OPEN_MANAGED) {
+- fd = open_uinput();
++ fd = open("/dev/uinput", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+- return fd;
++ goto error;
+
+ new_device->fd_is_managed = 1;
+ } else if (fd < 0) {
+ log_bug("Invalid fd %d\n", fd);
+- return -EBADF;
++ errno = EBADF;
++ goto error;
+ }
+
+ memset(&uidev, 0, sizeof(uidev));
+--
+1.8.5.3
+
diff --git a/0005-Don-t-sync-past-MAX_SLOTS-slots.patch b/0005-Don-t-sync-past-MAX_SLOTS-slots.patch
new file mode 100644
index 0000000..d3d1bf5
--- /dev/null
+++ b/0005-Don-t-sync-past-MAX_SLOTS-slots.patch
@@ -0,0 +1,52 @@
+From 449437ad6f9ca12901f42d6b876acfc8550745e2 Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 11:36:04 +1000
+Subject: [PATCH libevdev 05/14] Don't sync past MAX_SLOTS slots
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If a device has more than MAX_SLOTS slots, we'd run out-of-bounds on the sync
+array. This function is sig-safe, so we can't alloc here, merely limit the
+access.
+
+Reported-by: Jonas Ã…dahl <jadahl at gmail.com>
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 2e06aed955f55f704b14c265391dc2ce418b8e9f)
+---
+ libevdev/libevdev.c | 2 +-
+ libevdev/libevdev.h | 4 ++++
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index b1cd7ea..0989f6e 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -549,7 +549,7 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ ioctl_success = 1;
+ }
+
+- for (i = 0; i < dev->num_slots; i++) {
++ for (i = 0; i < min(dev->num_slots, MAX_SLOTS); i++) {
+ int j;
+ struct input_event *ev;
+
+diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
+index 1c91c82..1261ebe 100644
+--- a/libevdev/libevdev.h
++++ b/libevdev/libevdev.h
+@@ -672,6 +672,10 @@ enum libevdev_read_status {
+ * device state delta. This function returns LIBEVDEV_READ_STATUS_SYNC for
+ * each event part of that delta, until it returns -EAGAIN once all events
+ * have been synced.
++ * @note The implementation of libevdev limits the maximum number of slots
++ * that can be synched. If your device exceeds the number of slots
++ * (currently 32), slot indices equal and above this maximum are ignored and
++ * their value will not update until the next event in that slot.
+ *
+ * If a device needs to be synced by the caller but the caller does not call
+ * with the LIBEVDEV_READ_STATUS_SYNC flag set, all events from the diff are
+--
+1.8.5.3
+
diff --git a/0006-Add-unlikely-macro.patch b/0006-Add-unlikely-macro.patch
new file mode 100644
index 0000000..4f78b32
--- /dev/null
+++ b/0006-Add-unlikely-macro.patch
@@ -0,0 +1,30 @@
+From c041978f70496d93ed299d28f0b30720dc40a46c Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Tue, 4 Mar 2014 16:59:50 +1000
+Subject: [PATCH libevdev 06/14] Add unlikely() macro
+
+Taken from systemd.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Hans de Goede <hdegoede at redhat.com>
+(cherry picked from commit 5b0b2ae114ef366e3d524236df445f41f575609f)
+---
+ libevdev/libevdev-util.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/libevdev/libevdev-util.h b/libevdev/libevdev-util.h
+index 227363c..ddcf5e3 100644
+--- a/libevdev/libevdev-util.h
++++ b/libevdev/libevdev-util.h
+@@ -28,6 +28,8 @@
+ #include <string.h>
+ #include "libevdev-int.h"
+
++#define unlikely(x) (__builtin_expect(!!(x),0))
++
+ static inline bool
+ startswith(const char *str, size_t len, const char *prefix, size_t plen)
+ {
+--
+1.8.5.3
+
diff --git a/0007-Dynamically-allocate-the-slot-values.patch b/0007-Dynamically-allocate-the-slot-values.patch
new file mode 100644
index 0000000..a111d57
--- /dev/null
+++ b/0007-Dynamically-allocate-the-slot-values.patch
@@ -0,0 +1,164 @@
+From 731db693d83f7b81f5e14e43908507331bc51f7e Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 11:34:47 +1000
+Subject: [PATCH libevdev 07/14] Dynamically allocate the slot values
+
+Instead of relying on a static MAX_SLOTS array, allocated it based on the
+number of slots we have on the device. The previous checks for MAX_SLOTS were
+incomplete, causing out-of-bound reads.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+(cherry picked from commit 89e7998fee2d3ffc1cf1d0ab7bc129c5224ec598)
+---
+ libevdev/libevdev-int.h | 2 +-
+ libevdev/libevdev.c | 41 +++++++++++++++++++++++++++++++----------
+ 2 files changed, 32 insertions(+), 11 deletions(-)
+
+diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
+index 847fe56..42141ca 100644
+--- a/libevdev/libevdev-int.h
++++ b/libevdev/libevdev-int.h
+@@ -95,7 +95,7 @@ struct libevdev {
+ unsigned long led_values[NLONGS(LED_CNT)];
+ unsigned long sw_values[NLONGS(SW_CNT)];
+ struct input_absinfo abs_info[ABS_CNT];
+- int mt_slot_vals[MAX_SLOTS][ABS_MT_CNT];
++ int *mt_slot_vals; /* [num_slots * ABS_MT_CNT] */
+ int num_slots; /**< valid slots in mt_slot_vals */
+ int current_slot;
+ int rep_values[REP_CNT];
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index 0989f6e..e7646ca 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -44,6 +44,21 @@ LIBEVDEV_EXPORT const enum libevdev_read_flag LIBEVDEV_READ_BLOCKING = LIBEVDEV_
+
+ static int sync_mt_state(struct libevdev *dev, int create_events);
+
++static inline int*
++slot_value(const struct libevdev *dev, int slot, int axis)
++{
++ if (unlikely(slot > dev->num_slots)) {
++ log_bug("Slot %d exceeds number of slots (%d)\n", slot, dev->num_slots);
++ slot = 0;
++ }
++ if (unlikely(axis < ABS_MT_MIN || axis > ABS_MT_MAX)) {
++ log_bug("MT axis %d is outside the valid range [%d,%d]\n",
++ axis, ABS_MT_MIN, ABS_MT_MAX);
++ axis = ABS_MT_MIN;
++ }
++ return &dev->mt_slot_vals[slot * ABS_MT_CNT + axis - ABS_MT_MIN];
++}
++
+ static int
+ init_event_queue(struct libevdev *dev)
+ {
+@@ -116,6 +131,7 @@ libevdev_reset(struct libevdev *dev)
+ free(dev->name);
+ free(dev->phys);
+ free(dev->uniq);
++ free(dev->mt_slot_vals);
+ memset(dev, 0, sizeof(*dev));
+ dev->fd = -1;
+ dev->initialized = false;
+@@ -167,6 +183,7 @@ libevdev_free(struct libevdev *dev)
+ free(dev->name);
+ free(dev->phys);
+ free(dev->uniq);
++ free(dev->mt_slot_vals);
+ queue_free(dev);
+ free(dev);
+ }
+@@ -357,6 +374,11 @@ libevdev_set_fd(struct libevdev* dev, int fd)
+ if (i == ABS_MT_SLOT &&
+ !libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1)) {
+ dev->num_slots = abs_info.maximum + 1;
++ dev->mt_slot_vals = calloc(dev->num_slots * ABS_MT_CNT, sizeof(int));
++ if (!dev->mt_slot_vals) {
++ rc = -ENOMEM;
++ goto out;
++ }
+ dev->current_slot = abs_info.value;
+ }
+
+@@ -567,14 +589,14 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ if (!libevdev_has_event_code(dev, EV_ABS, j))
+ continue;
+
+- if (dev->mt_slot_vals[i][jdx] == mt_state[jdx].val[i])
++ if (*slot_value(dev, i, j) == mt_state[jdx].val[i])
+ continue;
+
+ if (create_events) {
+ ev = queue_push(dev);
+ init_event(dev, ev, EV_ABS, j, mt_state[jdx].val[i]);
+ }
+- dev->mt_slot_vals[i][jdx] = mt_state[jdx].val[i];
++ *slot_value(dev, i, j) = mt_state[jdx].val[i];
+ }
+ }
+
+@@ -655,14 +677,14 @@ update_mt_state(struct libevdev *dev, const struct input_event *e)
+ /* sync abs_info with the current slot values */
+ for (i = ABS_MT_SLOT + 1; i <= ABS_MT_MAX; i++) {
+ if (libevdev_has_event_code(dev, EV_ABS, i))
+- dev->abs_info[i].value = dev->mt_slot_vals[dev->current_slot][i - ABS_MT_MIN];
++ dev->abs_info[i].value = *slot_value(dev, dev->current_slot, i);
+ }
+
+ return 0;
+ } else if (dev->current_slot == -1)
+ return 1;
+
+- dev->mt_slot_vals[dev->current_slot][e->code - ABS_MT_MIN] = e->value;
++ *slot_value(dev, dev->current_slot, e->code) = e->value;
+
+ return 0;
+ }
+@@ -1053,13 +1075,13 @@ libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned
+ if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code))
+ return 0;
+
+- if (dev->num_slots < 0 || slot >= (unsigned int)dev->num_slots || slot >= MAX_SLOTS)
++ if (dev->num_slots < 0 || slot >= (unsigned int)dev->num_slots)
+ return 0;
+
+ if (code > ABS_MT_MAX || code < ABS_MT_MIN)
+ return 0;
+
+- return dev->mt_slot_vals[slot][code - ABS_MT_MIN];
++ return *slot_value(dev, slot, code);
+ }
+
+ LIBEVDEV_EXPORT int
+@@ -1068,7 +1090,7 @@ libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int co
+ if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code))
+ return -1;
+
+- if (dev->num_slots == -1 || slot >= (unsigned int)dev->num_slots || slot >= MAX_SLOTS)
++ if (dev->num_slots == -1 || slot >= (unsigned int)dev->num_slots)
+ return -1;
+
+ if (code > ABS_MT_MAX || code < ABS_MT_MIN)
+@@ -1080,8 +1102,7 @@ libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int co
+ dev->current_slot = value;
+ }
+
+- dev->mt_slot_vals[slot][code - ABS_MT_MIN] = value;
+-
++ *slot_value(dev, slot, code) = value;
+
+ return 0;
+ }
+@@ -1092,7 +1113,7 @@ libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigne
+ if (libevdev_has_event_type(dev, EV_ABS) &&
+ libevdev_has_event_code(dev, EV_ABS, code) &&
+ dev->num_slots >= 0 &&
+- slot < (unsigned int)dev->num_slots && slot < MAX_SLOTS) {
++ slot < (unsigned int)dev->num_slots) {
+ *value = libevdev_get_slot_value(dev, slot, code);
+ return 1;
+ } else
+--
+1.8.5.3
+
diff --git a/0008-Reduce-memory-requirement-for-MT-syncing.patch b/0008-Reduce-memory-requirement-for-MT-syncing.patch
new file mode 100644
index 0000000..122f88a
--- /dev/null
+++ b/0008-Reduce-memory-requirement-for-MT-syncing.patch
@@ -0,0 +1,144 @@
+From 82edf7fb52fdbf2f2ae41ed2239cee3a74cdc701 Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 13:10:35 +1000
+Subject: [PATCH libevdev 08/14] Reduce memory requirement for MT syncing
+
+Changes the algorithm: before we'd ioctl all axes for all slots, then generate
+events for all slots one-by-one.
+
+Now we ioctl the slot state for each axis, copy the new event value into
+the device and mark a bitfield that we've updated the value. Then loop through
+the slots and generate events where changed.
+
+Side-effect: this makes it easy to check if anything in the slot has updated,
+so we can skip empty slot events during sync.
+
+Min memory requirement for the state storage was:
+ MAX_SLOTS * (ABS_MT_CNT + 1) * sizeof(int) = 1980
+Min memory requirement now:
+ (ABS_MT_CNT + 1) * sizeof(int) + NLONGS((MAX_SLOTS * ABS_MT_CNT) bits) = 544
+
+This is sigsafe code, so this was stack memory. Reducing the requirement
+allows us to up MAX_SLOTS in the future if we need to.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 24c9ca81f0f680bf582d82978f014c069d5a1e47)
+---
+ libevdev/libevdev.c | 70 ++++++++++++++++++++++++++++++-----------------------
+ 1 file changed, 40 insertions(+), 30 deletions(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index e7646ca..6c7f04b 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -540,26 +540,26 @@ static int
+ sync_mt_state(struct libevdev *dev, int create_events)
+ {
+ int rc;
+- int i;
++ int axis, slot;
+ int ioctl_success = 0;
+ struct mt_state {
+ int code;
+ int val[MAX_SLOTS];
+- } mt_state[ABS_MT_CNT];
++ } mt_state;
++ unsigned long slot_update[NLONGS(MAX_SLOTS * ABS_MT_CNT)] = {0};
+
+- memset(&mt_state, 0, sizeof(mt_state));
++#define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN)
+
+- for (i = ABS_MT_MIN; i <= ABS_MT_MAX; i++) {
+- int idx;
+- if (i == ABS_MT_SLOT)
++ for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) {
++ if (axis == ABS_MT_SLOT)
+ continue;
+
+- if (!libevdev_has_event_code(dev, EV_ABS, i))
++ if (!libevdev_has_event_code(dev, EV_ABS, axis))
+ continue;
+
+- idx = i - ABS_MT_MIN;
+- mt_state[idx].code = i;
+- rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state[idx]);
++ memset(&mt_state, 0, sizeof(mt_state));
++ mt_state.code = axis;
++ rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state);
+ if (rc < 0) {
+ /* if the first ioctl fails with -EINVAL, chances are the kernel
+ doesn't support the ioctl. Simply continue */
+@@ -567,39 +567,49 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ rc = 0;
+ } else /* if the second, ... ioctl fails, really fail */
+ goto out;
+- } else if (ioctl_success == 0)
+- ioctl_success = 1;
++ } else {
++ if (ioctl_success == 0)
++ ioctl_success = 1;
++
++ for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
++
++ if (*slot_value(dev, slot, axis) == mt_state.val[slot])
++ continue;
++
++ *slot_value(dev, slot, axis) = mt_state.val[slot];
++
++ set_bit(slot_update, AXISBIT(slot, axis));
++ /* note that this slot has updates */
++ set_bit(slot_update, AXISBIT(slot, ABS_MT_SLOT));
++ }
++
++
++ }
+ }
+
+- for (i = 0; i < min(dev->num_slots, MAX_SLOTS); i++) {
+- int j;
++ for (slot = 0; create_events && slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+ struct input_event *ev;
+
+- if (create_events) {
+- ev = queue_push(dev);
+- init_event(dev, ev, EV_ABS, ABS_MT_SLOT, i);
+- }
++ if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT)))
++ continue;
+
+- for (j = ABS_MT_MIN; j <= ABS_MT_MAX; j++) {
+- int jdx = j - ABS_MT_MIN;
++ ev = queue_push(dev);
++ init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot);
+
+- if (j == ABS_MT_SLOT)
++ for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) {
++ if (axis == ABS_MT_SLOT ||
++ !libevdev_has_event_code(dev, EV_ABS, axis))
+ continue;
+
+- if (!libevdev_has_event_code(dev, EV_ABS, j))
+- continue;
+-
+- if (*slot_value(dev, i, j) == mt_state[jdx].val[i])
+- continue;
+-
+- if (create_events) {
++ if (bit_is_set(slot_update, AXISBIT(slot, axis))) {
+ ev = queue_push(dev);
+- init_event(dev, ev, EV_ABS, j, mt_state[jdx].val[i]);
++ init_event(dev, ev, EV_ABS, axis, *slot_value(dev, slot, axis));
+ }
+- *slot_value(dev, i, j) = mt_state[jdx].val[i];
+ }
+ }
+
++#undef AXISBIT
++
+ rc = 0;
+ out:
+ return rc ? -errno : 0;
+--
+1.8.5.3
+
diff --git a/0009-Increase-MAX_SLOTS-to-60.patch b/0009-Increase-MAX_SLOTS-to-60.patch
new file mode 100644
index 0000000..ba4b010
--- /dev/null
+++ b/0009-Increase-MAX_SLOTS-to-60.patch
@@ -0,0 +1,64 @@
+From 6e66c23cbeedbdb43eafcdfd620518d078ba814a Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 27 Feb 2014 14:35:35 +1000
+Subject: [PATCH libevdev 09/14] Increase MAX_SLOTS to 60
+
+As seen on 3M devices, which seems to be the maximum seen so far. Some Stantum
+devices report 255 touches but are only capable of 10, so the are not affected
+by our limits.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Acked-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit f3f31b47fc8c397e5266747b13ce3e423e0577fa)
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+
+Conflicts:
+ test/test-libevdev-events.c
+---
+ libevdev/libevdev-int.h | 2 +-
+ libevdev/libevdev.h | 2 +-
+ test/test-libevdev-events.c | 2 ++
+ 3 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
+index 42141ca..2bc8750 100644
+--- a/libevdev/libevdev-int.h
++++ b/libevdev/libevdev-int.h
+@@ -34,7 +34,7 @@
+ #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+ #define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0])))
+ #define MAX_NAME 256
+-#define MAX_SLOTS 32
++#define MAX_SLOTS 60
+ #define ABS_MT_MIN ABS_MT_SLOT
+ #define ABS_MT_MAX ABS_MT_TOOL_Y
+ #define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1)
+diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
+index 1261ebe..af96272 100644
+--- a/libevdev/libevdev.h
++++ b/libevdev/libevdev.h
+@@ -674,7 +674,7 @@ enum libevdev_read_status {
+ * have been synced.
+ * @note The implementation of libevdev limits the maximum number of slots
+ * that can be synched. If your device exceeds the number of slots
+- * (currently 32), slot indices equal and above this maximum are ignored and
++ * (currently 60), slot indices equal and above this maximum are ignored and
+ * their value will not update until the next event in that slot.
+ *
+ * If a device needs to be synced by the caller but the caller does not call
+diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
+index 14e20a4..30d1b73 100644
+--- a/test/test-libevdev-events.c
++++ b/test/test-libevdev-events.c
+@@ -28,6 +28,8 @@
+
+ #include "test-common.h"
+
++#define MAX_SLOTS 60 /* as in libevdev-int.h */
++
+ START_TEST(test_next_event)
+ {
+ struct uinput_device* uidev;
+--
+1.8.5.3
+
diff --git a/0010-Cap-slot-values-to-the-announced-maximum.patch b/0010-Cap-slot-values-to-the-announced-maximum.patch
new file mode 100644
index 0000000..b892df0
--- /dev/null
+++ b/0010-Cap-slot-values-to-the-announced-maximum.patch
@@ -0,0 +1,196 @@
+From 8a1f0b1c67f09bb7c87438590bc86c2801d7bddc Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Wed, 5 Mar 2014 10:57:01 +1000
+Subject: [PATCH libevdev 10/14] Cap slot values to the announced maximum
+
+A malicious device may announce N slots but then send a slot index >= N. The
+slot state is almost always allocated (definitely the case in libevdev and
+true for most callers), so providing a slot number higher than the announced
+maximum is likely to lead to invalid dereferences. Don't allow that.
+Likewise, don't allow negative slot numbers.
+
+Note that the kernel filters these events anyway, the only way to trigger this
+is to change the device fd to something outside the kernel's control.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 66fee1bec4c4b021e1b54adcd775cf6e2aa84869)
+---
+ libevdev/libevdev.c | 24 ++++++++++++++
+ libevdev/libevdev.h | 7 +++-
+ test/test-libevdev-events.c | 78 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 108 insertions(+), 1 deletion(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index 6c7f04b..e15c3ce 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -797,6 +797,26 @@ read_more_events(struct libevdev *dev)
+ return 0;
+ }
+
++/**
++ * Sanitize/modify events where needed.
++ * @return 0 if untouched, 1 if modified.
++ */
++static inline int
++sanitize_event(const struct libevdev *dev, struct input_event *ev)
++{
++ if (unlikely(dev->num_slots > -1 &&
++ libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT) &&
++ (ev->value < 0 || ev->value >= dev->num_slots))) {
++ log_bug("Device %s received an invalid slot index %d."
++ "Capping to announced max slot number %d.\n",
++ dev->name, ev->value, dev->num_slots - 1);
++ ev->value = dev->num_slots - 1;
++ return 1;
++ }
++
++ return 0;
++}
++
+ LIBEVDEV_EXPORT int
+ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
+ {
+@@ -865,6 +885,7 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
+ if (queue_shift(dev, ev) != 0)
+ return -EAGAIN;
+
++ sanitize_event(dev, ev);
+ update_state(dev, ev);
+
+ /* if we disabled a code, get the next event instead */
+@@ -1055,6 +1076,9 @@ libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int c
+ e.code = code;
+ e.value = value;
+
++ if (sanitize_event(dev, &e))
++ return -1;
++
+ switch(type) {
+ case EV_ABS: rc = update_abs_state(dev, &e); break;
+ case EV_KEY: rc = update_key_state(dev, &e); break;
+diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
+index af96272..005f7e9 100644
+--- a/libevdev/libevdev.h
++++ b/libevdev/libevdev.h
+@@ -1046,6 +1046,10 @@ int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsi
+ * event code is the value of the currently active slot. You should use
+ * libevdev_set_slot_value() instead.
+ *
++ * If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is
++ * ABS_MT_SLOT, the value must be a positive number less then the number of
++ * slots on the device. Otherwise, libevdev_set_event_value() returns -1.
++ *
+ * @param dev The evdev device, already initialized with libevdev_set_fd()
+ * @param type The event type for the code to query (EV_SYN, EV_REL, etc.)
+ * @param code The event code to set the value for, one of ABS_X, LED_NUML, etc.
+@@ -1053,7 +1057,8 @@ int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsi
+ *
+ * @return 0 on success, or -1 on failure.
+ * @retval -1 the device does not have the event type or code enabled, or the code is outside the
+- * allowed limits for the given type, or the type cannot be set.
++ * allowed limits for the given type, or the type cannot be set, or the
++ * value is not permitted for the given code.
+ *
+ * @see libevdev_set_slot_value
+ * @see libevdev_get_event_value
+diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
+index 30d1b73..c067f98 100644
+--- a/test/test-libevdev-events.c
++++ b/test/test-libevdev-events.c
+@@ -933,6 +933,83 @@ START_TEST(test_mt_event_values_invalid)
+ }
+ END_TEST
+
++START_TEST(test_mt_slot_ranges_invalid)
++{
++ struct uinput_device* uidev;
++ struct libevdev *dev;
++ struct input_event ev[2];
++ int rc;
++ struct input_absinfo abs[5];
++ int num_slots = 2;
++ int pipefd[2];
++
++ memset(abs, 0, sizeof(abs));
++ abs[0].value = ABS_X;
++ abs[0].maximum = 1000;
++ abs[1].value = ABS_MT_POSITION_X;
++ abs[1].maximum = 1000;
++
++ abs[2].value = ABS_Y;
++ abs[2].maximum = 1000;
++ abs[3].value = ABS_MT_POSITION_Y;
++ abs[3].maximum = 1000;
++
++ abs[4].value = ABS_MT_SLOT;
++ abs[4].maximum = num_slots - 1;
++
++ rc = test_create_abs_device(&uidev, &dev,
++ 5, abs,
++ EV_SYN, SYN_REPORT,
++ -1);
++ ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
++
++ rc = pipe2(pipefd, O_NONBLOCK);
++ ck_assert_int_eq(rc, 0);
++ libevdev_change_fd(dev, pipefd[0]);
++
++ ev[0].type = EV_ABS;
++ ev[0].code = ABS_MT_SLOT;
++ ev[0].value = num_slots;
++ ev[1].type = EV_SYN;
++ ev[1].code = SYN_REPORT;
++ ev[1].value = 0;
++ rc = write(pipefd[1], ev, sizeof(ev));
++ ck_assert_int_eq(rc, sizeof(ev));
++
++ libevdev_set_log_function(test_logfunc_ignore_error, NULL);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
++ ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
++ ck_assert_int_eq(ev[0].value, num_slots - 1);
++
++ /* drain the EV_SYN */
++ libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
++
++ ev[0].type = EV_ABS;
++ ev[0].code = ABS_MT_SLOT;
++ ev[0].value = -1;
++ ev[1].type = EV_SYN;
++ ev[1].code = SYN_REPORT;
++ ev[1].value = 0;
++ rc = write(pipefd[1], ev, sizeof(ev));
++ ck_assert_int_eq(rc, sizeof(ev));
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
++ ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
++ ck_assert_int_eq(ev[0].value, num_slots - 1);
++
++ ck_assert_int_eq(libevdev_get_current_slot(dev), num_slots - 1);
++
++ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, num_slots), -1);
++ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, -1), -1);
++
++ libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
++
++ uinput_device_free(uidev);
++ libevdev_free(dev);
++}
++END_TEST
++
+ START_TEST(test_ev_rep_values)
+ {
+ struct uinput_device* uidev;
+@@ -1232,6 +1309,7 @@ libevdev_events(void)
+ tcase_add_test(tc, test_event_values_invalid);
+ tcase_add_test(tc, test_mt_event_values);
+ tcase_add_test(tc, test_mt_event_values_invalid);
++ tcase_add_test(tc, test_mt_slot_ranges_invalid);
+ tcase_add_test(tc, test_ev_rep_values);
+ suite_add_tcase(s, tc);
+
+--
+1.8.5.3
+
diff --git a/0011-Move-some-functions-macros-between-libevdev-int.h-an.patch b/0011-Move-some-functions-macros-between-libevdev-int.h-an.patch
new file mode 100644
index 0000000..295d3f4
--- /dev/null
+++ b/0011-Move-some-functions-macros-between-libevdev-int.h-an.patch
@@ -0,0 +1,209 @@
+From db8c537467ffcae14c522547a566384791996f77 Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 6 Mar 2014 10:22:20 +1000
+Subject: [PATCH libevdev 11/14] Move some functions/macros between
+ libevdev-int.h and libevdev-util.h
+
+This allows libevdev-util.h to be used by tests, it no longer relies on
+libevdev internal structs.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 68a297577aed60bb81afc5e8bcfe0e3d2e95185f)
+---
+ libevdev/libevdev-int.h | 72 ++++++++++++++++++++++++++++++++++++-----------
+ libevdev/libevdev-util.h | 73 +++++++++++-------------------------------------
+ 2 files changed, 73 insertions(+), 72 deletions(-)
+
+diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
+index 2bc8750..5ff6026 100644
+--- a/libevdev/libevdev-int.h
++++ b/libevdev/libevdev-int.h
+@@ -29,10 +29,8 @@
+ #include <stdbool.h>
+ #include <errno.h>
+ #include "libevdev.h"
++#include "libevdev-util.h"
+
+-#define LONG_BITS (sizeof(long) * 8)
+-#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+-#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0])))
+ #define MAX_NAME 256
+ #define MAX_SLOTS 60
+ #define ABS_MT_MIN ABS_MT_SLOT
+@@ -42,19 +40,6 @@
+ #define LIBEVDEV_PRINTF(_format, _args) __attribute__ ((format (printf, _format, _args)))
+ #define ALIAS(_to) __attribute__((alias(#_to)))
+
+-#undef min
+-#undef max
+-#define min(a,b) \
+- ({ __typeof__ (a) _a = (a); \
+- __typeof__ (b) _b = (b); \
+- _a > _b ? _b : _a; \
+- })
+-#define max(a,b) \
+- ({ __typeof__ (a) _a = (a); \
+- __typeof__ (b) _b = (b); \
+- _a > _b ? _a : _b; \
+- })
+-
+ /**
+ * Sync state machine:
+ * default state: SYNC_NONE
+@@ -279,5 +264,60 @@ queue_set_num_elements(struct libevdev *dev, size_t nelem)
+
+ return 0;
+ }
++
++#define max_mask(uc, lc) \
++ case EV_##uc: \
++ *mask = dev->lc##_bits; \
++ max = libevdev_event_type_get_max(type); \
++ break;
++
++
++static inline int
++type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask)
++{
++ int max;
++
++ switch(type) {
++ max_mask(ABS, abs);
++ max_mask(REL, rel);
++ max_mask(KEY, key);
++ max_mask(LED, led);
++ max_mask(MSC, msc);
++ max_mask(SW, sw);
++ max_mask(FF, ff);
++ max_mask(REP, rep);
++ max_mask(SND, snd);
++ default:
++ max = -1;
++ break;
++ }
++
++ return max;
++}
++
++static inline int
++type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask)
++{
++ int max;
++
++ switch(type) {
++ max_mask(ABS, abs);
++ max_mask(REL, rel);
++ max_mask(KEY, key);
++ max_mask(LED, led);
++ max_mask(MSC, msc);
++ max_mask(SW, sw);
++ max_mask(FF, ff);
++ max_mask(REP, rep);
++ max_mask(SND, snd);
++ default:
++ max = -1;
++ break;
++ }
++
++ return max;
++}
++
++#undef max_mask
+ #endif
+
+diff --git a/libevdev/libevdev-util.h b/libevdev/libevdev-util.h
+index ddcf5e3..31c1a14 100644
+--- a/libevdev/libevdev-util.h
++++ b/libevdev/libevdev-util.h
+@@ -26,10 +26,26 @@
+ #include <config.h>
+ #include <stdbool.h>
+ #include <string.h>
+-#include "libevdev-int.h"
+
++#define LONG_BITS (sizeof(long) * 8)
++#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
++#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0])))
+ #define unlikely(x) (__builtin_expect(!!(x),0))
+
++#undef min
++#undef max
++#define min(a,b) \
++ ({ __typeof__ (a) _a = (a); \
++ __typeof__ (b) _b = (b); \
++ _a > _b ? _b : _a; \
++ })
++#define max(a,b) \
++ ({ __typeof__ (a) _a = (a); \
++ __typeof__ (b) _b = (b); \
++ _a > _b ? _a : _b; \
++ })
++
++
+ static inline bool
+ startswith(const char *str, size_t len, const char *prefix, size_t plen)
+ {
+@@ -63,59 +79,4 @@ set_bit_state(unsigned long *array, int bit, int state)
+ clear_bit(array, bit);
+ }
+
+-#define max_mask(uc, lc) \
+- case EV_##uc: \
+- *mask = dev->lc##_bits; \
+- max = libevdev_event_type_get_max(type); \
+- break;
+-
+-
+-static inline int
+-type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask)
+-{
+- int max;
+-
+- switch(type) {
+- max_mask(ABS, abs);
+- max_mask(REL, rel);
+- max_mask(KEY, key);
+- max_mask(LED, led);
+- max_mask(MSC, msc);
+- max_mask(SW, sw);
+- max_mask(FF, ff);
+- max_mask(REP, rep);
+- max_mask(SND, snd);
+- default:
+- max = -1;
+- break;
+- }
+-
+- return max;
+-}
+-
+-static inline int
+-type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask)
+-{
+- int max;
+-
+- switch(type) {
+- max_mask(ABS, abs);
+- max_mask(REL, rel);
+- max_mask(KEY, key);
+- max_mask(LED, led);
+- max_mask(MSC, msc);
+- max_mask(SW, sw);
+- max_mask(FF, ff);
+- max_mask(REP, rep);
+- max_mask(SND, snd);
+- default:
+- max = -1;
+- break;
+- }
+-
+- return max;
+-}
+-
+-#undef max_mask
+-
+ #endif
+--
+1.8.5.3
+
diff --git a/0012-Don-t-sync-the-MT-state-for-fake-MT-devices.patch b/0012-Don-t-sync-the-MT-state-for-fake-MT-devices.patch
new file mode 100644
index 0000000..fa85f96
--- /dev/null
+++ b/0012-Don-t-sync-the-MT-state-for-fake-MT-devices.patch
@@ -0,0 +1,170 @@
+From dbdd246e7354adfde4848eb6854256f386526bec Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Wed, 5 Mar 2014 13:15:20 +1000
+Subject: [PATCH libevdev 12/14] Don't sync the MT state for fake MT devices
+
+Devices with ABS_MT_SLOT-1 are fake MT devices, they merely overlap the
+axis range but don't actually provide slots. The EVIOCGABS ioctl won't work to
+retrieve the current value - the kernel does not store values for those axes
+and the return value is always 0.
+
+Thus, simply ignore those axes for fake MT devices and instead rely on the
+next event to update the caller with the correct state for each axis.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 12ff51b8710aca9640caacf02a941ec8ff234348)
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+
+Conflicts:
+ test/test-libevdev-events.c
+---
+ libevdev/libevdev.c | 5 +--
+ libevdev/libevdev.h | 6 ++++
+ test/test-libevdev-events.c | 83 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 92 insertions(+), 2 deletions(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index e15c3ce..142aa2f 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -650,8 +650,9 @@ sync_state(struct libevdev *dev)
+ rc = sync_sw_state(dev);
+ if (rc == 0 && libevdev_has_event_type(dev, EV_ABS))
+ rc = sync_abs_state(dev);
+- if (rc == 0 && libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT))
+- rc = sync_mt_state(dev, 1);
++ if (rc == 0 && dev->num_slots > -1 &&
++ libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT))
++ rc = sync_mt_state(dev, 1);
+
+ dev->queue_nsync = queue_num_elements(dev);
+
+diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
+index 005f7e9..0403bf9 100644
+--- a/libevdev/libevdev.h
++++ b/libevdev/libevdev.h
+@@ -370,6 +370,12 @@ extern "C" {
+ * device is not treated as multitouch device. No slot information is
+ * available and the ABS_MT axis range for these devices is treated as all
+ * other EV_ABS axes.
++ *
++ * Note that because of limitations in the kernel API, such fake multitouch
++ * devices can not be reliably synched after a SYN_DROPPED event. libevdev
++ * ignores all ABS_MT axis values during the sync process and instead
++ * relies on the device to send the current axis value with the first event
++ * after SYN_DROPPED.
+ */
+
+ /**
+diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
+index c067f98..e6054e6 100644
+--- a/test/test-libevdev-events.c
++++ b/test/test-libevdev-events.c
+@@ -25,6 +25,8 @@
+ #include <errno.h>
+ #include <unistd.h>
+ #include <fcntl.h>
++#include <stdio.h>
++#include <libevdev-util.h>
+
+ #include "test-common.h"
+
+@@ -580,6 +582,86 @@ START_TEST(test_syn_delta_sw)
+ }
+ END_TEST
+
++START_TEST(test_syn_delta_fake_mt)
++{
++ struct uinput_device* uidev;
++ struct libevdev *dev;
++ int rc;
++ struct input_event ev;
++ struct input_absinfo abs[] = { { ABS_X, 0, 1000 },
++ { ABS_Y, 0, 1000 },
++ { ABS_MT_POSITION_X, 0, 1000 },
++ { ABS_MT_POSITION_Y, 0, 1000 },
++ { ABS_MT_SLOT - 1, 0, 2 }};
++ /* don't set ABS_MT_SLOT here, otherwise uinput will init
++ * slots and the behavior is different to real devices with
++ * such events */
++ unsigned long received[NLONGS(ABS_CNT)] = {0};
++
++ rc = test_create_abs_device(&uidev, &dev, 5, abs,
++ -1);
++ ck_assert_msg(rc == 0, "Failed to uinput device: %s", strerror(-rc));
++
++ /* first set of events */
++ uinput_device_event(uidev, EV_ABS, ABS_X, 200);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 400);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 1);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++
++ /* second set of events */
++ uinput_device_event(uidev, EV_ABS, ABS_X, 201);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 401);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 101);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 501);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 2);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_STATUS_SYNC, &ev)) != -EAGAIN) {
++ if (ev.type != EV_ABS)
++ continue;
++
++ ck_assert(!bit_is_set(received, ev.code));
++
++ switch(ev.code) {
++ /* see comment below for ABS_MT_POSITION_X
++ * and ABS_MT_POSITION_Y */
++ case ABS_MT_POSITION_X:
++ case ABS_MT_POSITION_Y:
++ ck_abort();
++ break;
++
++ case ABS_MT_SLOT - 1: ck_assert_int_eq(ev.value, 2); break;
++ case ABS_X: ck_assert_int_eq(ev.value, 201); break;
++ case ABS_Y: ck_assert_int_eq(ev.value, 401); break;
++ default:
++ ck_abort();
++ }
++
++ set_bit(received, ev.code);
++ }
++
++ /* Dont' expect ABS_MT values, they are ignored during the sync
++ * process */
++ ck_assert(!bit_is_set(received, ABS_MT_POSITION_X));
++ ck_assert(!bit_is_set(received, ABS_MT_POSITION_Y));
++ ck_assert(bit_is_set(received, ABS_MT_SLOT - 1));
++ ck_assert(bit_is_set(received, ABS_X));
++ ck_assert(bit_is_set(received, ABS_Y));
++
++ ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 201);
++ ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 401);
++ ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_SLOT - 1), 2);
++
++ uinput_device_free(uidev);
++ libevdev_free(dev);
++}
++END_TEST
++
+ START_TEST(test_skipped_sync)
+ {
+ struct uinput_device* uidev;
+@@ -1296,6 +1378,7 @@ libevdev_events(void)
+ tcase_add_test(tc, test_syn_delta_mt);
+ tcase_add_test(tc, test_syn_delta_led);
+ tcase_add_test(tc, test_syn_delta_sw);
++ tcase_add_test(tc, test_syn_delta_fake_mt);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("skipped syncs");
+--
+1.8.5.3
+
diff --git a/0013-Send-an-extra-ABS_MT_SLOT-event-to-sync-the-client-u.patch b/0013-Send-an-extra-ABS_MT_SLOT-event-to-sync-the-client-u.patch
new file mode 100644
index 0000000..f83e2ad
--- /dev/null
+++ b/0013-Send-an-extra-ABS_MT_SLOT-event-to-sync-the-client-u.patch
@@ -0,0 +1,312 @@
+From 912451f509d17611c6e6fb92b345e167121b5cb1 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <btissoir at redhat.com>
+Date: Wed, 5 Mar 2014 12:03:57 +1000
+Subject: [PATCH libevdev 13/14] Send an extra ABS_MT_SLOT event to sync the
+ client up with the current slot
+
+If multiple slots have changed during the sync handling, the client must be
+re-set to the current slot before continuing with normal events.
+
+Signed-off-by: Benjamin Tissoires <btissoir at redhat.com>
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit d3ae3da90f871320de968924ef11ef1a02abc608)
+
+Conflicts:
+ test/test-libevdev-events.c
+---
+ libevdev/libevdev.c | 25 +++++-
+ test/test-libevdev-events.c | 197 +++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 216 insertions(+), 6 deletions(-)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index 142aa2f..c6d6e24 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -539,9 +539,12 @@ out:
+ static int
+ sync_mt_state(struct libevdev *dev, int create_events)
+ {
++ struct input_event *ev;
++ struct input_absinfo abs_info;
+ int rc;
+ int axis, slot;
+ int ioctl_success = 0;
++ int last_reported_slot = 0;
+ struct mt_state {
+ int code;
+ int val[MAX_SLOTS];
+@@ -587,14 +590,18 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ }
+ }
+
+- for (slot = 0; create_events && slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+- struct input_event *ev;
++ if (!create_events) {
++ rc = 0;
++ goto out;
++ }
+
++ for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+ if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT)))
+ continue;
+
+ ev = queue_push(dev);
+ init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot);
++ last_reported_slot = slot;
+
+ for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) {
+ if (axis == ABS_MT_SLOT ||
+@@ -608,6 +615,20 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ }
+ }
+
++ /* add one last slot event to make sure the client is on the same
++ slot as the kernel */
++
++ rc = ioctl(dev->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
++ if (rc < 0)
++ goto out;
++
++ dev->current_slot = abs_info.value;
++
++ if (dev->current_slot != last_reported_slot) {
++ ev = queue_push(dev);
++ init_event(dev, ev, EV_ABS, ABS_MT_SLOT, dev->current_slot);
++ }
++
+ #undef AXISBIT
+
+ rc = 0;
+diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
+index e6054e6..481416c 100644
+--- a/test/test-libevdev-events.c
++++ b/test/test-libevdev-events.c
+@@ -474,6 +474,197 @@ START_TEST(test_syn_delta_mt)
+ }
+ END_TEST
+
++START_TEST(test_syn_delta_mt_reset_slot)
++{
++ struct uinput_device* uidev;
++ struct libevdev *dev;
++ int rc;
++ struct input_event ev,
++ last_slot_event = { .type = 0};
++ struct input_absinfo abs[6];
++
++ memset(abs, 0, sizeof(abs));
++ abs[0].value = ABS_X;
++ abs[0].maximum = 1000;
++ abs[1].value = ABS_MT_POSITION_X;
++ abs[1].maximum = 1000;
++
++ abs[2].value = ABS_Y;
++ abs[2].maximum = 1000;
++ abs[3].value = ABS_MT_POSITION_Y;
++ abs[3].maximum = 1000;
++
++
++ abs[4].value = ABS_MT_SLOT;
++ abs[4].maximum = 1;
++ abs[5].value = ABS_MT_TRACKING_ID;
++ abs[5].minimum = -1;
++ abs[5].maximum = 2;
++
++ rc = test_create_abs_device(&uidev, &dev,
++ 6, abs,
++ EV_SYN, SYN_REPORT,
++ -1);
++ ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
++
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ do {
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT))
++ last_slot_event = ev;
++ } while (rc != -EAGAIN);
++
++ ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT));
++ ck_assert_int_eq(last_slot_event.value, 0);
++ ck_assert_int_eq(libevdev_get_current_slot(dev), 0);
++
++ last_slot_event.type = 0;
++
++ /* same thing again, this time swap the numbers */
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ do {
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT))
++ last_slot_event = ev;
++ } while (rc != -EAGAIN);
++
++ ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT));
++ ck_assert_int_eq(last_slot_event.value, 1);
++ ck_assert_int_eq(libevdev_get_current_slot(dev), 1);
++
++ uinput_device_free(uidev);
++ libevdev_free(dev);
++}
++END_TEST
++
++START_TEST(test_syn_delta_mt_too_many)
++{
++ struct uinput_device* uidev;
++ struct libevdev *dev;
++ int rc;
++ struct input_event ev;
++ struct input_absinfo abs[6];
++ int i;
++ int num_slots = MAX_SLOTS + 20;
++
++ memset(abs, 0, sizeof(abs));
++ abs[0].value = ABS_X;
++ abs[0].maximum = 1000;
++ abs[1].value = ABS_MT_POSITION_X;
++ abs[1].maximum = 1000;
++
++ abs[2].value = ABS_Y;
++ abs[2].maximum = 1000;
++ abs[3].value = ABS_MT_POSITION_Y;
++ abs[3].maximum = 1000;
++
++ abs[4].value = ABS_MT_SLOT;
++ abs[4].maximum = num_slots;
++ abs[5].value = ABS_MT_TOOL_Y;
++ abs[5].maximum = 500;
++
++ rc = test_create_abs_device(&uidev, &dev,
++ 6, abs,
++ EV_SYN, SYN_REPORT,
++ -1);
++ ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
++
++ for (i = num_slots; i >= 0; i--) {
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i);
++ uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TOOL_Y, 1 + i);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++ }
++
++ /* drain the fd, so libevdev_next_event doesn't pick up any events
++ before the FORCE_SYNC */
++ do {
++ rc = read(libevdev_get_fd(dev), &ev, sizeof(ev));
++ } while (rc > 0);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ i = 0;
++ while (i < num_slots) {
++ int slot;
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT))
++ break;
++
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_X) ||
++ libevdev_event_is_code(&ev, EV_ABS, ABS_Y))
++ continue;
++
++ ck_assert_int_eq(ev.type, EV_ABS);
++ ck_assert_int_eq(ev.code, ABS_MT_SLOT);
++ slot = ev.value;
++ ck_assert_int_lt(slot, MAX_SLOTS);
++
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++
++ /* last MT_SLOT event is on its own */
++ if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT))
++ break;
++
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++ ck_assert_int_eq(ev.type, EV_ABS);
++ ck_assert_int_eq(ev.code, ABS_MT_POSITION_X);
++ ck_assert_int_eq(ev.value, 100 + slot);
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++ ck_assert_int_eq(ev.type, EV_ABS);
++ ck_assert_int_eq(ev.code, ABS_MT_POSITION_Y);
++ ck_assert_int_eq(ev.value, 500 + slot);
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++ ck_assert_int_eq(ev.type, EV_ABS);
++ ck_assert_int_eq(ev.code, ABS_MT_TOOL_Y);
++ ck_assert_int_eq(ev.value, 1 + slot);
++
++ i++;
++ }
++
++ /* we expect eactly MAX_SLOTS to be synced */
++ ck_assert_int_eq(i, MAX_SLOTS);
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
++ ck_assert_int_eq(rc, -EAGAIN);
++
++ uinput_device_free(uidev);
++ libevdev_free(dev);
++}
++END_TEST
++
+ START_TEST(test_syn_delta_led)
+ {
+ struct uinput_device* uidev;
+@@ -1058,8 +1249,6 @@ START_TEST(test_mt_slot_ranges_invalid)
+ rc = write(pipefd[1], ev, sizeof(ev));
+ ck_assert_int_eq(rc, sizeof(ev));
+
+- libevdev_set_log_function(test_logfunc_ignore_error, NULL);
+-
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
+ ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
+ ck_assert_int_eq(ev[0].value, num_slots - 1);
+@@ -1085,8 +1274,6 @@ START_TEST(test_mt_slot_ranges_invalid)
+ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, num_slots), -1);
+ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, -1), -1);
+
+- libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+-
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+ }
+@@ -1376,6 +1563,8 @@ libevdev_events(void)
+ tcase_add_test(tc, test_syn_delta_button);
+ tcase_add_test(tc, test_syn_delta_abs);
+ tcase_add_test(tc, test_syn_delta_mt);
++ tcase_add_test(tc, test_syn_delta_mt_too_many);
++ tcase_add_test(tc, test_syn_delta_mt_reset_slot);
+ tcase_add_test(tc, test_syn_delta_led);
+ tcase_add_test(tc, test_syn_delta_sw);
+ tcase_add_test(tc, test_syn_delta_fake_mt);
+--
+1.8.5.3
+
diff --git a/0014-If-the-tracking-ID-changes-during-SYN_DROPPED-termin.patch b/0014-If-the-tracking-ID-changes-during-SYN_DROPPED-termin.patch
new file mode 100644
index 0000000..fe8a3dd
--- /dev/null
+++ b/0014-If-the-tracking-ID-changes-during-SYN_DROPPED-termin.patch
@@ -0,0 +1,254 @@
+From cfc2551a255f76d9d6c3732277b6b4647eb2486f Mon Sep 17 00:00:00 2001
+From: Peter Hutterer <peter.hutterer at who-t.net>
+Date: Thu, 6 Mar 2014 11:54:00 +1000
+Subject: [PATCH libevdev 14/14] If the tracking ID changes during SYN_DROPPED,
+ terminate the touch first
+
+Most clients can't deal with tracking ID changes unless a -1 is sent first. So
+if we notice that the tracking ID has changed during the sync process, send a
+set of ABS_MT_TRACKING_ID -1 events for each of those, then send the rest of
+the events.
+
+Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
+Reviewed-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
+(cherry picked from commit 41334b5b40cd5456f5f584b55d8888aaafa1f26e)
+---
+ libevdev/libevdev.c | 26 +++++++
+ test/test-libevdev-events.c | 163 ++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 189 insertions(+)
+
+diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
+index c6d6e24..0470d78 100644
+--- a/libevdev/libevdev.c
++++ b/libevdev/libevdev.c
+@@ -550,6 +550,8 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ int val[MAX_SLOTS];
+ } mt_state;
+ unsigned long slot_update[NLONGS(MAX_SLOTS * ABS_MT_CNT)] = {0};
++ unsigned long tracking_id_changes[NLONGS(MAX_SLOTS)] = {0};
++ int need_tracking_id_changes = 0;
+
+ #define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN)
+
+@@ -579,6 +581,13 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ if (*slot_value(dev, slot, axis) == mt_state.val[slot])
+ continue;
+
++ if (axis == ABS_MT_TRACKING_ID &&
++ *slot_value(dev, slot, axis) != -1 &&
++ mt_state.val[slot] != -1) {
++ set_bit(tracking_id_changes, slot);
++ need_tracking_id_changes = 1;
++ }
++
+ *slot_value(dev, slot, axis) = mt_state.val[slot];
+
+ set_bit(slot_update, AXISBIT(slot, axis));
+@@ -595,6 +604,23 @@ sync_mt_state(struct libevdev *dev, int create_events)
+ goto out;
+ }
+
++ if (need_tracking_id_changes) {
++ for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
++ if (!bit_is_set(tracking_id_changes, slot))
++ continue;
++
++ ev = queue_push(dev);
++ init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot);
++ ev = queue_push(dev);
++ init_event(dev, ev, EV_ABS, ABS_MT_TRACKING_ID, -1);
++
++ last_reported_slot = slot;
++ }
++
++ ev = queue_push(dev);
++ init_event(dev, ev, EV_SYN, SYN_REPORT, 0);
++ }
++
+ for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+ if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT)))
+ continue;
+diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
+index 481416c..27f68ac 100644
+--- a/test/test-libevdev-events.c
++++ b/test/test-libevdev-events.c
+@@ -773,6 +773,168 @@ START_TEST(test_syn_delta_sw)
+ }
+ END_TEST
+
++START_TEST(test_syn_delta_tracking_ids)
++{
++ struct uinput_device* uidev;
++ struct libevdev *dev;
++ int rc;
++ struct input_event ev;
++ struct input_absinfo abs[6];
++ int i;
++ const int num_slots = 15;
++ int slot = -1;
++ unsigned long terminated[NLONGS(num_slots)];
++ unsigned long restarted[NLONGS(num_slots)];
++
++ memset(abs, 0, sizeof(abs));
++ abs[0].value = ABS_X;
++ abs[0].maximum = 1000;
++ abs[1].value = ABS_MT_POSITION_X;
++ abs[1].maximum = 1000;
++
++ abs[2].value = ABS_Y;
++ abs[2].maximum = 1000;
++ abs[3].value = ABS_MT_POSITION_Y;
++ abs[3].maximum = 1000;
++
++ abs[4].value = ABS_MT_SLOT;
++ abs[4].maximum = num_slots - 1;
++
++ abs[5].minimum = -1;
++ abs[5].maximum = 255;
++ abs[5].value = ABS_MT_TRACKING_ID;
++
++ rc = test_create_abs_device(&uidev, &dev,
++ 6, abs,
++ EV_SYN, SYN_REPORT,
++ -1);
++ ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
++
++ /* Test the sync process to make sure we get touches terminated when
++ * the tracking id changes:
++ * 1) start a bunch of touch points
++ * 2) read data into libevdev, make sure state is up-to-date
++ * 3) change touchpoints
++ * 3.1) change the tracking ID on some (indicating terminated and
++ * re-started touchpoint)
++ * 3.2) change the tracking ID to -1 on some (indicating termianted
++ * touchpoint)
++ * 3.3) just update the data on others
++ * 4) force a sync on the device
++ * 5) make sure we get the right tracking ID changes in the caller
++ */
++
++ /* Start a bunch of touch points */
++ for (i = num_slots; i >= 0; i--) {
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, i);
++ uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i);
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++ do {
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
++ ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC);
++ } while (rc >= 0);
++ }
++
++ /* we have a bunch of touches now, and libevdev knows it. Change all
++ * touches */
++ for (i = num_slots; i >= 0; i--) {
++ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i);
++ if (i % 3 == 0) {
++ /* change some slots with a new tracking id */
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, num_slots + i);
++ uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i);
++ } else if (i % 3 == 1) {
++ /* stop others */
++ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
++ } else {
++ /* just update */
++ uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i);
++ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i);
++ }
++ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
++ }
++
++ /* Force sync */
++ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
++ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
++
++ /* now check for the right tracking IDs */
++ memset(terminated, 0, sizeof(terminated));
++ memset(restarted, 0, sizeof(restarted));
++ slot = -1;
++ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
++ if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT))
++ continue;
++
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) {
++ slot = ev.value;
++ continue;
++ }
++
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_X) ||
++ libevdev_event_is_code(&ev, EV_ABS, ABS_Y))
++ continue;
++
++ ck_assert_int_ne(slot, -1);
++
++ if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_TRACKING_ID)) {
++ if (slot % 3 == 0) {
++ if (!bit_is_set(terminated, slot)) {
++ ck_assert_int_eq(ev.value, -1);
++ set_bit(terminated, slot);
++ } else {
++ ck_assert_int_eq(ev.value, num_slots + slot);
++ set_bit(restarted, slot);
++ }
++ } else if (slot % 3 == 1) {
++ ck_assert(!bit_is_set(terminated, slot));
++ ck_assert_int_eq(ev.value, -1);
++ set_bit(terminated, slot);
++ } else
++ ck_abort();
++
++ continue;
++ }
++
++ switch(ev.code) {
++ case ABS_MT_POSITION_X:
++ ck_assert_int_eq(ev.value, 200 + slot);
++ break;
++ case ABS_MT_POSITION_Y:
++ ck_assert_int_eq(ev.value, 700 + slot);
++ break;
++ default:
++ ck_abort();
++ }
++ }
++
++ for (i = 0; i < num_slots; i++) {
++ if (i % 3 == 0) {
++ ck_assert(bit_is_set(terminated, i));
++ ck_assert(bit_is_set(restarted, i));
++ } else if (i % 3 == 1) {
++ ck_assert(bit_is_set(terminated, i));
++ ck_assert(!bit_is_set(restarted, i));
++ } else {
++ ck_assert(!bit_is_set(terminated, i));
++ ck_assert(!bit_is_set(restarted, i));
++ }
++ }
++
++ uinput_device_free(uidev);
++ libevdev_free(dev);
++}
++END_TEST
++
+ START_TEST(test_syn_delta_fake_mt)
+ {
+ struct uinput_device* uidev;
+@@ -1568,6 +1730,7 @@ libevdev_events(void)
+ tcase_add_test(tc, test_syn_delta_led);
+ tcase_add_test(tc, test_syn_delta_sw);
+ tcase_add_test(tc, test_syn_delta_fake_mt);
++ tcase_add_test(tc, test_syn_delta_tracking_ids);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("skipped syncs");
+--
+1.8.5.3
+
diff --git a/libevdev.spec b/libevdev.spec
index e27451e..d11e0e0 100644
--- a/libevdev.spec
+++ b/libevdev.spec
@@ -1,6 +1,6 @@
Name: libevdev
Version: 0.6
-Release: 3%{?dist}
+Release: 4%{?dist}
Summary: Kernel Evdev Device Wrapper Library
Group: System Environment/Libraries
@@ -10,8 +10,21 @@ Source0: http://www.freedesktop.org/software/%{name}/%{name}-%{version}.t
# 0.6 has an ABI change, revert here for now until the sonames are bumped, etc.
# https://bugzilla.redhat.com/show_bug.cgi?id=1046426
-Patch0: 0001-Revert-Drop-deprecated-functions.patch
-Patch1: 0001-Revert-Drop-some-leftover-deprecated-constants.patch
+Patch01: 0001-Revert-Drop-deprecated-functions.patch
+Patch02: 0002-Revert-Drop-some-leftover-deprecated-constants.patch
+Patch03: 0003-Plug-a-memory-leak-for-name-uniq-phys.patch
+Patch04: 0004-Fix-memory-leaks-when-failing-to-create-a-uinput-dev.patch
+Patch05: 0005-Don-t-sync-past-MAX_SLOTS-slots.patch
+Patch06: 0006-Add-unlikely-macro.patch
+Patch07: 0007-Dynamically-allocate-the-slot-values.patch
+Patch08: 0008-Reduce-memory-requirement-for-MT-syncing.patch
+Patch09: 0009-Increase-MAX_SLOTS-to-60.patch
+Patch10: 0010-Cap-slot-values-to-the-announced-maximum.patch
+Patch11: 0011-Move-some-functions-macros-between-libevdev-int.h-an.patch
+Patch12: 0012-Don-t-sync-the-MT-state-for-fake-MT-devices.patch
+Patch13: 0013-Send-an-extra-ABS_MT_SLOT-event-to-sync-the-client-u.patch
+Patch14: 0014-If-the-tracking-ID-changes-during-SYN_DROPPED-termin.patch
+
BuildRequires: automake libtool
BuildRequires: python
@@ -29,8 +42,20 @@ Kernel Evdev Device Wrapper Library Development Package.
%prep
%setup -q -n %{name}-%{version}
-%patch0 -p1 -b .revert_functions
-%patch1 -p1 -b .revert_constants
+%patch01 -p1
+%patch02 -p1
+%patch03 -p1
+%patch04 -p1
+%patch05 -p1
+%patch06 -p1
+%patch07 -p1
+%patch08 -p1
+%patch09 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
%build
autoreconf --force -v --install || exit 1
@@ -60,6 +85,9 @@ rm -f %{buildroot}%{_libdir}/*.la
%{_mandir}/man3/libevdev.3*
%changelog
+* Wed Mar 19 2014 Peter Hutterer <peter.hutterer at redhat.com> 0.6-4
+- Backport the SYN_DROPPED fixes from 1.1, too important not to have
+
* Fri Jan 03 2014 Peter Hutterer <peter.hutterer at redhat.com> 0.6-3
- Restore deprecated constants LIBEVDEV_READ_* dropped from 0.6 (#1046426)
More information about the scm-commits
mailing list