Map scancodes to keycodes - ArchWiki

Jump to content

# Map scancodes to keycodes

From ArchWiki

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]

There are two ways of mapping scancodes to keycodes:

- Using udev
- Using setkeycodes(8)

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>-<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 the `lsusb` 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 run `awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h` to get a list).
- 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 [4]. 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

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 installation script rebuilds `hwdb.bin` by running `udevadm hwdb --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: 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`.

Category:

- Keyboard configuration