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