[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