summaryrefslogtreecommitdiff
path: root/udev
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2019-09-04 15:09:13 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2019-09-11 12:24:02 +1000
commite7a9c07ffe9009c370ba73494729713ee9a7a279 (patch)
tree766711e7dd53599b02e9002448892dd390dfac10 /udev
parent1e6802b91b0c6ed377b21f553c3c842a95068df9 (diff)
udev: parse the EVDEV_ABS properties for a potential fuzz setting
Where a fuzz is defined in the 60-evdev.hwdb, we rely on a udev builtin to set the kernel device to that fuzz value. Unfortunately that happens after our program is called with this order of events: 1. 60-evdev.rules calls IMPORT(builtin) for the hwdb which sets the EVDEV_ABS_* properties. It also sets RUN{builtin}=keyboard but that's not invoked yet. 2. 90-libinput-fuzz-override.rules calls IMPORT{program} for our fuzz override bits. That sets the kernel fuzz value to 0 and sets the LIBINPUT_FUZZ_* propertie 3. The keyboard builtin is run once all the rules have been processed. Our problem is that where the fuzz is set in a hwdb entry, the kernel fuzz is still unset when we get to look at it, so we always end up with a fuzz of zero for us and a nonzero kernel fuzz. Work around this by checking the EVDEV_ABS property, extracting the fuzz from there and re-printing that property without the fuzz. This way we ensure the kernel remains at zero fuzz and we use the one from the hwdb instead. Fixes #346 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'udev')
-rw-r--r--udev/90-libinput-fuzz-override.rules.in15
-rw-r--r--udev/libinput-fuzz-extract.c150
-rw-r--r--udev/libinput-fuzz-to-zero.c (renamed from udev/libinput-fuzz-override.c)18
-rw-r--r--udev/test-libinput-fuzz-extract.c126
4 files changed, 293 insertions, 16 deletions
diff --git a/udev/90-libinput-fuzz-override.rules.in b/udev/90-libinput-fuzz-override.rules.in
index e3d8e537..bcee4638 100644
--- a/udev/90-libinput-fuzz-override.rules.in
+++ b/udev/90-libinput-fuzz-override.rules.in
@@ -6,15 +6,22 @@
ACTION!="add|change", GOTO="libinput_fuzz_override_end"
KERNEL!="event*", GOTO="libinput_fuzz_override_end"
-# libinput-fuzz-override must only be called once per device, otherwise
-# we'll lose the fuzz information
+# Two-step process: fuzz-extract sets the LIBINPUT_FUZZ property and
+# fuzz-to-zero sets the kernel fuzz to zero. They must be in IMPORT and RUN,
+# respectively, to correctly interact with the 60-evdev.hwdb
+#
+# Drawback: if this rule is triggered more than once, we'll lose the fuzz
+# information (because the kernel fuzz will then be zero). Nothing we can do
+# about that.
ATTRS{capabilities/abs}!="0", \
ENV{ID_INPUT_TOUCHPAD}=="1", \
- IMPORT{program}="@UDEV_TEST_PATH@libinput-fuzz-override %S%p", \
+ IMPORT{program}="@UDEV_TEST_PATH@libinput-fuzz-extract %S%p", \
+ RUN{program}="@UDEV_TEST_PATH@libinput-fuzz-to-zero %S%p", \
GOTO="libinput_fuzz_override_end"
ATTRS{capabilities/abs}!="0", \
ENV{ID_INPUT_TOUCHSCREEN}=="1", \
- IMPORT{program}="@UDEV_TEST_PATH@libinput-fuzz-override %S%p", \
+ IMPORT{program}="@UDEV_TEST_PATH@libinput-fuzz-extract %S%p", \
+ RUN{program}="@UDEV_TEST_PATH@libinput-fuzz-to-zero %S%p", \
GOTO="libinput_fuzz_override_end"
LABEL="libinput_fuzz_override_end"
diff --git a/udev/libinput-fuzz-extract.c b/udev/libinput-fuzz-extract.c
new file mode 100644
index 00000000..192b4700
--- /dev/null
+++ b/udev/libinput-fuzz-extract.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <libudev.h>
+#include <linux/input.h>
+#include <libevdev/libevdev.h>
+
+#include "util-prop-parsers.h"
+#include "util-macros.h"
+
+/**
+ * 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_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) {
+ int fuzz;
+
+ fuzz = libevdev_get_abs_fuzz(evdev, *code);
+ if (fuzz)
+ printf("LIBINPUT_FUZZ_%02x=%d\n", *code, fuzz);
+ }
+
+out:
+ close(fd);
+ libevdev_free(evdev);
+}
+
+/**
+ * Where a device has EVDEV_ABS_... set with a fuzz, that fuzz hasn't been
+ * applied to the kernel yet. So we need to extract it ourselves **and**
+ * update the property so the kernel won't actually set it later.
+ */
+static void
+handle_evdev_abs(struct udev_device *device)
+{
+ unsigned int *code;
+ unsigned int axes[] = {ABS_X,
+ ABS_Y,
+ ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y};
+
+ ARRAY_FOR_EACH(axes, code) {
+ const char *prop;
+ char name[64];
+ uint32_t mask;
+ struct input_absinfo abs;
+
+ snprintf(name, sizeof(name), "EVDEV_ABS_%02X", *code);
+ prop = udev_device_get_property_value(device, name);
+ if (!prop)
+ continue;
+
+ mask = parse_evdev_abs_prop(prop, &abs);
+ if (mask & ABS_MASK_FUZZ)
+ printf("LIBINPUT_FUZZ_%02x=%d\n", *code, abs.fuzz);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rc = 1;
+ struct udev *udev = NULL;
+ struct udev_device *device = NULL;
+ const char *syspath;
+
+ if (argc != 2)
+ return 1;
+
+ syspath = argv[1];
+
+ udev = udev_new();
+ if (!udev)
+ goto out;
+
+ device = udev_device_new_from_syspath(udev, syspath);
+ if (!device)
+ goto out;
+
+ handle_absfuzz(device);
+ handle_evdev_abs(device);
+
+ rc = 0;
+
+out:
+ if (device)
+ udev_device_unref(device);
+ if (udev)
+ udev_unref(udev);
+
+ return rc;
+}
diff --git a/udev/libinput-fuzz-override.c b/udev/libinput-fuzz-to-zero.c
index bb05886c..758b1ae2 100644
--- a/udev/libinput-fuzz-override.c
+++ b/udev/libinput-fuzz-to-zero.c
@@ -27,21 +27,19 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <libudev.h>
#include <linux/input.h>
#include <libevdev/libevdev.h>
-#include "libinput-util.h"
+#include "util-strings.h"
+#include "util-macros.h"
+#include "util-bits.h"
-/**
- * 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)
+reset_absfuzz_to_zero(struct udev_device *device)
{
const char *devnode;
struct libevdev *evdev = NULL;
@@ -58,8 +56,6 @@ handle_absfuzz(struct udev_device *device)
goto out;
fd = open(devnode, O_RDWR);
- if (fd == -1 && errno == EACCES)
- fd = open(devnode, O_RDONLY);
if (fd < 0)
goto out;
@@ -81,8 +77,6 @@ handle_absfuzz(struct udev_device *device)
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:
@@ -110,7 +104,7 @@ int main(int argc, char **argv)
if (!device)
goto out;
- handle_absfuzz(device);
+ reset_absfuzz_to_zero(device);
rc = 0;
diff --git a/udev/test-libinput-fuzz-extract.c b/udev/test-libinput-fuzz-extract.c
new file mode 100644
index 00000000..05d9f39d
--- /dev/null
+++ b/udev/test-libinput-fuzz-extract.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <check.h>
+
+/* remove the main() from the included program so we can define our own */
+#define main __disabled
+int main(int argc, char **argv);
+#include "libinput-fuzz-extract.c"
+#undef main
+
+START_TEST(test_parse_ev_abs)
+{
+ struct test {
+ uint32_t which;
+ const char *prop;
+ int min, max, res, fuzz, flat;
+
+ } tests[] = {
+ { .which = (MIN|MAX),
+ .prop = "1:2",
+ .min = 1, .max = 2 },
+ { .which = (MIN|MAX),
+ .prop = "1:2:",
+ .min = 1, .max = 2 },
+ { .which = (MIN|MAX|RES),
+ .prop = "10:20:30",
+ .min = 10, .max = 20, .res = 30 },
+ { .which = (RES),
+ .prop = "::100",
+ .res = 100 },
+ { .which = (MIN),
+ .prop = "10:",
+ .min = 10 },
+ { .which = (MAX|RES),
+ .prop = ":10:1001",
+ .max = 10, .res = 1001 },
+ { .which = (MIN|MAX|RES|FUZZ),
+ .prop = "1:2:3:4",
+ .min = 1, .max = 2, .res = 3, .fuzz = 4},
+ { .which = (MIN|MAX|RES|FUZZ|FLAT),
+ .prop = "1:2:3:4:5",
+ .min = 1, .max = 2, .res = 3, .fuzz = 4, .flat = 5},
+ { .which = (MIN|RES|FUZZ|FLAT),
+ .prop = "1::3:4:50",
+ .min = 1, .res = 3, .fuzz = 4, .flat = 50},
+ { .which = FUZZ|FLAT,
+ .prop = ":::5:60",
+ .fuzz = 5, .flat = 60},
+ { .which = FUZZ,
+ .prop = ":::5:",
+ .fuzz = 5 },
+ { .which = RES, .prop = "::12::",
+ .res = 12 },
+ /* Malformed property but parsing this one makes us more
+ * future proof */
+ { .which = (RES|FUZZ|FLAT), .prop = "::12:1:2:3:4:5:6",
+ .res = 12, .fuzz = 1, .flat = 2 },
+ { .which = 0, .prop = ":::::" },
+ { .which = 0, .prop = ":" },
+ { .which = 0, .prop = "" },
+ { .which = 0, .prop = ":asb::::" },
+ { .which = 0, .prop = "foo" },
+ };
+ struct test *t;
+
+ ARRAY_FOR_EACH(tests, t) {
+ struct input_absinfo abs;
+ uint32_t mask;
+
+ mask = parse_ev_abs_prop(t->prop, &abs);
+ ck_assert_int_eq(mask, t->which);
+
+ if (t->which & MIN)
+ ck_assert_int_eq(abs.minimum, t->min);
+ if (t->which & MAX)
+ ck_assert_int_eq(abs.maximum, t->max);
+ if (t->which & RES)
+ ck_assert_int_eq(abs.resolution, t->res);
+ if (t->which & FUZZ)
+ ck_assert_int_eq(abs.fuzz, t->fuzz);
+ if (t->which & FLAT)
+ ck_assert_int_eq(abs.flat, t->flat);
+ }
+}
+END_TEST
+
+int main(int argc, char **argv) {
+
+ SRunner *sr = srunner_create(NULL);
+ Suite *s = suite_create("fuzz-override");
+ TCase *tc = tcase_create("parser");
+ int nfailed;
+
+ tcase_add_test(tc, test_parse_ev_abs);
+ suite_add_tcase(s, tc);
+ srunner_add_suite(sr, s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nfailed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return nfailed;
+}