Map scancodes to keycodes: Difference between revisions

From ArchWiki
(update interlanguage links)
(simplification and beautification of wikilinks (interactive))
 
(24 intermediate revisions by 9 users not shown)
Line 2: Line 2:
[[ja:スキャンコードをキーコードにマップ]]
[[ja:スキャンコードをキーコードにマップ]]
[[zh-hans:Map scancodes to keycodes]]
[[zh-hans:Map scancodes to keycodes]]
{{Related articles start}}
{{Related|Xorg/Keyboard configuration}}
{{Related|Linux console/Keyboard configuration}}
{{Related articles end}}
This page assumes that you have read [[Keyboard input]], which provides wider context.
This page assumes that you have read [[Keyboard input]], which provides wider context.


Mapping ''scancodes'' to ''keycodes'' is achieved in a layer lower than Xorg and Linux console, which means that changes to this mapping will be effective in both. [https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h#n64][https://elixir.bootlin.com/linux/latest/A/ident/hid_keyboard][https://elixir.bootlin.com/linux/latest/A/ident/atkbd_set2_keycode]
Mapping ''scancodes'' to ''keycodes'' is achieved in a layer lower than Xorg and Linux console, which means that changes to this mapping will be effective in both. [https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input-event-codes.h#n64][https://elixir.bootlin.com/linux/latest/A/ident/hid_keyboard][https://elixir.bootlin.com/linux/latest/A/ident/atkbd_set2_keycode] Note that this method can only be used for simple 1:1 key remaps; see [[Input remap utilities]] for programs which allow more complex remaps at the same low level.


There are two ways of mapping ''scancodes'' to ''keycodes'':
There are two ways of mapping ''scancodes'' to ''keycodes'':
Line 18: Line 23:
== Using udev ==
== Using udev ==


[[udev]] provides a builtin function called ''hwdb'' to maintain the hardware database index in {{ic|/etc/udev/hwdb.bin}}. The database is compiled from files with ''.hwdb'' extension located in directories {{ic|/usr/lib/udev/hwdb.d/}}, {{ic|/run/udev/hwdb.d/}} and {{ic|/etc/udev/hwdb.d/}}. The default ''scancodes-to-keycodes'' mapping file is {{ic|/usr/lib/udev/hwdb.d/60-keyboard.hwdb}}. See {{man|7|udev}} for details.
[[udev]] provides a builtin function called ''hwdb'' to maintain the hardware database index in {{ic|/etc/udev/hwdb.bin}}. The database is compiled from files with ''.hwdb'' extension located in directories {{ic|/usr/lib/udev/hwdb.d/}}, {{ic|/run/udev/hwdb.d/}} and {{ic|/etc/udev/hwdb.d/}}. The default ''scancodes-to-keycodes'' mapping file is {{ic|/usr/lib/udev/hwdb.d/60-keyboard.hwdb}}. See {{man|7|hwdb}} for details.


{{Note|From systemd 220 the udev ABI changed. Users using custom udev hwdb rules should update them according to the new ABI}}
The ''.hwdb'' file can apply key mappings to one or more keyboards based on hardware ID glob patterns. You may obtain device identification info by running {{man|1|evemu-describe}} as the root user. This command is provided by the {{pkg|evemu}} package.


The ''.hwdb'' file can contain multiple blocks of mappings for different keyboards, or one block can be applied to multiple keyboards. The {{ic|evdev:}} prefix is used to match a block against a hardware, the following hardware matches are supported:
The {{ic|evdev:}} prefix is used to match hardware against a block of mappings. The following hardware matches are supported:


* Generic input devices (also USB keyboards) identified by the usb kernel modalias: {{bc|evdev:input:b''<bus_id>''v''<vendor_id>''p''<product_id>''e''<version_id>''-''<modalias>''}} where {{ic|''<vendor_id>''}}, {{ic|''<product_id>''}} and {{ic|''<version_id>''}} are the 4-digit hex uppercase vendor, product and version IDs (you can find those by running the {{ic|lsusb}} command) and {{ic|''<modalias>''}}  is an arbitrary length input-modalias describing the device capabilities. {{ic|''<bus_id>''}} is the 4-digit hex bus id and should be 0003 for usb devices. The possible {{ic|''<bus_id>''}} values are defined in {{ic|/usr/include/linux/input.h}} (you can run {{ic|awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h}} to get a list).
* Generic input devices (also USB keyboards) identified by the usb kernel [[modalias]]:
* AT keyboard DMI data matches: {{bc|evdev:atkbd:dmi:bvn*:bvr*:bd*:svn''<vendor>'':pn''<product>'':pvr*}} where {{ic|''<vendor>''}} and {{ic|''<product>''}} are the firmware-provided strings exported by the kernel DMI modalias.
:{{bc|evdev:input:b''<bus_id>''v''<vendor_id>''p''<product_id>''e''<version_id>''-''<input_modalias>''}}
:with the following 4-digit hex uppercase fields:
:*{{ic|''<vendor_id>''}}, {{ic|''<product_id>''}} and {{ic|''<version_id>''}}: vendor, product and version IDs matching the output of the {{ic|lsusb}} command.
:*{{ic|''<bus_id>''}} is the 4-digit hex bus id and should be 0003 for usb devices. The possible {{ic|''<bus_id>''}} values are defined in {{ic|/usr/include/linux/input.h}} (you can run {{ic|awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h}} to get a list).
:*{{ic|''<input_modalias>''}} is an arbitrary length input-modalias describing the device capabilities. The other fields are sufficient to uniquely identify the device, so you can use a glob here.
:If you have the device currently plugged into your computer then you can get the whole modalias at once using sysfs, as demonstrated in [[#Remap specific device]].
* Input driver device name and DMI data match: {{bc|evdev:name:''<input device name>'':dmi:bvn*:bvr*:bd*:svn''<vendor>'':pn*}} where {{ic|''<input_device_name>''}} is the name device specified by the driver and {{ic|''<vendor>''}} is the firmware-provided string exported by the kernel DMI modalias.
* Input driver device name and DMI data match: {{bc|evdev:name:''<input device name>'':dmi:bvn*:bvr*:bd*:svn''<vendor>'':pn*}} where {{ic|''<input_device_name>''}} is the name device specified by the driver and {{ic|''<vendor>''}} is the firmware-provided string exported by the kernel DMI modalias.


The format of each line in the block body is {{ic|1=KEYBOARD_KEY_''<scancode>''=''<keycode>''}}. The value of {{ic|''<scancode>''}} is hexadecimal, but without the leading {{ic|0x}} (i.e. specify {{ic|a0}} instead of {{ic|0xa0}}), whereas the value of {{ic|''<keycode>''}} is the lower-case keycode name string as listed in {{ic|/usr/include/linux/input-event-codes.h}} (see the {{ic|KEY_''<KEYCODE>''}} variables), a sorted list is available at [https://hal.freedesktop.org/quirk/quirk-keymap-list.txt]. It is not possible to specify decimal value in {{ic|''<keycode>''}}.
The format of each line in the block body is {{ic|1=KEYBOARD_KEY_''<scancode>''=''<keycode>''}}. The value of {{ic|''<scancode>''}} is hexadecimal, but without the leading {{ic|0x}} (i.e. specify {{ic|a0}} instead of {{ic|0xa0}}), whereas the value of {{ic|''<keycode>''}} is the lower-case keycode name string as listed in {{ic|/usr/include/linux/input-event-codes.h}} (see the {{ic|KEY_''<KEYCODE>''}} variables), a sorted list is available at [https://hal.freedesktop.org/quirk/quirk-keymap-list.txt]. It is not possible to specify decimal value in {{ic|''<keycode>''}}.


{{Tip|You can obtain the identificator for the device you want to set up a custom ''hwdb'' rule for by using {{ic|evemu-describe}} as the root user. This utility is provided by the {{pkg|evemu}} package. You can obtain the identificator for the device you want to set up a custom ''hwdb'' rule and the key scancode for by running ''evtest'' as the root user. This utility is provided by the {{pkg|evtest}} package.}}
=== Examples ===


=== Example for custom hwdb ===
==== Remap all devices ====


The example hwdb file will match all AT keyboards:
Suppose we want to remap a couple of common keys for all AT keyboards:


{{hc|/etc/udev/hwdb.d/90-custom-keyboard.hwdb|<nowiki>
{{hc|/etc/udev/hwdb.d/90-custom-keyboard.hwdb|<nowiki>
evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*
evdev:atkbd:*
  KEYBOARD_KEY_10=suspend
  KEYBOARD_KEY_10=suspend
  KEYBOARD_KEY_a0=search
  KEYBOARD_KEY_a0=search
</nowiki>}}
</nowiki>}}


Here is an example of rebinding modifiers on a laptop and USB keyboard:
==== Remap specific device ====
 
Suppose we want to remap a device that you happen to currently have plugged in. You should already have the evdev path (e.g. {{ic|/dev/input/event17}}) as well as the ''scancode'' (e.g. {{ic|70039}} for caps lock). Now, using the event number, you can query [[Wikipedia:sysfs|sysfs]] for the modalias:
 
{{hc|cat /sys/class/input/event17/device/modalias|
input:b0003v32ACp0012e0111-e0,1,4,1...}}


{{hc|/etc/udev/hwdb.d/10-my-modifiers.hwdb|<nowiki>
This device could be matched with the following hwdb rule:
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
{{hc|/etc/udev/hwdb.d/90-remap.hwdb|2=
  KEYBOARD_KEY_3a=leftalt    # bind capslock to leftalt
evdev:input:b0003v32ACp0012e0111*
KEYBOARD_KEY_38=leftctrl    # bind leftalt to leftctrl
  KEYBOARD_KEY_70039=rightctrl # This example maps the 70039 scancode to the "rightctrl" keycode.
</nowiki>}}
}}
 
==== Disable key ====


To block the {{ic|Sleep}} key, bind it to the "reserved" keyword. Alternatively, you can use "unknown" to map it to the {{ic|NoSymbol}} key. For example:
To block the {{ic|Sleep}} key, bind it to the "reserved" keyword. Alternatively, you can use "unknown" to map it to the {{ic|NoSymbol}} key. For example:
Line 63: Line 77:
=== Updating the Hardware Database Index ===
=== Updating the Hardware Database Index ===


After changing the configuration files, the hardware database index, {{ic|hwdb.bin}}, needs to be rebuilt.  
After changing the configuration files, the hardware database index, {{ic|hwdb.bin}} needs to be rebuilt.  


* Update {{ic|hwdb.bin}} manually by running
* Update {{ic|hwdb.bin}} manually by running
  # systemd-hwdb update
  # systemd-hwdb update


{{Out of date|{{ic|1=ConditionNeedsUpdate=/etc}} seems to be commented out by default in {{ic|systemd-hwdb-update.service}} (checked in {{pkg|systemd}} 232).}}
* Update automatically on each reboot by commenting out {{ic|ConditionNeedsUpdate}} in {{ic|systemd-hwdb-update.service}} using a [[replacement unit file]]
{{Accuracy|Don't edit in {{ic|/usr/lib/}}, use [[Systemd#Editing_provided_units|systemctl edit]].}}
 
* Update automatically on each reboot by commenting out {{ic|ConditionNeedsUpdate}} in {{ic|systemd-hwdb-update.service}}.


{{hc|/usr/lib/systemd/system/systemd-hwdb-update.service|<nowiki>
{{hc|/etc/systemd/system/systemd-hwdb-update.service|<nowiki>
#  This file is part of systemd.
#  This file is part of systemd.
.
.
Line 85: Line 97:
{{ic|hwdb.bin}}.
{{ic|hwdb.bin}}.


* Automatically after [[Systemd]] upgrade.
* Automatically after [[systemd]] upgrade.


On each upgrade of [[Systemd]], the installation script rebuilds {{ic|hwdb.bin}} by running {{ic|udevadm hwdb --update}} as the root user, so we do not need to care about it.
On each upgrade of [[systemd]], the {{ic|30-systemd-hwdb.hook}} rebuilds {{ic|hwdb.bin}} by running {{ic|systemd-hwdb --usr update}} as the root user, so we do not need to care about it.


=== Reloading the Hardware Database Index ===
=== Reloading the Hardware Database Index ===
Line 95: Line 107:
With {{ic|udevadm}} it is possible to load new key mapping from the updated {{ic|hwdb.bin}} by running
With {{ic|udevadm}} it is possible to load new key mapping from the updated {{ic|hwdb.bin}} by running
  # udevadm trigger
  # udevadm trigger
Be aware that with {{ic|udevadm}} only added or changed key mapping are loaded so if we delete a mapping from the config file, rebuild {{ic|hwdb.bin}} and run {{ic|udevadm trigger}} as the root user, then the deleted mapping still kept by the kernel, at least until a reboot.
Be aware that with {{ic|udevadm}} only added or changed key mapping are loaded so if we delete a mapping from the configuration file, rebuild {{ic|hwdb.bin}} and run {{ic|udevadm trigger}} as the root user, then the deleted mapping still kept by the kernel, at least until a reboot.


=== Querying the database ===
=== Querying the database ===
Line 113: Line 125:
It is possible to specify multiple pairs at once. ''Scancodes'' are given in hexadecimal, ''keycodes'' in decimal.
It is possible to specify multiple pairs at once. ''Scancodes'' are given in hexadecimal, ''keycodes'' in decimal.


{{Note|Apparently ''setkeycodes'' does not work with USB keyboards (Linux 3.14.44-1-lts):
{{Note|
{{Accuracy|On Linux 6.7.8-zen1-1-zen, this appears to fail quietly rather than emitting an error.}}
Apparently ''setkeycodes'' does not work with USB keyboards (Linux 3.14.44-1-lts):


  # setkeycodes 45 30    # bind NumLock (0x45) to KEY_A (30) on AT keyboard
  # setkeycodes 45 30    # bind NumLock (0x45) to KEY_A (30) on AT keyboard
Line 121: Line 135:
  failed to set scancode 620d3 to keycode 31
  failed to set scancode 620d3 to keycode 31
}}
}}
If using this simple command, changes will be lost after reboot. The changes can be made permanent by creating a new service:
{{hc|/etc/systemd/system/setkeycodes.service|<nowiki>
[Unit]
Description=Change keycodes at boot
[Service]
Type=oneshot
ExecStart=/usr/bin/setkeycodes [scancode] [keycode] [scancode] [keycode] [...]
[Install]
WantedBy=multi-user.target
</nowiki>}}
and [[enabling]] {{ic|setkeycodes.service}}.

Latest revision as of 12:20, 16 March 2024

This page assumes that you have read Keyboard input, which provides wider context.

Mapping scancodes to keycodes is achieved in a layer lower than Xorg and Linux console, which means that changes to this mapping will be effective in both. [1][2][3] Note that this method can only be used for simple 1:1 key remaps; see Input remap utilities for programs which allow more complex remaps at the same low level.

There are two ways of mapping scancodes to keycodes:

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.

Identifying scancodes

You need to know the scancodes of keys you wish to remap. See Keyboard input#Identifying 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 hwdb(7) for details.

The .hwdb file can apply key mappings to one or more keyboards based on hardware ID glob patterns. You may obtain device identification info by running evemu-describe(1) as the root user. This command is provided by the evemu package.

The evdev: prefix is used to match hardware against a block of mappings. 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>-<input_modalias>
with the following 4-digit hex uppercase fields:
  • <vendor_id>, <product_id> and <version_id>: vendor, product and version IDs matching the output of the lsusb command.
  • <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 run awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h to get a list).
  • <input_modalias> is an arbitrary length input-modalias describing the device capabilities. The other fields are sufficient to uniquely identify the device, so you can use a glob here.
If you have the device currently plugged into your computer then you can get the whole modalias at once using sysfs, as demonstrated in #Remap specific device.
  • 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 [4]. It is not possible to specify decimal value in <keycode>.

Examples

Remap all devices

Suppose we want to remap a couple of common keys for all AT keyboards:

/etc/udev/hwdb.d/90-custom-keyboard.hwdb
evdev:atkbd:*
 KEYBOARD_KEY_10=suspend
 KEYBOARD_KEY_a0=search

Remap specific device

Suppose we want to remap a device that you happen to currently have plugged in. You should already have the evdev path (e.g. /dev/input/event17) as well as the scancode (e.g. 70039 for caps lock). Now, using the event number, you can query sysfs for the modalias:

cat /sys/class/input/event17/device/modalias
input:b0003v32ACp0012e0111-e0,1,4,1...

This device could be matched with the following hwdb rule:

/etc/udev/hwdb.d/90-remap.hwdb
evdev:input:b0003v32ACp0012e0111*
 KEYBOARD_KEY_70039=rightctrl # This example maps the 70039 scancode to the "rightctrl" keycode.

Disable key

To block the Sleep key, bind it to the "reserved" keyword. Alternatively, you can use "unknown" to map it to the NoSymbol key. For example:

/etc/udev/hwdb.d/90-block-sleep.hwdb
evdev:input:b0003v03F0p020C* # hp 5308 keyboard controller
 KEYBOARD_KEY_10082=reserved

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 in systemd-hwdb-update.service using a replacement unit file
/etc/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 30-systemd-hwdb.hook rebuilds hwdb.bin by running systemd-hwdb --usr update as the root user, 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 configuration file, rebuild hwdb.bin and run udevadm trigger as the root user, 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.

Note:

The factual accuracy of this article or section is disputed.

Reason: On Linux 6.7.8-zen1-1-zen, this appears to fail quietly rather than emitting an error. (Discuss in Talk:Map scancodes to keycodes)

Apparently setkeycodes does not work with USB keyboards (Linux 3.14.44-1-lts):

# 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

If using this simple command, changes will be lost after reboot. The changes can be made permanent by creating a new service:

/etc/systemd/system/setkeycodes.service
[Unit]
Description=Change keycodes at boot

[Service]
Type=oneshot
ExecStart=/usr/bin/setkeycodes [scancode] [keycode] [scancode] [keycode] [...]

[Install]
WantedBy=multi-user.target

and enabling setkeycodes.service.