diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2019-09-04 15:09:13 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2019-09-11 12:24:02 +1000 |
commit | e7a9c07ffe9009c370ba73494729713ee9a7a279 (patch) | |
tree | 766711e7dd53599b02e9002448892dd390dfac10 /udev | |
parent | 1e6802b91b0c6ed377b21f553c3c842a95068df9 (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.in | 15 | ||||
-rw-r--r-- | udev/libinput-fuzz-extract.c | 150 | ||||
-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.c | 126 |
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; +} |