[PATCH libinput 3/3] Extract and reset the abs fuzz value for the x/y axes

Peter Hutterer peter.hutterer at who-t.net
Mon Mar 5 04:38:02 UTC 2018


The kernel fuzz handling is buggy, especially when we want to rely on the fuzz
value for our hysteresis. But since this is a hw property and (at least
sometimes) set by the driver, we can't make this a pure libinput hwdb set
either.

So our workaround is:
* extract the (non-zero) fuzz into a udev property so we don't lose it
* set the fuzz to 0 to disable the in-kernel hysteresis
* overwrite our internal absinfo with the property fuzz

This way we get to use the hw-specified fuzz without having the kernel muck
around with it. We also get to use the EVDEV_ABS_ values in 60-evdev.hwdb to
override a driver-set fuzz.

Two drawbacks:
- we're resetting the kernel fuzz to 0, this affects any other users of the
  device node. That's probably a minor impact only.
- we can only save this in a udev property there's a risk of this information
  getting lost when playing around with udev rules. That too should be a minor
  issue.

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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 meson.build                            |  2 +-
 src/evdev.c                            | 39 +++++++++++++++++++++++
 src/evdev.h                            |  3 ++
 udev/90-libinput-model-quirks.rules.in |  1 -
 udev/libinput-model-quirks.c           | 58 ++++++++++++++++++++++++++++++++++
 5 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/meson.build b/meson.build
index 60caf997..1c12fe1c 100644
--- a/meson.build
+++ b/meson.build
@@ -87,7 +87,7 @@ executable('libinput-device-group',
 	   install_dir : udev_dir)
 executable('libinput-model-quirks',
 	   'udev/libinput-model-quirks.c',
-	   dependencies : dep_udev,
+	   dependencies : [dep_udev, dep_libevdev],
 	   include_directories : [includes_src, includes_include],
 	   install : true,
 	   install_dir : udev_dir)
diff --git a/src/evdev.c b/src/evdev.c
index 257824aa..40a5d975 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1505,6 +1505,7 @@ static void
 evdev_extract_abs_axes(struct evdev_device *device)
 {
 	struct libevdev *evdev = device->evdev;
+	int fuzz;
 
 	if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_Y))
@@ -1512,6 +1513,12 @@ evdev_extract_abs_axes(struct evdev_device *device)
 
 	if (evdev_fix_abs_resolution(device, ABS_X, ABS_Y))
 		device->abs.is_fake_resolution = true;
+
+	if ((fuzz = evdev_read_fuzz_prop(device, ABS_X)))
+	    libevdev_set_abs_fuzz(evdev, ABS_X, fuzz);
+	if ((fuzz = evdev_read_fuzz_prop(device, ABS_Y)))
+	    libevdev_set_abs_fuzz(evdev, ABS_Y, fuzz);
+
 	device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X);
 	device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y);
 	device->abs.dimensions.x = abs(device->abs.absinfo_x->maximum -
@@ -1529,6 +1536,11 @@ evdev_extract_abs_axes(struct evdev_device *device)
 				     ABS_MT_POSITION_Y))
 		device->abs.is_fake_resolution = true;
 
+	if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_X)))
+	    libevdev_set_abs_fuzz(evdev, ABS_X, fuzz);
+	if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_Y)))
+	    libevdev_set_abs_fuzz(evdev, ABS_Y, fuzz);
+
 	device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
 	device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y);
 	device->abs.dimensions.x = abs(device->abs.absinfo_x->maximum -
@@ -2162,6 +2174,33 @@ evdev_read_calibration_prop(struct evdev_device *device)
 		       calibration[5]);
 }
 
+int
+evdev_read_fuzz_prop(struct evdev_device *device, unsigned int code)
+{
+	const char *prop;
+	char name[32];
+	int rc;
+	int fuzz = 0;
+
+	rc = snprintf(name, sizeof(name), "LIBINPUT_FUZZ_%02x", code);
+	if (rc == -1)
+		return 0;
+
+	prop = udev_device_get_property_value(device->udev_device, name);
+	if (prop == NULL)
+		return 0;
+
+	rc = safe_atoi(prop, &fuzz);
+	if (rc == -1 || fuzz < 0) {
+		evdev_log_bug_libinput(device,
+				       "invalid LIBINPUT_FUZZ property value: %s\n",
+				       prop);
+		return 0;
+	}
+
+	return fuzz;
+}
+
 bool
 evdev_device_has_capability(struct evdev_device *device,
 			    enum libinput_device_capability capability)
diff --git a/src/evdev.h b/src/evdev.h
index 7fc21690..e5361ad2 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -378,6 +378,9 @@ evdev_init_calibration(struct evdev_device *device,
 void
 evdev_read_calibration_prop(struct evdev_device *device);
 
+int
+evdev_read_fuzz_prop(struct evdev_device *device, unsigned int code);
+
 enum switch_reliability
 evdev_read_switch_reliability_prop(struct evdev_device *device);
 
diff --git a/udev/90-libinput-model-quirks.rules.in b/udev/90-libinput-model-quirks.rules.in
index e7d56bbe..5ddc0ba4 100644
--- a/udev/90-libinput-model-quirks.rules.in
+++ b/udev/90-libinput-model-quirks.rules.in
@@ -15,7 +15,6 @@ KERNEL!="event*", GOTO="libinput_model_quirks_end"
 # First, run the program and import the LIBINPUT_MODEL_FIRMWARE_VERSION
 # environment (if any)
 KERNELS=="*input*", \
-  ENV{ID_INPUT_TOUCHPAD}=="1", \
   IMPORT{program}="@UDEV_TEST_PATH at libinput-model-quirks %S%p"
 
 # Second, match on anything with that env set and import from the hwdb
diff --git a/udev/libinput-model-quirks.c b/udev/libinput-model-quirks.c
index 2dc917d5..d1d5a68a 100644
--- a/udev/libinput-model-quirks.c
+++ b/udev/libinput-model-quirks.c
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <libudev.h>
 #include <linux/input.h>
+#include <libevdev/libevdev.h>
 
 #include "libinput-util.h"
 
@@ -107,6 +108,61 @@ handle_touchpad(struct udev_device *device)
 		handle_touchpad_synaptics(device);
 }
 
+/**
+ * For a non-zero fuzz on the x/y axes, print that fuzz as property and
+ * reset the kernel's fuzz to 0.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=105202
+ */
+static void
+handle_absfuzz(struct udev_device *device)
+{
+	const char *devnode;
+	struct libevdev *evdev = NULL;
+	int fd = -1;
+	int rc;
+	unsigned int *code;
+	unsigned int axes[] = {ABS_X,
+			       ABS_Y,
+			       ABS_MT_POSITION_X,
+			       ABS_MT_POSITION_Y};
+
+	devnode = udev_device_get_devnode(device);
+	if (!devnode)
+		goto out;
+
+	fd = open(devnode, O_RDWR);
+	if (fd == -1 && errno == EACCES)
+		fd = open(devnode, O_RDONLY);
+	if (fd < 0)
+		goto out;
+
+	rc = libevdev_new_from_fd(fd, &evdev);
+	if (rc != 0)
+		goto out;
+
+	if (!libevdev_has_event_type(evdev, EV_ABS))
+		goto out;
+
+	ARRAY_FOR_EACH(axes, code) {
+		struct input_absinfo abs;
+		int fuzz;
+
+		fuzz = libevdev_get_abs_fuzz(evdev, *code);
+		if (!fuzz)
+			continue;
+
+		abs = *libevdev_get_abs_info(evdev, *code);
+		abs.fuzz = 0;
+		libevdev_kernel_set_abs_info(evdev, *code, &abs);
+
+		printf("LIBINPUT_FUZZ_%02x=%d\n", *code, fuzz);
+	}
+
+out:
+	close(fd);
+	libevdev_free(evdev);
+}
+
 int main(int argc, char **argv)
 {
 	int rc = 1;
@@ -127,6 +183,8 @@ int main(int argc, char **argv)
 	if (!device)
 		goto out;
 
+	handle_absfuzz(device);
+
 	if (prop_value(device, "ID_INPUT_TOUCHPAD"))
 		handle_touchpad(device);
 
-- 
2.14.3



More information about the wayland-devel mailing list