1. Introduction to the 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. Writing the simplest device driver layer code
    1. Step
    2. Register input device
      1. input_allocate_device()
      2. Initialize the input_dev structure
        1. Setting the event type
        2. setting the specific type
      3. example
    3. Improve the device driver layer code
      1. Report event
      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. The application layer obtains the 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. Device Number Allocation Analysis
      1. major device number
      2. secondary device number
        1. input_get_new_minor()
    3. File operation set functions
      1. open function analysis
        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. fasync function analysis
      5. llseek function analysis
      6. release function analysis
    4. Data reporting process 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. read function analysis
        1. edev_read()
      3. write function analysis
        1. edev_write()
  6. Input core layer code analysis.
    1. input_init()
    2. input_proc_init()
  7. Fix the device node of the input device
    1. Requirement
    2. Solution
Cover image for Linux Input Subsystem

Linux Input Subsystem

Words 21.1k
Views
Visitors

Timeline

Timeline

2025-12-18

init

This article introduces the core concepts and functions of the Linux input subsystem. The subsystem provides a unified framework and interface for input devices such as keyboards, mice, and touchscreens. By extracting common functions of devices and retaining differentiated code, it simplifies driver development and improves compatibility. The article summarizes three major advantages of the input subsystem: compatibility, a unified driver programming approach, and a unified application operation interface. It also discusses how to determine the correspondence between input devices and device nodes, including methods such as identifying by device name, probing command tests, and viewing the /proc/bus/input/devices file. Finally, the article outlines the framework of the input subsystem, focusing on the Event Handling Layer, which, as the top layer, is responsible for processing input device events.

Linux Driver Notes

Table of ContentsLinks
1. Linux Driver Framework
2. Linux Driver Loading Logic
3. Character Device Basics
4. Concurrency and Race Conditions
5. Advanced Character Device Topics
6. Interrupts
7. Platform Bus
8. Device Tree
9. Device Model
10. Hotplug
11. pinctrl Subsystem
12. GPIO subsystem
13. Input subsystem
14. 1-Wire
15. I2C
16. SPI
17. UART
18. PWM
19. RTC
20. Watchdog
21. CAN
22. Network devices
23. ADC
24. IIO
25. USB
26. LCD

Introduction to the 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, and so on.

The main purpose of using the input subsystem is to standardize and simplify the development process of input device drivers, thereby improving the universality and compatibility of drivers. It does this byextracting the common functions and processing logic of input devices and writing them as generic code, while leaving the differentiated code to the specific device driver developersThis division of labor allows driver developers to focus more on device-specific details, thereby greatly reducing the development difficulty for engineers.

In summary, the benefits 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 specification. 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. In this way, engineers do not need to write and maintain different driver code for each manufacturer’s devices, greatly improving device compatibility.
  • Unified driver programming approach: The input subsystem defines a set of common driver programming methods. Engineers only need to develop according to the input subsystem’s specifications. The driver modules for input devices need 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. As a result, engineers can focus more on device-specific details without worrying about the common 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 interact with input devices conveniently. Applications can read these device nodes to obtain input event information and process it accordingly. No matter what type of input device it is, applications can access and operate it in the same way. In this way, application developers do not need to worry about the details of the underlying input devices and can focus more on application logic development.

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 for determining the relationship between device nodes and input devices:

  1. Device name: The device nodes of the input subsystem can be divided intogeneric device namesandand dedicated device names. Dedicated device names usually allow the device type to be identified directly from the name, for example"keyboard"(keyboard) or"mouse"(mouse). Generic device names, on the other hand, do not directly indicate the device type. As shown in the figure below,event0-event4belongs to generic device names, whilemouse0andmouse2It is a dedicated device name.

/dev/input
/dev/input

  1. Trial method: You can use"cat"command to open the device node file, then operate the physical device and observe whether the terminal has output. For example, for a keyboard device, you can runcat /dev/input/eventX, where/dev/input/eventXis the path of the device node, then press keyboard keys and observe whether the terminal outputs the corresponding characters. Through this trial method, you can determine the correspondence between device nodes and specific devices. For example, use the following command to test the mouse, as shown below:
12
sudo cat /dev/input/mouse0sudo hexdump /dev/input/mouse0
  1. View input device information: You can use the following command to view/proc/bus/input/devicesfile:
1
cat /proc/bus/input/devices

Part of the printed output is shown below:

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

This file records information about all input devices in the current system. 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 device nodes and specific input devices. 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 name. In this example, the device name is"AT Translated Set 2 keyboard"

  • P: Phys=isa0060/serio0/input0

This line shows the physical location of the device. In this example, the physical location of the device isisa0060/serio0/input0

  • S: Sysfs=/devices/platform/i8042/serio0/input/input1

This line shows the path of the device in the sysfs file system. In this example, the device path is/devices/platform/i8042/serio0/input/input1

  • U: Uniq=

This line shows the unique identifier of the device. In this example, the unique identifier is empty.

  • H: Handlers=sysrq kbd event1 leds

This line shows the handlers of the device. It indicates the program or module that handles device 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 properties of the device. 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; 1 indicates the key is pressed, and 0 indicates the key is not pressed. This line shows the state of the keys, represented 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,LED=7i.e., binary0b111, indicating that 3 LED indicators are supported (the corresponding bits are set to 1), namely Num Lock, Caps Lock, and Scroll Lock.

Input subsystem framework

Input subsystem framework
Input subsystem framework

Event Handling Layer

The event handling layer is the topmost layer of the input subsystem and 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 the device nodes. It receives input events from the core layer and processes them accordingly based on the type and attributes of the events.

Core Layer

The main function of the core layer isto act as a matcher located between the event handling layer and the device driver layer. It serves to coordinate and connect these two layers, so as toensure that events from input devices are correctly delivered 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 the input data should be passed to 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 handling and distribution: Core layer**Responsible for handling and distributing events, passing input events to the corresponding event handling layer.**It receives and caches input events from the device driver layer through an event queue mechanism, 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 interfaces provided by the core layer and process them accordingly.
  • Abstract interfaces and event handling mechanism: Core layer**Provides a set of abstract interfaces and event handling mechanisms for upper-layer applications and the event handling layer.**It provides a unified event representation, so that events from different types of input devices (such as keyboards, mice, touch screens, etc.) can be represented and processed. Through the core layer 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 bottom layer of the input subsystem and is responsible for**communicating 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 in the device driver layer usually includes hardware initialization, interrupt handling, data transmission, and other operations to ensure the normal operation of input devices. Developers write device drivers in this layer to adapt to specific hardware devices.

  • The Linux source code already contains the core layer 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. Except for some requirements such as fixed device nodes,generally there is no need to write event handling layer code.

  • Since the device driver layer faces different hardware, and each hardware has a different initialization method,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 input device interfaces related to Advanced Power Management (APM).
evbug.cProvides virtual input devices for debugging, which can simulate 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 and provides them to upper-layer user-space programs.
ff-core.cProvides support for force feedback devices, allowing input devices to send force feedback information.
ff-memless.cProvides force feedback device support without memory allocation, suitable for resource-constrained embedded systems.
gameport/Contains a directory of drivers that support game controllers.
input.cProvides operations such as initialization and event handling of the input subsystem.
input-leds.cProvides support for LED indicator devices, allowing control of the LED indicator status.
joydev.cProvides drivers supporting joysticks, handling input events from joystick devices.
keyboard/Contains a directory of drivers that support keyboards.
misc/Contains a directory of drivers for other types of input devices, such as infrared remote controls, input audio, etc.
remotectl/Provides drivers supporting remote control, handling input events sent via remote controls.
serio/Provides drivers for input devices connected via serial ports, handling communication and processing of serial input devices.
sensors/Contains a directory of sensor-related drivers, used for communicating with and processing various sensor devices.
sparse-keymap.cProvides support for sparse keymaps, allowing key assignments by arbitrary key codes, suitable for devices with non-standard keyboard layouts or special function keys.
tablet/Provides drivers supporting graphics tablets and other types of graphic input devices, handling input events from tablet devices.
touchscreen/Provides drivers supporting touchscreens, handling input events from touchscreen devices.

In menuconfig:

12
Device Drivers --->	Input device support --->

As follows:

123456789101112131415161718192021222324252627
-*- 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 trim and configure the kernel, just check or uncheck the options.

Input subsystem data structures and their relationships

The code of the event handling layer is located indrivers/input/evdev.cIn the file, it provides 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, and other functions.

123456789101112131415161718192021222324
// drivers/input/evdev.cstatic 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 of the input device	.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);

Hereinput_register_handlerThe function willevdev_handlerAdd to the input subsystem’shandlerlist, and assign a uniquehandlernumber:

input_register_handler()

123456789101112131415161718192021222324252627282930313233
// 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;	// Try 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 list head.	INIT_LIST_HEAD(&handler->h_list);	// Add the handler to the end of the global handler list so that it can interact with other components of the input subsystem.	list_add_tail(&handler->node, &input_handler_list);	// Traverse the input device list and attach a handler to each device, thereby establishing a connection between each input device and the handler to process input events sent by the devices.	list_for_each_entry(dev, &input_dev_list, node)		input_attach_handler(dev, handler);	// Wake up the procfs reader to notify it that a new handler has been registered, so that the reader can obtain new input event information in a timely manner.	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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
// 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; parameters include 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; parameters include 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; the return value is a boolean indicating whether the event is accepted.    	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; the return value is a boolean indicating whether it is applicable.    	bool (*match)(struct input_handler *handler, struct input_dev *dev);	// Connect function pointer, used to establish a connection between an input device and a handler; the return value is an integer indicating the result of the connection.    	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);    	// Disconnect function pointer, used to disconnect the connection between an input device and a handler.	void (*disconnect)(struct input_handle *handle);    	// Start function pointer, used to start the data transfer or processing process 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 list head.	struct list_head	node;// Handler list node.	ANDROID_KABI_RESERVE(1);};

input_attach_handler()

input_register_handler()throughlist_for_each_entry(dev, &input_dev_list, node)Call in a loopinput_attach_handler(dev, handler);, andinput_attach_handlerThe function is as follows:

12345678910111213141516171819
// drivers/input/input.cstatic int input_attach_handler(struct input_dev *dev, struct input_handler *handler){	const struct input_device_id *id;	int error;	// Use the match function of the input device and handler to determine whether it applies to the device    	// This function will look up an ID matching the given input device in the handler's input device ID table,    	// and return the matching ID. If no matching ID is found, return 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, in 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 astruct input_device_idThe structure is an array of elements, each element representing a possible input device ID.

123456789101112131415
// drivers/input/input.cstatic const struct input_device_id *input_match_device(struct input_handler *handler,							struct input_dev *dev){	const struct input_device_id *id;	// Iterate 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) &&// 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 match function returns true (or there is no match function), then 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 match function compares the attributes of the input device with those specified in the ID, such as vendor ID, product ID, etc.

1234567891011121314151617181920212223242526272829303132333435
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_handlerofconnectThe function 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 attributes, and add the character device to the system.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
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;	// Get 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-in 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 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 the 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 the 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;}

struct input_handle

When callingconnectfunction (evdevofconnectAfter the function), astruct input_handle, used to record the successfully matched input handler (input_handler) and input device (input_dev), and establish the relationship between them.

123456789101112131415161718192021222324252627
// 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 list	struct list_head	h_node;// Node pointing to the input handler list};

input_register_handle()

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
/** * 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 the input handler	struct input_dev *dev = handle->dev;// Get the input device	int error;	/*	 * We take dev->mutex here to prevent race with	 * input_release_device().	 */   	/*	 * Get it 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.,Ordinary handler added 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 being from ->connect() Call,This and ->disconnect() are mutually exclusive,	 * So we cannot be with input_unregister_handle() competition,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 purpose of this function is to convert the input handler (input_handler) and input device (input_dev) Establish a link.

  • For input devices (input_devFor ), 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 the input handler (input_handler) said, it can be done by traversingdev->h_listA linked list is used to find the matching input device. This means that the input handler can find the corresponding device by traversing the linked list of input devices associated with it.

In this way, by establishing an association between the input handler and the input device, the input handler can process and control a specific input device.

input_register_device()

input_handlerStructs should useinput_register_handlerto register; input handler (input_handler) and input device (input_dev) connect (connect) structureinput_handlealso needsinput_register_handlefunction to register

input deviceinput_devThe structure certainly also needs a function to register,input_devThe structure’s registration function isinput_register_device

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
// drivers/input/input.cint 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) {// If 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 event. */	__set_bit(EV_SYN, dev->evbit);	/* KEY_RESERVED is not supposed to be transmitted to userspace. */    	/* KEY_RESERVED should not be passed to userspace. */	__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 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 input handler linked list and 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);

input_register_devicefunction used to register input device (input_dev), adding the input device to the input subsystem.

struct input_dev

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
// input/linux/input.hstruct input_dev {	const char *name;// device name	const char *phys;// physical location of the device	const char *uniq;// Device unique identifier	struct input_id id;// Input device identification information	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];// Device property bitmap	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];// Bitmap of event types supported by the device	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];// Bitmap of keys supported by the device	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];// Bitmap of relative coordinates supported by the device	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];// Bitmap of absolute coordinates supported by the device	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];// Bitmap of miscellaneous events supported by the device	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];// Bitmap of LEDs supported by the device	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];// Bitmap of sounds supported by the device	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];// Bitmap of force feedback supported by the device	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];// Bitmap of switches supported by the device	unsigned int hint_events_per_packet;// Hint for the number of events in each input event report	unsigned int keycodemax;// Maximum supported key code	unsigned int keycodesize;// Key code byte size	void *keycode;// Pointer to key code data	int (*setkeycode)(struct input_dev *dev,			  const struct input_keymap_entry *ke,			  unsigned int *old_keycode);// Callback function to set key codes	int (*getkeycode)(struct input_dev *dev,			  struct input_keymap_entry *ke);// Callback function to get key codes	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 key repeat	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 status bitmap	unsigned long snd[BITS_TO_LONGS(SND_CNT)];// Current sound status bitmap	unsigned long sw[BITS_TO_LONGS(SW_CNT)];// Current switch status bitmap	int (*open)(struct input_dev *dev);// Callback function for opening the device	void (*close)(struct input_dev *dev);// Callback function for closing the device	int (*flush)(struct input_dev *dev, struct file *file);// Callback function for refreshing the device	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);// Callback function for handling input events	struct input_handle __rcu *grab;// Current owner 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 users of the device	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];// Timestamp array 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

12345678910111213141516171819202122232425
// drivers/input/evdev.cstatic 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 of the input device	.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()

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
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) {// If 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 event. */	__set_bit(EV_SYN, dev->evbit);	/* KEY_RESERVED is not supposed to be transmitted to userspace. */    	/* KEY_RESERVED should not be passed to userspace. */	__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 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 input handler linked list and 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)function

input_attach_handler()

12345678910111213141516171819202122
// drivers/input/input.cstatic int input_attach_handler(struct input_dev *dev, struct input_handler *handler){	const struct input_device_id *id;	int error;	// Use the match function of the input device and handler to determine whether it applies to the device    	// This function will look up an ID matching the given input device in the handler's input device ID table,    	// and return the matching ID. If no matching ID is found, return 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, in the given input event handler (input_handler) find the input device ID that matches the specified input device (input device ID).

The handler’s input device ID table is astruct input_device_idThe structure is an array of elements, each element representing a possible input device ID.

123456789101112131415
// drivers/input/input.cstatic const struct input_device_id *input_match_device(struct input_handler *handler,							struct input_dev *dev){	const struct input_device_id *id;	// Iterate 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) && // 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 match function returns true (or there is no match function), then return that ID.			return id;		}	}	return NULL;}

Let’s look at evdev’sinput_handler

1234567891011121314151617
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, that is, it matches all …input_dev

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 match function compares the attributes of the input device with those specified in the ID, such as vendor ID, product ID, etc.

123456789101112131415161718192021222324252627282930313233343536
bool input_match_device_id(const struct input_dev *dev,			   const struct input_device_id *id)// id = one 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);

In the simplest device driver layer code written above, the flags parameter of id is not defined, soINPUT_DEVICE_ID_MATCH_BUSINPUT_DEVICE_ID_MATCH_VENDORINPUT_DEVICE_ID_MATCH_PRODUCTINPUT_DEVICE_ID_MATCH_VERSIONNone of these judgment conditions hold.

bitmap_subsetis an inline function used to determine whether two bitmaps have a subset relationship, that is,Determine whether the first bitmap is a subset of the second bitmap

id does not defineevbitkeybitrelbitetc., sobitmap_subsetThe judgment also does not hold (an empty bitmap is a subset of any bitmap), and ultimately the function returns true. Of course, this is only an analysis ofevdev.cthe analysis of this generic event handling code. After returning true, go back toinput_attach_handlerfunction, and then it will callhandler->connectestablish a connection with the input device.

Many-to-many matching analysis

drivers/input/joydev.cfile’sinput_handlerThe structure content is as follows

joydev_handler

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
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 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 (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 button 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 (button event)		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },// The matched button type is BTN_JOYSTICK (joystick)	},	{	// The sixth identifier matches the button 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 (button event)		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },// The matched button type is BTN_GAMEPAD (gamepad)	},	{	// The seventh identifier matches the happy key (BTN_TRIGGER_HAPPY) button event		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_KEYBIT,		.evbit = { BIT_MASK(EV_KEY) },// The matched event type is EV_KEY (button event		.keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },// The matched button 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);

with the above generic device driver layerevdev.cofstruct input_handlerthe difference is that the structure,joydev_handlerthe structure has corresponding matching functionsjoydev_match(), that is, when the device driver layer matches with the event handling layer, it is necessary tojoydev_idsthe structure array and the corresponding matching functions jointly determine.

12345
// drivers/input/evdev.cstatic const struct input_device_id evdev_ids[] = {	{ .driver_info = 1 },	/* Matches all devices */	{ },			/* Terminating zero entry */};

Structinput_device_idthe function isDescribe the characteristics of the input device, so that the kernel can identify and match the correct driver. In the generic device driver layerevdev.cinevdev_idsThe struct array is set todriver_infoindicate 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, useflagsthe field’sINPUT_DEVICE_ID_MATCH_EVBITandINPUT_DEVICE_ID_MATCH_ABSBITflag 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 that the event type to match 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 that the absolute event type to match isABS_X(X axis),ABS_Z(Z axis),ABS_WHEEL(wheel) orABS_THROTTLE(throttle).
  • keybit: The bitmask of the key type, used to specify the key type to match. Here,keybitthe bitmask of the field indicates that the key type to match 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_idsAlthough in the struct array,driver_infonot set, but the flags parameters all exist and are non-zero, so the condition of the for loop holds, and within the for loop it will callinput_match_device_idThe function determines whether the given input device matches the current ID.

Sinceid_tableis not set inbustype,vendor,product,version, so checking whether the device’s bus type, vendor ID, product ID, and device version number match will all succeed. However:

123456789101112
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 forDetermine whether the first bitmap is a subset of the second bitmap. The settings in the simplest device driver layer code are as follows:

12
__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 event bitmap set by, which requires that the event processing device driver layer required by the event handling layer must support all.

You can add a print:

1234567891011121314151617
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 that oneinput_devcan be associated with multipleinput_handlerassociated, while oneinput_handlercan also handle multipleinput_devevents.

Writing the simplest device driver layer code

Step

  1. Step 1:Create an input device structure variable In device driver development, you first need to 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 variable After creating the input device structure variable, it needs to be initialized. This includes setting the device name, supported event types, event handler functions, etc. The initialization process can be completed using the member variables and functions provided by the structure.
  3. Step 3:Register the input device structure variable After 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 event Once 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 variable When the device is no longer needed, it should be unregistered and released to ensure proper resource release. You can useinput_unregister_devicefunction to unregister the input device structure variable, and useinput_free_devicefunction to release related resources and memory.

Register input device

input_allocate_device()

123456789101112131415161718192021222324252627282930313233343536373839404142434445
// 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 the input_dev structure

After usinginput_allocate_devicea function to create ainput_devstruct, the next step is to initializeinput_devthe struct contents. In this step, there are two parts, namelySetting the event typeandsetting the specific type

Setting the event type

In the header fileinclude/uapi/linux/input-event-codes.h, the Linux kernel has already defined some input event types for us, and their meanings are as follows:

  • EV_SYN (0x00): Used forsynchronization event, indicating the end of a group of input events.
  • EV_KEY (0x01): Used forkey event, indicating pressing, releasing, or repeating a key.
  • EV_REL (0x02): Used forrelative displacement event, indicating the relative position change of the device, such as mouse movement.
  • EV_ABS (0x03): Used forabsolute displacement event, indicating the absolute position change of the device, such as the coordinates of a touchscreen.
  • EV_MSC (0x04): Used formiscellaneous event, including some special-purpose event types, such as device state changes, etc.
  • EV_SW (0x05): Used forswitch events, indicating state changes of switches, such as power buttons, lid open/close, etc.
  • EV_LED (0x11): Used for LED events, indicating state changes of the LED light.
  • 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 power state changes.
  • EV_FF_STATUS (0x17): Used forforce feedback status events, indicating state changes of force feedback devices.
  • EV_MAX (0x1f): The maximum value of input event types.
  • EV_CNT: The number of input event types.

And in theinput_devstructure, a series of bitmaps are defined, which are used in the input subsystem to represent the capabilities and supported functions of input devices, as specifically defined below:

12345678910
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; // Device property bitmapunsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // Bitmap of event types supported by the deviceunsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // Bitmap of keys supported by the deviceunsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // Bitmap of relative coordinates supported by the deviceunsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // Bitmap of absolute coordinates supported by the deviceunsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; // Bitmap of miscellaneous events supported by the deviceunsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; // Bitmap of LEDs supported by the deviceunsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; // Bitmap of sounds supported by the deviceunsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; // Bitmap of force feedback supported by the deviceunsigned long swbit[BITS_TO_LONGS(SW_CNT)]; // Bitmap of switches supported by the device
  • evbit(Event type bitmap) is an array of lengthEV_CNTwith each element corresponding to an event type. By setting the corresponding bits, the event types supported by the device can be indicated, such as key events, relative movement events, absolute movement events, miscellaneous events, etc.
  • keybit(Key type bitmap) indicates the key types supported by the input device, usually associated withEV_KEYthe event type. By setting the corresponding bits, the keys supported by the device can be indicated.
  • relbit(Relative displacement type bitmap) indicates the relative displacement types supported by the input device, usually associated withEV_RELthe event type. By setting the corresponding bits, the relative displacements supported by the device can be indicated, such as mouse movement.
  • absbit(Absolute displacement type bitmap) indicates the absolute displacement types supported by the input device, usually associated withEV_ABSthe event type. By setting the corresponding bits, the absolute displacements supported by the device can be indicated, such as touchscreen coordinates.
  • mscbit(Miscellaneous type bitmap) indicates the miscellaneous types supported by the input device, usually associated withEV_MSCthe event type. By setting the corresponding bits, the miscellaneous events supported by the device can be indicated, such as device status changes.
  • ledbit(LED type bitmap) indicates the LED types supported by the input device, usually associated withEV_LEDthe event type. By setting the corresponding bits, the LED light control supported by the device can be indicated.
  • sndbit(Sound type bitmap) indicates the sound types supported by the input device, usually associated withEV_SNDthe event type. 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_FFthe event type. 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 related toEV_SWevent types. By setting the corresponding bits, it can indicate the switch state changes supported by the device.

__set_bitis a bit operation function used to set a specific bit in a bitmap. For example, the following code can be used to set the input device to support key events:

1
__set_bit(EV_KEY,myinput_dev->evbit)

setting the specific type

After setting the event type, you also need to set the specific type. The macro definitions are defined in the header fileinclude/uapi/linux/input-event-codes.hSome of the contents are as follows:

12345678910111213
..#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 does it represent? Whether it is 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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#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 event is 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:

123456789101112
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  event3root@topeet:/root# rmmod input_dev_test.ko[09:45:12.391] event3  - myinput_dev: device removedroot@topeet:/root# ls /dev/input/by-path  event0  event1  event2

Improve the device driver layer code

Report event

Reporting events means that in the device driver layer, when an input device generates an event, the event is notified to the input subsystem

Before reporting an event, you must first determine the type of event to report. The event type can be a key event, relative position event, absolute position event, 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 event have already been confirmed.

After determining the event type, you need to use the corresponding reporting function to pass the event data to the input subsystem. Commonly used reporting functions include:

  • input_report_key(): reports key events, used to notify the pressed and released states of keys.
  • input_report_rel(): reports relative position events, used to notify the relative movement of the device, such as mouse movement.
  • input_report_abs(): reports absolute position events, used to notify the absolute position of the 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: pointing to the input device structureinput_devpointer to
-code: key event code (e.g.KEY_A,BTN_TOUCHetc.)
-value: key state (0 = released, non-zero = pressed)
Return valueNone (void
Functionreport a key event (EV_KEY). Used in scenarios such as keyboards, buttons, and touchscreen taps.

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: points toinput_devpointer to
-code: relative axis type (e.g.REL_X,REL_Y,REL_WHEEL
-value: offset relative to the last position (can be positive or negative)
Return valueNone (void
Functionreport a relative coordinate event (EV_REL). Commonly used in 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: points toinput_devpointer to
-code: absolute axis type (e.g.ABS_X,ABS_Y,ABS_MT_POSITION_X
-value: current absolute coordinate value
Return valueNone (void
Functionreport a absolute coordinate event (EV_ABS). Used in 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: points toinput_devpointer to
-code: force feedback effect ID (usually allocated by user space)
-value: force feedback state (0 = stopped, non-zero = playing)
Return valueNone (void
FunctionReport Force Feedback status event (EV_FF_STATUS), used to notify userspace 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 rather than 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: points toinput_devpointer to
-code: switch type (e.g.SW_LID,SW_TABLET_MODE
-value: switch state (0 = lid closed/open, non-0 = lid open/closed, exact meaning depends on the type)
Return valueNone (void
Functionreport a switch 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: points toinput_devpointer to
Return valueNone (void
Functionsend a synchronization event (EV_SYN / SYN_REPORT), indicating that a group of related events has been fully reported. Userspace 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 events may not be processed.

input_event()

12345678910111213141516171819202122232425262728293031
/** * 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 are inline functions, and internally ultimately callinput_event(dev, type, code, value)
  • 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 most essential APIs in Linux input subsystem driver development, used to convert raw data generated by hardware into standard input events for use by userspace (e.g., evdev, libinput, X11/Wayland).

After using the reporting function, one usually callsinput_sync()the function to synchronize. The purpose of the synchronization event is to inform the input subsystem of the end of the event, so that the subsystem can pass the event to the corresponding application or system component for processing. Calling the synchronization event can prevent loss or confusion of event data.

example

driver, using a timer to report events at regular intervals.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
#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 events        input_sync(myinput_dev); // Send synchronization 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");

The application layer obtains the reported data

input_event structure

The data read by the application layer isinput_eventStructure:

1234567891011121314151617181920
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, and these macro definitions are also in<linux/input.h>the header file, so the application needs to include this header file;
    • EV_SYN (0x00): Used forsynchronization event, indicating the end of a group of input events.
    • EV_KEY (0x01): Used forkey event, indicating pressing, releasing, or repeating a key.
    • EV_REL (0x02): Used forrelative displacement event, indicating the relative position change of the device, such as mouse movement.
    • EV_ABS (0x03): Used forabsolute displacement event, indicating the absolute position change of the device, such as the coordinates of a touchscreen.
    • EV_MSC (0x04): Used formiscellaneous event, including some special-purpose event types, such as device state changes, etc.
    • EV_SW (0x05): Used forswitch events, indicating state changes of switches, such as power buttons, lid open/close, etc.
    • EV_LED (0x11): Used for LED events, indicating state changes of the LED light.
    • 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 power state changes.
    • EV_FF_STATUS (0x17): Used forforce feedback status events, indicating state changes of force feedback devices.
    • EV_MAX (0x1f): The maximum value of input event types.
    • EV_CNT: The number of input event types.
  • code: code indicates which specific event within that event type. Each of the event types listed above contains a series of specific events. For example, a keyboard usually has many keys, and the code variable tells the application which key generated the input event.
123456789101112131415
#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 //Number 7 key#define KEY_8 9 //Number 8 key#define KEY_9 10 //Number 9 key#define KEY_0 11 //Number 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, you can refer toinput-event-codes.hthe header file (which is<linux/input.h>included).

  • value: The kernel sends a data value to the application layer for each reported event, and the interpretation of the value changes with the code.
    • For example, for key events:
      • If value equals 1, it means the key is pressed;
      • value equal to 0 indicates the key is released
      • value equal to 2 indicates the key is held down.
    • In absolute displacement events (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 time the value is equal to the Y-axis coordinate of the touch point.

Report data format

to use the commandhexdump /dev/input/event4to view the reported information.

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

1234567891011121314151617181920
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};
  • Beforeinput_eventIn the data packet, there are four member variables:time,type,code,value
    • time.tv_secandtime.tv_usecis of typelong int, occupies 8 bytes, sotimeoccupies 16 bytes
    • __u16 typeis of typeunsigned short int, occupies 2 bytes
    • __u16 codeis of typeunsigned short int, occupies 2 bytes
    • __s32 valueis of typeunsigned int, occupies 4 bytes

Therefore, ainput_eventdata packet occupies 8+8+2+2+4=24 8+8+2+2+4 =24 bytes

In general,input_eventThe byte order of the data packet is little-endian (Little Endian). This means that the lower byte is located at a higher memory address.

Assume the data output by hexdump (represented in hexadecimal) is as follows:

1234567
root@topeet:~$ hexdump /dev/input/event20000000 0f09 65d3 0000 0000 36fb 0001 0000 00000000010 0003 0039 0000 0000 0f09 65d3 0000 00000000020 36fb 0001 0000 0000 0003 0035 00f5 00000000030 0f09 65d3 0000 0000 36fb 0001 0000 00000000040 0003 0036 02b2 0000 0f09 65d3 0000 00000000050 36fb 0001 0000 0000 0003 0030 0021 0000

The firstinput_eventThe corresponding members are as follows:

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

example

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
#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:

1234567891011
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.ovalue is 1no support, event.type is 0value is 0no support, event.type is 0

Code analysis of the generic event handling layer evdev

Analysis of the connect function

evdev_handler

123456789101112131415
// drivers/input/evdev.cstatic 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 of the input device	.connect	= evdev_connect,// When input_dev and input_Connection handler function executed after handler matches successfully	.disconnect	= evdev_disconnect,// Disconnect 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 of the input device	.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

1234567891011
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;// Points 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 mutually 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 state 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 handling functions.
  • grab: Points to the client currently exclusively occupying the evdev device. When a clientEVIOCGRABuses ioctl to grab (exclusively occupy) the evdev device, other clients cannot receive events from the device.
  • 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 mutually exclusive access to the evdev device, ensuring that operations on the device are mutually exclusive in a multi-threaded environment.
  • dev: Device structure associated with the evdev device, used to represent specific device information, such as device name, device number, etc.
  • cdev: Character device structure of the evdev device, used to register and manage character devices.
  • exist: Flag indicating whether the evdev device exists. If the device exists, it is true; otherwise, it is false.

struct evdev_client

This structure defines the relevant information and status of an evdev client, used to manage clients associated with an evdev device. In an application, each time an event device node is opened, astruct evdev_clientstructure is used to represent it. The system can maintain multiple clients for each evdev device and manage the state and attributes of each client.

Beforeevdev.cIn this context, this structure is also operated on, and based on the client’s state and attributes, received events are written to the buffer or the client is notified.

123456789101112131415
struct evdev_client {	unsigned int head; // The head pointer of the buffer, pointing to the next writable position.	unsigned int tail; // The 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] The position of the first element of the next packet.	spinlock_t buffer_lock; /* protects access to buffer, head and tail */ // A spinlock used to protect access to the buffer, head pointer, and tail pointer.	wait_queue_head_t wait;	struct fasync_struct *fasync;// A structure pointer used for asynchronous notification.	struct evdev *evdev;// The evdev device pointer associated with the client.	struct list_head node;// The client list node associated with the evdev device.	enum input_clock_type clk_type;// Input clock type.	bool revoked;// A flag indicating whether the client has been revoked.	unsigned long *evmasks[EV_CNT];// An array for event masks.	unsigned int bufsize;// The size of the buffer.	struct input_event buffer[];// Input event buffer, a variable-length array.};
  • head: The head pointer of the buffer, pointing to the next writable position.
  • tail: The tail pointer of the buffer, pointing to the next readable position.
  • packet_head: The position of the first element of the next packet.
  • buffer_lock: A spinlock used to protect access to the buffer, head pointer, and tail pointer, ensuring that operations on the buffer are thread-safe in a multi-threaded environment.
  • fasync: A structure pointer used for asynchronous notification; when asynchronous notification is needed, set it to the corresponding value.
  • evdev: The evdev device pointer associated with the client, indicating the evdev device to which the client belongs.
  • node: The client list node associated with the evdev device, used to manage clients associated with the device.
  • clk_type: Input clock type, indicating the input clock type used by the client.
  • revoked: A flag indicating whether the client has been revoked.
  • evmasks: An array for event masks, storing masks for different types of events; the size of the array is determined byEV_CNTDefinition.
  • bufsize: The size of the buffer, indicating the number of input events the buffer can hold.
  • buffer[]: The input event buffer, which is a variable-length array that stores input event data.

evdev_connect()

input_handlerThe connect function.

input_register_handler()call ininput_attach_handler(), this function in turn callsinput_handlerthe connect function,error = handler->connect(handler, dev, id)that is,evdev_connect()

The main function of this function is to establish a connection with the input device, initialize and register the input handle, set device attributes, and add the character device to the system.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
#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;	// Get 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, 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 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-in 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 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 the 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 the 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;}

It can be seenconnectThe main task of the function is to associate input devices with event handlers, so that the corresponding handler function is called when an event occurs.

It achieves this association by registering input handlers and setting callback functions, and ensures that the correct event handler is called. This association mechanism allows developers to customize handling functions as needed, so as to perform corresponding processing based on events reported by input devices.

Device Number Allocation Analysis

secondary device number
secondary device number

From the above figure, it can be found thatevdev.cThe device nodes event0, event1, event2, and event3 created by the program all have major device number 13, and minor device numbers 64, 65, 66, and 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 increment 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.

12
// include/uapi/linux/major.h#define INPUT_MAJOR		13

secondary device number

In the connect function, we usedminor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);The function obtained the minor device number. And

12
#define EVDEV_MINOR_BASE	64#define EVDEV_MINORS		32

input_get_new_minor()

123456789101112131415161718192021222324252627282930313233
/** * 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 secondary device number. Its main function is to obtain a new secondary device number based on specified conditions. If specifiedlegacy_base, then it will first try to obtain the secondary device number from that range. If the acquisition fails or dynamic allocation is not allowed, it will try to obtain the secondary device number from the dynamically allocated range. Finally, it returns the obtained secondary device number.

  • iflegacy_baseIf greater than or equal to 0, execute the following logic:

    • useida_simple_get()Function frominput_idaobtain a minor device number fromminor, the range islegacy_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, execute the following logic:

    • useida_simple_get()Function frominput_idaobtain a minor device number fromminor, the range isINPUT_FIRST_DYNAMIC_DEVtoINPUT_MAX_CHAR_DEVICES

Andida_simple_get()defined as:

12
#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 macro parameters are explained as follows:

  • ida: Represents a pointer to the IDA object, used to manage the allocation and release of ID ranges.
  • start: Represents the starting ID of the allocated ID range.
  • end: Represents the ending ID of the allocated ID range.
  • gfp: Represents the GFP flags used for memory allocation.

File operation set functions

BeforeconnectIn the function, throughcdev_init(&evdev->cdev, &evdev_fops);Creating a character device. The most important operation in creating a character device is implementing the functions in the file operation set, as follows:

1234567891011121314
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,};

open function analysis

BeforeconnectIn the function, throughcdev_init(&evdev->cdev, &evdev_fops);Creating a character device. The most important operation in creating a character device is implementing the functions in the file operation set, as follows:

1234567891011121314
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()

12345678910111213141516171819202122232425262728293031323334353637383940414243
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 a pointer to the evdev_client structure.	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 to open, marking the file as a non-seekable stream device that does not support random access.	stream_open(inode, file);	return 0; err_free_client:    	// If opening the device fails, error handling is required to remove the client from evdev's client list.	evdev_detach_client(evdev, client);    	// Free the memory allocated for the client.	kvfree(client);	return error;}

stream_open()

123456789101112131415161718
/* * 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()

1234567891011121314151617181920
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 returns 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, indicating that the device's open counter is decremented.			evdev->open--;	}	mutex_unlock(&evdev->mutex); // Release the mutex lock of the input device.	return retval;}

input_open_device()

evdev_open_device()call ininput_open_device()The function opens the input device. This function callsinput_devofopen()function

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
/** * 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()

1234567
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg){    	// Call evdev_ioctl_handler function to handle the IO control operation, passing it the file pointer, cmd, and    	// the type-converted arg as parameters. The function converts arg to void __user * type, so that between user space and kernel space    	// between spaces to pass pointers. The function passes evdev_ioctl_handler's return value as its own return value, and returns it directly to the caller.	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);}

evdev_ioctl_handler()

1234567891011121314151617181920212223242526
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;    	// Try to acquire the mutex lock of evdev, and return the corresponding error code if it cannot be acquired.	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 indicating that the device does not exist.		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 evdev mutex.	return retval;}

evdev_do_ioctl()

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
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 version number
  • EVIOCGID: Get input device ID information
  • 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 the number of simultaneously playable effects
  • EVIOCGRAB: Occupy/release input device
  • EVIOCREVOKE: Revoke device access permission
  • EVIOCGMASK: Retrieve current event mask
  • EVIOCSMASK: Set event mask
  • EVIOCSCLOCKID: Set clock id for timestamps

poll function analysis

12345678910111213141516171819202122232425262728
/* No kernel lock - fine */static __poll_t evdev_poll(struct file *file, poll_table *wait){    	// Get the evdev_client structure pointer from the file private data	struct evdev_client *client = file->private_data;    	// Get the evdev pointer from the evdev_client structure	struct evdev *evdev = client->evdev;	__poll_t mask;    	// Add the current process to the wait queue, waiting for the wake-up event of evdev->wait	poll_wait(file, &client->wait, wait);    	// Check the 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;}

fasync function analysis

12345678910
static int evdev_fasync(int fd, struct file *file, int on){    	// Get the evdev_client structure pointer from the file private data	struct evdev_client *client = file->private_data;    	// Call the fasync_helper function to handle the process's asynchronous notification	// This function adds the process to or removes it from the asynchronous notification list based on the value of on	// And store the notification-related data in client->fasync	return fasync_helper(fd, file, on, &client->fasync);}

llseek function analysis

1234
loff_t no_llseek(struct file *file, loff_t offset, int whence){	return -ESPIPE;}

will-ESPIPEReturn directly as the return value. This function is used to prevent llseek operations on the device file, that is, it does not allow random access to the device file by changing the file position pointer.

release function analysis

1234567891011121314151617181920212223242526272829303132333435
static int evdev_release(struct inode *inode, struct file *file){    	// Get the evdev_client structure pointer from the file 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;    	// Get the evdev mutex to ensure that operations on evdev are atomic	mutex_lock(&evdev->mutex);    	// Check the values of evdev->exist and client->revoked	if (evdev->exist && !client->revoked)        	// If evdev exists and the client has not been revoked, call input_flush_The device function refreshes the device's input buffer		input_flush_device(&evdev->handle, file);	// Release evdev's exclusive state and remove the client from the grab state	evdev_ungrab(evdev, client);    	// Unlock the evdev mutex.	mutex_unlock(&evdev->mutex);    	// Detach and free the client from evdev	evdev_detach_client(evdev, client);    	// Free the client's event mask memory	for (i = 0; i < EV_CNT; ++i)		bitmap_free(client->evmasks[i]);    	// Free the client's memory	kvfree(client);    	// Close the evdev device	evdev_close_device(evdev);	return 0;}

Data reporting process analysis

When using the read function to read data reported by the input device, the file operation set in the driver will executeevdev_read()function. Similarly, when we use the write function to write data to the input device, the file operation set in the driver will executeevdev_write()function.

The device input layer is responsible for processing the data of the input device and passing it to the driver. When the input device reports data, the device input layer receives the data and forwards it to the registered driver.

event function analysis

input_event()

When reporting an event, it is necessary to callinput_event

123456789101112131415161718192021222324252627282930313233
/** * 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 the interrupt flag	// Check whether the input device supports the specified event type	if (is_event_supported(type, dev->evbit, EV_MAX)) {		// Acquire the event lock to ensure that event processing is atomic		spin_lock_irqsave(&dev->event_lock, flags);        	// Call input_handle_The event function processes the input event		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()

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
static void input_handle_event(struct input_dev *dev,			       unsigned int type, unsigned int code, int value){    	// Get the handling method of the input event, that is, 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 an EV_SYN type event, 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 function, call the event handler function	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 the handler's slot, 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 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 needs to refresh the value list	if (disposition & INPUT_FLUSH) {        	// If the value in the value list is greater than or equal to 2, pass the value in 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 when refreshing,To avoid stale 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 sync event to the value list		dev->vals[dev->num_vals++] = input_value_sync;        	// Pass the value in 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()

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
static int input_get_disposition(struct input_dev *dev,			  unsigned int type, unsigned int code, int *pval){	int disposition = INPUT_IGNORE_EVENT;// How the event is handled, defaults 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 state, pass directly to the handler			if (value == 2) {				disposition = INPUT_PASS_TO_HANDLERS;				break;			}			// Determine whether the key state has changed; if changed, update the state and pass 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 changed, update the state and pass 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 changed, update the state and pass 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 changed, update the state and pass 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 repeat event settings 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 event's disposition}

This function is used to determine how an event is handled based on the type, code, and value of the input device. First, it branches based on the event type. In each branch, it determines how the event is handled based on the event type and code, and updates the disposition (the way the event is handled) accordingly. The disposition has the following modes:

  • INPUT_IGNORE_EVENT: indicates that the input event is ignored and no processing is performed.
  • 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 an application or service process in user space.
  • INPUT_PASS_TO_DEVICE: indicates that the input event is passed to the 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. Usually, when processing multi-touch events, each contact 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 can be 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 of the above.INPUT_PASS_TO_HANDLERSandINPUT_PASS_TO_DEVICEfunctionality.

input_pass_values()

input_handle_event()Called in the function.input_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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
/* * 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;// The handle of the input device	struct input_value *v;// The value currently 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 {        	// Iterate over the device's handle list, pass the value to each open handle for processing, and update the number 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 RCU lock.	/* trigger auto repeat for key events */    	/* Trigger automatic repetition of key events. */	if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {        	// Iterate over the value list. For events with type EV_KEY and value not equal to 2:        	// If the value is true, start automatic repetition of the key;        	// If the value is false, stop automatic repetition 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()

123456789101112131415161718192021222324252627282930313233343536
/* * 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; // The handler corresponding to the input handle	struct input_value *end = vals; // The end of the processed values	struct input_value *v; // The value currently being processed	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 the 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 handler function, pass the processed values to the event handler function.	else if (handler->event)// If the handler defines a single event handler function, call 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 definition of the handler, if an event handler function is defined (events is not NULL), it passes the processed values 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

1234567
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()

12345678910111213141516171819202122
/* * 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, which is the evdev structure type here.	struct evdev_client *client;// Define an 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 section.	client = rcu_dereference(evdev->grab);// Safely obtain the current evdev client via RCU.	if (client)// If an exclusive client (grab) exists, pass the value to that 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 the RCU read-side critical section.}

Here RCU refers to the Linux kernel’s Read-Copy Update mechanism, which is a lock-free read synchronization mechanism

When an input event arrives, the event must be sent to all clients.

The problem is:

  • A thread is iterating over the client list
  • Another thread may be deleting a client

If a regular lock is used, it will lead to:

  • 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 it directly, but instead:

  1. Make a copy of the data
  2. Modify the new data
  3. Switch the 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 rather: ‘all old readers finish’, that is, readers who entered before the ‘pointer switch’; new readers don’t count)
  5. Release the old data

In this way, readers either see the old data or the new data. They will never see a ‘half-updated state’.

RCU is well suited for this:

  • High read volume

  • Very few writes

  • Allows briefly seeing the old state

edev_pass_values()

123456789101112131415161718192021222324252627282930313233343536373839404142434445
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;// The input value currently being processed	struct input_event event;// Input event struct	struct timespec64 ts;// Timestamp	bool wakeup = false;// Whether it is necessary 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;// The seconds field of the input event is set to the seconds value of the timestamp.	event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;// The microsecond field of the input event is set to the value obtained by dividing the nanosecond value of the timestamp by 1000.	/* Interrupts are disabled, just acquire the lock. */    	/* To 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 the current value.			wakeup = true;// Set wake 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 input event's code field to the current value's code.		event.value = v->value;// Set the value field of the input event to the value of the current value.		__pass_event(client, &event);// Pass input events 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, then wake up the threads in the waiting queue.		wake_up_interruptible_poll(&client->wait,			EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);}

__pass_event()

1234567891011121314151617181920212223242526272829303132
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 actually"discard"All unconsumed events,only retained EV_SYN/SYN_DROPPED Plus the latest events。		 */		client->tail = (client->head - 2) & (client->bufsize - 1);// Update the buffer tail pointer to point to the penultimate 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 an event was dropped		client->packet_head = client->tail;// Update the data head pointer to the buffer tail pointer	}	if (event->type == EV_SYN && event->code == SYN_REPORT) {		client->packet_head = client->head;// Update the data 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	}}

read function analysis

The driver can obtain data reported by the input device through the callback function registered with the device input layer. In the driver,evdev_readthe function is used to obtain data reported by the input device from the device input layer.

edev_read()

edev_read()Read input events from the evdev device

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
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'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.	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 size of an input event, return an invalid argument 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 the device not found error code		if (client->packet_head == client->tail &&		    (file->f_flags & O_NONBLOCK))			return -EAGAIN;// If the data head equals the tail and the non-blocking flag is set in the file flags, return the 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 I/O 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 the application to write data to the 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.

12345678910111213141516171819202122232425262728293031323334353637
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, defaults to 0.	if (count != 0 && count < input_event_size())		return -EINVAL;// If count is not 0 and is less than the size of an input event, return an invalid argument error.	retval = mutex_lock_interruptible(&evdev->mutex);// Lock the evdev mutex, interruptibly.	if (retval)		return retval;// If locking fails, return the error code.	if (!evdev->exist || client->revoked) {// If the evdev device does not exist or the client has been revoked, return the 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 into the event structure.			retval = -EFAULT;			goto out;		}		retval += input_event_size();// Update retval by adding the size of one 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, yielding the 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.}

Input core layer code analysis.

The core layer of the input subsystem is mainlykernel/drivers/input/input.cimplemented by the file. It is one of the key components in the Linux kernel for handling input devices.input.cThe file 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 the corresponding drivers, and creates data structures related to the devices. These data structures contain information such as device state, attributes, and operation functions.
  2. Event handling: The core layer is responsible for processing events generated by input devices. When a touch, key press, or other operation occurs on an input device, 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 the corresponding interactive operations.
  3. Event dispatch: The core layer is responsible for dispatching events to applications or subsystems that have registered for the corresponding device. It passes events to the appropriate handlers based on the device type and attributes. In this way, applications or subsystems can perform corresponding actions based on the event type, such as handling touch events, responding to key input, etc.
  4. Device node management: The core layer is responsible for creating and managing device nodes for input devices. Device nodes are usually located in/dev/inputdirectory, providing access interfaces to the 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, thereby implementing functions such as input device initialization and event processing.

input_init()

12345678910111213141516171819202122232425262728293031323334353637383940
// drivers/input/input.cstatic 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 startupmodule_exit(input_exit);

input_proc_init()

12345678910111213141516171819202122232425
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 "devices" 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 node of the input device

Requirement

In embedded Linux development, the loading order of peripherals from different manufacturers and models may differ during kernel startup. For example, devices such as touchpads and USB-to-serial adapters, this will result in/dev/inputThe evdevx nodes (where x=0,1,2,3…) created in the directory will be different. However, applications usually open fixed device nodes. If the device node changes, it will cause the application to open the wrong device node. Therefore, it is necessary to fix the device nodes created for input devices.

Solution

By analyzingevdev.cthe driver, we determined that the device node isevdev_connectcreated in the function.. Therefore, we only need toBeforeevdev_connectin the function, simply create a separate device node for the device that needs to have its device node fixed**.

First, determine the name of the device whose device node you want to fix, and usecat /proc/bus/input/devicescommand to find the device name.

goodix-ts
goodix-ts

Modifyevdev_connectfunction, determine whether it is the device you want to fix based on the device name, and then usedev_set_nameFunction to fix device node name

connect function
connect function

/dev/input/
/dev/input/

Loading comments…