Linux Keyboard Input

Last update: 2022-11-20 20:04:02 +0800 +0800
tags: Input | Keyboard | Linux
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Linux separates a keyboard’s transport-level report from a stable input key code and from the text or action produced by a keyboard layout. Keeping those layers distinct explains why a PS/2 and a USB keyboard can generate different hardware data but both report KEY_F.

Overview #

Input follows four conceptual stages:

  1. A transport driver receives bytes or reports. The atkbd driver handles AT and PS/2 scan-code sets, while usbhid parses USB HID usages.
  2. The driver parses make, break, prefix, and repeat state, then reports a Linux input key code such as KEY_F (33) with a press, release, or repeat value.
  3. The input core publishes events through evdev. A graphical compositor or input library normally applies its own layout to these events.
  4. For a Linux virtual console, the kernel keyboard layer applies the active console keymap and emits bytes, Unicode characters, strings, or console actions to the terminal layer.

A scan code is therefore not a character, and a Linux key code is not an X11, Wayland, or Unicode code point.

IBM PC, XT, and AT Protocols #

The original 83-key IBM PC and PC/XT keyboards established the byte assignments now called scan-code set 1. Their interface was effectively keyboard-to-host: the keyboard supplied clocked serial data, but the host did not have the AT command protocol for controlling LEDs, repeat, and scan-set selection. An ordinary set-1 press uses a make byte from 0x01 through 0x7f; releasing that key sets bit 7. For example, Escape produces 0x01 on press and 0x81 on release.

The 84-key IBM PC/AT keyboard introduced a bidirectional interface and scan-code set 2. Set 2 changed positions rather than extending set 1 numerically: left Control is 0x14 in set 2 but 0x1d in set 1. A break sequence uses an 0xf0 prefix, so left Control produces 14 on press and f0 14 on release. The host can send commands to set LEDs, typematic behavior, and the active set.

PC-compatible AT controllers commonly translate keyboard set-2 bytes into set-1 form for software compatibility. Linux consequently identifies many built-in keyboards as AT Translated Set 2 keyboard: the physical keyboard speaks set 2, while the controller exposes translated bytes. Scan-code set 3, associated with IBM terminal and later PS/2 designs, assigns additional keys independently and supports per-key make, break, and typematic configuration. Current Linux atkbd source supports translated set 2, raw set 2, and set 3; set 2 remains its default.

USB keyboards do not send these AT byte streams. They send HID reports containing usage identifiers, and the HID input driver maps those usages to the same Linux KEY_* namespace.

Scan-Code Sequences #

Press is also called make, and release is called break. A held typematic key generates repeated make reports after the configured delay; it does not alternate make and break. The Linux input interface represents hardware or software autorepeat as EV_KEY value 2.

The 0xe0 prefix extends both set 1 and set 2. For example, set-1 left Control uses 1d/9d, while right Control uses e0 1d/e0 9d. Set 2 instead uses 14/f0 14 and e0 14/e0 f0 14. The 0xe1 prefix introduces exceptional sequences such as Pause. Print Screen and Pause are multi-byte compatibility sequences rather than ordinary keys with a single byte. A driver must parse the stream as a state machine because treating each byte independently creates phantom Shift or Control events.

The term “scan code” is also used for higher-level transport identifiers. For example, an evdev EV_MSC/MSC_SCAN value can encode a USB HID usage such as 0x70009, while the associated EV_KEY event is still KEY_F (33). Numeric coincidence between old set-1 values and early Linux key codes is historical, not an interface invariant.

Kernel Scan-Code-to-Keycode Mapping #

Each low-level driver owns its protocol mapping. The AT driver indexes a default, replaceable scan-code-to-keycode table; HID drivers derive key codes from HID usages. Linux key codes are defined in the UAPI input-event-codes.h and extend beyond the original 1–255 console range.

setkeycodes modifies selected mappings for the console AT keyboard path. It accepts pairs such as an escaped set-1 scan code and a decimal key code:

sh
sudo setkeycodes e06f 112

The command does not remap arbitrary USB HID devices, and it does not define the character produced by key code 112. A console keymap loaded with loadkeys performs that second operation. Modern desktop input stacks apply userspace layouts instead.

Console Keymaps, Modifiers, and Character Sets #

A keymaps(5) file describes the Linux virtual-console translation table. keycode N = ... selects an action according to the active modifier mask. The kernel keyboard layer defines weights for Shift (1), AltGr (2), Control (4), Alt (8), ShiftL (16), ShiftR (32), CtrlL (64), CtrlR (128), and CapsShift (256). Map files list only the combinations they need:

text
keymaps 0-2,4-5,8,12
keycode 30 = +a A
control alt keycode 111 = Boot

Keysyms can emit characters, strings, change a modifier or lock, switch virtual consoles, or request another console action. A leading + marks a letter as Caps-Lock-sensitive. string F100 = "text" binds a function-string action, while a compose definition combines two input characters after a Compose or dead-key action:

text
compose '`' 'a' to 'à'

A charset "iso-8859-1" declaration controls how subsequent symbolic keysyms are interpreted. Numeric keysyms are not portable between kernel versions; symbolic names or U+hhhh Unicode notation are preferable. The virtual console’s K_UNICODE mode emits UTF-8 for Unicode keysyms, whereas K_XLATE applies the legacy byte-oriented translation. K_MEDIUMRAW exposes keycode-oriented data and K_RAW exposes an approximation of raw scan bytes. Those console modes do not change the evdev ABI.

Input and Console Device Nodes #

/proc/bus/input/devices and /sys/class/input enumerate input devices and their handlers. /dev/input/eventN is a binary evdev stream of native struct input_event records. Keyboard records commonly include:

  • EV_MSC/MSC_SCAN for a transport identifier when the driver supplies one;
  • EV_KEY with a KEY_* code and value 0 (release), 1 (press), or 2 (repeat); and
  • EV_SYN/SYN_REPORT to terminate one event frame.

Use evtest or libevdev rather than decoding a hexdump, because record size and time fields follow the userspace ABI of the reading process:

sh
grep -A5 -B1 -i keyboard /proc/bus/input/devices
sudo evtest /dev/input/event4

Reading an event node requires the permissions assigned by the system’s input-device policy and can expose passwords typed anywhere on that device.

/dev/tty1, /dev/tty2, and the other virtual-terminal nodes expose terminal input after console keymap translation and line-discipline processing. /dev/tty0 refers to the active virtual terminal; /dev/console refers to the configured system console and is not a raw keyboard device.

Console Utilities #

Run console-mode tools from a real virtual terminal rather than a terminal emulator:

  • showkey -s, showkey -k, and showkey -a display scan-code, keycode, and character-oriented views. Raw mode can make the console temporarily unusable if the program is interrupted.
  • getkeycodes displays the AT driver’s current scan-code mappings.
  • setkeycodes SCANCODE KEYCODE ... changes selected AT mappings.
  • dumpkeys writes the current console layout in keymaps(5) syntax; dumpkeys --long-info also lists known keysyms.
  • loadkeys FILE compiles and installs a keymap into the kernel console tables.

The upstream Linux input documentation, event-code specification, current atkbd source, and upstream keymaps(5) define the interfaces described here.

Comments

Read or comment on GitHub (sign-in required to comment)