输入子系统分析

参考文章:https://www.cnblogs.com/lifexy/p/7542989.html

输入子系统本身也是个驱动,在input_init入口函数可以看到他注册了input类,并且注册了input这个字符设备:

输入子系统分析

也就是说输入子系统作为编译进内核的一部分,在启动内核的时候就被加载了,input_init函数会被调用,生成input类和input设备,为后续注册进输入子系统的设备做好准备。

可以看到注册input设备的时候,一起注册了一个input_fops:

输入子系统分析

 input_fops定义如下:

输入子系统分析

 里面只注册了一个.open函数。

之前自己定义fops的时候,会自己注册open、read、write函数,当应用程序调用open函数时,内核就会调用驱动中相应的open函数。

这个input_open_file函数应该也是同理,当我们使用输入子系统时(在驱动程序中向输入子系统注册了设备),当应用程序调用open函数时,内核会调用input_open_file函数:

输入子系统分析

 input_open_file函数从input_table[]中根据次设备号取出一项,赋值给handler:
输入子系统分析

再通过handler找到他的成员fops复制给new_fops:

输入子系统分析

 最终执行这个new_fops的open函数:

输入子系统分析

 这里的关键点在于,要从input_table[]中找到对应的handler。

在input.c里查找代码可以发现,input_table[]在input_register_handler中被赋值:

输入子系统分析

 那么什么时候会调用input_register_handler函数,给input_table赋值?在内核文件中搜索可以发现:

输入子系统分析

 输入子系统分析

 输入子系统分析

他们都是在驱动程序的入口被注册的。

猜想:和input子系统类似,evdev、joydev、tsdev等驱动程序在源码阶段就已经编译进内核,因此内核启动时会像input那样注册设备,执行各自的init程序,从而填充input_table[]数组,这部分代码不需要自己实现。

输入子系统的框架如下图:

输入子系统分析

 之前介绍的是内核自带的驱动处理相关的代码,要让这部分代码执行,还需要和具体的设备“配对”,这样当具体的设备产生事件时,才能执行相应的驱动处理函数。

举个例子,看看evdev_handler的内容,evdev_handler是input_handler类型的结构体,属于上图右边驱动处理的一种:

输入子系统分析

假设内核已经启动,上述各种内核自带的驱动程序已经被加载,也就是说input_table[]数组中已经填充了各种handler,包括evdev_handler。这时装载自己写的模块,向输入子系统注册input_dev。

对输入子系统来说,如果有设备(input_dev)注册(设备注册由我们自己实现),就会根据input_handler的.id_table判断这个handler是否支持这个dev,如果支持的话就执行.connect函数将设备input_dev和某个input_handler建立连接。

这个将input_dev和input_handler匹配的过程可以通过比对他们各自的注册函数知道细节。

对于input_dev来说(input_register_device函数):

输入子系统分析

 从input_handler_list这个链表中取出每一项input_handler结构体赋值给handler这个局部变量,再通过input_attach_handler函数判断handler和dev能否匹配。

对于input_handler来说(input_register_handler函数):

输入子系统分析

 从input_dev_list这个链表中取出每一项input_dev结构体赋值给dev这个局部变量,再通过input_attach_handler函数判断handler和dev能否匹配。

可以看出,不管是向子系统注册input_dev还是input_handler,都会从对面的链表中取出每一项,来判断能否支持自己。

看一下input_attach_handler函数的具体实现:

输入子系统分析

可以看到先调用了input_match_device函数,如果匹配,再调用handler中的.connect成员进行连接,和之前说的相符。

再看看.connnect是如何连接双方的:

 1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
 2 {
 3   ... ...
 4   for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驱动设备的子设备号
 5     if (minor == EVDEV_MINORS) {  // EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
 6         printk(KERN_ERR "evdev: no more free evdev devices\n");
 7         return -ENFILE;                //没找到驱动设备
 8     }
 9    ... ...
10    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一个input_handle,包含在evdev结构体里
11    ... ...
12   evdev->handle.dev = dev;              //指向input_dev
13   evdev->handle.name = evdev->name;
14   evdev->handle.handler = handler;    //指向input_handler
15   evdev->handle.private = evdev;
16   sprintf(evdev->name, "event%d", minor);    //保存驱动设备名字, event%d
17   ... ...
18   devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  //将主设备号和次设备号转换成dev_t类型
19   cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); 
//在input类下创建驱动设备(在配对成功后才会创建设备驱动,合理) 20 21   ... ... 22   error = input_register_handle(&evdev->handle); //注册这个input_handle结构体 23 24   ... ... 25 }

再看看input_register_handle是怎么注册的:

输入子系统分析

输入子系统分析

把handle->d_node(对应dev)添加到input_dev的h_list的末尾

把handle->h_node(对应handler)添加到input_dev的h_list的末尾

猜测:某一设备不一定只对应一个驱动程序,某个驱动程序也不一定只支持一个设备,所以这里用链表。

这样input_dev可以通过input_handle找到input_handler,input_handler也能通过input_handle找到input_dev,由此建立了连接。

参考文章:https://www.cnblogs.com/lifexy/p/7542989.html

输入子系统分析

上一篇:Solution -「多校联训」取石子游戏


下一篇:亚马逊店铺代运营结合跟卖软件,跟卖ERP系统,只跟卖不铺货