Map scancodes to keycodes
Mapping scancodes to keycodes is universal and not specific to Linux console or Xorg, which means that changes to this mapping will be effective in both.
There are two ways of mapping scancodes to keycodes:
- Using udev
- Using setkeycodes
The preferred method is to use udev because it uses hardware information (which is a quite reliable source) to choose the keyboard model in a database. It means that if your keyboard model has been found in the database, your keys are recognized out of the box.
Contents
Identifying scancodes
You need to know the scancodes of keys you wish to remap. See Extra keyboard keys#Scancodes for details.
Using udev
udev provides a builtin function called hwdb to maintain the hardware database index in /etc/udev/hwdb.bin
. The database is compiled from files with .hwdb extension located in directories /usr/lib/udev/hwdb.d/
, /run/udev/hwdb.d/
and /etc/udev/hwdb.d/
. The default scancodes-to-keycodes mapping file is /usr/lib/udev/hwdb.d/60-keyboard.hwdb
. See man udev
for details.
The .hwdb file can contain multiple blocks of mappings for different keyboards, or one block can be applied to multiple keyboards. The evdev:
prefix is used to match a block against a hardware, the following hardware matches are supported:
- Generic input devices (also USB keyboards) identified by the usb kernel modalias:
evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<modalias>
where<vendor_id>
,<product_id>
and<version_id>
are the 4-digit hex uppercase vendor, product and version IDs (you can find those by running thelsusb
command) and<modalias>
is an arbitrary length input-modalias describing the device capabilities.<bus_id>
is the 4-digit hex bus id and should be 0003 for usb devices. The possible<bus_id>
values are defined in/usr/include/linux/input.h
(you can use the following command to get a listawk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h
). - AT keyboard DMI data matches:
evdev:atkbd:dmi:bvn*:bvr*:bd*:svn<vendor>:pn<product>:pvr*
where<vendor>
and<product>
are the firmware-provided strings exported by the kernel DMI modalias. - Input driver device name and DMI data match:
evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
where<input_device_name>
is the name device specified by the driver and<vendor>
is the firmware-provided string exported by the kernel DMI modalias.
The format of each line in the block body is KEYBOARD_KEY_<scancode>=<keycode>
. The value of <scancode>
is hexadecimal, but without the leading 0x
(i.e. specify a0
instead of 0xa0
), whereas the value of <keycode>
is the lower-case keycode name string as listed in /usr/include/linux/input-event-codes.h
(see the KEY_<KEYCODE>
variables), a sorted list is available at [1]. It is not possible to specify decimal value in <keycode>
.
Example for custom hwdb
The example hwdb file will match all AT keyboards:
/etc/udev/hwdb.d/90-custom-keyboard.hwdb
evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr* KEYBOARD_KEY_10=suspend KEYBOARD_KEY_a0=search
Here is an example of rebinding modifiers on a laptop and USB keyboard:
/etc/udev/hwdb.d/10-my-modifiers.hwdb
evdev:input:b0003v05AFp8277* # was tested on Kensington Slim Type USB (with old ABI) KEYBOARD_KEY_70039=leftalt # bind capslock to leftalt KEYBOARD_KEY_700e2=leftctrl # bind leftalt to leftctrl evdev:atkbd:dmi:* # built-in keyboard: match all AT keyboards for now KEYBOARD_KEY_3a=leftalt # bind capslock to leftalt KEYBOARD_KEY_38=leftctrl # bind leftalt to leftctrl
Updating the Hardware Database Index
After changing the configuration files, the hardware database index, hwdb.bin
, needs to be rebuilt.
- Update
hwdb.bin
manually by running
# systemd-hwdb update
- Update automatically on each reboot by commenting out
ConditionNeedsUpdate
insystemd-hwdb-update.service
.
/usr/lib/systemd/system/systemd-hwdb-update.service
# This file is part of systemd. . . #ConditionNeedsUpdate=/etc . .
After systemd-hwdb-update.service
finished loading systemd-trigger.service
will reload the changes from
hwdb.bin
.
- Automatically after Systemd upgrade.
On each upgrade of Systemd, the installation script rebuilds hwdb.bin
by running # udevadm hwdb --update
so we do not need to care about it.
Reloading the Hardware Database Index
The kernel loads hwdb.bin
as part of the boot process, rebooting the system will promise the loading of the updated hwdb.bin
.
With udevadm
it is possible to load new key mapping from the updated hwdb.bin
by running
# udevadm trigger
Be aware that with udevadm
only added or changed key mapping are loaded so if we delete a mapping from the config file, rebuild hwdb.bin
and run # udevadm trigger
then the deleted mapping still kept by the kernel, at least until a reboot.
Querying the database
You can check that your configuration was loaded either by pressing keys, or by running udevadm info
. For the USB keyboard in the above example, this outputs the mapping we configured as follows:
# udevadm info /dev/input/by-path/*-usb-*-kbd | grep KEYBOARD_KEY E: KEYBOARD_KEY_70039=leftalt E: KEYBOARD_KEY_700e2=leftctrl
Using setkeycodes
setkeycodes is a tool to load scancodes-to-keycodes mapping table into Linux kernel. Its usage is:
# setkeycodes scancode keycode ...
It is possible to specify multiple pairs at once. Scancodes are given in hexadecimal, keycodes in decimal.
# setkeycodes 45 30 # bind NumLock (0x45) to KEY_A (30) on AT keyboard (successful) # setkeycodes 70053 30 # bind NumLock (0x70053) to KEY_A (30) on USB keyboard KDSETKEYCODE: Invalid argument failed to set scancode 620d3 to keycode 31