Article count:1382 Read by:1966155

Account Entry

Detailed explanation of Linux input subsystem

Latest update time:2023-12-06 07:01
    Reads:

Click on the blue text


Follow us


Part 1 : Concept of Linux input subsystem

Linux systems have many input devices, such as buttons, keyboards, touch screens, and mice. These input devices are all character devices. However, these input devices are of different types, different principles, and different input and output information. So how to unify these input devices?

Answer: In Linux , all input devices are abstracted from the input subsystem, a software system that provides unified interface functions and achieves great unification.


The input subsystem is divided into three layers:

1. Enter the subsystem device driver layer .
2. Input subsystem core layer
3. Input subsystem event processing layer

in:

1. The device driver layer provides read and write access to hardware registers and converts the underlying hardware's response to user input access into standard input events, which are then submitted to the event processing layer through the core layer.
2. The core layer provides a programming interface for the device driver layer below, and a programming interface for the event layer above.
3. The event layer provides a unified device access interface for user applications and event processing submitted by the driver layer.

The input subsystem makes the driver part of our input device no longer care about the operation of the device file, but only needs to care about the operation of each hardware register and submitting the corresponding input events .

To sum up: In Linux , the input subsystem exists as a kernel module, providing interface functions for the user layer upwards and unified interface functions for the driver downwards. This makes our driver construction very simple and flexible. We only need to call some simple functions to present the functions of an input device to the application. In this way, events of the input device can be sent to the application layer through the input subsystem, and the application can also notify the driver to complete certain tasks through the input subsystem.






Part 2 : Code analysis of Linux input subsystem ( input core )



The core code of the input subsystem: drivers\input\input.c .


"one"

Entry function of the input module: As shown in Figure 1-1 , the entry function registers a character device by calling register_chrdev() . in

INPUT_MAJOR : Major device number: 13 (as shown in Figure 1-2 , defined under include\linux\major.h ).

&input_fops : file_operations structure (as shown in Figure 1-3 ). You will find that only the open function is registered in the structure ( input_open_file() ), which is different from the character device I wrote in my previous article. There are no read and write functions. what happened?

Picture 1-1

Figure 1-2

Figure 1-3


"two"

The file_operations structure only has the open function, so let’s see what the open function does? The entity of the input_open_file() function is shown in Figure 2-1 . in

iminor(inode) 函数调用了函数 MINOR(inode->i_rdev) (其中 iminor 函数原型如图 2-2 ,通过获取子设备号左移 5 位后,获取挂载的 input 设备驱动的数组号,从而获得 input 设备驱动的 input_handler input_handler 结构体,如图 2-3 所示,其中需要 关乎结构体中的成员 event connect ,和 file_operations

如果 handler 存在,说明挂载了这个设备驱动。然后获取 handler 的成员 fops 赋值给 new_fops (其中 , 在第①点中提到要关乎 input_handler 结构体成员 file_operations )。

将新的 new_fops 赋值给 file->f_op ,此时的 input 子系统的 file_operations 为新挂载的 input 设备的 file_operations

调用新挂载的 input 设备的 open 函数。

如果打开失败, input 子系统的 file_operations 将使用回旧的 file_operations ,所以第③点,做了保存机制,保存了旧的 file_operations

2-1

2-2

2-3


《三》

input_table[] 数组从以上的代码中都没有赋值,那么他在哪里赋值的呢?在 drivers\input\input.c 中, input_table[] 是静态全局变量,所以只需要在 input.c 中查找,可以发现在 input_register_handler() 函数中可以看到 input_table[] 有赋值,如图 3-1

判断 input_handler 驱动处理程序是否存在,不存在则将 handler 赋值给 input_table[] 中。

并将其添加到 input_handler_list 链表中。

对每一个的 input_dev 调用 input_match_handler( ) ,判断 input_handler 是否有支持 input_dev

3-1

通过查找可以发现,如图 3-2 所示,有键盘设备,鼠标设备等通过 input_register_handler() 注册了设备处理函数。

3-2


《四》

这里我们以 evdev.c (事件设备) 来讲解如何注册 handler ??

evdev.c 中入口函数中(图 4-1 )通过 input_register_handler() 函数,注册了一个结构体 evdev_handler (图 4-2 .

fops 注册了 file_operations 结构体(图 4-3 ),似曾相识,跟我们之前的注册的字符设备的结构很像,文件的操作。

minor 子设备号( evdev 64 ),用于上面说到 input_table[] 数组中。

id_table 用来和 input_dev 匹配(图 4-4 ),从注释上可以获知,支持所有的输入设备。

event 从字面意思理解就是事件处理函数,下面将进一步讲解这个函数。

connect 在通过 input_register_handler() 注册 handler 时,会调用 input_match_handler() 进行匹配(图 3-1 ),如果匹配成功会调用, handler->connect(handler,dev, id)。

4-1

4-2

4-3

4-4


《五》

在上一篇文章中,有说到核心层对下提供设备驱动的编程接口,对上提供事件层的编程接口。在《三》和《四》中,我们写到事件层接口的实现,那么接下在讲解一下设备驱动的编程接口。

5-1

5-1 是上一篇文章写到的内容,我们将红框的文字转为图 5-2 所示。这样我们进一步了解 input_dev input_handler 的关系。

5-2

drivers\input\input.c 中,我们看到提供给 input_dev 的接口为 input_register_device() ,函数实体(图 5-3 )。通过 input_register_device() 函数注册一个驱动设备,然后加到 input_handler_list 链表中,对每一个的 input_handler , 调用 input_match_device () ,判断 input_dev 是否有支持 input_handler

5-3

在图 3-1 中,注册 handler 的时候,对每一个的 input_dev , 调用 input_match_device() ,判断 input_handler 是否有支持 input_dev 。在图 5-3 ,对每一个的 input_handler , 调用 input_match_device () ,判断 input_dev 是否有支持 input_handler

显然,你会发现跟平台总线很像,字符设备通过 platform_match() 函数设备和驱动进行匹配。而 input 子系统通过调用 input_match_device () 函数将 input_dev input_handler 进行匹配。

在平台总线上不管是注册设备先还是注册驱动,都可以。其实 input 子系统也一样,驱动跟 handle 的注册也是没有优先顺序的。

5-4


《六》

Input 子系统中注册 input_dev 和注册 input_handler 都调用了 input_match_device() 函数。那么我们来看看这个函数实现了什么?图 6-1 input_match_device () 函数的实体。

在《四》中,我们以 evdev.c (事件设备)。在图 4-4 中,我们可以看到 input_device_id 只注册了 driver_info ,所以我们前面四个 if 可以不解读。直接看看 MATCH() MATCH 是一个宏,结构如图 6-2 ,我们以图 6-1 中红框的 evbit 为例,可以将图 6-2 改写为图 6-3 ,如果 dev 支持某一种事件类型,则会将 dev->bit[0] 中置 1

在图 3-1 中的 input_match_device(handler->id_table,dev) ,传入的参数 handler->id_table ,我们看看 evdev.c (事件设备)的 id_table 赋值了什么?可以看到图 4-2 和图 4-4 handler->id_table->evbit[0] 等成员全部都为 0 ,所以 0& 任何数都为 0 0 != 0 不成立,所以不会跳出循环,返回 id ,匹配成功。

6-1

6-2

6-3

我们看图 3-1 和图 5-3 ,当匹配成功,则会调用 handler connect 函数。


《七》

7-1 所示为 evdev.c (事件设备)的 connect() 函数实体。 dev handler 通过一个中间件 hande 连接起来。通过 devfs_mk_cdev() 函数创建设备文件。然后创建一个简单类。

7-1


《八》

最后还有一个关键的函数接口 input_event() ,它用来接收应用层产生的事件。 input_event() 函数的实体如图 8-1 。红框部分可以看出, 驱动 input_dev 和处理 input_handler 已经通过 input_handler .connect 函数建立起了连接 , 那么就调用 input_handler .event 事件函数

8-1



第三篇 Linux input子系统的驱动程序编写



input 子系统的驱动编写要点:

1. 分配 input_dev 结构体(函数: struct input_dev *input_allocate_device(void)

2. 注册 input 设备(函数: int input_register_device(struct input_dev *dev)

3. 注销 input 设备(函数: void input_unregister_device(struct input_dev *dev)

4. 设置 input 设备支持的 事件类型 事件码 事件值 input_id 等信息。

5. 在发生输入事件时,先上报告事件。


input 设备是使用 input_dev 结构体描述,使用 input 子系统实现输入设备驱动,驱动的核心是向系统报告输入事件,不在关心文件操作接口,驱动报告的事件经过 input 核心层 input handler 最终到达用户空间。从这句话中,可以看出 input 子系统的驱动部分会变得简单。


input 子系统的驱动还是比较简单的,因为大部分工作,都在 input 核心层, input handler 做完了。 input 驱动代码,我是在之前文章 linux 中断机制》 input 子系统的驱动编写要点结合进行修改的。你会发现代码很简单。

上面说到, input 设备是使用 input_dev 结构体 来描述。下图为 input_dev 中我们比较关乎的主要内容。

The types of events that evbit can generate:

These event types correspond to key values:

Driver code explanation:

Entry function:

  1. First use the function: input_allocate_device() to allocate an input_dev structure .

  2. Use the function: set_bit() to set the event types and event codes supported by the device.

  3. Through the function: input_register_device() , register the input device.

  4. Registration interrupted.


Export function:

  1. Logout interrupt.

  2. Through the function: input_unregister_device() , unregister the input device.

  3. Through the function: input_free_device() , release input_dev memory.


Interrupt service function:

When the key is pressed, the interrupt service routine is entered, and then the event type, event code, and event value are reported through the function: input_event() according to the key value. Send synchronization signal through function: input_sync() .


in:

Event code ( code ): time code. If the event type is EV_KEY , the code code is the device keyboard code. Code values ​​0~127 are the key codes on the keyboard, 0x110~0x116 are the key codes on the mouse, where 0x110 (BTN_LEFT) is the left mouse button, 0x111 (BTN_RIGHT) is the right mouse button , and 0x112 (BTN_ MIDDLE) is the middle mouse button. For other code meanings, please refer to the include/linux/input.h file.

Event value ( value ): The value of the event. If the event type is EV_KEY, 1 when the key is pressed and 0 when it is released .

input_sync() : used for event synchronization, which informs the receiver of the event that the driver has issued a complete report. The picture below shows the prototype of the function: input_sync . It can be seen that an event is actually reported upwards.


Test application:

#include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <linux/input.h>  int main(void) {   int buttons_fd;   int key_value,i=0,count;   struct input_event ev_key;    buttons_fd = open("/dev/event1", O_RDWR);    if (buttons_fd < 0) {     perror("open device buttons");     exit(1);   }   while(1) {     count = read(buttons_fd,&ev_key,sizeof(struct input_event));     for(i=0; i<(int)count/sizeof(struct input_event); i++)     if(EV_KEY==ev_key.type)       printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);     if(EV_SYN==ev_key.type)       printf("syn event\n\n");   }   close(buttons_fd);   return 0; }


Test Results:









All the original dry goods of this official account have been organized into a catalog, which can be obtained by clicking " Dry Goods " .


You can join the technical exchange group by replying " Join the group " in the background. The benefits of joining the group: free Linux learning materials .


Featured Posts


Latest articlesabout

 
EEWorld WeChat Subscription

 
EEWorld WeChat Service Number

 
AutoDevelopers

About Us About Us Service Contact us Device Index Site Map Latest Updates Mobile Version

Site Related: TI Training

Room 1530, Zhongguancun MOOC Times Building,Block B, 18 Zhongguancun Street, Haidian District,Beijing, China Tel:(010)82350740 Postcode:100190

EEWORLD all rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved