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:
- A transport driver receives bytes or reports. The
atkbddriver handles AT and PS/2 scan-code sets, whileusbhidparses USB HID usages. - 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. - The input core publishes events through evdev. A graphical compositor or input library normally applies its own layout to these events.
- 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:
sudo setkeycodes e06f 112The 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:
keymaps 0-2,4-5,8,12
keycode 30 = +a A
control alt keycode 111 = BootKeysyms 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:
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_SCANfor a transport identifier when the driver supplies one;EV_KEYwith aKEY_*code and value 0 (release), 1 (press), or 2 (repeat); andEV_SYN/SYN_REPORTto 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:
grep -A5 -B1 -i keyboard /proc/bus/input/devices
sudo evtest /dev/input/event4Reading 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, andshowkey -adisplay scan-code, keycode, and character-oriented views. Raw mode can make the console temporarily unusable if the program is interrupted.getkeycodesdisplays the AT driver’s current scan-code mappings.setkeycodes SCANCODE KEYCODE ...changes selected AT mappings.dumpkeyswrites the current console layout inkeymaps(5)syntax;dumpkeys --long-infoalso lists known keysyms.loadkeys FILEcompiles 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)