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