作者:Bright-Ho
联系方式:836665637@qq.com
input输入子系统框架分析(纯软件方面):
上一节,我们简单的描述的什么是输入子系统;什么是字符设备;以及其作用;重点是我们讲到分析输入子系统必须结合硬件设备来分析;那么这一节,我们主要讲解输入子系统的软件框架;接下来,我们就进入主题;
那么在进入主题之前,我们先来了解一个简单的字符设备驱动程序的框架;
(1)构造一个file_operations结构体;里面实现了read,write,open等函数,用于应用层调用;也就是给应用层提供接口;
(2)通过 register_chrdev()函数来注册驱动程序;所谓注册,就是以主设备号为下标,把file_operations结构体放入一个内核数组;
(3)通过udev机制,自动创建设备节点,通过下面两个函数来实现;
class_create(THIS_MODULE,"firstdrv");先创建一个类firstdrv,会在sys/class下生成;
class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");在类下面创建一个xyz设备;
(4)通过void first_drv_exit(void)函数,对所有资源进行卸载;
(5)修饰驱动程序的入口函数;
-
module_init(first_drv_init); /*加载驱动(insmod)的时候,会执行该宏*/
-
module_exit(first_drv_exit); /*卸载驱动(rmmod)的时候,会执行该宏*/
注意,内核版本不一样, class_create() class_device_create()这两函数也不一样,但实际效果是一样的;
这上面5个步骤就是一个简单的字符设备框架,不涉及字符设备的高级技巧;也不对应任何具体设备;就单单是一个简单字符设备的框架;
上面这5部需要理解两个问题:
-
从应用层的角度来看,应用层调用read,write,open等函数,是如何调用到驱动程序里面的open,read,write等函数的?
-
就是所谓udev机制,需要知道怎么使用内核提供的函数来自动创建设备节点;
上面这种字符设备驱动框架有一个很大缺点:它提供给应用程序的接口,也就是设备节点,只有自己清楚,或者只有自己公司的人知道它所提供的接口,别人不知到!所以它不是一个通用的设备驱动程序;这是引入udev机制的原因;
接下来,我们就要真正的讲解输入子系统了;
我一直认为内核就是最好的老师,所以学习输入子系统,就得去分析内核源代码;我采用的内核版本为2.6.22.6的版本,该版本有点老,有些东西和新版本内核不一样,但是对于学习来说没什么太大影响;如果对新老版本都了解的话,那就更好了;那么,在学习输入子系统之前,需要牢记一个问题,加入输入子系统的字符设备驱动的步骤和上面没有加入输入子系统的字符设备框架的5个步骤有什么区别?这个问题,只有分析了内核自带的输入子系统源码,你才能知道其中的区别;弄清楚了这个问题之后,你应该清楚,哪些事情是由内核帮我们实现的,哪些事情是需要我们自己完成的;
input输入子系统的内核源码位于:linux-2.6.22.6/drivers/input下;
首先明确一点,输入子系统分为三层,核心层,事件处理层,设备硬件层;
核心层(input.c):分析主要代码!!!
(1)入口函数input_init()
static int __init input_init(void)/*入口*/
{
int err;
err = class_register(&input_class); /*sys/class/input 创建设备类*/
err = input_proc_init();/* proc文件系统相关的初始化*/
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); /*注册字符设备,以majoy 13为索引,存放于把内核数组*/
...
}
input_fops结构体如下:
1279 static const struct file_operations input_fops = {
1280 .owner = THIS_MODULE,
1281 .open = input_open_file,
1282 };
<解析> 在入口函数中:
1. 在sys/class目录下,创建设备类,用于生成设备节点;
class_register(&input_class);
2. 注册字符设备驱动,以主设备号为下标,把input_fops 放入内核数组中,方便应用层通过主设备号索引;
register_chrdev(INPUT_MAJOR, "input", &input_fops);
注意:该函数执行后会在proc/devices里面生成设备信息;
问题: 我们知道应用程序调用open,read,write函数来操作设备,最终会调用到驱动程序里面所提供的open_drv,read_drv,write_drv函数,但是input_fops结构里面只提供了open函数,那么这是怎么回事呢?所以接下来,继续分析input_open_file函数;
(2)input_open_file函数分析:
static int input_open_file(struct inode *inode, struct file *file)
1245 {
1246 /*(1)从input_table数组中以次设备号为下标,取出一项给handler*/
1247 struct input_handler *handler = input_table[iminor(inode) >> 5];
1248 const struct file_operations *old_fops, *new_fops = NULL;
1252 /*(2)把handler里面的fops赋给new_fops*/
1253 if (!handler || !(new_fops = fops_get(handler->fops)))
1264 /*(3)把new_fops赋给file_f_op,实际上也就是handler里面的fops;*/
1265 old_fops = file->f_op;
1266 file->f_op = new_fops;
1267
1268 /*(4)调用new_fops里面的open函数*/
1269 err = new_fops->open(inode, file);
...
}
<解析>在分析input_open_file函数之前,有一个非常重要的结构体需要了解!!!
struct input_handler *handler
1065 struct input_handler {
1066
1067 void *private;
1069 void (*event)(struct input_handle *handle, unsign