[systemd-devel] [RFC PATCH] udev: add an "abs-override" builtin to override EV_ABS data

Peter Hutterer peter.hutterer at who-t.net
Thu Mar 19 02:52:34 PDT 2015


This builtin parses properties in the form
EV_ABS_OVERRIDE_00="<min>:<max>:<res>:<fuzz>:<flat>"

and applies 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.
---
This is just an RFC for now, a couple of questions remain:
* we need this for touchpads, but this seems something that could be more
  generic and have it's own ruleset, possibly depending on ID_INPUT_TOUCH*
  or so
* input_id is already called and opens the device, we could merge this in
  there
* happy to do any changes in the property format
* having the axis name spelled out would be nice (EV_OVERRIDE_ABS_X) but
  that would be a bit of work (without using libevdev anyway) and probably
  not worth it.
* for the apple touchpads, this should be fairly straightforward since there
  aren't that many of them and they have usbids. for alps, elantech we may
  have to resort to dmi matching.

other comments welcome too.

Cheers,
   Peter

 Makefile.am                          |   1 +
 hwdb/70-touchpad.hwdb                |   9 +++
 rules/70-touchpad.rules              |   5 ++
 src/udev/udev-builtin-abs-override.c | 132 +++++++++++++++++++++++++++++++++++
 src/udev/udev-builtin.c              |   1 +
 src/udev/udev.h                      |   2 +
 6 files changed, 150 insertions(+)
 create mode 100644 src/udev/udev-builtin-abs-override.c

diff --git a/Makefile.am b/Makefile.am
index 856accb..86bf53d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3771,6 +3771,7 @@ libudev_core_la_SOURCES = \
 	src/udev/udev-builtin-net_setup_link.c \
 	src/udev/udev-builtin-path_id.c \
 	src/udev/udev-builtin-usb_id.c \
+	src/udev/udev-builtin-abs-override.c \
 	src/udev/net/link-config.h \
 	src/udev/net/link-config.c \
 	src/udev/net/ethtool-util.h \
diff --git a/hwdb/70-touchpad.hwdb b/hwdb/70-touchpad.hwdb
index 8a32446..2a34762 100644
--- a/hwdb/70-touchpad.hwdb
+++ b/hwdb/70-touchpad.hwdb
@@ -41,3 +41,12 @@ touchpad:pnpid:*LEN004a*:
 # Lenovo T450s
 touchpad:pnpid:*LEN200f*:
  TOUCHPAD_HAS_TRACKPOINT_BUTTONS=1
+
+# Macbook5,1 (unibody), aka wellspring3
+touchpad:usb:v5acp0236:
+touchpad:usb:v5acp0237:
+touchpad:usb:v5acp0238:
+ EV_ABS_OVERRIDE_00=::92
+ EV_ABS_OVERRIDE_01=::90
+ EV_ABS_OVERRIDE_35=::92
+ EV_ABS_OVERRIDE_36=::90
diff --git a/rules/70-touchpad.rules b/rules/70-touchpad.rules
index 88e6fd2..4127cac 100644
--- a/rules/70-touchpad.rules
+++ b/rules/70-touchpad.rules
@@ -9,4 +9,9 @@ KERNELS=="serio1", \
     IMPORT{builtin}="hwdb 'touchpad:pnpid:$attr{firmware_id}:'", \
     GOTO="touchpad_end"
 
+KERNELS=="usb", \
+    IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:$attr{id/vendor}p$attr{id/product}:'", \
+    IMPORT{builtin}="abs-override", \
+    GOTO="mouse_end"
+
 LABEL="touchpad_end"
diff --git a/src/udev/udev-builtin-abs-override.c b/src/udev/udev-builtin-abs-override.c
new file mode 100644
index 0000000..08e8741
--- /dev/null
+++ b/src/udev/udev-builtin-abs-override.c
@@ -0,0 +1,132 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Red Hat, Inc.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "udev.h"
+#include <linux/input.h>
+
+static int override_abs(int fd, unsigned evcode, const char *value) {
+        struct input_absinfo absinfo;
+        int rc;
+        char *next, *current;
+        int32_t val;
+
+        rc = ioctl(fd, EVIOCGABS(evcode), &absinfo);
+        if (rc < 0) {
+                log_error_errno(errno, "Error, unable to EVIOCABS the device");
+                return EXIT_FAILURE;
+        }
+
+        val = strtol(value, &next, 0);
+        if (*next && *next != ':')
+                goto parse_error;
+        if (next != value)
+                absinfo.minimum = val;
+
+        if (*next) {
+                current = ++next;
+                val = strtol(next, &next, 0);
+                if (*next && *next != ':')
+                        goto parse_error;
+                if (next != current)
+                        absinfo.maximum = val;
+        }
+
+        if (*next) {
+                current = ++next;
+                val = strtol(next, &next, 0);
+                if (*next && *next != ':')
+                        goto parse_error;
+                if (next != current)
+                        absinfo.resolution = val;
+        }
+
+        if (*next) {
+                current = ++next;
+                val = strtol(next, &next, 0);
+                if (*next && *next != ':')
+                        goto parse_error;
+                if (next != current)
+                        absinfo.fuzz = val;
+        }
+
+        if (*next) {
+                current = ++next;
+                val = strtol(next, &next, 0);
+                if (*next && *next != ':')
+                        goto parse_error;
+                if (next != current)
+                        absinfo.flat = val;
+        }
+
+        rc = ioctl(fd, EVIOCSABS(evcode), &absinfo);
+        if (rc < 0) {
+                log_error_errno(errno, "Error, unable to update the device");
+                return EXIT_FAILURE;
+        }
+
+        return EXIT_SUCCESS;
+
+parse_error:
+        log_error("Error, unable to parse EV_ABS override '%s'\n", value);
+        return EXIT_FAILURE;
+}
+
+static int builtin_abs_override(struct udev_device *dev, int argc, char *argv[], bool test) {
+        struct udev_list_entry *entry;
+        int fd = -1;
+        int rc = EXIT_SUCCESS;
+
+        udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) {
+                const char KEY_PREFIX[] = "EV_ABS_OVERRIDE_";
+                const char *key, *value;
+                char *endptr;
+                unsigned evcode;
+
+                key = udev_list_entry_get_name(entry);
+                if (!startswith(key, KEY_PREFIX))
+                        continue;
+
+                /* EV_ABS_OVERRIDE_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
+                evcode = strtoul(key + strlen(KEY_PREFIX), &endptr, 16);
+                if (endptr[0] != '\0') {
+                        log_error("Error, unable to parse EV_ABS code from '%s'", key);
+                        continue;
+                }
+
+                if (fd == -1) {
+                        fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+                        if (fd < 0)
+                                return EXIT_FAILURE;
+                }
+
+                value = udev_list_entry_get_value(entry);
+                if (override_abs(fd, evcode, value) != EXIT_SUCCESS)
+                        rc = EXIT_FAILURE;
+        }
+
+        close(fd);
+
+        return rc;
+}
+
+const struct udev_builtin udev_builtin_abs_override = {
+        .name = "abs-override",
+        .cmd = builtin_abs_override,
+        .help = "EV_ABS override",
+};
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index fabc653..eb0825e 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -43,6 +43,7 @@ static const struct udev_builtin *builtins[] = {
 #ifdef HAVE_ACL
         [UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
 #endif
+        [UDEV_BUILTIN_ABS_OVERRIDE] = &udev_builtin_abs_override,
 };
 
 void udev_builtin_init(struct udev *udev) {
diff --git a/src/udev/udev.h b/src/udev/udev.h
index dece6ec..ec6c719 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -164,6 +164,7 @@ enum udev_builtin_cmd {
 #ifdef HAVE_ACL
         UDEV_BUILTIN_UACCESS,
 #endif
+        UDEV_BUILTIN_ABS_OVERRIDE,
         UDEV_BUILTIN_MAX
 };
 struct udev_builtin {
@@ -190,6 +191,7 @@ extern const struct udev_builtin udev_builtin_net_setup_link;
 extern const struct udev_builtin udev_builtin_path_id;
 extern const struct udev_builtin udev_builtin_usb_id;
 extern const struct udev_builtin udev_builtin_uaccess;
+extern const struct udev_builtin udev_builtin_abs_override;
 void udev_builtin_init(struct udev *udev);
 void udev_builtin_exit(struct udev *udev);
 enum udev_builtin_cmd udev_builtin_lookup(const char *command);
-- 
2.3.2



More information about the systemd-devel mailing list