参考文章: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