板牙  
失败是什么?没有什么,只是更走近成功一步;而成功是走过了所有通向失败的路...只要你心够决!

17.3  input子系统

为了对输入子系统有一个清晰的认识,本节将分析输入系统的初始化过程。在Linux中,输入子系统作为一个模块存在,向上,为用户层提供接口函数, 向下,为驱动层程序提供统一的接口函数。这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通知驱动 程序完成某项功能。

17.3.1  子系统初始化函数input_init()

输入子系统作为一个模块存在,必然有一个初始化函数。在/drivers/input/input.c文件中定义了输入子系统的初始化函数input_init(),该函数的代码如下:

    01  static int __init input_init(void)  
    
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类的定义只是一个名字,代码如下:

    struct class 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的定义代码如下:

static const struct file_operations 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()函数的代码如下:

    01  static int input_open_file(struct inode *inode, struct file *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()函数,重新打开设备。


posted on 2011-09-12 23:11  板牙  阅读(298)  评论(0编辑  收藏  举报