diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/keymap/Makefile.am | 58 | ||||
-rw-r--r-- | src/keymap/README.keymap.txt | 97 | ||||
-rw-r--r-- | src/keymap/check-keymaps.sh.in | 45 | ||||
-rwxr-xr-x | src/keymap/findkeyboards | 68 | ||||
-rw-r--r-- | src/keymap/keyboard-force-release.sh.in | 22 | ||||
-rw-r--r-- | src/keymap/keymap.c | 453 | ||||
-rw-r--r-- | src/udev/Makefile.am | 35 | ||||
-rw-r--r-- | src/udev/udev-builtin-keyboard.c | 163 | ||||
-rw-r--r-- | src/udev/udev-builtin.c | 3 | ||||
-rw-r--r-- | src/udev/udev.h | 6 |
11 files changed, 207 insertions, 748 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index dcf5b54336..d6079f051b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,8 +18,3 @@ if ENABLE_GUDEV SUBDIRS += \ gudev endif - -if ENABLE_KEYMAP -SUBDIRS += \ - keymap -endif diff --git a/src/keymap/Makefile.am b/src/keymap/Makefile.am deleted file mode 100644 index dcc29e9b65..0000000000 --- a/src/keymap/Makefile.am +++ /dev/null @@ -1,58 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} - -AM_CPPFLAGS = \ - -I $(top_srcdir)/src/libudev \ - -I $(top_srcdir)/src/udev \ - -DUDEV_LIBEXEC_DIR=\"$(udevlibexecdir)\" - -udevlibexec_PROGRAMS = \ - keymap - -keymap_SOURCES = \ - keymap.c - -keymap_CPPFLAGS = \ - $(AM_CPPFLAGS) - -keymap_LDADD = \ - $(top_builddir)/src/udev/libudev-core.la - -nodist_keymap_SOURCES = \ - keys-from-name.h \ - keys-to-name.h - -BUILT_SOURCES = \ - $(nodist_keymap_SOURCES) - -dist_doc_DATA = \ - README.keymap.txt - -dist_udevlibexec_SCRIPTS = \ - findkeyboards \ - keyboard-force-release.sh - -TESTS = \ - check-keymaps.sh - -CLEANFILES = \ - keys.txt \ - keys-from-name.gperf \ - keyboard-force-release.sh \ - $(BUILT_SOURCES) - -EXTRA_DIST = \ - check-keymaps.sh \ - keyboard-force-release.sh.in - -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/' > $@ - -keys-from-name.gperf: keys.txt Makefile - $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print $$1 ", " $$1 }' < $< > $@ - -keys-from-name.h: keys-from-name.gperf Makefile - $(AM_V_GEN)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_key -H hash_key_name -p -C < $< > $@ - -keys-to-name.h: keys.txt Makefile - $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ diff --git a/src/keymap/README.keymap.txt b/src/keymap/README.keymap.txt deleted file mode 100644 index 2cf2a4e88c..0000000000 --- a/src/keymap/README.keymap.txt +++ /dev/null @@ -1,97 +0,0 @@ -= The udev keymap tool = - -== Introduction == - -This udev extension configures computer model specific key mappings. This is -particularly necessary for the non-standard extra keys found on many laptops, -such as "brightness up", "next song", "www browser", or "suspend". Often these -are accessed with the Fn key. - -Every key produces a "scan code", which is highly vendor/model specific for the -nonstandard keys. This tool maintains mappings for these scan codes to standard -"key codes", which denote the "meaning" of the key. The key codes are defined -in /usr/include/linux/input.h. - -If some of your keys on your keyboard are not working at all, or produce the -wrong effect, then a very likely cause of this is that the scan code -> key -code mapping is incorrect on your computer. - -== Structure == - -udev-keymap consists of the following parts: - - keymaps/*:: mappings of scan codes to key code names - - 95-keymap.rules:: udev rules for mapping system vendor/product names and - input module names to one of the keymaps above - - keymap:: manipulate an evdev input device: - * write a key map file into a device (used by udev rules) - * dump current scan → key code mapping - * interactively display scan and key codes of pressed keys - - findkeyboards:: display evdev input devices which belong to actual keyboards, - i. e. those suitable for the keymap program - -== Fixing broken keys == - -In order to make a broken key work on your system and send it back to upstream -for inclusion you need to do the following steps: - - 1. Find the keyboard device. - - Run /usr/lib/udev/findkeyboards. This should always give you an "AT - keyboard" and possibly a "module". Some laptops (notably Thinkpads, Sonys, and - Acers) have multimedia/function keys on a separate input device instead of the - primary keyboard. The keyboard device should have a name like "input/event3". - In the following commands, the name will be written as "input/eventX" (replace - X with the appropriate number). - - 2. Find broken scan codes: - - sudo /usr/lib/udev/keymap -i input/eventX - - Press all multimedia/function keys and check if the key name that gets printed - out is plausible. If it is unknown or wrong, write down the scan code (looks - like "0x1E") and the intended functionality of this key. Look in - /usr/include/linux/input.h for an available KEY_XXXXX constant which most - closely approximates this functionality and write it down as the new key code. - - For example, you might press a key labeled "web browser" which currently - produces "unknown". Note down this: - - 0x1E www # Fn+F2 web browser - - Repeat that for all other keys. Write the resulting list into a file. Look at - /usr/lib/udev/keymaps/ for existing key map files and make sure that you use the - same structure. - - If the key only ever works once and then your keyboard (or the entire desktop) - gets stuck for a long time, then it is likely that the BIOS fails to send a - corresponding "key release" event after the key press event. Please note down - this case as well, as it can be worked around in - /usr/lib/udev/keymaps/95-keyboard-force-release.rules . - - 3. Find out your system vendor and product: - - cat /sys/class/dmi/id/sys_vendor - cat /sys/class/dmi/id/product_name - - 4. Generate a device dump with "udevadm info --export-db > /tmp/udev-db.txt". - - 6. Send the system vendor/product names, the key mapping from step 2, - and /tmp/udev-db.txt from step 4 to the linux-hotplug@vger.kernel.org mailing - list, so that they can be included in the next release. - -For local testing, copy your map file to /usr/lib/udev/keymaps/ with an appropriate -name, and add an appropriate udev rule to /usr/lib/udev/rules.d/95-keymap.rules: - - * If you selected an "AT keyboard", add the rule to the section after - 'LABEL="keyboard_vendorcheck"'. - - * If you selected a "module", add the rule to the top section where the - "ThinkPad Extra Buttons" are. - -== Author == - -keymap is written and maintained by Martin Pitt <martin.pitt@ubuntu.com>. diff --git a/src/keymap/check-keymaps.sh.in b/src/keymap/check-keymaps.sh.in deleted file mode 100644 index a62b48d4d5..0000000000 --- a/src/keymap/check-keymaps.sh.in +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# check that all key names in keymaps/* are known in <linux/input.h> -# and that all key maps listed in the rules are valid and present in -# Makefile.am - -top_srcdir=@top_srcdir@ -top_builddir=@top_builddir@ - -SRCDIR=${1:-${top_srcdir}} -BUILDDIR=${1:-${top_builddir}} -KEYLIST=${2:-${BUILDDIR}/src/keymap/keys.txt} - -KEYMAPS_DIR=${SRCDIR}/keymaps -KEYMAPS_LIST=$(ls ${KEYMAPS_DIR}/* | grep -v Makefile) -RULES=${SRCDIR}/rules/95-keymap.rules - -[ -e "$KEYLIST" ] || { - echo "need $KEYLIST please build first" >&2 - exit 1 -} - -missing=$(join -v 2 <(awk '{print tolower(substr($1,5))}' $KEYLIST | sort -u) \ - <(grep -hv '^#' ${KEYMAPS_LIST}| awk '{print $2}' | sort -u)) -[ -z "$missing" ] || { - echo "ERROR: unknown key names in keymaps/*:" >&2 - echo "$missing" >&2 - exit 1 -} - -# check that all maps referred to in $RULES exist -maps=$(sed -rn '/keymap \$name/ { s/^.*\$name ([^"[:space:]]+).*$/\1/; p }' $RULES) -for m in $maps; do - # ignore inline mappings - [ "$m" = "${m#0x}" ] || continue - - [ -e ${KEYMAPS_DIR}/$m ] || { - echo "ERROR: unknown map name in $RULES: $m" >&2 - exit 1 - } - grep -q "$m\>" ${SRCDIR}/keymaps/Makefile.am || { - echo "ERROR: map file $m is not added to Makefile.am" >&2 - exit 1 - } -done diff --git a/src/keymap/findkeyboards b/src/keymap/findkeyboards deleted file mode 100755 index c6b50d12d0..0000000000 --- a/src/keymap/findkeyboards +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -e -# Find "real" keyboard devices and print their device path. -# Author: Martin Pitt <martin.pitt@ubuntu.com> -# -# Copyright (C) 2009, Canonical Ltd. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 -# General Public License for more details. - -# returns OK if $1 contains $2 -strstr() { - [ "${1#*$2*}" != "$1" ] -} - -# returns OK if $1 contains $2 at the beginning -str_starts() { - [ "${1#$2*}" != "$1" ] -} - -str_line_starts() { - while read a; do str_starts "$a" "$1" && return 0;done - return 1; -} - -# print a list of input devices which are keyboard-like -keyboard_devices() { - # standard AT keyboard - for dev in `udevadm trigger --dry-run --verbose --property-match=ID_INPUT_KEYBOARD=1`; do - env=`udevadm info --query=env --path=$dev` - # filter out non-event devices, such as the parent input devices which have no devnode - if ! echo "$env" | str_line_starts 'DEVNAME='; then - continue - fi - walk=`udevadm info --attribute-walk --path=$dev` - if strstr "$walk" 'DRIVERS=="atkbd"'; then - echo -n 'AT keyboard: ' - elif echo "$env" | str_line_starts 'ID_USB_DRIVER=usbhid'; then - echo -n 'USB keyboard: ' - else - echo -n 'Unknown type: ' - fi - udevadm info --query=name --path=$dev - done - - # modules - module=$(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*Extra Buttons') - module="$module - $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*extra buttons')" - module="$module - $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='Sony Vaio Keys')" - for m in $module; do - for evdev in $m/event*/dev; do - if [ -e "$evdev" ]; then - echo -n 'module: ' - udevadm info --query=name --path=${evdev%%/dev} - fi - done - done -} - -keyboard_devices diff --git a/src/keymap/keyboard-force-release.sh.in b/src/keymap/keyboard-force-release.sh.in deleted file mode 100644 index b82674840f..0000000000 --- a/src/keymap/keyboard-force-release.sh.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -e -# read list of scancodes, convert hex to decimal and -# append to the atkbd force_release sysfs attribute -# $1 sysfs devpath for serioX -# $2 file with scancode list (hex or dec) - -case "$2" in - /*) scf="$2" ;; - *) scf="@udevlibexecdir@/keymaps/force-release/$2" ;; -esac - -read attr <"/sys/$1/force_release" -while read scancode dummy; do - case "$scancode" in - \#*) ;; - *) - scancode=$(($scancode)) - attr="$attr${attr:+,}$scancode" - ;; - esac -done <"$scf" -echo "$attr" >"/sys/$1/force_release" diff --git a/src/keymap/keymap.c b/src/keymap/keymap.c deleted file mode 100644 index 793c53429f..0000000000 --- a/src/keymap/keymap.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * keymap - dump keymap of an evdev device or set a new keymap from a file - * - * Based on keyfuzz by Lennart Poettering <mzqrovna@0pointer.net> - * Adapted for udev-extras by Martin Pitt <martin.pitt@ubuntu.com> - * - * Copyright (C) 2006, Lennart Poettering - * Copyright (C) 2009, Canonical Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <ctype.h> -#include <unistd.h> -#include <errno.h> -#include <limits.h> -#include <fcntl.h> -#include <getopt.h> -#include <sys/ioctl.h> -#include <linux/limits.h> -#include <linux/input.h> - -static const struct key* lookup_key (const char *str, unsigned int len); - -#include "keys-from-name.h" -#include "keys-to-name.h" -#include "macro.h" -#include "util.h" - -#define MAX_SCANCODES 1024 - -static int evdev_open(const char *dev) -{ - int fd; - char fn[PATH_MAX]; - - if (!startswith(dev, "/dev")) { - snprintf(fn, sizeof(fn), "/dev/%s", dev); - dev = fn; - } - - if ((fd = open(dev, O_RDWR)) < 0) { - fprintf(stderr, "error open('%s'): %m\n", dev); - return -1; - } - return fd; -} - -static int evdev_get_keycode(int fd, unsigned scancode, int e) -{ - unsigned codes[2]; - - codes[0] = scancode; - if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) { - if (e && errno == EINVAL) { - return -2; - } else { - fprintf(stderr, "EVIOCGKEYCODE for scan code 0x%x: %m\n", scancode); - return -1; - } - } - return codes[1]; -} - -static int evdev_set_keycode(int fd, unsigned scancode, int keycode) -{ - unsigned codes[2]; - - codes[0] = scancode; - codes[1] = (unsigned) keycode; - - if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) { - fprintf(stderr, "EVIOCSKEYCODE: %m\n"); - return -1; - } - return 0; -} - -static int evdev_driver_version(int fd, char *v, size_t l) -{ - int version; - - if (ioctl(fd, EVIOCGVERSION, &version)) { - fprintf(stderr, "EVIOCGVERSION: %m\n"); - return -1; - } - - snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff); - return 0; -} - -static int evdev_device_name(int fd, char *n, size_t l) -{ - if (ioctl(fd, EVIOCGNAME(l), n) < 0) { - fprintf(stderr, "EVIOCGNAME: %m\n"); - return -1; - } - return 0; -} - -/* Return a lower-case string with KEY_ prefix removed */ -static const char* format_keyname(const char* key) { - static char result[101]; - const char* s; - int len; - - for (s = key+4, len = 0; *s && len < 100; ++len, ++s) - result[len] = tolower(*s); - result[len] = '\0'; - return result; -} - -static int dump_table(int fd) { - char version[256], name[256]; - unsigned scancode; - int r = -1; - - if (evdev_driver_version(fd, version, sizeof(version)) < 0) - goto fail; - - if (evdev_device_name(fd, name, sizeof(name)) < 0) - goto fail; - - printf("### evdev %s, driver '%s'\n", version, name); - - r = 0; - for (scancode = 0; scancode < MAX_SCANCODES; scancode++) { - int keycode; - - if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) { - if (keycode == -2) - continue; - r = -1; - break; - } - - if (keycode < KEY_MAX && key_names[keycode]) - printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode])); - else - printf("0x%03x 0x%03x\n", scancode, keycode); - } -fail: - return r; -} - -static void set_key(int fd, const char* scancode_str, const char* keyname) -{ - unsigned scancode; - char *endptr; - char t[105] = "KEY_UNKNOWN"; - const struct key *k; - - scancode = (unsigned) strtol(scancode_str, &endptr, 0); - if (*endptr != '\0') { - fprintf(stderr, "ERROR: Invalid scancode\n"); - exit(1); - } - - snprintf(t, sizeof(t), "KEY_%s", keyname); - - if (!(k = lookup_key(t, strlen(t)))) { - fprintf(stderr, "ERROR: Unknown key name '%s'\n", keyname); - exit(1); - } - - if (evdev_set_keycode(fd, scancode, k->id) < 0) - fprintf(stderr, "setting scancode 0x%2X to key code %i failed\n", - scancode, k->id); - else - printf("setting scancode 0x%2X to key code %i\n", - scancode, k->id); -} - -static int merge_table(int fd, FILE *f) { - int r = 0; - int line = 0; - - while (!feof(f)) { - char s[256], *p; - unsigned scancode; - int new_keycode, old_keycode; - - if (!fgets(s, sizeof(s), f)) - break; - - line++; - p = s+strspn(s, "\t "); - if (*p == '#' || *p == '\n') - continue; - - if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) { - char t[105] = "KEY_UNKNOWN"; - const struct key *k; - - if (sscanf(p, "%i %100s", &scancode, t+4) != 2) { - fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line); - r = -1; - continue; - } - - if (!(k = lookup_key(t, strlen(t)))) { - fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line); - r = -1; - continue; - } - - new_keycode = k->id; - } - - - if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) { - r = -1; - continue; - } - - if (evdev_set_keycode(fd, scancode, new_keycode) < 0) { - r = -1; - continue; - } - - if (new_keycode != old_keycode) - fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n", - scancode, new_keycode, old_keycode); - } - - fclose(f); - return r; -} - - -/* read one event; return 1 if valid */ -static int read_event(int fd, struct input_event* ev) -{ - int ret; - ret = read(fd, ev, sizeof(struct input_event)); - - if (ret < 0) { - perror("read"); - return 0; - } - if (ret != sizeof(struct input_event)) { - fprintf(stderr, "did not get enough data for event struct, aborting\n"); - return 0; - } - - return 1; -} - -static void print_key(unsigned scancode, uint16_t keycode, int has_scan, int has_key) -{ - const char *keyname; - - /* ignore key release events */ - if (has_key == 1) - return; - - if (has_key == 0 && has_scan != 0) { - fprintf(stderr, "got scan code event 0x%02X without a key code event\n", - scancode); - return; - } - - if (has_scan != 0) - printf("scan code: 0x%02X ", scancode); - else - printf("(no scan code received) "); - - keyname = key_names[keycode]; - if (keyname != NULL) - printf("key code: %s\n", format_keyname(keyname)); - else - printf("key code: %03X\n", keycode); -} - -static void interactive(int fd) -{ - struct input_event ev; - unsigned last_scan = 0; - uint16_t last_key = 0; - int has_scan; /* boolean */ - int has_key; /* 0: none, 1: release, 2: press */ - - /* grab input device */ - ioctl(fd, EVIOCGRAB, 1); - puts("Press ESC to finish, or Control-C if this device is not your primary keyboard"); - - has_scan = has_key = 0; - while (read_event(fd, &ev)) { - /* Drivers usually send the scan code first, then the key code, - * then a SYN. Some drivers (like thinkpad_acpi) send the key - * code first, and some drivers might not send SYN events, so - * keep a robust state machine which can deal with any of those - */ - - if (ev.type == EV_MSC && ev.code == MSC_SCAN) { - if (has_scan) { - fputs("driver did not send SYN event in between key events; previous event:\n", - stderr); - print_key(last_scan, last_key, has_scan, has_key); - has_key = 0; - } - - last_scan = ev.value; - has_scan = 1; - /*printf("--- got scan %u; has scan %i key %i\n", last_scan, has_scan, has_key); */ - } - else if (ev.type == EV_KEY) { - if (has_key) { - fputs("driver did not send SYN event in between key events; previous event:\n", - stderr); - print_key(last_scan, last_key, has_scan, has_key); - has_scan = 0; - } - - last_key = ev.code; - has_key = 1 + ev.value; - /*printf("--- got key %hu; has scan %i key %i\n", last_key, has_scan, has_key);*/ - - /* Stop on ESC */ - if (ev.code == KEY_ESC && ev.value == 0) - break; - } - else if (ev.type == EV_SYN) { - /*printf("--- got SYN; has scan %i key %i\n", has_scan, has_key);*/ - print_key(last_scan, last_key, has_scan, has_key); - - has_scan = has_key = 0; - } - - } - - /* release input device */ - ioctl(fd, EVIOCGRAB, 0); -} - -_noreturn_ static void help(int error) -{ - const char* h = "Usage: keymap <event device> [<map file>]\n" - " keymap <event device> scancode keyname [...]\n" - " keymap -i <event device>\n"; - if (error) { - fputs(h, stderr); - exit(2); - } else { - fputs(h, stdout); - exit(0); - } -} - -int main(int argc, char **argv) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "interactive", no_argument, NULL, 'i' }, - {} - }; - int fd = -1; - int opt_interactive = 0; - int i; - - while (1) { - int option; - - option = getopt_long(argc, argv, "hi", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'h': - help(0); - - case 'i': - opt_interactive = 1; - break; - default: - return 1; - } - } - - if (argc < optind+1) - help (1); - - if ((fd = evdev_open(argv[optind])) < 0) - return 3; - - /* one argument (device): dump or interactive */ - if (argc == optind+1) { - if (opt_interactive) - interactive(fd); - else - dump_table(fd); - return 0; - } - - /* two arguments (device, mapfile): set map file */ - if (argc == optind+2) { - const char *filearg = argv[optind+1]; - if (strchr(filearg, '/')) { - /* Keymap file argument is a path */ - FILE *f = fopen(filearg, "re"); - if (f) - merge_table(fd, f); - else - perror(filearg); - } else { - /* Keymap file argument is a filename */ - /* Open override file if present, otherwise default file */ - char keymap_path[PATH_MAX]; - FILE *f; - - snprintf(keymap_path, sizeof(keymap_path), "/etc/udev/keymaps/%s", filearg); - f = fopen(keymap_path, "re"); - if (f) { - merge_table(fd, f); - } else { - snprintf(keymap_path, sizeof(keymap_path), UDEV_LIBEXEC_DIR "/keymaps/%s", filearg); - f = fopen(keymap_path, "re"); - if (f) - merge_table(fd, f); - else - perror(keymap_path); - } - } - return 0; - } - - /* more arguments (device, scancode/keyname pairs): set keys directly */ - if ((argc - optind - 1) % 2 == 0) { - for (i = optind+1; i < argc; i += 2) - set_key(fd, argv[i], argv[i+1]); - return 0; - } - - /* invalid number of arguments */ - help(1); - return 1; /* not reached */ -} diff --git a/src/udev/Makefile.am b/src/udev/Makefile.am index 2bceb0c698..4a5f5fefa5 100644 --- a/src/udev/Makefile.am +++ b/src/udev/Makefile.am @@ -117,6 +117,41 @@ EXTRA_DIST = \ CLEANFILES = \ udev.pc +if ENABLE_KEYMAP +AM_CPPFLAGS += \ + -I $(top_builddir)/src/udev + +libudev_core_la_SOURCES += \ + udev-builtin-keyboard.c + +nodist_libudev_core_la_SOURCES = \ + keyboard-keys-from-name.h \ + keyboard-keys-to-name.h + +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/' > $@ + +keyboard-keys-from-name.gperf: 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 }' < $< > $@ + +keyboard-keys-from-name.h: keyboard-keys-from-name.gperf Makefile + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t -N keyboard_lookup_key -H hash_key_name -p -C < $< > $@ + +keyboard-keys-to-name.h: keyboard-keys.txt Makefile + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ + + +BUILT_SOURCES = \ + $(nodist_libudev_core_la_SOURCES) + +CLEANFILES += \ + keyboard-keys-from-name.gperf \ + keyboard-keys.txt \ + $(BUILT_SOURCES) + +endif + # install udevadm symlink in sbindir install-exec-hook: if test "$(bindir)" != "$(sbindir)"; then \ diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c new file mode 100644 index 0000000000..ddd853594e --- /dev/null +++ b/src/udev/udev-builtin-keyboard.c @@ -0,0 +1,163 @@ +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers <kay@vrfy.org> + + 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 <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/limits.h> +#include <linux/input.h> + +#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_<hex scan code>=<key identifier string> */ + 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 22deaac1a8..d9cca9dde8 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -39,6 +39,9 @@ static const struct udev_builtin *builtins[] = { #endif [UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb, [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, +#ifdef ENABLE_KEYMAP + [UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard, +#endif #ifdef HAVE_MODULES [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, #endif diff --git a/src/udev/udev.h b/src/udev/udev.h index f0256a4d09..80fdd4c0ec 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -145,6 +145,9 @@ enum udev_builtin_cmd { #endif UDEV_BUILTIN_HWDB, UDEV_BUILTIN_INPUT_ID, +#ifdef ENABLE_KEYMAP + UDEV_BUILTIN_KEYBOARD, +#endif #ifdef HAVE_MODULES UDEV_BUILTIN_KMOD, #endif @@ -171,6 +174,9 @@ 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; +#ifdef ENABLE_KEYMAP +extern const struct udev_builtin udev_builtin_keyboard; +#endif #ifdef HAVE_MODULES extern const struct udev_builtin udev_builtin_kmod; #endif |