17.3 input子系统
为了对输入子系统有一个清晰的认识,本节将分析输入系统的初始化过程。在Linux中,输入子系统作为一个模块存在,向上,为用户层提供接口函数, 向下,为驱动层程序提供统一的接口函数。这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通知驱动 程序完成某项功能。
17.3.1 子系统初始化函数input_init()
输入子系统作为一个模块存在,必然有一个初始化函数。在/drivers/input/input.c文件中定义了输入子系统的初始化函数input_init(),该函数的代码如下:
02 {
03 int err;
04 err = class_register(&input_class);
05 if (err) {
06 printk(KERN_ERR "input: unable to register input_dev class\n");
07 return err;
08 }
09 err = input_proc_init();
10 if (err)
11 goto fail1;
12 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
13 if (err) {
14 printk(KERN_ERR "input: unable to register char major %d",
INPUT_MAJOR);
15 goto fail2;
16 }
17 return 0;
18 fail2: input_proc_exit();
19 fail1: class_unregister(&input_class);
20 return err;
21 }
下面对该函数进行简要的分析。
第04行,调用class_register()函数先注册了一个名为input的类。所有input device都属于这个类。在sysfs中表现就是,所有input device所代表的目录都位于/dev/class/input下面。input_class类的定义只是一个名字,代码如下:
.name = "input",
};
第09行,调用input_proc_init()在/proc下面建立相关的交互文件。
第12行,调用register_chrdev()注册了主设备号为 INPUT_MAJOR(13)。次设备号为0~255的字符设备。它的操作指针为input_fops。在这里,可以看到所有主设备号13的字符设备的 操作最终都会转入到input_fops中。例如/dev/input/event0~/ dev/input/event4的主设备号为13,对其的操作会落在input_fops中。input_fops只定义了一个 input_open_file()函数,input_fops的定义代码如下:
.owner = THIS_MODULE,
.open = input_open_file,
};
17.3.2 文件打开函数input_open_file()
文件操作指针中定义了input_open_file()函数,该函数将控制转到input_handler中定义的fops文件指针的 open()函数。该函数在input_handler中实现,这样就使不同的handler处理器对应了不同的文件打开方法,为完成不同功能提供了方 便。input_open_file()函数的代码如下:
02 {
03 struct input_handler *handler;
04 const struct file_operations *old_fops, *new_fops = NULL;
05 int err;
06 lock_kernel();
07 /* No load-on-demand here? */
08 handler = input_table[iminor(inode) >> 5];
09 if (!handler || !(new_fops = fops_get(handler->fops))) {
10 err = -ENODEV;
11 goto out;
12 }
13 if (!new_fops->open) {
14 fops_put(new_fops);
15 err = -ENODEV;
16 goto out;
17 }
18 old_fops = file->f_op;
19 file->f_op = new_fops;
20 err = new_fops->open(inode, file);
21 if (err) {
22 fops_put(file->f_op);
23 file->f_op = fops_get(old_fops);
24 }
25 fops_put(old_fops);
26 out:
27 unlock_kernel();
28 return err;
29 }
下面对该函数进行简要的分析。
第03~05行,声明一些局部变量,供下面的操作使用。
第08行,出现了熟悉的input_table[]数组。iminor(inode)为打开文件所对应的次设备号。input_table是一个struct input_handler全局数组,只有8个元素,其定义为:
static struct input_handler *input_table[8];
在这里,首先将设备结点的次设备号右移5位做为索引值到input_table中取对应项,从这里也可以看到,一个handler代表 32(1<<5)个设备结点,也就是一个handler最多可以处理32个设备结点。因为在input_table中取值是以次备号右移5位 为索引的,即第5位相同的次备号对应的是同一个索引。回忆input_register_handler()函数的第14行 input_table[handler->minor >> 5] = handler,其将handler赋给了input_table数组,所使用的规则也是右移5位。
第09~12行,在input_table中找到对应的handler之后,就会检验这个handler是否存在,如果没有,则返回一个设备不存在 的错误。在存在的情况下,从handler->fops中获得新的文件操作指针file_operation,并增加引用计数。此后,对设备的操作 都通过新的文件操作指针new_fops来完成。
第13~17行,判断new_fops->open()函数是否定义,如果没有定义,则表示设备不存在。
第20行,使用新的open()函数,重新打开设备。