Timeline
Timeline
2025-12-18
init
This article introduces the basic concepts and core functions of the Linux input subsystem (including improving device compatibility, unifying driver programming methods and application operation interfaces), elaborates on methods to determine the correspondence between input devices and nodes through device names, tentative operations, and viewing device information, and briefly describes its basic architecture framework such as the event processing layer.
Linux Driver Notes
| Table of Contents | Links |
|---|---|
| 1. Linux Driver Framework | |
| 2. Linux Driver Loading Logic | |
| 3. Character Device Basics | |
| 4. Concurrency and Competition | |
| 5. Advanced Character Device Progression | |
| 6. Interrupts | |
| 7. Platform Bus | |
| 8. Device Tree | |
| 9. Device Model | |
| 10. Hot Plug | |
| 11. pinctrl Subsystem | |
| 12. GPIO Subsystem | |
| 13. Input Subsystem | |
| 14. Single Bus | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. Network Device | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
Introduction to Input Subsystem
In Linux, the input subsystem is a subsystem or framework specifically designed for handling input devices. It provides a set of common interfaces and mechanisms for driver developers to write and manage drivers for input devices. Input devices include keyboards, mice, touchscreens, game controllers, etc.
The main purpose of using the input subsystem is to standardize and simplify the development process of input device drivers, thereby improving driver universality and compatibility. It achieves this byextracting common functions and processing logic of input devices into generic code, while leaving device-specific code to the respective driver developers. This division of labor allows driver developers to focus more on device-specific details, thereby significantly reducing development difficulty for engineers.
In summary, the roles of using the input subsystem are as follows:
- Compatibility: The input subsystem provides a unified framework and interface, allowing input devices from different manufacturers to be developed according to the same specifications. Whether it is a keyboard, mouse, or other input device, as long as it conforms to the interfaces and event formats defined by the input subsystem, it can work properly in the Linux system. This way, engineers do not need to write and maintain different driver codes for each manufacturer’s device, greatly improving device compatibility.
- Unified driver programming approach: The input subsystem defines a set of general driver programming methods. Engineers only need to develop according to the input subsystem’s specifications. The driver module of an input device needs to implement corresponding interface functions, such as initialization functions and event handling functions. The implementation of these interface functions is the same, whether it is a keyboard driver or a mouse driver, they can be developed in a unified manner. This allows engineers to focus more on device-specific details without worrying about the general driver framework, simplifying the development process.
- Unified application operation interface: The input subsystem provides a set of unified application operation interfaces, such as
/dev/input/eventX, allowing applications to conveniently interact with input devices. Applications can read these device nodes to obtain input event information and perform corresponding processing. Regardless of the type of input device, the application
can access and operate them in the same way. This way, application developers do not need to worry about the details of the underlying input devices and can focus more on the logic development of the application.
Determining the relationship between input devices and nodes
In the input subsystem, there is a certain correspondence between input devices and device nodes. The following are methods to determine the relationship between device nodes and input devices:
- Device name: The device nodes of the input subsystem can be divided intogeneral device namesandspecific device names. Specific device names can usually directly identify the device type from the name, for example
"keyboard"(keyboard) or"mouse"(mouse). However, a generic device name cannot directly determine the device type. As shown in the figure below,event0-event4belongs to a generic device name, whilemouse0andmouse2belong to dedicated device names.

- Trial method: You can use the
"cat"command to open the device node file, then operate the physical device and observe whether there is output on the terminal. For example, for a keyboard device, you can runcat /dev/input/eventX, where/dev/input/eventXis the path of the device node, then press a key on the keyboard and observe whether the terminal outputs the corresponding character. Through this trial method, you can determine the correspondence between the device node and the specific device. For example, use the following command to test the mouse, as shown below:
1 | sudo cat /dev/input/mouse0 |
- View input device information: You can use the following command to view the
/proc/bus/input/devicesfile:
1 | cat /proc/bus/input/devices |
A partial screenshot of the output is shown below:

This file records information about all input devices in the current system. In this file, you can find information related to device nodes, such as device name, vendor ID, product ID, etc. By comparing the path of the device node with the corresponding fields in the device information, you can determine the relationship between the device node and a specific input device. For example, from the above printed information, you can see that
The device node corresponding to the keyboard is/dev/input/event1, as shown in the figure below

I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
This line shows the device’s bus type, vendor ID, product ID, and firmware version. In this example, the device’s bus type is 0011, vendor ID is 0001,
product ID is 0001, and firmware version is ab41.
N: Name="AT Translated Set 2 keyboard"
This line shows the device’s name. In this example, the device’s name is"AT Translated Set 2 keyboard"。
P: Phys=isa0060/serio0/input0
This line shows the device’s physical location. In this example, the device’s physical location isisa0060/serio0/input0。
S: Sysfs=/devices/platform/i8042/serio0/input/input1
This line shows the device’s path in the sysfs file system. In this example, the device’s path is/devices/platform/i8042/serio0/input/input1。
U: Uniq=
This line shows the device’s unique identifier. In this example, the unique identifier is empty.
H: Handlers=sysrq kbd event1 leds
This line shows the device’s handlers. It indicates the program or module that handles the device’s input events. In this example, the device hassysrq、kbd、event1andledsthese handlers, where event1 indicates that the device node is/dev/input/event1。
B: PROP=0
This line shows the device’s properties. In this example, the device’s property value is 0.
B: EV=120013
This line shows the event types supported by the device. In this example, the device supportsEV_SYN、EV_KEY、EV_MSCandEV_LEDthese event types.
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
This line shows the keys supported by the device. Each key corresponds to a bit, where 1 indicates the key is pressed and 0 indicates it is not pressed. This line displays the key status in hexadecimal.
B: MSC=10
This line shows the miscellaneous events supported by the device. In this example, the device supportsMSC_SCANevents.
B: LED=7
This line shows the LED lights supported by the device. In this example, the device supports 3 LED lights, using a 7-bit binary number to represent the light status.
Input Subsystem Framework

Event Handling Layer
The event handling layer is the topmost layer of the input subsystem, which canprocess events generated by input devices and pass them to upper-layer applications, andcreate device nodes in the operating system, so that applications can communicate with input devices through device nodes. It receives input events from the core layer and processes them according to the event type and attributes.
Core Layer
The main function of the core layer is toact as a matcher between the event handling layer and the device driver layer. It serves to coordinate and connect these two layers, in order toensure that events from input devices are correctly passed to the corresponding event handlers. The following are the main functions of the core layer in the input subsystem:
- Event Matching: The core layer is responsible formatching the raw input data generated by input devices with the corresponding event handlers. It parses the raw input data and, based on predefined rules and configuration information, determines which event handler should receive the input data for processing.
- Device Management and Control: The core layer is responsible formaintaining the status, attributes, and configuration information of input devices, and providing device registration, deregistration, and management functions. It interacts with the device driver layer, receives input events from the device driver layer, and converts them into an abstract event representation. The core layer provides a consistent interface, allowing upper-layer applications to operate independently of specific hardware devices.
- Event Processing and Distribution: The core layeris responsible for processing and distributing events, passing input events to the corresponding event handling layer. It uses an event queue mechanism to receive and buffer input events from the device driver layer, and distributes events to the corresponding event handling layer or application according to specific rules. In this way, the event handling layer can obtain input events through the interface provided by the core layer and perform corresponding processing.
- Abstract interfaces and event handling mechanisms: Core Layerprovides a set of abstract interfaces and event handling mechanisms for upper-layer applications and the event handling layer. It provides a unified event representation, allowing events from different types of input devices (such as keyboards, mice, touchscreens, etc.) to be represented and processed. Through the core layer’s interfaces, the event handling layer can register event listeners, subscribe to specific types of events, and obtain status information of input devices.
Device Driver Layer
The device driver layer is the lowest layer of the input subsystem, responsible forcommunicating and interacting with hardware devices. Its main responsibility is to abstract the operations and functions of hardware devices into unified interfaces, so that the core layer and event handling layer can interact with them. The code of the device driver layer typically includes hardware initialization, interrupt handling, data transmission, and other operations to ensure the normal operation of input devices. Developers write drivers at this layer to adapt to specific hardware devices.
The core layer code is already written in the Linux source code, so when writing input subsystem drivers later,the core layer code does not need to be written
The event handling layer also provides a template in Linux, and except for some requirements like fixed device nodes,generally, the event handling layer code does not need to be written
The device driver layer, since it deals with different hardware and each hardware has different initialization methods,the device driver layer code needs to be filled in when writing input subsystem drivers。
Input Subsystem Source Code
Path:kernel/drivers/input
| File/Directory | Function |
|---|---|
apm-power.c | Provides an input device interface related to Advanced Power Management (APM). |
evbug.c | Provides a virtual input device for debugging, capable of simulating events such as key presses and mouse movements. |
evdev.c | Provides a generic input event layer that converts events from all input devices into a standardized input event format for use by upper-layer user-space programs. |
ff-core.c | Provides support for force feedback devices, allowing input devices to send force feedback information. |
ff-memless.c | Provides a memory-allocation-free force feedback device support, suitable for resource-constrained embedded systems. |
gameport/ | Contains a directory of drivers supporting game controllers. |
input.c | Provides operations such as initialization and event handling for the input subsystem. |
input-leds.c | Provides support for LED indicator devices, allowing control of LED indicator states. |
joydev.c | Provides drivers supporting joysticks, handling input events from joystick devices. |
keyboard/ | Contains a directory of drivers supporting keyboards. |
misc/ | Contains a directory of drivers for other types of input devices, such as infrared remote controls and input audio. |
remotectl/ | Provides drivers supporting remote control, handling input events sent via remote controls. |
serio/ | Provides a driver for input devices connected via serial ports, handling communication and processing of serial input devices. |
sensors/ | Contains a directory of sensor-related drivers for communicating with and processing various sensor devices. |
sparse-keymap.c | Provides support for sparse key mapping, allowing key assignments by arbitrary key codes, suitable for devices with non-standard keyboard layouts or special function keys. |
tablet/ | Provides drivers for graphics tablets and other types of graphic input devices, handling input events from tablet devices. |
touchscreen/ | Provides drivers for touchscreens, handling input events from touchscreen devices. |
In menuconfig:
1 | Device Drivers ---> |
As follows:
1 | -*- Generic input layer (needed for keyboard, mouse, ...) //Input core layer |
If you want to customize and configure the kernel, simply check or uncheck options.
Input subsystem data structures and their relationships
The code for the event handling layer is located indrivers/input/evdev.cfile, providing a unified event handling mechanism for upper-layer applications. It defines functions for handling input device events and provides interfaces for reading events, controlling devices, etc.
1 | // drivers/input/evdev.c |
Here, theinput_register_handlerfunction will addevdev_handlerto the input subsystem’shandlerlist, and allocate a uniquehandlernumber:
input_register_handler()
1 | // drivers/input/input.c |
struct input_handler
1 | // include/linux/input.h |
input_attach_handler()
input_register_handler()throughlist_for_each_entry(dev, &input_dev_list, node)loop callinput_attach_handler(dev, handler);, whileinput_attach_handlerThe function is as follows:
1 | // drivers/input/input.c |
input_match_device()
The role of this function in the input subsystem is to, within the given input event handler (input_handler) find the input that matches the specified input deviceinput device ID。
The handler’s input device ID table is an array withstruct input_device_idstructures as elements, each element representing a possible input device ID.
1 | // drivers/input/input.c |
input_match_device_id()
input_match_device_id(dev, id)Call the input device ID matching function to determine whether the given input device matches the current ID. The matching function compares the attributes of the input device with those specified in the ID, such as vendor ID, product ID, etc.
1 | bool input_match_device_id(const struct input_dev *dev, |
evdev_connect()
edev’sinput_handler’sconnectfunction isevdev_connect()
The main function of this function is to establish a connection with the input device, initialize and register the input handle, set device properties, and add the character device to the system.
1 | struct evdev { |
struct input_handle
After callingconnectfunction (evdevofconnectfunction), astruct input_handle, used to record successfully matched input handlers (input_handler) and input devices (input_dev), and establish the relationship between them.
1 | // include/linux/input.h |
input_register_handle()
1 | /** |
the main function of this function is to associate the input handler (input_handler) with the input device (input_dev) to establish a connection.
- for the input device (
input_dev), it can be done by traversinghandler->h_listlinked list to find the matching input handler. This means that an input device can find the corresponding handler by traversing the linked list of input handlers associated with it. - For an input handler (
input_handler) it can traverse thedev->h_listlinked list to find the matching input device. This means that an input handler can find the corresponding device by traversing the linked list of input devices associated with it.
In this way, by establishing the association between input handlers and input devices, the input handler can process and control specific input devices.
input_register_device()
input_handlerThe structure needs to useinput_register_handlerto register; the input handler (input_handler) and input device (input_dev) are connected (connect) by the structureinput_handlealso needs theinput_register_handlefunction to register
The input deviceinput_devstructure certainly also needs a function to register,input_devthe registration function for the structure isinput_register_device。
1 | // drivers/input/input.c |
input_register_deviceFunction used to register the input device (input_dev), add the input device to the input subsystem
struct input_dev
1 | // input/linux/input.h |
Data structure relationship diagram

Analyze matching rules
evdev_handlerDefined as follows
1 | // drivers/input/evdev.c |
input_register_device()
1 | int input_register_device(struct input_dev *dev) |
The key point islist_for_each_entry(handler, &input_handler_list, node)under this loopinput_attach_handler(dev, handler)the function
input_attach_handler()
1 | // drivers/input/input.c |
input_match_device()
The role of this function in the input subsystem is to, within the given input event handler (input_handler)search for an input device ID that matches the specified input device.
The handler’s input device ID table is an array ofstruct input_device_idstructures, each element representing a possible input device ID.
1 | // drivers/input/input.c |
Let’s look at evdev’sinput_handler
1 | static const struct input_device_id evdev_ids[] = { |
Sincedriver_infois 1, soinput_match_device()the condition of the for loop in will always hold true, and in each iteration it will determine whether the id matches, meaning it matches allinput_dev。
input_match_device_id()
input_match_device_id(dev, id)calls the input device ID matching function to determine whether the given input device matches the current ID. The matching function compares the attributes of the input device with those specified in the ID, such as vendor ID, product ID, etc.
1 | bool input_match_device_id(const struct input_dev *dev, |
However, the simplest input device driver layer code we wrote above does not define the flags parameter of id in the simplest device driver layer code, so the judgments in lines 5, 10, 15, and 20 are all invalid.
bitmap_subsetis an inline function used to determine whether two bitmaps have a subset relationship, i.e.,determine whether the first bitmap is a subset of the second bitmap。
id does not defineevbit、keybit、relbitetc., so the if judgment in lines 25-34 is also invalid, and ultimately the function returns true. Of course, this is only an analysis of theevdev.cgeneral event handling code. After returning true, it goes back to theinput_attach_handlerfunction, and then it callshandler->connectto establish a connection with the input device.
Many-to-Many Matching Analysis
drivers/input/joydev.cfile’sinput_handlerstructure content is as follows
joydev_handler
1 | static const struct input_device_id joydev_ids[] = { |
Unlike the above generic device driver layerevdev.c’sstruct input_handlerstructure, the difference is thatjoydev_handlerthe structure has a corresponding matching functionjoydev_match(), that is, when the device driver layer matches with the event handling layer, it requiresjoydev_idsthe structure array and the corresponding matching function to jointly determine.
1 | // drivers/input/evdev.c |
The structureinput_device_id’s role is todescribe the characteristics of the input device, so that the kernel can identify and match the correct driver. In the generic device driver layerevdev.c’sevdev_idsThe struct array setsdriver_infoindicates matching all devices, whilejoydev.cinjoydev_idsThe struct array contains the following fields:
flags: The flag bits of the identifier, used to specify the matching method. Here, usingflagsfield’sINPUT_DEVICE_ID_MATCH_EVBITandINPUT_DEVICE_ID_MATCH_ABSBITflags indicates matching event type and absolute event type.evbit: The bitmask of the event type, used to specify the event type to match. Here,evbitThe bitmask of the field indicates the matched event type isEV_ABS(absolute event) orEV_KEY(key event).absbit: The bitmask of the absolute event type, used to specify the absolute event type to match. Here,absbitThe bitmask of the field indicates the matched absolute event type isABS_X(X axis),ABS_Z(Z axis),ABS_WHEEL(scroll wheel) orABS_THROTTLE(throttle).keybit: A bitmask of key types, used to specify the key types to match. Here,keybitthe bitmask of the field indicates that the matched key type isBTN_JOYSTICK(joystick),BTN_GAMEPAD(gamepad) orBTN_TRIGGER_HAPPY(happy key).
input_match_device()
For the simplest input device driver layer code we wrote earlier:
input_match_device()In the for loop, because eachjoydev_idsin the structure array, althoughdriver_infois not set, the flags parameters all exist and are non-zero, so the for loop condition holds, and within the for loop, theinput_match_device_idfunction is called to determine whether the given input device matches the current ID.
Becauseid_tableis not set inbustype,vendor,product,version, so checking whether the device’s bus type, vendor ID, product ID, and version number match can all succeed. However:
1 | if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || |
bitmap_subsetused todetermine if the first bitmap is a subset of the second bitmap. The settings in the simplest device driver layer code written are as follows:
1 | __set_bit(EV_KEY, myinput_dev->evbit); // set to support key events |
determineinput_handlerwhether the event bitmap ofinput_devis a subset of the set event bitmap, which requires that the event processing device driver layer must support all events required by the event handling layer.
you can add a print:
1 | if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || |

summary
In the input subsystem, the relationship between input devices and input handlers is many-to-many.
This means oneinput_devcan be associated with multipleinput_handlerassociated, while ainput_handlercan also handle multipleinput_devevents.
Write the simplest device driver layer code
Steps
- Step 1:Create an input device structure variableIn device driver development, first create an input device structure variable, which will be used to represent and manage the device’s attributes and status. You can use
input_allocate_devicefunction to allocate memory for the input device structure. - Step 2:Initialize the input device structure variableAfter creating the input device structure variable, it needs to be initialized. This includes setting the device name, supported event types, event handling functions, etc. You can use the member variables and functions provided by the structure to complete the initialization process.
- Step 3:Register the input device structure variableAfter initializing the input device structure variable, it needs to be registered with the system so that the system can correctly identify and use the device. You can use
input_register_devicefunction to register the input device structure variable. During the registration process, the system will complete device matching and initialization. - Step 4:Report EventsOnce the device is successfully registered, events can be reported through the input device structure variable. This can be done by calling functions provided by the input device structure, such as
input_eventfunction. Based on the device type and event type, corresponding input events can be generated and sent to the system by calling this function. - Step 5:Unregister and Release the Input Device Structure VariableWhen the device is no longer needed, unregistration and release operations should be performed to ensure proper resource deallocation. The
input_unregister_devicefunction can be used to unregister the input device structure variable, and theinput_free_devicefunction can be used to release related resources and memory.
Register Input Device
input_allocate_device()
1 | // drivers/input/input.c |
Initialize input_dev structure
After using theinput_allocate_devicefunction to create ainput_devstructure, the next step is to initialize theinput_devstructure content. This step includes two parts:Set event typeandSet specific type。
Set event type
In the header fileinclude/uapi/linux/input-event-codes.h, the Linux kernel has defined some input event types for us, with meanings as follows:
EV_SYN (0x00): Used forsynchronization events, indicating the end of a set of input events.EV_KEY (0x01): Used forkey events, indicating pressing, releasing, or repeating a key.EV_REL (0x02): Used forrelative motion events, indicating relative position changes of a device, such as mouse movement.EV_ABS (0x03): Used forabsolute motion events, indicating absolute position changes of a device, such as touchscreen coordinates.EV_MSC (0x04): Used formiscellaneous events, including some special-purpose event types, such as device state changes.EV_SW (0x05): Used forswitch events, indicating state changes of switches, such as power buttons, lid open/close, etc.EV_LED (0x11): Used forLED events, indicating state changes of LED lights.EV_SND (0x12): Used forsound events, indicating events related to sound playback.EV_REP (0x14): Used forrepeat events, indicating keyboard repeat events.EV_FF (0x15): Used forforce feedback events, indicating output events of force feedback devices.EV_PWR (0x16): Used forpower events, indicating changes in power status.EV_FF_STATUS (0x17): Used forforce feedback status events, indicating status changes of force feedback devices.EV_MAX (0x1f): Maximum value of input event types.EV_CNT: Number of input event types.
In theinput_devstructure, a series of bitmaps are defined, used in the input subsystem to represent the capabilities and supported functions of input devices. The specific definitions are as follows:
1 | unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; // Device property bitmap |
evbit(Event type bitmap) is an array of lengthEV_CNT, where each element corresponds to an event type. By setting the corresponding bits, it indicates the event types supported by the device, such as key events, relative displacement events, absolute displacement events, miscellaneous events, etc.keybit(Key type bitmap) indicates the key types supported by the input device, typically associated withEV_KEYevent types. By setting the corresponding bits, it indicates the keys supported by the device.relbit(Relative displacement type bitmap) indicates the relative displacement types supported by the input device, typically associated withEV_RELevent types. By setting the corresponding bits, it indicates the relative displacements supported by the device, such as mouse movement.absbit(Absolute displacement type bitmap) indicates the absolute displacement types supported by the input device, typically associated withEV_ABSevent types. By setting the corresponding bits, it indicates the absolute displacements supported by the device, such as touchscreen coordinates.mscbit(Miscellaneous type bitmap) indicates the miscellaneous types supported by the input device, typically associated withEV_MSCevent types. By setting the corresponding bits, it indicates the miscellaneous events supported by the device, such as device status changes, etc.ledbit(LED type bitmap) Indicates the LED types supported by the input device, usually associated withEV_LEDevent types. By setting the corresponding bits, the LED control supported by the device can be indicated.sndbit(Sound type bitmap) Indicates the sound types supported by the input device, usually associated withEV_SNDevent types. By setting the corresponding bits, the sound events supported by the device can be indicated.ffbit(Force feedback type bitmap) Indicates the force feedback types supported by the input device, usually associated withEV_FFevent types. By setting the corresponding bits, the force feedback events supported by the device can be indicated.swbit(Switch type bitmap) Indicates the switch types supported by the input device, usually associated withEV_SWevent types. By setting the corresponding bits, the switch state changes supported by the device can be indicated.
__set_bitis a bit manipulation function used to set a specific bit in a bitmap. For example, the following code can set the input device to support key events:
1 | __set_bit(EV_KEY,myinput_dev->evbit) |
Set specific type
After setting the event type, the specific type also needs to be set. The macro definitions are in the header fileinclude/uapi/linux/input-event-codes.h, with partial content as follows:
1 | .. |
The previous section only set the input device to key events, but what exactly to represent—key 1, key 2, or other keys—cannot be determined, so it is still necessary to use__set_bitfunction to determine the specific type. For example, use the following program to set the input device to key 1
1 | __set_bit(KEY_1,myinput_dev->keybit) |
Example
1 |
|
Note: No events are reported here.
- Use input_free_device() to free devices that have not been registered;
- Use input_unregister_device() should be used for already registered devices.
Test:
1 | root@topeet:/root# insmod input_dev_test.ko |
Improve the device driver layer code
Report events
Reporting events refers to notifying the input subsystem when an input device generates an event in the device driver layer。
Before reporting events, first determine the type of event to report. Event types can be key events, relative position events, absolute position events, etc., depending on the characteristics and capabilities of the input device.
In the Linux kernel, event types are represented by predefined constants, such asEV_KEYrepresents key events,EV_RELrepresents relative position events,EV_ABSrepresents absolute position events, etc.
In the second step of the simplest device driver layer code written earlier, the event type and specific events have been confirmed.
After determining the event type, the corresponding reporting function needs to be used to pass the event data to the input subsystem. Commonly used reporting functions include:
input_report_key(): Reports key events, used to notify the press and release status of keys.input_report_rel(): Reports relative position events, used to notify the relative movement of a device, such as mouse movement.input_report_abs(): Reports absolute position events, used to notify the absolute position of a device, such as touchscreen coordinates.

Report Function
input_report_key()
| Item | Description |
|---|---|
| Header File | <linux/input.h> |
| Function Prototype | void input_report_key(struct input_dev *dev, unsigned int code, int value) |
| Parameters | -dev: Pointer to the input device structureinput_devpointer- code: Key event code (e.g.,KEY_A,BTN_TOUCHetc.)- value: Key state (0 = released, non-zero = pressed) |
| Return value | None (void) |
| Function | Report akey event (EV_KEY). Used for scenarios such as keyboards, buttons, and touchscreen clicks. |
input_report_rel()
| Item | Description |
|---|---|
| Header file | <linux/input.h> |
| Function prototype | void input_report_rel(struct input_dev *dev, unsigned int code, int value) |
| Parameters | -dev: Pointer toinput_devpointer- code: Relative axis type (e.g.,REL_X,REL_Y,REL_WHEEL) - value: Offset relative to the previous position (can be positive or negative) |
| Return value | None (void) |
| Function | Report arelative coordinate event (EV_REL). Commonly used for devices such as mice and scroll wheels. |
input_report_abs()
| Item | Description |
|---|---|
| Header file | <linux/input.h> |
| Function prototype | void input_report_abs(struct input_dev *dev, unsigned int code, int value) |
| Parameters | -dev: Pointer toinput_devpointer- code: Absolute axis type (e.g.,ABS_X,ABS_Y,ABS_MT_POSITION_X) - value: Current absolute coordinate value |
| Return value | None (void) |
| Function | Report anabsolute coordinate event (EV_ABS). Used for devices such as touchscreens, graphics tablets, and game controller joysticks. |
input_report_ff_status()
| Item | Description |
|---|---|
| Header file | <linux/input.h> |
| Function prototype | void input_report_ff_status(struct input_dev *dev, unsigned int code, int value) |
| Parameters | -dev: Pointer toinput_devpointer- code: Force feedback effect ID (usually assigned by user space)- value: Force feedback status (0 = stopped, non-zero = playing) |
| Return Value | None (void) |
| Function | ReportForce Feedback Status Event (EV_FF_STATUS), used to notify user space of the current running status of a force feedback effect. |
⚠️ Note: This function is rarely used; most force feedback is actively controlled by the kernel, not reported by the device.
input_report_switch()
| Item | Description |
|---|---|
| Header File | <linux/input.h> |
| Function Prototype | void input_report_switch(struct input_dev *dev, unsigned int code, int value) |
| Parameters | -dev: Pointer toinput_devPointer- code: Switch type (e.g.,SW_LID,SW_TABLET_MODE)- value: Switch state (0 = closed/lid open, non-zero = open/lid closed, specific meaning depends on type) |
| Return value | None (void) |
| Function | Report aswitch state event (EV_SW). Commonly used for hardware switches such as laptop lid detection and tablet mode switching. |
input_sync()
| Item | Description |
|---|---|
| Header file | <linux/input.h> |
| Function prototype | void input_sync(struct input_dev *dev) |
| Parameters | -dev: Pointer toinput_devpointer |
| Return value | None (void) |
| Effect | Send asynchronization event (EV_SYN / SYN_REPORT), indicating that a set of related events has been fully reported. User space will treat all previously unsynchronized events as a single event packet that occurred at the same time.This function must be called after each set of data is reported, otherwise the events may not be processed. |
input_event()
1 | /** |
- All the above functions areinline functions, which internally call
input_event(dev, type, code, value)。 - The event type (
type) is implicitly determined by the function:input_report_key→EV_KEYinput_report_rel→EV_RELinput_report_abs→EV_ABSinput_report_ff_status→EV_FF_STATUSinput_report_switch→EV_SWinput_sync→EV_SYN(code = SYN_REPORT)
These functions are the core APIs in Linux input subsystem driver development, used to convert raw data generated by hardware into standard input events for use by user space (such as evdev, libinput, X11/Wayland).
After using the reporting function, it is common to call theinput_sync()function for synchronization. The purpose of the synchronization event is to inform the input subsystem of the end of events, so that the subsystem can pass the events to the corresponding application or system component for processing. Calling the synchronization event prevents loss or confusion of event data.
Example
Driver, uses a timer to report events at regular intervals.
1 |
|
Application layer retrieves reported data
input_event structure
The data read by the application layer isinput_eventStructure:
1 | struct input_event { |
- type: type is used to describe which type of event occurred (classification of events). The input event types supported by the Linux system are as follows; these macro definitions are also in the
<linux/input.h>header file, so the application needs to include this header file;EV_SYN (0x00): used forSynchronization event, indicates the end of a set of input events.EV_KEY (0x01): Used forKey event, indicates pressing, releasing, or repeating a key.EV_REL (0x02): Used forRelative motion event, indicates relative position changes of a device, such as mouse movement.EV_ABS (0x03): Used forAbsolute motion event, indicates absolute position changes of a device, such as touchscreen coordinates.EV_MSC (0x04): Used forMiscellaneous event, includes some special-purpose event types, such as device status changes.EV_SW (0x05): Used forSwitch event, indicating a change in switch state, such as power button, lid open/close, etc.EV_LED (0x11): Used forLED event, indicating a change in LED light state.EV_SND (0x12): Used forSound event, indicating events related to sound playback.EV_REP (0x14): Used forRepeat event, indicating keyboard repeat send events.EV_FF (0x15): Used forForce feedback event, indicating output events from force feedback devices.EV_PWR (0x16): Used forPower event, indicating a change in power status.EV_FF_STATUS (0x17): Used forForce feedback status event, indicating a change in the status of the force feedback device.EV_MAX (0x1f): Maximum value of the input event type.EV_CNT: Number of input event types.
- code: The code indicates which specific event within that event type. Each event type listed above contains a series of specific events. For example, a keyboard typically has many keys, and the code variable tells the application which key triggered the input event.
1 |
For code values of other input events, refer toinput-event-codes.hheader file (this header file is<linux/input.h>included).
- value: Each time the kernel reports an event, it sends a data value to the application layer. The interpretation of the value changes with the code.
- For example, for key events:
- value equals 1 indicates the key is pressed;
- value equals 0 indicates the key is released;
- value equals 2 indicates the key is held down.
- In the absolute displacement event (type=3)
code=0(touch point X coordinateABS_X), then the value equals the X-axis coordinate of the touch point;code=1(touch point Y coordinateABS_Y), at this point the value equals the Y-axis coordinate of the touch point.
- For example, for key events:
Report data format
Use the commandhexdump /dev/input/event4to view the reported information.

1 | struct input_event { |
- In the
input_eventdata packet, there are four member variables:time,type,code,value。time.tv_secandtime.tv_usecare of typelong int, occupies 8 bytes, sotimeoccupies 16 bytes__u16 typethe type isunsigned short int, occupies 2 bytes__u16 codethe type isunsigned short int, occupies 2 bytes__s32 valuethe type isunsigned int, occupies 4 bytes
Therefore, ainput_eventthe byte size of a data packet is bytes
Generally,
input_eventthe byte order of a data packet is Little Endian. This means that the lower bytes are located at higher memory addresses.
Assume the hexdump output data (in hexadecimal) is as follows:
1 | root@topeet:~$ hexdump /dev/input/event2 |
The firstinput_eventcorresponds to the following members:
tv_sec:0f09 65d3 0000 0000tv_usec:36fb 0001 0000 0000type:0003code:0039value:0000 0000
Example
1 |
|
Test:
1 | root@topeet:/root# insmod input_report_event.ko |
Code Analysis of the Generic Event Handling Layer evdev
Analysis of the connect Function
evdev_handler
1 | // drivers/input/evdev.c |
struct evdev
1 | struct evdev { |
open: Records the open status of the evdev device, possible values are 0 (closed) or 1 (open).handle: Handle used to process input events, containing information related to the event handler, such as the opened input device and event processing functions.grab: Pointer to the client currently occupying the evdev device. When a client occupies the evdev device, other clients cannot access it.client_list: Linked list of clients associated with the evdev device, used to manage clients connected to the device.client_lock: Spinlock used to protect the client linked list, ensuring thread-safe operations on the client linked list in a multi-threaded environment.mutex: Used to protect mutual exclusive access to the evdev device, ensuring mutually exclusive operations on the device in a multi-threaded environment.dev: Device structure associated with the evdev device, used to represent specific information about the device, such as device name, device number, etc.cdev: Character device structure for evdev devices, used to register and manage character devices.exist: Flag indicating whether the evdev device exists. true if the device exists; otherwise false.
struct evdev_client
This structure defines the information and status of an evdev client, used to manage clients associated with an evdev device. Each time an application opens an event device node, astruct evdev_clientstructure is used to represent it. The system can maintain multiple clients for each evdev device and manage the status and properties of each client.
Inevdev.c, operations are also performed on this structure, and based on the client’s status and properties, received events are written to the buffer or the client is notified.
1 | struct evdev_client { |
head: Head pointer of the buffer, pointing to the next writable position.tail: Tail pointer of the buffer, pointing to the next readable position.packet_head: Position of the first element of the next data packet.buffer_lock: Spinlock used to protect access to the buffer, head pointer, and tail pointer, ensuring thread-safe operations on the buffer in a multi-threaded environment.fasync: Pointer to a structure for asynchronous notification; set to the appropriate value when asynchronous notification is needed.evdev: Pointer to the evdev device associated with the client, indicating the evdev device to which the client belongs.node: Linked list node for clients associated with the evdev device, used to manage clients associated with the device.clk_type: Input clock type, indicating the type of input clock used by the client.revoked: Flag indicating whether the client has been revoked.evmasks: Array for event masks, storing masks for different types of events; the size of the array is defined byEV_CNT.bufsize: Size of the buffer, indicating the number of input events the buffer can hold.buffer[]: The input event buffer is a variable-length array that stores input event data.
evdev_connect()
input_handler’s connect function.
input_register_handler()called ininput_attach_handler(), which in turn callsinput_handler’s connect function,error = handler->connect(handler, dev, id)namelyevdev_connect()
The main function of this function is to establish a connection with the input device, initialize and register the input handle, set device properties, and add the character device to the system.
1 |
|
VisibleconnectThe main task of the function is to associate the input device with the event handler, so that the corresponding handler function is called when an event occurs.
It achieves this association by registering an input handler and setting a callback function, ensuring the correct event handler is invoked. This association mechanism allows developers to customize handler functions as needed to process events reported by the input device accordingly.
Allocation of device number analysis

From the above figure, it can be seen that theevdev.cThe device nodes event0, event1, event2, event3 created by the program all have a major device number of 13, and minor device numbers of 64, 65, 66, 67. The pattern for all device numbers is that the major device number is always 13, and the minor device numbers start from 64 and increase sequentially.
Major device number
In the connect function, useevdev->dev.devt = MKDEV(INPUT_MAJOR, minor);Set the major device number, which is INPUT_MAJOR, i.e., 13
1 | // include/uapi/linux/major.h |
Minor device number
In the connect function, usedminor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);The function obtains a minor device number. And
1 |
input_get_new_minor()
1 | /** |
The above function is used to obtain a new minor device number. Its main function is to obtain a new minor device number based on specified conditions. Iflegacy_baseis specified, it first attempts to obtain a minor device number from that range. If the attempt fails or dynamic allocation is not allowed, it then tries to obtain a minor device number from the dynamically allocated range. Finally, it returns the obtained minor device number.
If
legacy_baseis greater than or equal to 0, the following logic is executed:Using the
ida_simple_get()function frominput_idato obtain a minor device numberminor, with a range fromlegacy_basetolegacy_base + legacy_numIf the obtained minor device number minor is greater than or equal to 0, or dynamic allocation is not allowed (
allow_dynamicis false), then return the minor device number minor.
If the above conditions are not met, the following logic is executed:
- Using the
ida_simple_get()function frominput_idaObtain a minor device number fromminor, with a range ofINPUT_FIRST_DYNAMIC_DEVtoINPUT_MAX_CHAR_DEVICES。
- Using the
andida_simple_get()is defined as:
1 |
ida_alloc_rangeThe function is used to allocate a contiguous range of IDs in the ID allocator. The parameters of the macro are explained as follows:
ida: Indicates the pointer to the IDA object, used to manage the allocation and release of ID ranges.start: Indicates the starting ID of the allocated ID range.end: Indicates the ending ID of the allocated ID range.gfp: Indicates the GFP flags used for memory allocation.
File operation set functions
In theconnectfunction, bycdev_init(&evdev->cdev, &evdev_fops);creating a character device, the most important operation in creating a character device is to implement the functions in the file operation set, as follows:
1 | static const struct file_operations evdev_fops = { |
Analysis of the open function
In theconnectfunction, bycdev_init(&evdev->cdev, &evdev_fops);Creating a character device, the most important operation in creating a character device is to implement the functions in the file operations set, as follows:
1 | static const struct file_operations evdev_fops = { |
evdev_open()
1 | static int evdev_open(struct inode *inode, struct file *file) |
stream_open()
1 | /* |
edev_open_device()
1 | static int evdev_open_device(struct evdev *evdev) |
input_open_device()
evdev_open_device()called ininput_open_device()function opens the input device. This function callsinput_dev’sopen()function
1 | /** |
ioctl function analysis
evdev_ioctl()
1 | static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
evdev_ioctl_handler()
1 | static long evdev_ioctl_handler(struct file *file, unsigned int cmd, |
evdev_do_ioctl()
1 | static long evdev_do_ioctl(struct file *file, unsigned int cmd, |
The commands in the above code are explained as follows:
EVIOCGVERSION: Get the version number.EVIOCGID: Get the ID information of the input device.EVIOCSREP: Get key repeat settingsEVIOCGKEYCODE: Get key codeEVIOCGKEYCODE_V2: Get key mapping tableEVIOCSKEYCODE: Set key valueEVIOCSKEYCODE_V2: Set key mapping tableEVIOCGNAME(len): Get device nameEVIOCGPHYS(len): Get physical locationEVIOCGUNIQ(len): Get unique identifierEVIOCGPROP(len): Get device propertiesEVIOCGMTSLOTS(len): Get multi-touch informationEVIOCGKEY(len): Get global key stateEVIOCGLED(len): Get all LED statesEVIOCGSND(len): Get all sound statesEVIOCGSW(len): Get all switch statesEVIOCGBIT(ev,len): Get event bitmapEVIOCGABS(abs): Get absolute value/rangeEVIOCSABS(abs): Set absolute value/rangeEVIOCSFF: Send force feedback effect to force feedback deviceEVIOCRMFF: Delete force feedback effectEVIOCGEFFECTS: Report number of simultaneously playable effectsEVIOCGRAB: Claim/release input deviceEVIOCREVOKE: Revoke device access permissionEVIOCGMASK: Retrieve current event maskEVIOCSMASK: Set event maskEVIOCSCLOCKID: Set clock identifier for timestamps
Poll function analysis
1 | /* No kernel lock - fine */ |
Analysis of the fasync function
1 | static int evdev_fasync(int fd, struct file *file, int on) |
Analysis of the llseek function
1 | loff_t no_llseek(struct file *file, loff_t offset, int whence) |
Return-ESPIPEdirectly as the return value. The purpose of this function is to prevent llseek operations on the device file, meaning it does not allow random access to the device file by changing the file position pointer.
Analysis of the release function
1 | static int evdev_release(struct inode *inode, struct file *file) |
Data reporting flow analysis
When using the read function to read data reported by the input device, the file operations in the driver will executeevdev_read()function. Similarly, when we use the write function to write data to the input device, the file operations in the driver will executeevdev_write()function.
The device input layer is responsible for processing data from input devices and passing it to the driver. When an input device reports data, the device input layer receives this data and forwards it to the registered driver.
event function analysis
input_event()
must be called when reporting eventsinput_event
1 | /** |
input_handle_event()
1 | static void input_handle_event(struct input_dev *dev, |
input_get_disposition()
1 | static int input_get_disposition(struct input_dev *dev, |
This function is used to determine the handling method of an event based on the type, code, and value of the input device. First, it branches based on the event type. In each branch, it determines the handling method according to the event type and code, and updates the disposition accordingly. The disposition has the following methods
INPUT_IGNORE_EVENT: Indicates ignoring the input event without any processingINPUT_PASS_TO_HANDLERS: Indicates that the input event is passed to a handler for processing. The handler can be a callback function in the input driver, or a user-space application or service process.INPUT_PASS_TO_DEVICE: Indicates that the input event is passed to a device for processing. Devices may include physical devices (such as keyboards, mice) or virtual devices (such as touchscreen simulators).INPUT_SLOT: Used for touchscreen simulators, indicating that the input event is an event for a specific slot on the touch screen. Typically, when handling multi-touch events, each touch point corresponds to a slot.INPUT_FLUSH: Indicates that the input event queue needs to be flushed. When the input event queue accumulates to a certain number or becomes full, this flag is used to flush the queue and pass events to the device for processing.INPUT_PASS_TO_ALL(INPUT_PASS_TO_HANDLERS|INPUT_PASS_TO_DEVICE: Indicates that the input event is passed to both the handler and the device for processing, i.e., a combination ofINPUT_PASS_TO_HANDLERSandINPUT_PASS_TO_DEVICEfunctionalities.
input_pass_values()
input_handle_event()Called in the functioninput_pass_values(dev, dev->vals, dev->num_vals);This function is used to pass the value of the input device to the corresponding handle for processing, and to trigger automatic repetition of key events.
1 | /* |
input_to_handler()
1 | /* |
It can be seen that: according to the handler definition, if an event handler function is defined (events is not NULL), it passes the processed value to the event handler function. Otherwise, if only a single event handler function is defined (event is not NULL), it calls the event handler function for each value, passing the handle, type, code, and value as parameters.
evdev_event()
handler->eventisevdev_event
1 | static void evdev_event(struct input_handle *handle, |
edev_eventis to callevdev_events
evdev_events()
1 | /* |
Here,RCUrefers to theRead-Copy Updatemechanism in the Linux kernel, which is aLock-free read synchronization mechanism。
When an input event arrives, it must be sent to all clients.
The problem is:
- One thread istraversing the client list
- while another thread might bedeleting a client
Using a regular lock would cause:
- High lock contention
- Increased input latency
- Interrupt context may be unsafe
RCU allows readers to access data with almost no locking, while ensuring writers can safely update data.
RCU = Read + Copy + Update
When a writer updates data, it does not modify directly, but instead:
- Copy a new set of data
- Modify the new data
- Switch pointer (atomically change the shared pointer from the old object to the new object)
- Wait for all “old readers” to exit (RCU does not wait for “all readers to finish”, but for “allold readersto finish”, meaning readers that entered before the pointer switch, not new readers)
- Release old data
Thus, readers either see old data or new data, never a “half-updated state”
RCU is well-suited for this:
Many reads
Few writes
Allows briefly seeing old state
edev_pass_values()
1 | static void evdev_pass_values(struct evdev_client *client, |
__pass_event()
1 | static void __pass_event(struct evdev_client *client, |
Analysis of the read function
The driver can obtain data reported by the input device through a callback function registered with the device input layer. In the driver,evdev_readThe function is used to obtain data reported by input devices from the device input layer.
edev_read()
edev_read()Read input events from the evdev device
1 | static ssize_t evdev_read(struct file *file, char __user *buffer, |
Write function analysis
When we use the write function in an application to write data to an input device, the input data is passed to the driver’sevdev_writefunction. The driver can process and respond to this written data as needed.
edev_write()
This function writes the input event to the buffer of the evdev device
1 | static ssize_t evdev_write(struct file *file, const char __user *buffer, |
Analysis of the input core layer code
The core layer of the input subsystem is mainly implemented bykernel/drivers/input/input.cfile, which is one of the key components for handling input devices in the Linux kernel.input.cfile is responsible for registering, managing, and processing input devices, providing core functions and interfaces related to input devices.
The main functions of the core layer are as follows:
- Device registration and management: The core layer is responsible for registering and managing input devices. It interacts with device drivers to match input devices with corresponding drivers and creates data structures related to the devices. These data structures contain information such as device status, attributes, and operation functions.
- Event handling: The core layer is responsible for handling events generated by input devices. When an input device experiences touch, key press, or other operations, the core layer receives the corresponding event data and processes it. It passes the event data to upper-layer applications or other subsystems to implement corresponding interactive operations.
- Event distribution: The core layer is responsible for distributing events to applications or subsystems that have registered for the corresponding devices. It passes events to the appropriate handlers based on the device type and attributes. This allows applications or subsystems to perform corresponding actions according to the event type, such as handling touch events or responding to key inputs.
- Device Node Management: The core layer is responsible for creating and managing device nodes for input devices. Device nodes are typically located in the
/dev/inputdirectory, providing access interfaces to input devices. The core layer creates corresponding information in the device nodes based on the device type and attributes, and ensures the correctness and consistency of the device nodes. - Device Driver Interface: The core layer provides an interface for device drivers to interact with the input subsystem. Drivers can communicate and exchange data with the core layer by registering callback functions, enabling functions such as input device initialization and event handling.
input_init()
1 | // drivesr/input/input.c |
input_proc_init()
1 | static int __init input_proc_init(void) |
Fix the device nodes of input devices
Requirement
During embedded Linux development, the loading order of peripherals from different manufacturers and models may vary during kernel startup. For example, devices such as touchpads and USB-to-serial converters, this can lead to/dev/inputThe evdevx nodes (where x=0,1,2,3…) created under the directory differ. However, applications typically open fixed device nodes. If the device node changes, it may cause the application to open the wrong device node. Therefore, it is necessary to fix the device nodes created for input devices.
Solution
By analyzing theevdev.cdriver, we determined that the device node is created in theevdev_connectfunction**. Therefore, we only need toin theevdev_connectfunction, create a separate device node for the device that needs a fixed device node.
First, determine the name of the device for which you want to fix the device node, using thecat /proc/bus/input/devicescommand to find the device name.

Modifyevdev_connect 函数, and based on the device name, determine whether it is the name of the node you want to fix, then use thedev_set_namefunction to fix the device node



