From e6682abae36dcfe8a093686a3bd67b3568157c35 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 19 Mar 2015 14:19:58 +1000 Subject: [PATCH] udev: builtin-keyboard: add support for EVDEV_ABS_* Parse properties in the form EVDEV_ABS_00="::::" and apply them to the kernel device. Future processes that open that device will see the updated EV_ABS range. This is particularly useful for touchpads that don't provide a resolution in the kernel driver but can be fixed up through hwdb entries (e.g. bcm5974). All values in the property are optional, e.g. a string of "::45" is valid to set the resolution to 45. The order intentionally orders resolution before fuzz and flat despite it being the last element in the absinfo struct. The use-case for setting fuzz/flat is almost non-existent, resolution is probably the most common case we'll need. To avoid multiple hwdb invocations for the same device, replace the hwdb "keyboard:" prefix with "evdev:" and drop the separate 60-keyboard.rules file. The new 60-evdev.rules is called for all event nodes anyway, we don't need a separate rules file and second callout to the hwdb builtin. (cherry picked from commit 51c0c2869845a058268d54c3111d55d0dd485704) Changes to the upstream commit: - 60-keyboard.rules is left in place, the 60-keyboard.hwdb is left unmodified. This avoids any potential breakage from user-installed hwdb files that are triggered by that rule. Conflicts: hwdb/60-keyboard.hwdb rules/60-keyboard.rules Resolves: #1500119 --- Makefile.am | 2 + hwdb/60-evdev.hwdb | 37 +++++++++++++++++ rules/60-evdev.rules | 14 +++++++ src/udev/udev-builtin-keyboard.c | 86 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 hwdb/60-evdev.hwdb create mode 100644 rules/60-evdev.rules diff --git a/Makefile.am b/Makefile.am index a1ebf5cb0..13c93f485 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3499,6 +3499,7 @@ dist_udevrules_DATA += \ rules/42-usb-hid-pm.rules \ rules/50-udev-default.rules \ rules/60-drm.rules \ + rules/60-evdev.rules \ rules/60-keyboard.rules \ rules/60-persistent-storage-tape.rules \ rules/60-persistent-serial.rules \ @@ -3702,6 +3703,7 @@ dist_udevhwdb_DATA = \ hwdb/20-acpi-vendor.hwdb \ hwdb/20-OUI.hwdb \ hwdb/20-net-ifname.hwdb \ + hwdb/60-evdev.hwdb \ hwdb/60-keyboard.hwdb \ hwdb/70-mouse.hwdb \ hwdb/70-touchpad.hwdb diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb new file mode 100644 index 000000000..ad2d09e72 --- /dev/null +++ b/hwdb/60-evdev.hwdb @@ -0,0 +1,37 @@ +# This file is part of systemd. +# +# The lookup keys are composed in: +# 60-evdev.rules +# +# Note: The format of the "evdev:" prefix match key is a +# contract between the rules file and the hardware data, it might +# change in later revisions to support more or better matches, it +# is not necessarily expected to be a stable ABI. +# +# Match string formats: +# evdev: +# evdev:name::dmi: +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/61-evdev-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the device in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, open a bug report on +# http://bugs.freedesktop.org/enter_bug.cgi?product=systemd +# and include your new rules, a description of the device, and the +# output of +# udevadm info /dev/input/eventXX +# (or /dev/input/event*). +# +# Allowed properties are: +# EVDEV_ABS_=:::: +# +# where is the hexadecimal EV_ABS code as listed in linux/input.h +# and min, max, res, fuzz, flat are the decimal values to the respective +# fields of the struct input_absinfo as listed in linux/input.h. +# If a field is missing the field will be left as-is. Not all fields need to +# be present. e.g. ::45 sets the resolution to 45 units/mm. diff --git a/rules/60-evdev.rules b/rules/60-evdev.rules new file mode 100644 index 000000000..67308ad23 --- /dev/null +++ b/rules/60-evdev.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="evdev_end" +KERNEL!="event*", GOTO="evdev_end" + +# skip later rules when we find something for this input device +IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +# device matching the input device name and the machine's DMI data +KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +LABEL="evdev_end" diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index 86f4018ef..eaa21abf6 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -99,6 +99,69 @@ static void map_keycode(int fd, const char *devnode, int scancode, const char *k log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key); } +static inline char* parse_token(const char *current, int32_t *val_out) { + char *next; + int32_t val; + + if (!current) + return NULL; + + val = strtol(current, &next, 0); + if (*next && *next != ':') + return NULL; + + if (next != current) + *val_out = val; + + if (*next) + next++; + + return next; +} + +static void override_abs(int fd, const char *devnode, + unsigned evcode, const char *value) { + struct input_absinfo absinfo; + int rc; + char *next; + + rc = ioctl(fd, EVIOCGABS(evcode), &absinfo); + if (rc < 0) { + log_error_errno(errno, "Error, unable to EVIOCGABS device '%s'", + devnode); + return; + } + + next = parse_token(value, &absinfo.minimum); + next = parse_token(next, &absinfo.maximum); + next = parse_token(next, &absinfo.resolution); + next = parse_token(next, &absinfo.fuzz); + next = parse_token(next, &absinfo.flat); + if (!next) { + log_error("Error, unable to parse EV_ABS override '%s' for '%s'\n", + value, devnode); + return; + } + + log_debug("keyboard: override %x with %d/%d/%d/%d/%d", evcode, + absinfo.minimum, absinfo.maximum, absinfo.resolution, + absinfo.fuzz, absinfo.flat); + rc = ioctl(fd, EVIOCSABS(evcode), &absinfo); + if (rc < 0) + log_error_errno(errno, "Error, unable to update device '%s'", + devnode); +} + +static int open_device(const char *devnode) { + int fd; + + fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + log_error_errno(errno, "Error, opening device '%s': %m", devnode); + + return fd < 0 ? -errno : fd; +} + static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) { struct udev_list_entry *entry; unsigned release[1024]; @@ -143,14 +206,29 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo } if (fd == -1) { - fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (fd < 0) { - log_error_errno(errno, "Error, opening device '%s': %m", node); + fd = open_device(node); + if (fd < 0) return EXIT_FAILURE; - } } map_keycode(fd, node, scancode, keycode); + } else if (startswith(key, "EVDEV_ABS_")) { + unsigned evcode; + + /* EVDEV_ABS_=:::: */ + evcode = strtoul(key + 10, &endptr, 16); + if (endptr[0] != '\0') { + log_error("Error, unable to parse EV_ABS code from '%s'", key); + continue; + } + + if (fd == -1) { + fd = open_device(node); + if (fd < 0) + return EXIT_FAILURE; + } + + override_abs(fd, node, evcode, udev_list_entry_get_value(entry)); } }