|
|
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 +
|
|
|
ecbff1 |
hwdb/60-evdev.hwdb | 37 +++++++++++++++++
|
|
|
ecbff1 |
rules/60-evdev.rules | 14 +++++++
|
|
|
ecbff1 |
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 |
|