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