From 9d7d42bc406a2ac04639674281ce3ff6beeda790 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 10 Jul 2013 16:02:24 +0200 Subject: [PATCH] udev: add builtin 'keyboard' to manage key mappings --- Makefile.am | 27 +++++ rules/60-keyboard.rules | 22 +++++ shell-completion/bash/udevadm | 2 +- src/udev/.gitignore | 4 + src/udev/udev-builtin-keyboard.c | 163 +++++++++++++++++++++++++++++++ src/udev/udev-builtin.c | 1 + src/udev/udev.h | 2 + 7 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 rules/60-keyboard.rules create mode 100644 src/udev/udev-builtin-keyboard.c diff --git a/Makefile.am b/Makefile.am index aa7e83022..f1dfeedf5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -174,6 +174,7 @@ AM_CPPFLAGS = \ -I $(top_srcdir)/src/core \ -I $(top_srcdir)/src/libudev \ -I $(top_srcdir)/src/udev \ + -I $(top_builddir)/src/udev \ -I $(top_srcdir)/src/libsystemd-bus \ $(OUR_CPPFLAGS) @@ -2094,6 +2095,7 @@ dist_udevrules_DATA += \ rules/99-systemd.rules \ rules/42-usb-hid-pm.rules \ rules/50-udev-default.rules \ + rules/60-keyboard.rules \ rules/60-persistent-storage-tape.rules \ rules/60-persistent-serial.rules \ rules/60-persistent-input.rules \ @@ -2157,6 +2159,19 @@ rootlibexec_PROGRAMS += \ noinst_LTLIBRARIES += \ libudev-core.la +src/udev/keyboard-keys.txt: Makefile + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include linux/input.h - < /dev/null | $(AWK) '/^#define[ \t]+KEY_[^ ]+[ \t]+[0-9]/ { if ($$2 != "KEY_MAX") { print $$2 } }' | sed 's/^KEY_COFFEE$$/KEY_SCREENLOCK/' > $@ + +src/udev/keyboard-keys-from-name.gperf: src/udev/keyboard-keys.txt Makefile + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print tolower(substr($$1 ,5)) ", " $$1 }' < $< > $@ + +src/udev/keyboard-keys-from-name.h: src/udev/keyboard-keys-from-name.gperf Makefile + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t -N keyboard_lookup_key -H hash_key_name -p -C < $< > $@ + +src/udev/keyboard-keys-to-name.h: src/udev/keyboard-keys.txt Makefile + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ + libudev_core_la_SOURCES = \ src/udev/udev.h \ src/udev/udev-event.c \ @@ -2168,10 +2183,22 @@ libudev_core_la_SOURCES = \ src/udev/udev-builtin-btrfs.c \ src/udev/udev-builtin-hwdb.c \ src/udev/udev-builtin-input_id.c \ + src/udev/udev-builtin-keyboard.c \ src/udev/udev-builtin-net_id.c \ src/udev/udev-builtin-path_id.c \ src/udev/udev-builtin-usb_id.c +nodist_libudev_core_la_SOURCES = \ + src/udev/keyboard-keys-from-name.h \ + src/udev/keyboard-keys-to-name.h + +BUILT_SOURCES += \ + $(nodist_libudev_core_la_SOURCES) + +CLEANFILES += \ + src/udev/keyboard-keys-from-name.gperf \ + src/udev/keyboard-keys.txt + libudev_core_la_CFLAGS = \ $(AM_CFLAGS) \ $(BLKID_CFLAGS) \ diff --git a/rules/60-keyboard.rules b/rules/60-keyboard.rules new file mode 100644 index 000000000..b925853c3 --- /dev/null +++ b/rules/60-keyboard.rules @@ -0,0 +1,22 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add", GOTO="keyboard_end" +KERNEL!="event*", GOTO="keyboard_end" +ENV{ID_INPUT_KEY}=="", GOTO="keyboard_end" + +# ignore all bluetooth devices +SUBSYSTEMS=="bluetooth", GOTO="keyboard_end" + +# import key mapping for USB device +SUBSYSTEMS=="usb", IMPORT{builtin}="hwdb --subsystem=usb --lookup-prefix=keyboard:", \ + RUN{program}+="keyboard", GOTO="keyboard_end" + +# import key mapping for AT keyboard from DMI data +DRIVERS=="atkbd", IMPORT{builtin}="hwdb 'keyboard:$attr{[dmi/id]modalias}'", \ + RUN{program}+="keyboard", GOTO="keyboard_end" + +# import key mapping for platform input device +KERNELS=="input*", IMPORT{builtin}="hwdb 'keyboard:name:$attr{name}:$attr{[dmi/id]modalias}'", \ + RUN{program}+="keyboard", GOTO="keyboard_end" + +LABEL="keyboard_end" diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index 123fb5163..e9ad17920 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -93,7 +93,7 @@ _udevadm() { fi elif __contains_word "$verb" ${VERBS[TESTBUILTIN]}; then - comps='blkid btrfs hwdb input_id kmod net_id path_id usb_id uaccess' + comps='blkid btrfs hwdb input_id keyboard kmod net_id path_id usb_id uaccess' fi COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) diff --git a/src/udev/.gitignore b/src/udev/.gitignore index 3e375a772..a229430e3 100644 --- a/src/udev/.gitignore +++ b/src/udev/.gitignore @@ -1 +1,5 @@ /udev.pc +/keyboard-keys-from-name.gperf +/keyboard-keys-from-name.h +/keyboard-keys-to-name.h +/keyboard-keys.txt diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c new file mode 100644 index 000000000..ddd853594 --- /dev/null +++ b/src/udev/udev-builtin-keyboard.c @@ -0,0 +1,163 @@ +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static const struct key *keyboard_lookup_key(const char *str, unsigned int len); +#include "keyboard-keys-from-name.h" +#include "keyboard-keys-to-name.h" + +static int install_force_release(struct udev_device *dev, const unsigned int *release, unsigned int release_count) { + struct udev_device *atkbd; + const char *cur; + char codes[4096]; + char *s; + size_t l; + unsigned int i; + int ret; + + atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); + if (!atkbd) + return -ENODEV; + + cur = udev_device_get_sysattr_value(atkbd, "force_release"); + if (!cur) + return -ENODEV; + + s = codes; + l = sizeof(codes); + + /* copy current content */ + l = strpcpy(&s, l, cur); + + /* append new codes */ + for (i = 0; i < release_count; i++) + l = strpcpyf(&s, l, ",%d", release[i]); + + log_debug("keyboard: updating force-release list with '%s'\n", codes); + ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); + if (ret < 0) + log_error("Error writing force-release attribute: %s", strerror(-ret)); + return ret; +} + +static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) { + struct udev_list_entry *entry; + struct { + unsigned int scan; + unsigned int key; + } map[1024]; + unsigned int map_count = 0; + unsigned int release[1024]; + unsigned int release_count = 0; + + udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) { + const char *key; + unsigned int scancode; + char *endptr; + const char *keycode; + const struct key *k; + + key = udev_list_entry_get_name(entry); + if (!startswith(key, "KEYBOARD_KEY_")) + continue; + + /* KEYBOARD_KEY_= */ + scancode = strtol(key + 13, &endptr, 16); + if (endptr[0] != '\0') { + log_error("Error, unable to parse scan code from '%s'\n", key); + continue; + } + + keycode = udev_list_entry_get_value(entry); + + /* a leading '!' needs a force-release entry */ + if (keycode[0] == '!') { + keycode++; + + release[release_count] = scancode; + if (release_count < ELEMENTSOF(release)-1) + release_count++; + + if (keycode[0] == '\0') + continue; + } + + /* translate identifier to key code */ + k = keyboard_lookup_key(keycode, strlen(keycode)); + if (!k) { + log_error("Error, unknown key identifier '%s'\n", keycode); + continue; + } + + map[map_count].scan = scancode; + map[map_count].key = k->id; + if (map_count < ELEMENTSOF(map)-1) + map_count++; + } + + if (map_count > 0 || release_count > 0) { + const char *node; + int fd; + unsigned int i; + + node = udev_device_get_devnode(dev); + if (!node) { + log_error("Error, no device node for '%s'\n", udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd < 0) { + log_error("Error, opening device '%s': %m\n", node); + return EXIT_FAILURE; + } + + /* install list of map codes */ + for (i = 0; i < map_count; i++) { + log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)\n", + map[i].scan, map[i].scan, map[i].key, map[i].key); + if (ioctl(fd, EVIOCSKEYCODE, &map[i]) < 0) + log_error("Error calling EVIOCSKEYCODE: %m\n"); + } + + /* install list of force-release codes */ + if (release_count > 0) + install_force_release(dev, release, release_count); + + close(fd); + } + + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_keyboard = { + .name = "keyboard", + .cmd = builtin_keyboard, + .help = "keyboard scan code to key mapping", +}; diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index c7d431988..6b3a518c2 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -39,6 +39,7 @@ static const struct udev_builtin *builtins[] = { #endif [UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb, [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, + [UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard, #ifdef HAVE_KMOD [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, #endif diff --git a/src/udev/udev.h b/src/udev/udev.h index 4f10c452e..c9408f2d4 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -145,6 +145,7 @@ enum udev_builtin_cmd { #endif UDEV_BUILTIN_HWDB, UDEV_BUILTIN_INPUT_ID, + UDEV_BUILTIN_KEYBOARD, #ifdef HAVE_KMOD UDEV_BUILTIN_KMOD, #endif @@ -174,6 +175,7 @@ extern const struct udev_builtin udev_builtin_firmware; #endif extern const struct udev_builtin udev_builtin_hwdb; extern const struct udev_builtin udev_builtin_input_id; +extern const struct udev_builtin udev_builtin_keyboard; #ifdef HAVE_KMOD extern const struct udev_builtin udev_builtin_kmod; #endif -- 2.30.2