summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIan Stakenvicius <axs@gentoo.org>2013-07-24 14:00:06 -0400
committerIan Stakenvicius <axs@gentoo.org>2013-07-24 16:27:01 -0400
commit35876baf30ceab34fc16e3fb52763fc0be08f683 (patch)
treeacc96c905d60da830cd4b08b499f50ca4eb901a7 /src
parent87b69cb04556d5dacae2eb1a9f2122ce5f6a4620 (diff)
Switch from external keymaps to internal (hwdb) keymaps
This commit imports the new internal keyboard handling from upstream. This is a combination of many upstream commits, including those that added code, removed old code, and updated the hwdb. Some commits (hwdb ones specifically) were unrelated but brought in anyways to keep the whole hwdb consistent. Each upstream commit included is as follows: 9d7d42bc406a2ac04639674281ce3ff6beeda790 - internal keymap support 0c959b39175b126fdb70ae00de37ca6d9c8ca3a1 - hwdb: keyboard -- add file e8193554925a22b63bef0e77b8397b56d63a91ff - hwdb: keyboard -- update comments c79d894d590fc9df4861738555cc43c477e33376 - hwdb: import data aedc2eddd16e48d468e6ad0aea2caf00c7d37365 - hwdb: keyboard update 97a9313cafccf772ce03f5ebd36fe4d9d8412583 - hwdb: drop non-existant Samsung 900XC3 from keymap ddc77f62244bb41d5c8261517e2e1ff1b763fc94 - switch from udev keymaps to hwdb 0c3815773331b263713f4f7b9d80bc1ca159338e - also remove keymaps-force-release directory 1b6bce89b3383904d0dab619dd38bff673f7286e - keymap: re-add Logitech USB corded/cordless models bf89b99c5a39115112c2eda4c2103e2db54988d2 - 60-keyboard.hwdb: Fix syntax error ce39bb6909578017aa10031638e724e038f0b859 - hwdb: data update, upstream 884c86812c51479496edd50b278383d7bb67baf0 - rules: keyboard - use builtin command All code from each of the above commits is attributed to the original authors. There were some adjustments made in order to support the code differences between upstream and eudev, which was done by myself. Also of note is that the code can still be disabled via the --disable-keymaps configure option, which was removed from upstream. Signed-off-by: Ian Stakenvicius <axs@gentoo.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am5
-rw-r--r--src/keymap/Makefile.am58
-rw-r--r--src/keymap/README.keymap.txt97
-rw-r--r--src/keymap/check-keymaps.sh.in45
-rwxr-xr-xsrc/keymap/findkeyboards68
-rw-r--r--src/keymap/keyboard-force-release.sh.in22
-rw-r--r--src/keymap/keymap.c453
-rw-r--r--src/udev/Makefile.am35
-rw-r--r--src/udev/udev-builtin-keyboard.c163
-rw-r--r--src/udev/udev-builtin.c3
-rw-r--r--src/udev/udev.h6
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