Keyboard Remapping

Sometimes we want to use some keyboard keys for specific functions, but the keys are sending keystrokes that are not usable for that function. An example of this situation is Logitech R400 Wireless Presenter.

The presenter keys by default send:
< PageUp
> PageDown
[>] alternately F5 and Esc
[ ] . (period)

These keys are not quite usable as global hotkeys. PageUp and PageDown are used to change values in spinboxes, alternating F5 and Esc are problematic, and the period is just a text input. To get more usable input from the R400, we need to do some keys remapping.

For remapping, we need to know the device info (bus ID, vendor ID and product ID) and the hex scan codes of the keys. For this, we run the evtest command, select our device and press the keys on the device.

pi@dyno:~ $ sudo apt install evtest
pi@dyno:~ $ sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:	Logitech USB Receiver
/dev/input/event1:	vc4-hdmi-0
/dev/input/event2:	vc4-hdmi-1
Select the device event number [0-2]: 0
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0xc538 version 0x111
Input device name: "Logitech USB Receiver"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 52 (KEY_DOT)
    Event code 63 (KEY_F5)
    Event code 104 (KEY_PAGEUP)
    Event code 109 (KEY_PAGEDOWN)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1710405655.041799, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004b
Event: time 1710405655.041799, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 1
Event: time 1710405655.041799, -------------- SYN_REPORT ------------
Event: time 1710405655.298946, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 2
Event: time 1710405655.298946, -------------- SYN_REPORT ------------
Event: time 1710405655.329831, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004b
Event: time 1710405655.329831, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 0
Event: time 1710405655.329831, -------------- SYN_REPORT ------------
Event: time 1710405656.681984, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004e
Event: time 1710405656.681984, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 1
Event: time 1710405656.681984, -------------- SYN_REPORT ------------
Event: time 1710405656.906038, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004e
Event: time 1710405656.906038, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 0
Event: time 1710405656.906038, -------------- SYN_REPORT ------------
Event: time 1710405661.058548, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70029
Event: time 1710405661.058548, type 1 (EV_KEY), code 1 (KEY_ESC), value 1
Event: time 1710405661.058548, -------------- SYN_REPORT ------------
Event: time 1710405661.082544, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70029
Event: time 1710405661.082544, type 1 (EV_KEY), code 1 (KEY_ESC), value 0
Event: time 1710405661.082544, -------------- SYN_REPORT ------------
Event: time 1710405661.930700, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7003e
Event: time 1710405661.930700, type 1 (EV_KEY), code 63 (KEY_F5), value 1
Event: time 1710405661.930700, -------------- SYN_REPORT ------------
Event: time 1710405661.954744, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7003e
Event: time 1710405661.954744, type 1 (EV_KEY), code 63 (KEY_F5), value 0
Event: time 1710405661.954744, -------------- SYN_REPORT ------------
Event: time 1710405664.003004, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70037
Event: time 1710405664.003004, type 1 (EV_KEY), code 52 (KEY_DOT), value 1
Event: time 1710405664.003004, -------------- SYN_REPORT ------------
Event: time 1710405664.251024, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70037
Event: time 1710405664.251024, type 1 (EV_KEY), code 52 (KEY_DOT), value 0
Event: time 1710405664.251024, -------------- SYN_REPORT ------------

With this information, we can proceed with creation of the mapping file. We create new file:

pi@dyno:~ $ sudo nano /etc/udev/hwdb.d/70-keyboard.hwdb
# Logitech Presenter R400
evdev:input:b0003v046DpC538*
 KEYBOARD_KEY_7004b=f1
 KEYBOARD_KEY_7004e=f2
 KEYBOARD_KEY_7003e=f9
 KEYBOARD_KEY_70029=f9
 KEYBOARD_KEY_70037=f10

First line is just the comment.

Then there is a device match string, which is formatted in the following way:
evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV
where
ZZZZ is the bus-id
YYYY, XXXX and WWWW are the 4-digit hex uppercase vendor, product and version ID
VVVV is an arbitrary length input-modalias describing the device capabilities.
For us the bus ID, vendor ID and product ID are enough to identify the device, so we replace the rest of the parameters with a wildcard.

Then there are keys mapping in format:
KEYBOARD_KEY_<hex scan code>=<key code identifier>

In this example, we construct the mapping, so the arrow keys send F1 and F2 keys that will activate the controller low side outputs. The other keys will send F9 and F10 which can be assigned in SETTINGS / Remote control to other dyno functions.

You can find a whole documentation on the mapping file syntax here.

After saving the file, we need to reload the mapping with following commands:

pi@dyno:~ $ sudo udevadm hwdb --update
pi@dyno:~ $ sudo udevadm trigger --sysname-match="event*"

Now the R400 should send the codes we programmed.