Wei Dongshan Phase 2 Driver Encyclopedia-002_Input Subsystem_Lesson 13 Section 1 Introduction to the Concept of Input Subsystem

Reprinted from 12.Linux input subsystem analysis (detailed)

Before this section, what we have learned is simple character-driven, the content involved includes character-driven framework, automatic creation of device nodes, linux interrupt, poll mechanism, asynchronous notification, synchronous mutual exclusion/non-blocking, and timer debounce.

The driving framework is as follows:

1) Write the member functions of the file_operations structure: .open(), .read(), .write()
2) Create the driver name through register_chrdev() in the entry function, generate the main device number, and assign it to the file_operations structure
3) Unregister the driver through unregister_chrdev() in the exit function

An introduction to the input subsystem

The input subsystem of linux is used to manage all input devices and abstract all input devices to make it easier to write drivers for input devices.

The same input subsystem also needs an input driver framework to identify which input driver the application wants to open,
such as: mouse, keyboard, gamepad, etc. These all belong to input devices; these input device drivers are all through the input sub System to achieve (Of course, these devices also rely on the usb subsystem)

These input devices are all different, so the input subsystem can only realize their commonality, and the difference is realized by the device driver. Where is the difference?

The most intuitive manifestation is the difference in the functions of these devices. For those of us who write drivers, we only need to use the tools (that is, functions) provided by the input subsystem to complete these "differences" in the device driver, and the rest is the work of the input subsystem. This idea not only exists in the input subsystem, but also in other subsystems (such as: usb subsystem, video subsystem, etc.)

The most important thing we learn is to learn the code framework of the input subsystem!

Two-input subsystem framework analysis

The input subsystem is composed of input subsystem core layer (Input Core), driver layer and event processing layer (Event Handler).

An input event arrives in the user space from the device driver of the specific device -> input driver -> Input core -> Event handler -> userspace to the application.
Insert picture description here

1. System core layer (corresponding to the input core layer in the figure)

The main function

a. Register the major device number
b. Transfer station: The system calls the open function entered through the virtual file system for the first layer processing, and selects the handler through the minor device number to enter the second layer open, which is the file_operation where the real open is located, And return the fd of the file_opration.
c. Provide input_register_device and input_register_handler functions to register device and handler respectively

There are the following two paragraphs:

subsys_initcall(input_init);   //修饰入口函数
module_exit(input_exit);     //修饰出口函数

(1) Obviously the input subsystem exists as a module, let’s first analyze the input_int() entry function

 1 static int __init input_init(void)
 2 {
    
    
 3        int err;
 4        err = class_register(&input_class);   //(1)注册类,放在/sys/class
 5        if (err) {
    
    
 6               printk(KERN_ERR "input: unable to register input_dev class\n");
 7               return err;
 8        }
 9  
10        err = input_proc_init();    //在/proc下面建立相关的文件
11        if (err)
12               goto fail1;
13 		  input_fops为本驱动的file_operations结构体
14        err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //(2)注册驱动
15        if (err) {
    
    
16               printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
17               goto fail2;
18        }
22        return 0;
31 
32 }

(1) The 4th line above "err = class_register(&input_class);" is to create an
input class in /sys/class , the input_class variable is as shown in the figure below:
Insert picture description here
As shown in the figure below, we start the kernel, and then start an input subsystem driver, You can also see that an "input" class
Insert picture description here
is created : why the code here only creates a class, and does not use the class_device_create() function to create a driver device under the class?

I will talk about it in detail below, here is a brief description: When the driver of the input subsystem is registered, there will be a driver device. At this time, the code here is not driven

(2) The above line 14 creates a drive device through register_chrdev, where the variable INPUT_MAJOR
= 13, so an "input" device with a master device of 13 is created.

Then let's take a look at its operation structure input_fops, as shown below:
Insert picture description here
There is only one .open function, for example, when we mount a new input driver, the kernel will call the .open function, and then analyze the .open function

(2) Then enter the input_open_file function (drivers/input/input.c)

static int input_open_file(struct inode *inode, struct file *file)
 {
    
    
     struct input_handler *handler = input_table[iminor(inode) >> 5]; // (1)
     const struct file_operations *old_fops, *new_fops = NULL;
     int err;

     if (!handler || !(new_fops = fops_get(handler->fops)))  //(2)
          return -ENODEV; 

    if (!new_fops->open) {
    
    
           fops_put(new_fops);
           return -ENODEV;
    }

    old_fops = file->f_op;
    file->f_op = new_fops;     //(3)

    err = new_fops->open(inode, file);   //(4)
    if (err) {
    
    
          fops_put(file->f_op);
           file->f_op = fops_get(old_fops);
   }

   fops_put(old_fops);

    return err;
}

(1) In line 3, the imino
(inode) function calls MINOR(inode->i_rdev); read the sub-device number, and then divide the sub-device by 32 to find the array number of the newly mounted input driver, and then Put it in the input_handler
driver processing function handler. The handler also contains the file_operations structure.
Insert picture description here

(2) In line 7, if the handler has a value, it means that the driver is mounted, and the member
file_operations * fops in the handler structure is assigned to the new file_operations *new_fops

(3) In line 16, assign the new file_operations *new_fops to file-> file_operations
*f_op. At this time, the file_operations of the input subsystem is equal to the file_operations structure of the newly mounted input driver, realizing an effect .

(4) In line 18, call the member.open function in *new_fops of the newly mounted input driver

(3) The input_table[] array of the above code has no value at the beginning, so let's see in which function the data in the input_table array is assigned
in the input.c function (drivers/input/input.c) Search input_table in and find that it is assigned in the input_register_handler() function. The code is as follows:

int input_register_handler(struct input_handler *handler)
{
    
    
	//将驱动处理程序input_handler 注册到input_table[]中
	input_table[handler->minor >> 5] = handler;
	//将驱动处理程序input_handler 放在input_handler_list链表中,
	list_add_tail(&handler->node, &input_handler_list);
}

(4) Continue to search input_register_handler to see who calls this function

As shown in the figure below, there are evdev.c (event device), tsdev.c (touch screen device), joydev.c (joystick joystick device), keyboard.c (keyboard device), mousedev.c (mouse device). The kernel's built-in device processing function is registered in the input subsystem.
Insert picture description here
Let's take evdev.c as an example, which is registered in the evdev_ini() function:

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

(5) Let's take a look at what structure this evdev_handler variable is:

static struct input_handler evdev_handler = {
    
    
       .event =  evdev_event,    
       .connect =      evdev_connect,  //(4)
       .disconnect = evdev_disconnect,
       .fops =           &evdev_fops, //(1)
       .minor =  EVDEV_MINOR_BASE, //(2)
       .name =         "evdev",
       .id_table =      evdev_ids, //(3)
};

(1) In line 5, .fops: file operation structure, where the evdev_fops function is the operation function written by itself, and then assigned to .fops

(2) In the 6th line.minor: used to store the minor device number, where EVDEV_MINOR_BASE=64,

Then after calling input_register_handler(&evdev_handler), because EVDEV_MINOR_BASE/32=2, it is stored in input_table[2]

So when open opens the input device, it will enter the input_open_file() function and execute the evdev_handler->
evdev_fops -> .open function, as shown in the following figure:
Insert picture description here
(3) In line 8, .id_table:
indicates which input devices can be supported, such as If the input_dev-> id of a certain drive device matches the id_table of a certain input_handler, the .connect connection function will be called, as shown below

(4) In line 3, .connect: connect function, connect the device input_dev and an input_handler, as shown below
Insert picture description here

2. System event handler layer (corresponding to the event handler layer in the figure)

1. Let's take a look at the input_register_device() function in the figure above, how to create the driver device
(1) and then enter the input_register_device() function, the code is as follows:

int input_register_device(struct input_dev *dev)   //*dev:要注册的驱动设备
{
    
    
 ... ...
       list_add_tail(&dev->node, &input_dev_list);   //(1)放入链表中
 ... ...
       list_for_each_entry(handler, &input_handler_list, node)  //(2)
       input_attach_handler(dev, handler); //(3)
 ... ...
}

(1) In line 4, put the input_dev driver device to be registered in the input_dev_list linked list
(2) In line 6, where input_handler_list is mentioned in (3), it is to store each input_handle driver processing structure,
and then list_for_each_entry( ) Function will take out each input_handle from the linked list and put it in the handler
(3) Finally, it will call the input_attach_handler() function to judge the id_table of each input_handle, and connect if the two support.

(2) Then we look back at the input_register_handler() function of registering input_handler, as shown in the figure below.
Insert picture description here
Therefore, regardless of the newly added input_dev or input_handler, it will enter input_attach_handler() to determine whether the two IDs are supported. connection.
(3) Let's take a look at how input_attach_handler() matches the two IDs:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    
    
... ...
id = input_match_device(handler->id_table, dev);  //匹配两者

if (!id)                                     //若不匹配,return退出
return -ENODEV; 

error = handler->connect(handler, dev, id);  //调用input_handler ->connect函数建立连接
... ...

}

2. We still take the evdev_handler->connect function of evdev.c (event-driven) as an example to analyze how to establish a connection, as shown in the figure below:
Insert picture description here
(1) The .connect function of evdev_handler is evdev_connect(), the code is as follows:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)     
{
    
    
//第一步:分配一个input_handle全局结构体(没有r)
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   
//第二步:
evdev->handle.dev = dev;              //指向参数input_dev驱动设备
evdev->handle.handler = handler;    //指向参数 input_handler驱动处理结构体
//第三步
error = input_register_handle(&evdev->handle); //注册这个input_handle结构体
}

Finally it will enter the input_register_handle() function to register, the code is below

(2) The input_register_handle() function is as follows:

int input_register_handle(struct input_handle *handle)
{
    
    
      struct input_handler *handler = handle->handler; //handler= input_handler驱动处理结构体 

      list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
      list_add_tail(&handle->h_node, &handler->h_list);    // (2)
 
      if (handler->start)
             handler->start(handle);
      return 0;
}

(1) In line 5, because handle->dev points to the input_dev drive device, the handle->d_node is put into the h_list linked list of the input_dev drive device, that is, the h_list linked list of the input_dev drive device points to handle->d_node

(2) In line 6, the h_list of the input_handler driver processing structure also points to handle->h_node

The final picture is shown below:
Insert picture description here

Both .h_list points to the same handle structure, and then through .h_list to find the members of the handle. Dev and handler can find each other and establish a connection

3. After the connection is established, how to read the evdev_handler->.fops->.read function of evdev.c (event-driven)?

(1) The event-driven .read function is the evdev_read() function, let’s analyze it:

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
    
    
 ... ...
/*判断应用层要读取的数据是否正确*/
if (count < evdev_event_size())
return -EINVAL;

/*在非阻塞操作情况下,若client->head == client->tail|| evdev->exist时(没有数据)||非阻塞,则return返回*/
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
 
/*若client->head == client->tail|| evdev->exist时(没有数据),等待中断进入睡眠状态  */
retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
//上传数据
  ... ...           
}

(2) If the read function enters the dormant state, who will wake it up?

We search this evdev->wait wait queue variable and find the wake-up in the evdev_event function: (For details, please refer to the previous explanation of the interrupt system)

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
    
    
 wake_up_interruptible(&evdev->wait);   //有事件触发,便唤醒等待中断
}

Among them, evdev_event() is the evdev_handler->.event member of evdev.c (event-driven), as shown in the following figure:
Insert picture description here
When an event occurs, for example, for a button driver, when a button is pressed, it will enter the .event function Handling events.

(3) Under analysis, who called evdev_event() this .event event-driven function

It should be called by the input_dev layer analyzed before. Let's
take a look at the kernel gpio_keys_isr() function code example (driver/input/keyboard/gpio_keys.c)

In the interrupt service routine of the device, determine what the event is, and then call the event processing function of the corresponding input_handler

static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
    
    
 /*获取按键值,赋到state里*/
 ... ...

/*上报事件*/
input_event(input, type, button->code, !!state);  
input_sync(input);                        //同步信号通知,表示事件发送完毕
}

Obviously, the .event event function is called through input_event(), let's take a look:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    
    
struct input_handle *handle;
... ...

/* 通过input_dev ->h_list链表找到input_handle驱动处理结构体*/
list_for_each_entry(handle, &dev->h_list, d_node)    
if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数 

}

If the driver input_dev and the processing input_handler have been connected through the .connect function of input_handler before, then the .event event function of evdev_event() is called, as shown in the following figure:
Insert picture description here

4. Summary

  1. Register the input subsystem and enter put_init():

1) Create an "input" character device with a major device number of 13

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  1. Open open the driver and enter input_open_file():
1)更新设备的file_oprations

file->f_op=fops_get(handler->fops);
 

2)执行file_oprations->open函数

err = new_fops->open(inode, file);
  1. Register input_handler, enter input_register_handler():
1)添加到input_table[]处理数组中
input_table[handler->minor >> 5] = handler;
 
2)添加到input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);

3)判断input_dev的id,是否有支持这个驱动的设备
 list_for_each_entry(dev, &input_dev_list, node)   //遍历查找input_dev_list链表里所有input_dev
 input_attach_handler(dev, handler);             //判断两者id,若两者支持便进行连接。
  1. Register input_dev, enter input_register_device():
1)放在input_dev_list链表中
list_add_tail(&dev->node, &input_dev_list);
 
2)判断input_handler的id,是否有支持这个设备的驱动
list_for_each_entry(handler, &input_handler_list, node)  //遍历查找input_handler_list链表里所有input_handler
input_attach_handler(dev, handler);                      //判断两者id,若两者支持便进行连接。
  1. Determine the id of input_handler and input_dev and enter input_attach_handler():
1)匹配两者id,
input_match_device(handler->id_table, dev);        //匹配input_handler和dev的id,不成功退出函数
 
2)匹配成功调用input_handler ->connect
handler->connect(handler, dev, id);              //建立连接
  1. To establish a connection between input_handler and input_dev, enter input_handler->connect():
1)创建全局结构体,通过input_handle结构体连接双方

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //创建两者连接的input_handle全局结构体
list_add_tail(&handle->d_node, &handle->dev->h_list); //连接input_dev->h_list
list_add_tail(&handle->h_node, &handler->h_list);    // 连接input_handle->h_list
  1. When an event occurs, such as a key interrupt, you need to enter input_event() to report the event in the interrupt function:
1)找到驱动处理结构体,然后执行input_handler->event()
list_for_each_entry(handle, &dev->h_list, d_node)     // 通过input_dev ->h_list链表找到input_handle驱动处理结构体
if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式)
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数 

Guess you like

Origin blog.csdn.net/xiaoaojianghu09/article/details/104661652