1. Introduction to Input Subsystem
    1. Determining the relationship between input devices and nodes
    2. Input Subsystem Framework
      1. Event Handling Layer
      2. Core Layer
      3. Device Driver Layer
    3. Input Subsystem Source Code
  2. Input subsystem data structures and their relationships
    1. input_register_handler()
      1. struct input_handler
      2. input_attach_handler()
        1. input_match_device()
          1. input_match_device_id()
      3. evdev_connect()
        1. struct input_handle
        2. input_register_handle()
    2. input_register_device()
      1. struct input_dev
    3. Data structure relationship diagram
  3. Analyze matching rules
    1. input_register_device()
      1. input_attach_handler()
        1. input_match_device()
          1. input_match_device_id()
    2. Many-to-Many Matching Analysis
      1. joydev_handler
      2. input_match_device()
      3. summary
  4. Write the simplest device driver layer code
    1. Steps
    2. Register Input Device
      1. input_allocate_device()
      2. Initialize input_dev structure
        1. Set event type
        2. Set specific type
      3. Example
    3. Improve the device driver layer code
      1. Report events
      2. Report Function
        1. input_report_key()
        2. input_report_rel()
        3. input_report_abs()
        4. input_report_ff_status()
        5. input_report_switch()
        6. input_sync()
        7. input_event()
      3. Example
    4. Application layer retrieves reported data
      1. input_event structure
      2. Report data format
      3. Example
  5. Code Analysis of the Generic Event Handling Layer evdev
    1. Analysis of the connect Function
      1. evdev_handler
      2. struct evdev
      3. struct evdev_client
      4. evdev_connect()
    2. Allocation of device number analysis
      1. Major device number
      2. Minor device number
        1. input_get_new_minor()
    3. File operation set functions
      1. Analysis of the open function
        1. evdev_open()
        2. stream_open()
        3. edev_open_device()
        4. input_open_device()
      2. ioctl function analysis
        1. evdev_ioctl()
        2. evdev_ioctl_handler()
        3. evdev_do_ioctl()
      3. Poll function analysis
      4. Analysis of the fasync function
      5. Analysis of the llseek function
      6. Analysis of the release function
    4. Data reporting flow analysis
      1. event function analysis
        1. input_event()
        2. input_handle_event()
        3. input_get_disposition()
        4. input_pass_values()
        5. input_to_handler()
        6. evdev_event()
        7. evdev_events()
        8. edev_pass_values()
        9. __pass_event()
      2. Analysis of the read function
        1. edev_read()
      3. Write function analysis
        1. edev_write()
  6. Analysis of the input core layer code
    1. input_init()
    2. input_proc_init()
  7. Fix the device nodes of input devices
    1. Requirement
    2. Solution
Cover image for Linux Input Subsystem

Linux Input Subsystem


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 ContentsLinks
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:

  1. 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.

/dev/input
/dev/input

  1. 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
2
sudo cat /dev/input/mouse0
sudo hexdump /dev/input/mouse0
  1. 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:

/proc/bus/input/devices
/proc/bus/input/devices

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

keyboard
keyboard

  • 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 hassysrqkbdevent1andledsthese 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_SYNEV_KEYEV_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

Input Subsystem Framework
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/DirectoryFunction
apm-power.cProvides an input device interface related to Advanced Power Management (APM).
evbug.cProvides a virtual input device for debugging, capable of simulating events such as key presses and mouse movements.
evdev.cProvides 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.cProvides support for force feedback devices, allowing input devices to send force feedback information.
ff-memless.cProvides a memory-allocation-free force feedback device support, suitable for resource-constrained embedded systems.
gameport/Contains a directory of drivers supporting game controllers.
input.cProvides operations such as initialization and event handling for the input subsystem.
input-leds.cProvides support for LED indicator devices, allowing control of LED indicator states.
joydev.cProvides 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.cProvides 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
2
Device Drivers --->
Input device support --->

As follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-*- Generic input layer (needed for keyboard, mouse, ...) //Input core layer
<*> Export input device LEDs in sysfs
<*> Support for memoryless force-feedback devices
-*- Polled input device skeleton
< > Sparse keymap support library
-*- Matrix keymap support library

*** Userland interfaces ***
< > Mouse interface
< > Joystick interface
<*> Event interface
< > Event debugging

*** Input Device Drivers ***
[*] Keyboards --->
[*] Mice --->
[ ] Joysticks/Gamepads ----
[ ] Tablets ----
[*] Touchscreens --->
<*> rockchip remotectl --->

*** handle all sensors ***
< > handle angle,accel,compass,gyroscope,lsensor psensor etc
[*] Miscellaneous devices --->
< > Synaptics RMI4 bus support

Hardware I/O ports --->

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event, // Event handling function pointer, pointing to the function named evdev_event, used to handle input events
.events = evdev_events,// Batch event handling function pointer, pointing to the function named evdev_events, used to handle multiple events from input devices
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",// Device name, set to the string "evdev"
.id_table = evdev_ids,// Input device ID table, pointing to a table named evdev_ids, used to match the IDs of input devices
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

Here, theinput_register_handlerfunction will addevdev_handlerto the input subsystem’shandlerlist, and allocate a uniquehandlernumber:

input_register_handler()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// drivers/input/input.c
/**
* input_register_handler - register a new input handler
* @handler: handler to be registered
*
* This function registers a new input handler (interface) for input
* devices in the system and attaches it to all input devices that
* are compatible with the handler.
*/
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error;
// Attempt to acquire the input mutex to ensure that the registration of the handler is not interrupted
error = mutex_lock_interruptible(&input_mutex);
if (error)
return error;
// Initialize the handler linked list head
INIT_LIST_HEAD(&handler->h_list);
// Add the handler to the end of the global handler linked list, enabling it to interact with other components of the input subsystem
list_add_tail(&handler->node, &input_handler_list);
// Traverse the input device linked list, attaching a handler to each device, thereby establishing a connection between each input device and the handler to process input events sent by the device
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
// Wake up the procfs reader to notify it of a new handler registration, so that the reader can promptly obtain new input event information.
input_wakeup_procfs_readers();

// Release the input mutex to allow other threads to continue accessing the input subsystem
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_handler);

struct input_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// include/linux/input.h
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
* @events: event sequence handler. This method is being called by
* input core with interrupts disabled and dev->event_lock
* spinlock held and so it may not sleep
* @filter: similar to @event; separates normal event handlers from
* "filters".
* @match: called after comparing device's id with handler's id_table
* to perform fine-grained matching between device and handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process
* that "grabbed" a device releases it
* @legacy_minors: set to %true by drivers using legacy minor ranges
* @minor: beginning of range of 32 legacy minors for devices this driver
* can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*
* Input handlers attach to input devices and create input handles. There
* are likely several handlers attached to any given input device at the
* same time. All of them will get their copy of input event generated by
* the device.
*
* The very same structure is used to implement input filters. Input core
* allows filters to run first and will not pass event to regular handlers
* if any of the filters indicate that the event should be filtered (by
* returning %true from their filter() method).
*
* Note that input core serializes calls to connect() and disconnect()
* methods.
*/
struct input_handler {

void *private;// Private data pointer, used to store private data of a specific handler
// Event handler function pointer, called when an input event occurs, with parameters including the input handle, event type, event code, and event value
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
// Batch event handler function pointer, called when an input device has multiple events occurring simultaneously, with parameters including the input handle, an array of event values, and the number of events
void (*events)(struct input_handle *handle, const struct input_value *vals, unsigned int count);
// Event filter function pointer, used to determine whether to receive and process events of a specific type and code; returns a boolean value indicating whether to accept the event
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
// Match function pointer, used to determine whether the handler is applicable to a given input device; returns a boolean value indicating applicability
bool (*match)(struct input_handler *handler, struct input_dev *dev);
// Connect function pointer, used to establish a connection between the input device and the handler; returns an integer value indicating the connection result
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
// Disconnect function pointer, used to disconnect the input device from the handler
void (*disconnect)(struct input_handle *handle);
// Start function pointer, used to start data transmission or processing of the input device
void (*start)(struct input_handle *handle);

bool legacy_minors;// Whether to use the legacy minor device number
int minor;// Device minor number
const char *name;// Device name

const struct input_device_id *id_table;// Input device ID table

struct list_head h_list;// Handler linked list head
struct list_head node;// Handler linked list node

ANDROID_KABI_RESERVE(1);
};

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// drivers/input/input.c
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
// Determine whether it is applicable to the device through the matching function of the input device and handler.
// This function will search the handler's input device ID table for an ID that matches the given input device,
// and return the matching ID. If no matching ID is found, it returns NULL.
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
// Call the handler's connect function to establish a connection between the device and the handler.
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// drivers/input/input.c
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
// Traverse the handler's input device ID table until a matching ID is found or all IDs have been traversed.
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) &&// Use the input device ID matching function to determine whether the given input device matches the current ID.
(!handler->match || handler->match(handler, dev))) {// If the input device matches the ID and the handler's matching function returns true (or there is no matching function), return that ID.
return id;
}
}

return NULL;
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
return false;

if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}

return true;
}
EXPORT_SYMBOL(input_match_device_id);

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
struct evdev {
int open;
struct input_handle handle;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
};

struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
wait_queue_head_t wait;
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
enum input_clock_type clk_type;
bool revoked;
unsigned long *evmasks[EV_CNT];
unsigned int bufsize;
struct input_event buffer[];
};

/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
// Obtain a new minor device number.
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
// Allocate and initialize the evdev structure.
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
// Initialize the members of the evdev structure.
INIT_LIST_HEAD(&evdev->client_list);// Initialize the client list.
spin_lock_init(&evdev->client_lock);// Initialize the spinlock for the client list.
mutex_init(&evdev->mutex);// Initialize the mutex lock.
evdev->exist = true;// Set the evdev existence flag to true.

dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)// If the device number is within the legacy range, perform normalization.
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);// Set the device name.

evdev->handle.dev = input_get_device(dev);// Set the input device of the input handle.
evdev->handle.name = dev_name(&evdev->dev);// Set the name of the input handle to the device name.
evdev->handle.handler = handler;// Set the handler of the input handle to the passed handler.
evdev->handle.private = evdev;// Set the private data pointer of the input handle to the pointer of the evdev structure.

evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// Set the device number.
evdev->dev.class = &input_class;// Set the device class
evdev->dev.parent = &dev->dev;// Set the parent device of the device
evdev->dev.release = evdev_free;// Set the device release function to evdev_free
device_initialize(&evdev->dev);// Initialize the device

error = input_register_handle(&evdev->handle);// Register the input handle
if (error)
goto err_free_evdev;

cdev_init(&evdev->cdev, &evdev_fops);// Initialize the character device structure

error = cdev_device_add(&evdev->cdev, &evdev->dev);// Add the character device
if (error)
goto err_cleanup_evdev;

return 0;

err_cleanup_evdev:
evdev_cleanup(evdev);// Clean up the evdev structure
input_unregister_handle(&evdev->handle);// Unregister the input handle
err_free_evdev:
put_device(&evdev->dev);// Release the device
err_free_minor:
input_free_minor(minor);// Release the minor device number
return error;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// include/linux/input.h
/**
* struct input_handle - links input device with an input handler
* @private: handler-specific data
* @open: counter showing whether the handle is 'open', i.e. should deliver
* events from its device
* @name: name given to the handle by handler that created it
* @dev: input device the handle is attached to
* @handler: handler that works with the device through this handle
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
*/
struct input_handle {

void *private;// Private data pointer

int open;// Open count
const char *name;// Name

struct input_dev *dev;// Input device
struct input_handler *handler;// Input handler

struct list_head d_node;// Node pointing to the input device linked list
struct list_head h_node;// Node pointing to the input handler linked list

};

input_register_handle()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
*
* This function is supposed to be called from handler's
* connect() method.
*/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;// Get input handler
struct input_dev *dev = handle->dev;// Get input device
int error;

/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
/*
* Get here dev->mutex Lock,to prevent with input_release_device() competition。
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;

/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
/*
* add the filter to the head of the linked list,add the normal handler to the tail of the linked list。
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);

mutex_unlock(&dev->mutex);

/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
/*
* since we assume it is removed from ->connect() call,this is mutually exclusive with ->disconnect() is mutually exclusive,
* so we cannot compete with input_unregister_handle() compete,therefore no additional locking is needed here。
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);

if (handler->start)
handler->start(handle);

return 0;
}
EXPORT_SYMBOL(input_register_handle);

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// drivers/input/input.c
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;// Input device resource structure pointer
struct input_handler *handler;// Input handler pointer
unsigned int packet_size;// Packet size
const char *path;// Device path string pointer
int error;

if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
dev_err(&dev->dev,
"Absolute device without dev->absinfo, refusing to register\n");
return -EINVAL;
}

if (dev->devres_managed) {// Allocate device resource structure if device resources are managed
devres = devres_alloc(devm_input_device_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;

devres->input = dev;
}

/* Every input device generates EV_SYN/SYN_REPORT events. */
/* Each input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);

/* KEY_RESERVED is not supposed to be transmitted to userspace. */
/* KEY_RESERVED should not be passed to user space. */
__clear_bit(KEY_RESERVED, dev->keybit);

/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
/* Ensure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);

packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;

dev->max_vals = dev->hint_events_per_packet + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}

/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
/*
* If delay and period are preset by the driver,
* Then auto-repeat is handled by the driver itself,We do not input.c handle in。
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
input_enable_softrepeat(dev, 250, 33);

if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

if (dev->poller)
input_dev_poller_finalize(dev->poller);

error = device_add(&dev->dev);
if (error)
goto err_free_vals;

path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);

error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;

list_add_tail(&dev->node, &input_dev_list);
// Traverse the input handler linked list, associating the input device with each handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;

err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);

input_register_deviceFunction used to register the input device (input_dev), add the input device to the input subsystem

struct input_dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// input/linux/input.h
struct input_dev {
const char *name;// Name of the device
const char *phys;// Physical location of the device
const char *uniq;// Unique identifier of the device
struct input_id id;// Identification information of the input device

unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];// Property bitmap of the device

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];// Event type bitmap supported by the device
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];// Key bitmap supported by the device
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];// Relative coordinate bitmap supported by the device
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];// Absolute coordinate bitmap supported by the device
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];// Miscellaneous event bitmap supported by the device
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];// LED bitmap supported by the device
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];// Sound bitmap supported by the device
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];// Force feedback bitmap supported by the device
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];// Switch bitmap supported by the device

unsigned int hint_events_per_packet;// Number of events per input event report

unsigned int keycodemax;// Maximum supported key code
unsigned int keycodesize;// Byte size of key code
void *keycode;// Pointer to key code data

int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);// Set key code callback function
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);// Get key code callback function

struct ff_device *ff;// Force feedback device

struct input_dev_poller *poller;

unsigned int repeat_key;// Repeat key code
struct timer_list timer;// Timer for handling repeated keys

int rep[REP_CNT];// Key repeat settings

struct input_mt *mt;// Multi-touch information

struct input_absinfo *absinfo;// Absolute coordinate information

unsigned long key[BITS_TO_LONGS(KEY_CNT)];// Current key state bitmap
unsigned long led[BITS_TO_LONGS(LED_CNT)];// Current LED state bitmap
unsigned long snd[BITS_TO_LONGS(SND_CNT)];// Current sound state bitmap
unsigned long sw[BITS_TO_LONGS(SW_CNT)];// Current switch status bitmap

int (*open)(struct input_dev *dev);// Callback function to open the device
void (*close)(struct input_dev *dev);// Callback function to close the device
int (*flush)(struct input_dev *dev, struct file *file);// Callback function to refresh the device
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);// Callback function to handle input events

struct input_handle __rcu *grab;// Current occupant of the device

spinlock_t event_lock;// Event lock, used to protect the event queue
struct mutex mutex;// Mutex lock, used to protect device state

unsigned int users;// Number of device users
bool going_away;// Whether the device is about to be removed

struct device dev;// Device structure

struct list_head h_list;// Linked list for device management
struct list_head node;// Linked list for device management

unsigned int num_vals;// Number of input values
unsigned int max_vals;// Maximum number of input values
struct input_value *vals;// Array of input values

bool devres_managed;// Whether managed by device resources

ktime_t timestamp[INPUT_CLK_MAX];// Array of timestamps for input events
};
#define to_input_dev(d) container_of(d, struct input_dev, dev)

Data structure relationship diagram

Input subsystem data structure relationship diagram
Input subsystem data structure relationship diagram

Analyze matching rules

evdev_handlerDefined as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event, // Event handler function pointer, pointing to the function named evdev_event, used to handle input events
.events = evdev_events,// Batch event handler function pointer, pointing to the function named evdev_events, used to handle multiple events from input devices
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",// Device name, set to the string "evdev"
.id_table = evdev_ids,// Input device ID table, pointing to the table named evdev_ids, used to match input device IDs
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

input_register_device()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;// Pointer to input device resource structure
struct input_handler *handler;// Pointer to input handler
unsigned int packet_size;// Packet size
const char *path;// Pointer to device path string
int error;

if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
dev_err(&dev->dev,
"Absolute device without dev->absinfo, refusing to register\n");
return -EINVAL;
}

if (dev->devres_managed) {// If the device resources are managed, allocate the device resource structure
devres = devres_alloc(devm_input_device_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;

devres->input = dev;
}

/* Every input device generates EV_SYN/SYN_REPORT events. */
/* Each input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);

/* KEY_RESERVED is not supposed to be transmitted to userspace. */
/* KEY_RESERVED should not be passed to user space. */
__clear_bit(KEY_RESERVED, dev->keybit);

/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
/* Ensure that bit masks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);

packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;

dev->max_vals = dev->hint_events_per_packet + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}

/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
/*
* If the delay and period are preset by the driver,
* then auto-repeat is handled by the driver itself,We do not input.c handle in。
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
input_enable_softrepeat(dev, 250, 33);

if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

if (dev->poller)
input_dev_poller_finalize(dev->poller);

error = device_add(&dev->dev);
if (error)
goto err_free_vals;

path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);

error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;

list_add_tail(&dev->node, &input_dev_list);
// Traverse the linked list of input handlers to associate the input device with each handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;

err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// drivers/input/input.c
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;

// determines whether it applies to the device through the match function of the input device and handler
// This function will look up the handler's input device ID table for an ID matching the given input device,
// and returns the matching ID. If no matching ID is found, it returns NULL.
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;

// calls the handler's connect function to establish a connection between the device and the handler.
error = handler->connect(handler, dev, id);

if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// drivers/input/input.c
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
// iterates through the handler's input device ID table until a matching ID is found or all IDs have been traversed.
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) && // uses the input device ID matching function to determine whether the given input device matches the current ID.
(!handler->match || handler->match(handler, dev))) {// If the input device matches the ID and the handler's match function returns true (or there is no match function), it returns that ID.
return id;
}
}

return NULL;
}

Let’s look at evdev’sinput_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)// id = an entry in the handler->id_table array
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
return false;

if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}

return true;
}
EXPORT_SYMBOL(input_match_device_id);

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 defineevbitkeybitrelbitetc., 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static const struct input_device_id joydev_ids[] = {
{ // First identifier, matches the absolute event of the X axis (ABS_X)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) },// The matched event type is EV_ABS (absolute event)
.absbit = { BIT_MASK(ABS_X) },// The matched absolute event type is ABS_X (X axis
},
{ // Second identifier, matches the absolute event of the Z axis (ABS_Z)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) },// The matched event type is EV_ABS (absolute event)
.absbit = { BIT_MASK(ABS_Z) },// The matched absolute event type is ABS_Z (Z-axis)
},
{ // The third identifier matches the absolute event of the scroll wheel (ABS_WHEEL)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) },// The matched event type is EV_ABS (absolute event
.absbit = { BIT_MASK(ABS_WHEEL) },// The matched absolute event type is ABS_WHEEL (scroll wheel)
},
{ // The fourth identifier matches the absolute event of the throttle (ABS_THROTTLE)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) },// The matched event type is EV_ABS (absolute event)
.absbit = { BIT_MASK(ABS_THROTTLE) },// The matched absolute event type is ABS_THROTTLE (throttle)
},
{ // The fifth identifier matches the key event of the joystick (BTN_JOYSTICK)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT_MASK(EV_KEY) },// The matched event type is EV_KEY (key event)
.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },// The matched key type is BTN_JOYSTICK (joystick)
},
{ // The sixth identifier matches the key event of the gamepad (BTN_GAMEPAD)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT_MASK(EV_KEY) },// The matched event type is EV_KEY (key event)
.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },// The matched key type is BTN_GAMEPAD (gamepad)
},
{ // The seventh identifier matches the joy button (BTN_TRIGGER_key event of HAPPY)
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT_MASK(EV_KEY) },// the matched event type is EV_KEY (key event
.keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },// the matched key type is BTN_TRIGGER_HAPPY (happy key)
},
{ } /* Terminating entry */
};

MODULE_DEVICE_TABLE(input, joydev_ids);

static struct input_handler joydev_handler = {
.event = joydev_event,
.match = joydev_match,
.connect = joydev_connect,
.disconnect = joydev_disconnect,
.legacy_minors = true,
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
};

static int __init joydev_init(void)
{
return input_register_handler(&joydev_handler);
}

static void __exit joydev_exit(void)
{
input_unregister_handler(&joydev_handler);
}

module_init(joydev_init);
module_exit(joydev_exit);

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
2
3
4
5
// drivers/input/evdev.c
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};

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
2
3
4
5
6
7
8
9
10
11
12
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}

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
2
__set_bit(EV_KEY, myinput_dev->evbit); // set to support key events
__set_bit(KEY_1, myinput_dev->keybit); // set to support key 1

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {

printk("input dev is error %s\n", dev->name);
return false;
}

printk("input dev is ok %s\n", dev->name);

many-to-many matching test
many-to-many matching test

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

  1. 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 useinput_allocate_devicefunction to allocate memory for the input device structure.
  2. 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.
  3. 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 useinput_register_devicefunction to register the input device structure variable. During the registration process, the system will complete device matching and initialization.
  4. 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 asinput_eventfunction. Based on the device type and event type, corresponding input events can be generated and sent to the system by calling this function.
  5. 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. Theinput_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// drivers/input/input.c
/**
* input_allocate_device - allocate memory for new input device
*
* Returns prepared struct input_dev or %NULL.
*
* NOTE: Use input_free_device() to free devices that have not been
* registered; input_unregister_device() should be used for already
* registered devices.
*/
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(-1);
struct input_dev *dev;
// Allocate Memory for the Input Device Structure
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev) {
// Set Device Type and Device Class
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;

// Initialize Device
device_initialize(&dev->dev);

// Initialize Mutex and Event Spinlock
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);

// Initialize timer
timer_setup(&dev->timer, NULL, 0);

// Initialize list head
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);

// Set device name, use atomic variable increment to ensure uniqueness
dev_set_name(&dev->dev, "input%lu",
(unsigned long)atomic_inc_return(&input_no));
// Increase module reference count
__module_get(THIS_MODULE);
}

return dev;
}
EXPORT_SYMBOL(input_allocate_device);

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
2
3
4
5
6
7
8
9
10
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; // Device property bitmap
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // Event types bitmap supported by the device
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // Key bitmap supported by the device
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // Relative coordinate bitmap supported by the device
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // Absolute coordinate bitmap supported by the device
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; // Miscellaneous event bitmap supported by the device
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; // LED bitmap supported by the device
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; // Sound bitmap supported by the device
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; // Force feedback bitmap supported by the device
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; // Switch bitmap supported by the device
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
..
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
...

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>

struct input_dev *myinput_dev;

static int __init myinput_dev_test_init(void)
{
int ret = 0;

myinput_dev = input_allocate_device();
if (!myinput_dev) {
pr_info("input_allocate_device error\n");
return -EFAULT;
}
// Set the name of the input device
myinput_dev->name = "myinput_dev";
// Set the event type of the input device

__set_bit(EV_KEY, myinput_dev->evbit);
__set_bit(KEY_1, myinput_dev->keybit);

// Set the event types supported by the input device
ret = input_register_device(myinput_dev);
if(ret < 0){
pr_info("input_register_dev error\n");
goto error;
}

return 0;
error:
input_free_device(myinput_dev);
return ret;
}

static void __exit myinput_dev_test_exit(void)
{
input_unregister_device(myinput_dev);
}

module_init(myinput_dev_test_init);
module_exit(myinput_dev_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<asqwgo@outlook.com>");
MODULE_DESCRIPTION("This is a test sample for input dev");

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
2
3
4
5
6
7
8
9
10
11
12
root@topeet:/root# insmod input_dev_test.ko
[17081.726702] input: myinput_dev as /devices/virtual/input/input6
[09:44:20.762] event3 - myinput_dev: is tagged by udev as: Keyboard
[09:44:20.763] event3 - myinput_dev: device is a keyboard
[09:44:20.768] libinput: configuring device "myinput_dev".
[09:44:20.773] associating input device event3 with output DSI-1 (none by udev)
root@topeet:/root# ls /dev/input/
by-path event0 event1 event2 event3
root@topeet:/root# rmmod input_dev_test.ko
[09:45:12.391] event3 - myinput_dev: device removed
root@topeet:/root# ls /dev/input/
by-path event0 event1 event2

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 Event
Report Event

Report Function

input_report_key()

ItemDescription
Header File<linux/input.h>
Function Prototypevoid 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 valueNone (void
FunctionReport akey event (EV_KEY). Used for scenarios such as keyboards, buttons, and touchscreen clicks.

input_report_rel()

ItemDescription
Header file<linux/input.h>
Function prototypevoid 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 valueNone (void
FunctionReport arelative coordinate event (EV_REL). Commonly used for devices such as mice and scroll wheels.

input_report_abs()

ItemDescription
Header file<linux/input.h>
Function prototypevoid 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 valueNone (void
FunctionReport anabsolute coordinate event (EV_ABS). Used for devices such as touchscreens, graphics tablets, and game controller joysticks.

input_report_ff_status()

ItemDescription
Header file<linux/input.h>
Function prototypevoid 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 ValueNone (void
FunctionReportForce 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()

ItemDescription
Header File<linux/input.h>
Function Prototypevoid 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 valueNone (void
FunctionReport aswitch state event (EV_SW). Commonly used for hardware switches such as laptop lid detection and tablet mode switching.

input_sync()

ItemDescription
Header file<linux/input.h>
Function prototypevoid input_sync(struct input_dev *dev)
Parameters-dev: Pointer toinput_devpointer
Return valueNone (void
EffectSend 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices to report input events. See also input_inject_event().
*
* NOTE: input_event() may be safely used right after input device was
* allocated with input_allocate_device(), even before it is registered
* with input_register_device(), but the event will not reach any of the
* input handlers. Such early invocation of input_event() may be used
* to 'seed' initial state of a switch or initial position of absolute
* axis, etc.
*/
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;

if (is_event_supported(type, dev->evbit, EV_MAX)) {

spin_lock_irqsave(&dev->event_lock, flags);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_event);

  • All the above functions areinline functions, which internally callinput_event(dev, type, code, value)
  • The event type (type) is implicitly determined by the function:
    • input_report_keyEV_KEY
    • input_report_relEV_REL
    • input_report_absEV_ABS
    • input_report_ff_statusEV_FF_STATUS
    • input_report_switchEV_SW
    • input_syncEV_SYNcode = 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/timer.h>

struct input_dev *myinput_dev;
static int value = 0;

static void test_timer_func(struct timer_list *t)
{
value = !value;

input_event(myinput_dev, EV_KEY, KEY_1, value); // Report key event
input_sync(myinput_dev); // Send sync event

mod_timer(t, jiffies + msecs_to_jiffies(1000)); // Update timer
}

DEFINE_TIMER(test_timer, test_timer_func);

static int input_report_event_test_init(void)
{
int ret;

myinput_dev = input_allocate_device();
if (myinput_dev == NULL)
return -ENOMEM;
myinput_dev->name = "myinput_dev";

set_bit(EV_KEY, myinput_dev->evbit);
set_bit(EV_SYN, myinput_dev->evbit);
set_bit(KEY_1, myinput_dev->keybit);

ret = input_register_device(myinput_dev);
if (ret < 0)
goto err;

mod_timer(&test_timer, jiffies + msecs_to_jiffies(1000));

return 0;

err:
input_free_device(myinput_dev);
return ret;
}

static void input_report_event_test_exit(void)
{
del_timer_sync(&test_timer);
input_unregister_device(myinput_dev);
}

module_init(input_report_event_test_init);
module_exit(input_report_event_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<asqwgo@outlook.com>");
MODULE_DESCRIPTION("This is a test sample for input subsys");

Application layer retrieves reported data

input_event structure

The data read by the application layer isinput_eventStructure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
unsigned int __pad;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;// Type
__u16 code;// Specific event
__s32 value;// Corresponding value
};
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define KEY_RESERVED 0
#define KEY_ESC 1 //ESC key
#define KEY_1 2 //Number 1 key
#define KEY_2 3 //Number 2 key
#define KEY_3 4 //Number 3 key
#define KEY_4 5 //Number 4 key
#define KEY_5 6 //Number 5 key
#define KEY_6 7 //Number 6 key
#define KEY_7 8 //Digit 7 key
#define KEY_8 9 //Digit 8 key
#define KEY_9 10 //Digit 9 key
#define KEY_0 11 //Digit 0 key
#define KEY_MINUS 12 //Minus key
#define KEY_EQUAL 13//Plus key
#define KEY_BACKSPACE 14 //Backspace key

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.

Report data format

Use the commandhexdump /dev/input/event4to view the reported information.

hexdump /dev/input/event4
hexdump /dev/input/event4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
unsigned int __pad;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;// Type
__u16 code;// Specific event
__s32 value;// Corresponding value
};
  • In theinput_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 8+8+2+2+4=248+8+2+2+4=24 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
2
3
4
5
6
7
root@topeet:~$ hexdump /dev/input/event2
0000000 0f09 65d3 0000 0000 36fb 0001 0000 0000
0000010 0003 0039 0000 0000 0f09 65d3 0000 0000
0000020 36fb 0001 0000 0000 0003 0035 00f5 0000
0000030 0f09 65d3 0000 0000 36fb 0001 0000 0000
0000040 0003 0036 02b2 0000 0f09 65d3 0000 0000
0000050 36fb 0001 0000 0000 0003 0030 0021 0000

The firstinput_eventcorresponds to the following members:

  • tv_sec:0f09 65d3 0000 0000
  • tv_usec:36fb 0001 0000 0000
  • type:0003
  • code:0039
  • value:0000 0000

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char **argv)
{
int fd, ret = 0;

struct input_event evt;

fd = open("/dev/input/event3", O_RDONLY);
if (fd < 0) {
perror("open /dev/input/event3 error");
exit(EXIT_FAILURE);
}

while (1) {
ret = read(fd, &evt, sizeof(struct input_event));
if (ret < 0)
goto err;

switch (evt.type) {
case EV_KEY:
switch (evt.code) {
case KEY_1:
switch (evt.value) {
case 0:
printf("value is 0\n");
break;
case 1:
printf("value is 1\n");
break;
case 2:
printf("value is 2\n");
break;
default:
printf("no support, event.value is %d\n", evt.value);
break;
}

break;
default:
printf("no support, event.code is %d\n", evt.code);
break;
}

break;
case EV_SYN:
printf("SYN\n");
break;
default:

printf("no support, event.type is %d\n", evt.type);
break;
}
}

return 0;
err:
close(fd);

return ret;
}

Test:

1
2
3
4
5
6
7
8
9
10
11
root@topeet:/root# insmod input_report_event.ko
[ 7406.160492] input: myinput_dev as /devices/virtual/input/input5
[13:13:06.168] event3 - myinput_dev: is tagged by udev as: Keyboard
[13:13:06.168] event3 - myinput_dev: device is a keyboard
[13:13:06.174] libinput: configuring device "myinput_dev".
[13:13:06.179] associating input device event3 with output DSI-1 (none by udev)
root@topeet:/root# ./test_input_report_event.o
value is 1
no support, event.type is 0
value is 0
no support, event.type is 0

Code Analysis of the Generic Event Handling Layer evdev

Analysis of the connect Function

evdev_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event, // Event handler function pointer, pointing to the function named evdev_event, used to handle input events
.events = evdev_events,// Batch event handler function pointer, pointing to the function named evdev_events, used to handle multiple events from input devices
.connect = evdev_connect,// When input_dev and input_Connection handler function executed after successful matching of handler
.disconnect = evdev_disconnect,// Disconnection handler function
.legacy_minors = true,// Set to true to support traditional minor device numbers; if set to false, dynamic minor device number allocation is used
.minor = EVDEV_MINOR_BASE,// Base minor device number for input devices
.name = "evdev",// Device name, set to the string "evdev"
.id_table = evdev_ids,// Input device ID table, pointing to the table named evdev_ids, used to match input device IDs
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

struct evdev

1
2
3
4
5
6
7
8
9
10
11
struct evdev {
int open;// Records the open state of the evdev device
struct input_handle handle;// Handle of the input event handler
struct evdev_client __rcu *grab;// Pointer to the client currently occupying the evdev device
struct list_head client_list;// Linked list of clients associated with the evdev device
spinlock_t client_lock; /* protects client_list */ // Spinlock used to protect the client linked list
struct mutex mutex;// Used to protect mutual exclusive access to the evdev device
struct device dev;// Device structure associated with the evdev device
struct cdev cdev;// Character device structure of the evdev device
bool exist;// Indicates whether the evdev device exists
};
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct evdev_client {
unsigned int head; // Head pointer of the buffer, pointing to the next writable position
unsigned int tail; // Tail pointer of the buffer, pointing to the next readable position
unsigned int packet_head; /* [future] position of the first element of next packet */ // [Future] Position of the first element of the next data packet
spinlock_t buffer_lock; /* protects access to buffer, head and tail */ // Spinlock used to protect access to the buffer, head pointer, and tail pointer
wait_queue_head_t wait;
struct fasync_struct *fasync;// Pointer to the structure for asynchronous notification
struct evdev *evdev;// Pointer to the evdev device associated with the client
struct list_head node;// Linked list node for clients associated with the evdev device
enum input_clock_type clk_type;// Input clock type
bool revoked;// Flag indicating whether the client has been revoked
unsigned long *evmasks[EV_CNT];// Array for event masks
unsigned int bufsize;// Size of the buffer
struct input_event buffer[];// Input event buffer, variable-length array
};
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#define EVDEV_MINOR_BASE	64
#define EVDEV_MINORS 32
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
// Obtain a new minor device number
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
// Allocate and initialize the evdev structure
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
// Initialize members of the evdev structure
INIT_LIST_HEAD(&evdev->client_list);// Initialize the client list
spin_lock_init(&evdev->client_lock);// Initialize the spinlock for the client list
mutex_init(&evdev->mutex);// Initialize the mutex lock
evdev->exist = true;// Set the evdev existence flag to true, indicating that evdev exists.

dev_no = minor;// Calculate the device number dev_no based on the minor device number, and normalize it to a device number within the traditional range as appropriate.
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)// If the device number is within the old version range, perform normalization processing
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);// Set device name

evdev->handle.dev = input_get_device(dev);// Set the input device of the input handle
evdev->handle.name = dev_name(&evdev->dev);// Set the name of the input handle to the device name
evdev->handle.handler = handler;// Set the handler of the input handle to the passed handler
evdev->handle.private = evdev;// Set the private data pointer of the input handle to the pointer of the evdev structure

evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// Set device number
evdev->dev.class = &input_class;// Set device class
evdev->dev.parent = &dev->dev;// Set parent device of the device
evdev->dev.release = evdev_free;// Set the release function of the device to evdev_free
device_initialize(&evdev->dev);// Initialize device

error = input_register_handle(&evdev->handle);// Register input handle
if (error)
goto err_free_evdev;

cdev_init(&evdev->cdev, &evdev_fops);// Initialize character device structure

error = cdev_device_add(&evdev->cdev, &evdev->dev);// Add character device
if (error)
goto err_cleanup_evdev;

return 0;

err_cleanup_evdev:
evdev_cleanup(evdev);// Clean up evdev structure
input_unregister_handle(&evdev->handle);// Unregister input handle
err_free_evdev:
put_device(&evdev->dev);// Release device
err_free_minor:
input_free_minor(minor);// Release minor device number
return error;
}

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

Minor device number
Minor device number

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
2
// include/uapi/linux/major.h
#define INPUT_MAJOR 13

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
2
#define EVDEV_MINOR_BASE	64
#define EVDEV_MINORS 32

input_get_new_minor()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* input_get_new_minor - allocates a new input minor number
* @legacy_base: beginning or the legacy range to be searched
* @legacy_num: size of legacy range
* @allow_dynamic: whether we can also take ID from the dynamic range
*
* This function allocates a new device minor for from input major namespace.
* Caller can request legacy minor by specifying @legacy_base and @legacy_num
* parameters and whether ID can be allocated from dynamic range if there are
* no free IDs in legacy range.
*/
int input_get_new_minor(int legacy_base, unsigned int legacy_num,
bool allow_dynamic)
{
/*
* This function should be called from input handler's ->connect()
* methods, which are serialized with input_mutex, so no additional
* locking is needed here.
*/
if (legacy_base >= 0) {
int minor = ida_simple_get(&input_ida,
legacy_base,
legacy_base + legacy_num,
GFP_KERNEL);
if (minor >= 0 || !allow_dynamic)
return minor;
}

return ida_simple_get(&input_ida,
INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES,
GFP_KERNEL);
}
EXPORT_SYMBOL(input_get_new_minor);

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.

  • Iflegacy_baseis greater than or equal to 0, the following logic is executed:

    • Using theida_simple_get()function frominput_idato obtain a minor device numberminor, with a range fromlegacy_basetolegacy_base + legacy_num

    • If 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 theida_simple_get()function frominput_idaObtain a minor device number fromminor, with a range ofINPUT_FIRST_DYNAMIC_DEVtoINPUT_MAX_CHAR_DEVICES

andida_simple_get()is defined as:

1
2
#define ida_simple_get(ida, start, end, gfp)	\
ida_alloc_range(ida, start, (end) - 1, gfp)

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
2
3
4
5
6
7
8
9
10
11
12
13
14
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.llseek = no_llseek,
};

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
2
3
4
5
6
7
8
9
10
11
12
13
14
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.llseek = no_llseek,
};

evdev_open()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static int evdev_open(struct inode *inode, struct file *file)
{
// Obtain the pointer to the evdev structure from the i_cdev member of the inode
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
// Calculate the buffer size
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
// Define the evdev_client structure pointer
struct evdev_client *client;
int error;

// Allocate memory for storing the evdev_client structure and the input event buffer
client = kvzalloc(struct_size(client, buffer, bufsize), GFP_KERNEL);
if (!client)
return -ENOMEM;

// Initialize the wait queue
init_waitqueue_head(&client->wait);
// Initialize the member variables of the client structure
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
// Add the client to the evdev client list
evdev_attach_client(evdev, client);

// Open the underlying device
error = evdev_open_device(evdev);
if (error)
goto err_free_client;

// Set the client structure as the file's private data
file->private_data = client;
// Use stream_Open with open, mark the file as no_seekable, does not support random access
stream_open(inode, file);

return 0;

err_free_client:
// Failed to open device, error handling required, remove client from evdev's client list
evdev_detach_client(evdev, client);
// Free the memory allocated by client
kvfree(client);
return error;
}

stream_open()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* stream_open is used by subsystems that want stream-like file descriptors.
* Such file descriptors are not seekable and don't have notion of position
* (file.f_pos is always 0 and ppos passed to .read()/.write() is always NULL).
* Contrary to file descriptors of other regular files, .read() and .write()
* can run simultaneously.
*
* stream_open never fails and is marked to return int so that it could be
* directly used as file_operations.open .
*/
int stream_open(struct inode *inode, struct file *filp)
{
filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE | FMODE_ATOMIC_POS);
filp->f_mode |= FMODE_STREAM;
return 0;
}

EXPORT_SYMBOL(stream_open);

edev_open_device()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int evdev_open_device(struct evdev *evdev)
{
int retval;

retval = mutex_lock_interruptible(&evdev->mutex);// Acquire the mutex lock of the input device
if (retval)// If the lock cannot be acquired, the function will return the corresponding error code
return retval;

if (!evdev->exist)// If this field is false (0), it indicates that the input device does not exist
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);// Call input_open_The device function opens the input device and stores the return value in the retval variable
if (retval)// If opening the device fails, the function decrements evdev->open by one, indicating the device's open counter decreases
evdev->open--;
}

mutex_unlock(&evdev->mutex); // Release the mutex lock of the input device
return retval;
}

input_open_device()

evdev_open_device()called ininput_open_device()function opens the input device. This function callsinput_dev’sopen()function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to start receive events from given input device.
*/
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;

retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;

if (dev->going_away) {
retval = -ENODEV;
goto out;
}

handle->open++;

if (dev->users++) {
/*
* Device is already opened, so we can exit immediately and
* report success.
*/
goto out;
}

if (dev->open) {
retval = dev->open(dev);
if (retval) {
dev->users--;
handle->open--;
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
goto out;
}
}

if (dev->poller)
input_dev_poller_start(dev->poller);

out:
mutex_unlock(&dev->mutex);
return retval;
}
EXPORT_SYMBOL(input_open_device);

ioctl function analysis

evdev_ioctl()

1
2
3
4
5
6
7
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
// Call evdev_ioctl_The handler function processes IO control operations, passing it the file pointer, cmd, and
// the type-cast arg as parameters. The function casts arg to void __user * type to pass pointers between user space and kernel space.
// The function passes the evdev_ioctl_handler's return value as its own return value and directly returns it to the caller.
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
}

evdev_ioctl_handler()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
// Get the pointer to evdev_client from the file structure.
struct evdev_client *client = file->private_data;
// Get the pointer to evdev from evdev_client.
struct evdev *evdev = client->evdev;
int retval;

// Attempt to acquire the mutex lock of evdev; if unable to acquire, return the corresponding error code.
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;

// Check whether the device exists or whether the client has been revoked.
if (!evdev->exist || client->revoked) {
retval = -ENODEV;// If the device does not exist or the client has been revoked, return the error code for device not existing.
goto out;
}
// Call evdev_do_ioctl function to perform the actual IO control operation and store the return value in the retval variable.
retval = evdev_do_ioctl(file, cmd, p, compat_mode);

out:
mutex_unlock(&evdev->mutex);// Unlock the mutex lock of evdev.
return retval;
}

evdev_do_ioctl()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct input_mask mask;
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
unsigned int size;
int error;

/* First we check for fixed-length commands */
switch (cmd) {

case EVIOCGVERSION:
return put_user(EV_VERSION, ip);

case EVIOCGID:
if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
return -EFAULT;
return 0;

case EVIOCGREP:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (put_user(dev->rep[REP_DELAY], ip))
return -EFAULT;
if (put_user(dev->rep[REP_PERIOD], ip + 1))
return -EFAULT;
return 0;

case EVIOCSREP:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (get_user(u, ip))
return -EFAULT;
if (get_user(v, ip + 1))
return -EFAULT;

input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);

return 0;

case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file);

case EVIOCGEFFECTS:
i = test_bit(EV_FF, dev->evbit) ?
dev->ff->max_effects : 0;
if (put_user(i, ip))
return -EFAULT;
return 0;

case EVIOCGRAB:
if (p)
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);

case EVIOCREVOKE:
if (p)
return -EINVAL;
else
return evdev_revoke(evdev, client, file);

case EVIOCGMASK: {
void __user *codes_ptr;

if (copy_from_user(&mask, p, sizeof(mask)))
return -EFAULT;

codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
return evdev_get_mask(client,
mask.type, codes_ptr, mask.codes_size,
compat_mode);
}

case EVIOCSMASK: {
const void __user *codes_ptr;

if (copy_from_user(&mask, p, sizeof(mask)))
return -EFAULT;

codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
return evdev_set_mask(client,
mask.type, codes_ptr, mask.codes_size,
compat_mode);
}

case EVIOCSCLOCKID:
if (copy_from_user(&i, p, sizeof(unsigned int)))
return -EFAULT;

return evdev_set_clk_type(client, i);

case EVIOCGKEYCODE:
return evdev_handle_get_keycode(dev, p);

case EVIOCSKEYCODE:
return evdev_handle_set_keycode(dev, p);

case EVIOCGKEYCODE_V2:
return evdev_handle_get_keycode_v2(dev, p);

case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
}

size = _IOC_SIZE(cmd);

/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {

case EVIOCGPROP(0):
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
size, p, compat_mode);

case EVIOCGMTSLOTS(0):
return evdev_handle_mt_request(dev, size, ip);

case EVIOCGKEY(0):
return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
KEY_MAX, size, p, compat_mode);

case EVIOCGLED(0):
return evdev_handle_get_val(client, dev, EV_LED, dev->led,
LED_MAX, size, p, compat_mode);

case EVIOCGSND(0):
return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
SND_MAX, size, p, compat_mode);

case EVIOCGSW(0):
return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
SW_MAX, size, p, compat_mode);

case EVIOCGNAME(0):
return str_to_user(dev->name, size, p);

case EVIOCGPHYS(0):
return str_to_user(dev->phys, size, p);

case EVIOCGUNIQ(0):
return str_to_user(dev->uniq, size, p);

case EVIOC_MASK_SIZE(EVIOCSFF):
if (input_ff_effect_from_user(p, size, &effect))
return -EFAULT;

error = input_ff_upload(dev, &effect, file);
if (error)
return error;

if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;

return 0;
}

/* Multi-number variable-length handlers */
if (_IOC_TYPE(cmd) != 'E')
return -EINVAL;

if (_IOC_DIR(cmd) == _IOC_READ) {

if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
return handle_eviocgbit(dev,
_IOC_NR(cmd) & EV_MAX, size,
p, compat_mode);

if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {

if (!dev->absinfo)
return -EINVAL;

t = _IOC_NR(cmd) & ABS_MAX;
abs = dev->absinfo[t];

if (copy_to_user(p, &abs, min_t(size_t,
size, sizeof(struct input_absinfo))))
return -EFAULT;

return 0;
}
}

if (_IOC_DIR(cmd) == _IOC_WRITE) {

if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {

if (!dev->absinfo)
return -EINVAL;

t = _IOC_NR(cmd) & ABS_MAX;

if (copy_from_user(&abs, p, min_t(size_t,
size, sizeof(struct input_absinfo))))
return -EFAULT;

if (size < sizeof(struct input_absinfo))
abs.resolution = 0;

/* We can't change number of reserved MT slots */
if (t == ABS_MT_SLOT)
return -EINVAL;

/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
* of event.
*/
spin_lock_irq(&dev->event_lock);
dev->absinfo[t] = abs;
spin_unlock_irq(&dev->event_lock);

return 0;
}
}

return -EINVAL;
}

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 settings
  • EVIOCGKEYCODE: Get key code
  • EVIOCGKEYCODE_V2: Get key mapping table
  • EVIOCSKEYCODE: Set key value
  • EVIOCSKEYCODE_V2: Set key mapping table
  • EVIOCGNAME(len): Get device name
  • EVIOCGPHYS(len): Get physical location
  • EVIOCGUNIQ(len): Get unique identifier
  • EVIOCGPROP(len): Get device properties
  • EVIOCGMTSLOTS(len): Get multi-touch information
  • EVIOCGKEY(len): Get global key state
  • EVIOCGLED(len): Get all LED states
  • EVIOCGSND(len): Get all sound states
  • EVIOCGSW(len): Get all switch states
  • EVIOCGBIT(ev,len): Get event bitmap
  • EVIOCGABS(abs): Get absolute value/range
  • EVIOCSABS(abs): Set absolute value/range
  • EVIOCSFF: Send force feedback effect to force feedback device
  • EVIOCRMFF: Delete force feedback effect
  • EVIOCGEFFECTS: Report number of simultaneously playable effects
  • EVIOCGRAB: Claim/release input device
  • EVIOCREVOKE: Revoke device access permission
  • EVIOCGMASK: Retrieve current event mask
  • EVIOCSMASK: Set event mask
  • EVIOCSCLOCKID: Set clock identifier for timestamps

Poll function analysis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* No kernel lock - fine */
static __poll_t evdev_poll(struct file *file, poll_table *wait)
{
// Get evdev_client structure pointer from file private data
struct evdev_client *client = file->private_data;
// Get evdev pointer from evdev_client structure
struct evdev *evdev = client->evdev;
__poll_t mask;

// Add current process to wait queue, waiting for wake-up event of evdev->wait
poll_wait(file, &client->wait, wait);

// Check values of evdev->exist and client->revoked
if (evdev->exist && !client->revoked)
// If evdev exists and client is not revoked, set mask to EPOLLOUT | EPOLLWRNORM
mask = EPOLLOUT | EPOLLWRNORM;
else
// Otherwise, set mask to EPOLLHUP | EPOLLERR
mask = EPOLLHUP | EPOLLERR;

// Check the values of packet_head and tail in client
if (client->packet_head != client->tail)
// If packet_head and tail are not equal, set mask to mask | EPOLLIN | EPOLLRDNORM
mask |= EPOLLIN | EPOLLRDNORM;

return mask;
}

Analysis of the fasync function

1
2
3
4
5
6
7
8
9
10
static int evdev_fasync(int fd, struct file *file, int on)
{
// Get the evdev_client structure pointer from the file's private data
struct evdev_client *client = file->private_data;

// Call the fasync_helper function to handle asynchronous notification of the process
// This function adds or removes the process from the asynchronous notification list based on the value of on
// And stores the notification-related data in client->fasync
return fasync_helper(fd, file, on, &client->fasync);
}

Analysis of the llseek function

1
2
3
4
loff_t no_llseek(struct file *file, loff_t offset, int whence)
{
return -ESPIPE;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static int evdev_release(struct inode *inode, struct file *file)
{
// Get the evdev_client structure pointer from the file's private data
struct evdev_client *client = file->private_data;
// Get the evdev pointer from the evdev_client structure
struct evdev *evdev = client->evdev;
unsigned int i;

// Acquire the mutex lock of evdev to ensure atomic operations on evdev
mutex_lock(&evdev->mutex);

// Check the values of evdev->exist and client->revoked
if (evdev->exist && !client->revoked)
// If evdev exists and client is not revoked, call input_flush_The device function flushes the input buffer of the device
input_flush_device(&evdev->handle, file);
// Release the preemption state of evdev, removing the client from the preemption list
evdev_ungrab(evdev, client);
// Unlock the mutex lock of evdev
mutex_unlock(&evdev->mutex);

// Detach and release the client from evdev
evdev_detach_client(evdev, client);

// Free the event mask memory of the client
for (i = 0; i < EV_CNT; ++i)
bitmap_free(client->evmasks[i]);

// Free the memory of the client
kvfree(client);

// Close the evdev device
evdev_close_device(evdev);

return 0;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices to report input events. See also input_inject_event().
*
* NOTE: input_event() may be safely used right after input device was
* allocated with input_allocate_device(), even before it is registered
* with input_register_device(), but the event will not reach any of the
* input handlers. Such early invocation of input_event() may be used
* to 'seed' initial state of a switch or initial position of absolute
* axis, etc.
*/
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags; // used to save interrupt flags

// check if the input device supports the specified event type
if (is_event_supported(type, dev->evbit, EV_MAX)) {
// acquire the event lock to ensure atomic event handling
spin_lock_irqsave(&dev->event_lock, flags);
// call input_handle_the event function processes input events
input_handle_event(dev, type, code, value);
// release the event lock
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_event);

input_handle_event()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
// get the handling method for the input event, i.e., determine whether the event should be ignored, passed to the device, or passed to the handler
int disposition = input_get_disposition(dev, type, code, &value);

// if the event should not be ignored and is not of type EV_SYN, add the event's type, code, and value to the input random number pool
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
// if the event should be passed to the device and the device has an event handler, call the event handler
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
// if the input device has no value list, return directly
if (!dev->vals)
return;
// if the event should be passed to the handler
if (disposition & INPUT_PASS_TO_HANDLERS) {
struct input_value *v;

// if the event needs to be passed to a slot of the handler, add the slot information to the value list
if (disposition & INPUT_SLOT) {
v = &dev->vals[dev->num_vals++];
v->type = EV_ABS;
v->code = ABS_MT_SLOT;
v->value = dev->mt->slot;
}
// add the event's type, code, and value to the value list
v = &dev->vals[dev->num_vals++];
v->type = type;
v->code = code;
v->value = value;
}
// If the event requires refreshing the value list
if (disposition & INPUT_FLUSH) {
// If the value in the value list is greater than or equal to 2, pass the value from the value list to the device's handler function
if (dev->num_vals >= 2)
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
/*
* Reset the timestamp on flush so we won't end up
* with a stale one. Note we only need to reset the
* monolithic one as we use its presence when deciding
* whether to generate a synthetic timestamp.
*/
/*
* Reset the timestamp at the time of refresh,To avoid outdated timestamps。
* Note,We only need to reset a single timestamp(INPUT_CLK_MONO),
* Because when deciding whether to generate a synthetic timestamp,We use its existence。
*/
dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0);
} else if (dev->num_vals >= dev->max_vals - 2) {// If the value in the value list is greater than or equal to the device's maximum value minus 2
// Add the synchronization event to the value list
dev->vals[dev->num_vals++] = input_value_sync;
// Pass the value from the value list to the device's handler function
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
}

}

input_get_disposition()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
static int input_get_disposition(struct input_dev *dev,
unsigned int type, unsigned int code, int *pval)
{
int disposition = INPUT_IGNORE_EVENT;// Event handling method, default is to ignore
int value = *pval;

switch (type) {

case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;// Pass the event to all handlers
break;

case SYN_REPORT:
disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;// Pass the event to the handler and refresh the value list
break;
case SYN_MT_REPORT:
disposition = INPUT_PASS_TO_HANDLERS;// Pass the event to the handler
break;
}
break;

case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX)) {

/* auto-repeat bypasses state updates */
// Auto-repeat events do not update the state and are passed directly to the handler
if (value == 2) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
// Determine whether the key state has changed; if so, update the state and pass it to the handler
if (!!test_bit(code, dev->key) != !!value) {

__change_bit(code, dev->key);
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;

case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != !!value) {
// Determine whether the switch state has changed; if so, update the state and pass it to the handler
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;

case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);// Handle special cases of absolute events

break;

case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;// Pass the event to the handler

break;

case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;// Pass the event to all handlers

break;

case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != !!value) {
// Determine whether the LED state has changed; if so, update the state and pass it to all handlers
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
// Determine whether the sound state has changed; if so, update the state and pass it to all handlers
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
// Update the settings for repeat events and pass the event to all handlers
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;// Pass the event to all handlers
break;

case EV_PWR:
disposition = INPUT_PASS_TO_ALL;// Pass the event to all handlers
break;
}

*pval = value; // Update the event's value to the processed value
return disposition; // Return the handling method of the event
}

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 processing
  • INPUT_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
* Pass values first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
struct input_handle *handle;// Handle of the input device
struct input_value *v;// Current value being processed

if (!count)
return;// If the number of values is 0, return directly

rcu_read_lock();// Read RCU lock

handle = rcu_dereference(dev->grab);// Get the device handle
if (handle) {
count = input_to_handler(handle, vals, count);// Pass the value to the handle for processing, and update the number of values
} else {
// Traverse the list of device handles, pass values to each open handle for processing, and update the count of values
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open) {
count = input_to_handler(handle, vals, count);
if (!count)
break;
}
}

rcu_read_unlock();// Unlock the RCU lock

/* trigger auto repeat for key events */
/* Trigger auto-repeat of key events */
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
// Traverse the list of values; for events of type EV_KEY with a value not equal to 2:
// If the value is true, start auto-repeat of the key;
// If the value is false, stop auto-repeat of the key
for (v = vals; v != vals + count; v++) {
if (v->type == EV_KEY && v->value != 2) {
if (v->value)
input_start_autorepeat(dev, v->code);
else
input_stop_autorepeat(dev);
}
}
}
}

input_to_handler()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
* Pass event first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *vals, unsigned int count)
{
struct input_handler *handler = handle->handler; // Handler corresponding to the input handle
struct input_value *end = vals; // End of processed values
struct input_value *v; // Currently processed value

if (handler->filter) {
// If the handler defines a filter function, filter each value in the value list
for (v = vals; v != vals + count; v++) {
if (handler->filter(handle, v->type, v->code, v->value))
continue;// If the filter function returns true, skip the current value
if (end != v)
*end = *v;// Copy the current value to the end of processed values
end++;
}
count = end - vals;// Update the count of processed values
}

if (!count)
return 0;// If the count of processed values is 0, return directly

if (handler->events)
handler->events(handle, vals, count);// If the handler defines an event processing function, pass the processed values to the event processing function
else if (handler->event)//// If the handler defines a single event handler function, it calls the event handler function for each value
for (v = vals; v != vals + count; v++)
handler->event(handle, v->type, v->code, v->value);

return count;
}

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
2
3
4
5
6
7
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = { { type, code, value } };

evdev_events(handle, vals, 1);
}

edev_eventis to callevdev_events

evdev_events()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* Pass incoming events to all connected clients.
*/
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;// Get the private data of the input handle, here it is the evdev structure type
struct evdev_client *client;// Define the evdev client pointer
ktime_t *ev_time = input_get_timestamp(handle->dev);// Get the timestamp of the input device

rcu_read_lock();// Start reading the RCU-protected region

client = rcu_dereference(evdev->grab);// Safely obtain the current evdev client via RCU

if (client)// If a preempting client exists, pass the value to the preempting client
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time);// Otherwise, pass the value to all registered clients

rcu_read_unlock();// End reading the RCU-protected region
}

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:

  1. Copy a new set of data
  2. Modify the new data
  3. Switch pointer (atomically change the shared pointer from the old object to the new object)
  4. 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)
  5. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
const struct input_value *v;// Currently processed input value
struct input_event event;// Input event structure
struct timespec64 ts;// Timestamp
bool wakeup = false;// Whether to wake up waiting threads

if (client->revoked)
return;// If the client has been revoked, return directly

ts = ktime_to_timespec64(ev_time[client->clk_type]);// Convert ev_time to a timestamp of type struct timespec64
event.input_event_sec = ts.tv_sec;// Set the seconds field of the input event to the seconds value of the timestamp
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;// Set the microseconds field of the input event to the value obtained by dividing the nanosecond value of the timestamp by 1000

/* Interrupts are disabled, just acquire the lock. */
/* Disable interrupts, just acquire the lock. */
spin_lock(&client->buffer_lock);// Acquire the client's buffer lock

for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;// If the input value is filtered, skip processing of the current value

if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
/* Discard empty SYN_REPORT */
if (client->packet_head == client->head)
continue;// If the client's packet header and data header are the same, skip processing of the current value

wakeup = true;// Set the wake-up flag to true
}

event.type = v->type;// Set the type field of the input event to the type of the current value
event.code = v->code;// Set the code field of the input event to the code of the current value
event.value = v->value;// Set the value field of the input event to the value of the current value
__pass_event(client, &event);// Pass the input event to the client's event handler function
}

spin_unlock(&client->buffer_lock);// Release the client's buffer lock

if (wakeup)// If it is necessary to wake up waiting threads, wake up the threads in the wait queue
wake_up_interruptible_poll(&client->wait,
EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
}

__pass_event()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void __pass_event(struct evdev_client *client,
const struct input_event *event)
{
client->buffer[client->head++] = *event;// Copy the event into the client's buffer, then increment the buffer head pointer
client->head &= client->bufsize - 1; // Mask the buffer head pointer to ensure it is within the buffer range

if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
/*
* This effectively"discards"all unconsumed events,retaining only EV_SYN/SYN_DROPPED plus the latest event。
*/
client->tail = (client->head - 2) & (client->bufsize - 1);// Update the buffer tail pointer to point to the second-to-last event

client->buffer[client->tail] = (struct input_event) {
.input_event_sec = event->input_event_sec,
.input_event_usec = event->input_event_usec,
.type = EV_SYN,
.code = SYN_DROPPED,
.value = 0,
}; // Insert an EV at the buffer tail pointer position_SYN/SYN_DROPPED event, indicating that events were discarded

client->packet_head = client->tail;// Update the data packet head pointer to the buffer tail pointer
}

if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;// Update the data packet head pointer to the buffer head pointer
kill_fasync(&client->fasync, SIGIO, POLL_IN);// Send a SIGIO signal to the registered asynchronous notification handler, notifying that new events are available to read
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;// Get the evdev client structure pointer from the file private data
struct evdev *evdev = client->evdev;// Get the evdev structure pointer from the client structure
struct input_event event;// Define an input event structure
size_t read = 0;// Number of bytes read
int error;

if (count != 0 && count < input_event_size())
return -EINVAL;// If count is not 0 and is less than the input event size, return an invalid parameter error

for (;;) {// Read input events in a loop and copy them to the user-space buffer
if (!evdev->exist || client->revoked)
return -ENODEV;// If the evdev device does not exist or the client has been revoked, return a device not found error code

if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;// If the data packet head equals the tail and the non-blocking flag is set in the file flags, return a no data available error code

/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)// If count is 0, exit the loop without performing IO operations, but still check error conditions
break;

while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {

if (input_event_to_user(buffer + read, &event))// Copy the input event data to the user-space buffer
return -EFAULT;

read += input_event_size();// Update the number of bytes read
}

if (read)
break;// If the number of bytes read is greater than 0, exit the loop

if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(client->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);// Wait for an event to occur, blocking the current thread
if (error)
return error;// If the wait is interrupted, return an error code
}
}

return read;// Return the number of bytes read
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static ssize_t evdev_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;// Get the evdev client structure pointer from the file's private data
struct evdev *evdev = client->evdev;// Get the evdev structure pointer from the client structure
struct input_event event;// Define an input event structure
int retval = 0;// Return value variable, default is 0

if (count != 0 && count < input_event_size())
return -EINVAL;// If count is not 0 and is less than the input event size, return an invalid parameter error

retval = mutex_lock_interruptible(&evdev->mutex);// Lock the evdev mutex, interruptible
if (retval)
return retval;// If locking fails, return an error code

if (!evdev->exist || client->revoked) {// If the evdev device does not exist or the client has been revoked, return a device not found error code
retval = -ENODEV;
goto out;
}

while (retval + input_event_size() <= count) {

if (input_event_from_user(buffer + retval, &event)) {// Copy the input event from user space to the event structure
retval = -EFAULT;
goto out;
}
retval += input_event_size();// Update retval, increase the size of an input event

input_inject_event(&evdev->handle,
event.type, event.code, event.value);// Inject the input event into the evdev event handler
cond_resched();// Conditional scheduling, yield CPU to other threads for execution
}

out:
mutex_unlock(&evdev->mutex);// Unlock the evdev mutex
return retval;// Return retval as the number of bytes written or an error code
}

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// drivesr/input/input.c
static int __init input_init(void)
{
int err;

err = class_register(&input_class);// Attempt to register the input_dev class
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}

err = input_proc_init();// Initialize the proc file system interface of the input subsystem. This interface is used to provide information about input devices in the /proc file system.
if (err)
goto fail1;

err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}

return 0;

fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}

static void __exit input_exit(void)
{
input_proc_exit();
unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES);
class_unregister(&input_class);
}

subsys_initcall(input_init);// Called during kernel startup
module_exit(input_exit);

input_proc_init()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;

proc_bus_input_dir = proc_mkdir("bus/input", NULL);// Create a directory named "bus/input" to represent the bus type of input devices
if (!proc_bus_input_dir)
return -ENOMEM;

entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_proc_ops);//Create a file named "device" and associate it with the previously created "bus/input" directory
if (!entry)
goto fail1;

entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_proc_ops);//Create a file named "handlers" and associate it with the "bus/input" directory.
if (!entry)
goto fail2;

return 0;

fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}

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.

goodix-ts
goodix-ts

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

connect function
connect function

/dev/input/
/dev/input/