Chapter 45: Input subsystem¶
What: the input subsystem, the kernel framework that turns “a GPIO went low” or “an I²C read returned a touch coordinate” into a standardised event stream consumed by
evdev, X11, Wayland, framebuffer toolkits, and command-line tools. A small button-driver case connects the IRQ handler toevtestreading/dev/input/eventNand contrasts it with the maintainedgpio-keysdriver. MCU bridge: Think of an IRQ like an EXTI/NVIC interrupt path, except Linux splits the hard interrupt from deferred work and must share lines across drivers. MCU bridge: Think of Linux GPIO like the same pin set/reset block you used on STM32, but accessed through a kernel subsystem that owns numbering, direction, interrupts, and user-space exposure. IRQ: interrupt request, the signal path that tells the CPU or interrupt controller that hardware needs service. GPIO: General-Purpose Input/Output, a pin controlled as a digital input, output, or interrupt source.Why: every input device on a Linux box, keyboard, mouse, touchscreen, joystick, IR remote, goes through the input subsystem. Once you understand
input_register_deviceandinput_event, every input driver in the kernel looks familiar. The framework handles event multiplexing, queueing, sysfs/evdevintegration, autorepeat, and userspace device-node creation, your driver just callsinput_report_key()andinput_sync(). sysfs: a kernel-generated filesystem under /sys that exposes devices, drivers, and attributes.Focus: type, code, value, the three-element tuple that describes every input event. Once that triple makes sense,
EV_KEY+KEY_ENTER+1means “Enter was pressed”, the rest of the input subsystem (abs axes, relative motion, multi-touch slots) is just different combinations of type/code/value.
45.1 The picture¶
When you press a key on a USB keyboard:
physical key down
│
USB-HID interrupt-in transfer reports scan-code 0x28 (Enter)
│
usbhid driver: hid_input_report → input_report_key(dev, KEY_ENTER, 1)
│
input core: queue an event {EV_KEY, KEY_ENTER, 1} on every evdev handler
│
/dev/input/event3 becomes readable
│
user-space: read() returns 24 bytes — a struct input_event
│
X11 / Wayland / your application: "Enter was pressed"
The driver’s only job is to call input_report_*() and input_sync(). The core handles queueing, multiplexing, and user-space delivery. Your driver feeds events into the type/code/value protocol. The input core delivers them to user-space. You never talk to user-space directly.
45.2 Event types and codes¶
Security boundary: Do not burn fuses, enroll production keys, or sign release images when exploring example code. Use throwaway keys and back up the unsigned image plus the key directory before testing irreversible security flows.
#include <linux/input.h>, defines hundreds of EV_*, KEY_*, BTN_*, ABS_*, REL_*, SW_*, LED_*, SND_*, MSC_* constants.
Common event types:
Type |
Meaning |
Typical codes |
|---|---|---|
|
Synchronization, end-of-event-group marker |
|
|
Key/button pressed/released |
|
|
Relative axis (mouse motion, scroll wheel) |
|
|
Absolute axis (touchscreen, joystick, IMU) |
|
|
Miscellaneous (scancode, raw value pass-through) |
|
|
Switch (lid open/closed, headphone jack, dock) |
|
|
LED state output (driver consumes from userspace) |
|
|
Sound output (PC speaker beep) |
|
Each event has a value appropriate to its type:
EV_KEYvalue:0= released,1= pressed,2= autorepeat.EV_RELvalue: signed delta (+1, -3, etc.).EV_ABSvalue: absolute position, in whatever range the driver declared.EV_SYNvalue: usually 0.
A coherent group of events ends with EV_SYN/SYN_REPORT. The input core delivers all events between two SYN_REPORTs atomically to userspace, important when a single touch update sends multiple coordinates that must arrive together.
45.3 The mainline gpio-keys driver¶
The kernel already has gpio-keys, the in-tree driver that exposes DT-described GPIOs as keyboard keys. It is the normal implementation choice when its capabilities match the product. The DT looks like this:
gpio_keys {
compatible = "gpio-keys";
key-enter {
label = "Enter";
linux,code = <KEY_ENTER>;
gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
debounce-interval = <50>;
wakeup-source;
};
key-up {
label = "Up";
linux,code = <KEY_UP>;
gpios = <&gpio4 14 GPIO_ACTIVE_LOW>;
debounce-interval = <50>;
};
};
Set CONFIG_KEYBOARD_GPIO=y in the kernel config, boot. Pressing the GPIO19 button now generates real KEY_ENTER events on /dev/input/eventN.
The smaller version below isolates the framework’s registration and reporting interfaces without requiring a separate implementation from the reader.
45.5 Auto-repeat, debounce, and key mapping¶
The input core provides autorepeat for free: register EV_REP capability and the core will generate repeat events (value=2) while a key is held. gpio-keys does this automatically. Our minimal driver doesn’t bother.
Debounce filters electrical transitions into a stable logical state. A delayed-work design postpones sampling until the debounce interval has elapsed, then reports the stable press or release state. Further transitions restart the settling interval. The minimal driver above omits this policy, while gpio-keys provides debounce handling.
Key mapping. The KEY_* codes are logical, KEY_ENTER is the same value regardless of whether the user’s keymap is US QWERTY or Dvorak. The user-space keymap translates KEY_* to characters. For embedded devices with only a few buttons, you pick meaningful KEY_* codes:
KEY_VOLUMEUP/KEY_VOLUMEDOWNfor media buttons.KEY_POWERfor the power button (the kernel and systemd both recognise this).KEY_HOME,KEY_BACK,KEY_MENUfor navigation.KEY_WAKEUPfor a wakeup-from-suspend button.
The full list is in include/uapi/linux/input-event-codes.h. Pick a code that matches the role of the button. User-space will know what to do with it.
45.6 Absolute axes, touchscreens and joysticks¶
For a touchscreen or joystick, you have coordinates, not key presses. Declare EV_ABS capabilities and report values:
input_set_abs_params(input, ABS_X, 0, 4095, 0, 0);
input_set_abs_params(input, ABS_Y, 0, 4095, 0, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
/* In the IRQ/work handler when a sample arrives: */
input_report_abs(input, ABS_X, x_coord);
input_report_abs(input, ABS_Y, y_coord);
input_report_abs(input, ABS_PRESSURE, pressure);
input_report_key(input, BTN_TOUCH, 1);
input_sync(input);
input_set_abs_params(dev, code, min, max, fuzz, flat), min/max is the valid range, fuzz is the noise floor (changes ≤ fuzz are suppressed), flat is the center deadzone for joysticks.
For multi-touch, the protocol is more involved, the MT-B (slot-based) protocol uses ABS_MT_SLOT + per-finger ABS_MT_POSITION_X/Y. We’ll cover that in Ch 55G (GT911 multi-touch driver).
45.7 Polled vs interrupt-driven¶
Some input devices don’t have IRQs, accelerometers configured for continuous mode, for instance, or a button on a slow I²C expander where IRQ wiring isn’t practical. The input_polled_dev framework (now subsumed into the regular input_dev via input_setup_polling) polls a device at a fixed rate:
input_setup_polling(input, my_poll_callback);
input_set_poll_interval(input, 20); /* 20 ms = 50 Hz */
input_register_device(input);
The core calls my_poll_callback(input) every 20 ms. Inside, sample the hardware, input_report_*, input_sync. No IRQ wiring needed.
45.8 User-space, evdev¶
/dev/input/event* is the chardev that streams struct input_event records to user-space:
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
Read one record per event. The most common tool is evtest (interactive) for debugging. For production, applications either use libevdev (a thin wrapper) or higher-level libraries:
libinput: used by Wayland and modern X11. Handles gesture recognition, palm rejection, tap-to-click, etc.libevdev: minimal wrapper for reading raw events./dev/input/eventNdirectly: fine for embedded with one app.
For a quick test from shell:
$ sudo evtest /dev/input/event2 # interactive
$ sudo cat /dev/input/event2 | hexdump -C
45.9 Key Notes¶
An input driver translates device-specific observations into declared type/code/value events. Evdev and higher-level user-space consumers own delivery and interpretation.
Capabilities describe the device’s event vocabulary before registration.
input_sync()marks the end of a coherent update, such as a key transition or coordinate sample.Press and release are both state changes. Debounce and autorepeat are distinct policies, and a simple edge handler does not supply both automatically.
gpio-keysis an implementation choice, while the custom button case exposes the same architectural boundary with fewer features. Neither DT examples nor event listings qualify MINI wiring.Logical key codes describe roles, not characters or mandatory system actions. Power-button shutdown policy belongs to the user-space system configuration.
IRQs, polling, and delayed work must stop reporting before the input device’s state is released. Framework registration does not remove the need for coordinated teardown.
45.10 Pitfalls¶
Forgetting
input_sync. Events are buffered untilSYN_REPORT. Without it, user-space never sees them. After any group ofinput_report_*calls, callinput_sync.Reporting an unsupported event. If you
input_report_key(input, KEY_ENTER, 1)but didn’tinput_set_capability(..., EV_KEY, KEY_ENTER), the event is silently dropped. Always declare capabilities first.Not using
devm_input_allocate_device. Forgettinginput_free_devicein error paths leaks the device.devm_handles it.Calling
input_register_devicebefore setting capabilities. Capabilities must be set before register. Order: alloc → set_capability → register.Mixing
input_allocate_devicewith separateinput_register_device. Both can fail, at different points. Use standardgotocleanup, or just usedevm_input_allocate_deviceto avoid the problem.Confusing absolute and relative axes. Mice use
EV_REL(delta motion). Touchscreens useEV_ABS(absolute position). Mixing them gives weird user-space behavior.Multi-touch with single-touch protocol. Don’t try to emit
ABS_Xfor multiple fingers, that’s not how it works. Use the MT-B slot protocol (Ch 55G).Repeating events that haven’t actually changed. The core does not dedupe. Every
input_report_key(..., 1)followed byinput_syncis one event. Polling a held button without state-tracking spams the queue.IRQ flag
IRQF_TRIGGER_FALLINGwithoutIRQF_TRIGGER_RISING: you only get press events, not release. For a press/release-capable button, request both edges (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING).
45.11 Going deeper¶
Documentation/input/: the input subsystem’s full documentation. Readinput.rst,event-codes.rst,multi-touch-protocol.rst.include/uapi/linux/input-event-codes.h: the canonical list ofEV_*,KEY_*,ABS_*, etc.drivers/input/keyboard/gpio_keys.c: the in-tree gpio-keys driver. Read it. ~600 lines and covers debounce, wakeup, autorepeat, runtime PM, polling, every feature you’d add to a production button driver.drivers/input/evdev.c: the evdev “handler” that exposes input events as/dev/input/eventN.libevdevsource (freedesktop.org), for high-level user-space input access.Documentation/input/input.rst: overview of the input architecture, including the handler/handle/dev triangle.
Next chapter: Chapter 46: I²C drivers. With GPIO and input behind us, we move to a real bus: the i.MX6ULL’s I²C controllers, the
i2c_client/i2c_drivermodel, and how a single I²C bus accommodates a half-dozen sensors at different addresses.