主题 : 【分享】对输入子系统分析总结 复制链接 | 浏览器收藏 | 打印
畅游在知识的海洋...
级别: 论坛版主
UID: 33629
精华: 4
发帖: 554
金钱: 3075 两
威望: 615 点
贡献值: 5 点
综合积分: 1188 分
注册时间: 2010-12-03
最后登录: 2015-09-22
楼主  发表于: 2011-11-15 21:51

 【分享】对输入子系统分析总结

管理提醒: 本帖被 xoom 执行置顶操作(2012-03-14)
图片:
这两天学习了看了韦东山的第二期视频关于输入子系统部分,做了相关记录,分享给大家
在drivers/input/input.c中:
    进入模块入口函数input_init :
    
复制代码
  1.     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

    而input_fops只有open和llseek函数:
    
复制代码
  1.  static const struct file_operations input_fops = {
  2.     .owner = THIS_MODULE,
  3.     .open = input_open_file,
  4.     .llseek = noop_llseek,
  5.     };
 
    那么没有read函数的话该怎么读数据呢?
    进入 input_open_file函数:
        
复制代码
  1.   struct input_handler *handler;//定义一个input_handler指针
  2.         handler = input_table[iminor(inode) >> 5];//根据传入文件的次设备号从
  3.         //input_table中提取出一个input_handler
  4.         if (handler)
  5.         new_fops = fops_get(handler->fops);//从input_handler中提取出new_fops
  6.         file->f_op = new_fops;//将new_fops赋值给当前文件的file_operations
  7.         err = new_fops->open(inode, file);
 
  经过这些操作后,当app再来调用read,write,open等函数时,最终都会调用到file->f_op 中的read,write,open等函数。    
    那么input_open_file函数中的input_table 又是从何而来的呢?
    搜索代码可知,
    在input.c的input_register_handler函数中构造了input_table:
       
复制代码
  1.   if (handler->fops != NULL) {
  2.             if (input_table[handler->minor >> 5]) {
  3.                 retval = -EBUSY;
  4.                 goto out;
  5.             }
  6.             //给input_table中的 第handler->minor >> 5项赋值
  7.             input_table[handler->minor >> 5] = handler;
  8.         }
 
    又是谁在调用input_register_handler呢?
    搜索内核可知:
    evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件调用了    input_register_handler,我们以evdev.c为例
    在drivers/input/evdev.c中:
    进入模块入口函数evdev_init:
    
复制代码
  1.      return input_register_handler(&evdev_handler);
  2.     evdev_handler的定义如下:
  3.         static struct input_handler evdev_handler = {
  4.         .event        = evdev_event,
  5.         .connect    = evdev_connect,
  6.         .disconnect    = evdev_disconnect,
  7.         .fops        = &evdev_fops,//前面所用到的 new_fops指的就是这里定义的的fops
  8.         .minor        = EVDEV_MINOR_BASE,
  9.         .name        = "evdev",
  10.         .id_table    = evdev_ids,
  11.         };
 
    这里需要插入的讲解一下输入子系统的架构!如图:

    当通过input_register_device注册一个input_dev设备或者通过    input_register_handler注册一个input_handler时,input_dev与input_handler    会进行匹配,如何匹配?
    在input_handler中有一个id_table,里面包含的就是这个input_handler能处    理的input_dev,当input_handler与input_dev匹配成功的话,则会调用    input_handler里    的connect函数。
    
    下面我们来看下input_register_device和input_register_handler分别做了什么:
  
复制代码
  1.    input_register_device:
  2.         //将刚注册的input_dev放入input_dev_list链表
  3.         list_add_tail(&dev->node, &input_dev_list);
  4.         
  5.         /*对 input_handler_list中的每一个input_handler都调用                       
  6.         *input_attach_handler(dev, handler);这个函数,来查看是否有input_handler        *合适该input_dev
  7.         */    
  8.         list_for_each_entry(handler, &input_handler_list, node)
  9.         input_attach_handler(dev, handler);
  10.         
  11.     input_register_handler:
  12.         //将刚注册的input_handler放入input_table中
  13.         input_table[handler->minor >> 5] = handler;
  14.     
  15.         //将刚注册的input_handler放入 input_handler_list链表中
  16.         list_add_tail(&handler->node, &input_handler_list);
  17.         /*对 input_dev_list 中的每一个input_dev都调用                               
  18.         *input_attach_handler(dev, handler);这个函数,来查看是否有input_dev合        *适该input_handler
  19.         */
  20.         list_for_each_entry(dev, &input_dev_list, node)
  21.         input_attach_handler(dev, handler);
 
    由此可以看出,无论是先注册input_handler还是先注册input_dev最终    都    会调用来input_attach_handler(dev, handler);来进行两两匹配。现在我们来看看    input_attach_handler是如何将input_handler和input_dev进行匹配的。
    在input_attach_handler函数中:
        /*先进行匹配,匹配的依据就是input_handler中的id_table与
        *input_dev中的id里的信息是否相同
        */
        id = input_match_device(handler, dev);
    
        /*再调用input_handler中的connect函数完成连接,具体如何连接,
        *需要分析connect函数
        */
        error = handler->connect(handler, dev, id);
    
    我们以evdev.c中的 input_handler结构中的connect函数为例,分析connect    函数做了些什么。
    在evdev_connect函数中:
复制代码
  1.  
  2.         /*分配一个evdev结构体,该结构体中含一个input_handle结构
  3.         *注意:不同于input_handler结构
  4.         */
  5.         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  6.         
  7.         /*设置evdev结构中的input_handle
  8.         *@input_handle的dev变量指向input_dev结构
  9.         *@input_handle的handler变量指向input_handler结构
  10.         */
  11.         evdev->handle.dev = input_get_device(dev);
  12.         evdev->handle.name = dev_name(&evdev->dev);
  13.         evdev->handle.handler = handler;
  14.         evdev->handle.private = evdev;
  15.       
  16.         /*向内核注册input_handle*/
  17.         error = input_register_handle(&evdev->handle);
  18.      
    
        下面我们来看注册 input_register_handle做了些什么:
       
复制代码
  1.       /*将input_handle添加到input_dev中的h_list链表中
  2.             *以后当input_dev需要使用对应的input_handler时就可以通过自身的            *h_list链表找到input_handle,从而找到匹配的input_handler。
  3.             */
  4.             list_add_tail_rcu(&handle->d_node, &dev->h_list);
  5.             
  6.             /*将input_handle添加到input_handler中的h_list链表中,
  7.             *以后当input_handler需要使用对应的input_dev时就可以通过自身的
  8.             *h_list链表找到input_handle,从而找到匹配的input_dev
  9.             */
  10.             list_add_tail_rcu(&handle->h_node, &handler->h_list);
 
    对于输入设备,我们知道一般需要读取其数据,那么经过上述的一些列动作    后又是如何读取到数据的呢?我们来看下evdev.c中的input_handler中的fops
    中的read函数:
    在evdev_read函数中:
复制代码
  1.         //如果没有数据且nonblock的话,则EAGAIN
  2.        if (client->head == client->tail && evdev->exist &&
  3.          (file->f_flags & O_NONBLOCK))
  4.         return -EAGAIN;
  5.         //否则,睡眠
  6.         retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !        evdev->exist);
 
    既然有睡眠,那么何时被唤醒,搜索代码。
    在evdev_event函数中:
        //唤醒
        wake_up_interruptible(&evdev->wait);
    evdev_event是input_handler中的成员,当有数据可读(如触摸屏被按下,按键    被按下)时,event函数会被调用。而event函数是怎么被调用到的?这就得看设    备层了,设备层的驱动做了如下工作:
    <1>在中断函数中确定判断发生了什么事件,并且相应的值是多少
    <2>通过input_event()函数上报事件
    <3>通过input_sync()函数表明上报结束
    分析input_event函数我们就可以知道input_handler中的event函数是如何被调用到的了。
    在input_event中,调用了input_handle_event函数,在input_handle_event函数    中调用了input_pass_event函数;
    在input_pass_event函数中:
复制代码
  1.  
  2.         struct input_handler *handler;
  3.         /*从注册的input_dev的h_list中将input_handle一个个拿出来*/
  4.         list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
  5.             if (!handle->open)
  6.                 continue;
  7.             //如果该input_handle被打开,则该input_handle->input_handler即为可            //处理该input_dev的handler
  8.             handler = handle->handler;
  9.             if (!handler->filter) {
  10.                 if (filtered)
  11.                     break;
  12.                 /*最终调用到event函数*/
  13.                 handler->event(handle, type, code, value);
  14.             } else if (handler->filter(handle, type, code, value))
  15.                 filtered = true;
  16.         }
 
最后,回归到如何写符合输入子系统框架的驱动程序,四个步骤:
<1>分配一个input_dev结构体
<2>设置input_dev
<3>    注册input_dev
<4>在中断函数中上报事件    
         输入子系统记录完毕
    估计看到这的人已经疯了
        

转载请注明来至wuweidong:http://www.100ask.net/showtopic-3567.aspx
[ 此帖被wuweidong在2012-01-28 10:37重新编辑 ]
好好学习,天天鲁管
级别: 侠客
UID: 54139
精华: 0
发帖: 58
金钱: 340 两
威望: 69 点
贡献值: 0 点
综合积分: 116 分
注册时间: 2011-08-24
最后登录: 2015-01-21
1楼  发表于: 2012-03-09 21:28
试看了韦东山老师的视频,的确是比其他视频好N倍啊,想组织个团购视频,这样优惠些,有想法的学友一起参加啊
QQ群: 218530234


第1期下载地址:
http://dl.dbank.com/c03o1ebwlo
第2期下载地址:
http://dl.dbank.com/c06bbt0sxp
第1期视频目录:
第1课 环境搭建及工具、概念介绍.avi
第2课 GPIO实验.avi
第3课 存储管理器实验.avi
第4课 MMU实验.avi
第5课 NAND FLASH控制器.avi
第6课 中断控制器.avi
第7课 系统时钟和UART实验.avi
第8课 LCD实验.avi
第9课第1节 u-boot分析之编译体验.avi
第9课第2节 u-boot分析之Makefile结构分析.avi
第9课第3节 u-boot分析之源码第1阶段.avi
第9课第3节 u-boot分析之源码第2阶段.avi
第9课第4节 u-boot分析之u-boot命令实现.avi
第9课第5节 u-boot分析_uboot启动内核.avi
第10课第1节 内核启动流程分析之编译体验.avi
第10课第2节 内核启动流程分析之配置.avi
第10课第3节 内核启动流程分析之Makefile.avi
第10课第4节 内核启动流程分析之内核启动.avi
第11课第1节 构建根文件系统之启动第1个程序.avi
第11课第2节 构建根文件系统之init进程分析.avi
第11课第3节 构建根文件系统之busybox.avi
第11课第4节 构建根文件系统之构建根文件系统.avi
第12课第1节 字符设备驱动程序之概念介绍.avi
第12课第2.1节 字符设备驱动程序之LED驱动程序_编写编译.avi
第12课第2.2节 字符设备驱动程序之LED驱动程序_测试改进.avi
第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED.avi
第12课第3节 字符设备驱动程序之查询方式的按键驱动程序.avi
第12课第4.1节 字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构.avi
第12课第4.2节 字符设备驱动程序之中断方式的按键驱动_Linux中断处理结构.avi
第12课第4.3节 字符设备驱动程序之中断方式的按键驱动_编写代码.avi
第12课第5节 字符设备驱动程序之poll机制.avi
第12课第6节 字符设备驱动程序之异步通知.avi
第12课第7节 字符设备驱动程序之同步互斥阻塞.avi


第2期视频目录:
第12课第8节 字符设备驱动程序之定时器防抖动_P.wmv
第13课第1节 输入子系统概念介绍_P.wmv
第13课第2节 输入子系统第编写驱动程序_P.wmv
第14课 驱动程序分层分离概念_总线驱动设备模型_P.wmv
第15课第1节 LCD驱动程序之层次分析_P.wmv
第15课第2节 LCD驱动程序之硬件操作_P.wmv
第15课第3节 LCD驱动程序之编写代码之1初步编写_P.wmv
第15课第3节 LCD驱动程序之编写代码之2硬件设置_P.WMV
第15课第3节 LCD驱动程序之编写代码之3显存和调色板设置_P.WMV
第15课第4节 LCD驱动程序之编译测试_P.WMV
第16课第1节 触摸屏驱动程序之概念介绍_P.wmv
第16课第2节 触摸屏驱动程序之编写驱动_P.wmv
第16课第3节 触摸屏驱动程序之使用TSLIB测试_P.wmv
第17课第1节 USB驱动程序之概念介绍_P.wmv
第17课第2节 USB驱动程序之USB总线驱动程序_P.wmv
第17课第3节 USB驱动程序之USB设备驱动程序1简单编写_P.wmv
第17课第3节 USB驱动程序之USB设备驱动程序2鼠标用作键盘_P.WMV
第18课第1节 块设备驱动程序的引入_P.wmv
第18课第2节 块设备驱动程序的框架_P.wmv
第18课第3节 块设备驱动程序的编写驱动之用内存模拟磁盘_P.WMV
第19课第1节 NAND FLASH原理及硬件操作_P.WMV
第19课第2节 NAND FLASH驱动程序框架_P.WMV
第19课第3节 NAND FLASH驱动程序之编写代码之1简单编写_P.WMV
第19课第3节 NAND FLASH驱动程序之编写代码之2完善识别过程_P.WMV
第19课第3节 NAND FLASH驱动程序之编写代码之3添加MTD设备及测试_P.WMV
第20课第1节 NOR FLASH原理及硬件操作_P.WMV
第20课第2节 NOR FLASH驱动程序之框架_P.WMV
第20课第3节 NOR FLASH驱动程序之编写代码_P.WMV
第21课第1节 网卡驱动程序之框架_P.WMV
第21课第2节 网卡驱动程序之编写虚拟网卡_P.WMV
第22课第1节 移植DM9000C驱动程序之确定相异性_P.WMV
第22课第2节 移植DM9000C驱动程序之设置时序_P.WMV
第22课第3节 移植DM9000C驱动程序之测试及内存控制器简介_P.WMV
第23课第1节 I2C设备裸板程序之I2C总线介绍_P.WMV
第23课第2节 I2C设备裸板程序之编写代码_P.WMV
第23课第3节 I2C设备裸板程序之测试_P.WMV
第24课第1节 I2C驱动程序之框架_P.WMV
第24课第2节 I2C驱动程序之编写代码_P.WMV
第25课第1节 字符设备驱动程序概念纠正之另一种写法_P.WMV
第25课第2节 字符设备驱动程序概念纠正之RTC驱动分析_P.WMV
第26课第1节 声音采集播放及IIS接口介绍_P.WMV
第26课第2节 UDA1341芯片之L3接口介绍_P.WMV
第26课第3节 UDA1341声卡驱动程序分析及测试_P.WMV
第26课第4节 WM9876声卡驱动程序编写及测试_P.WMV
第26课第5节 使用madplay播放mp3文件_P.WMV

别说我没提醒啊,第1期下载即可观看,第2期是需要密码的,我就是想组团买第二期视频.
[ 此帖被hk2011在2012-03-12 07:47重新编辑 ]
级别: 侠客
UID: 54139
精华: 0
发帖: 58
金钱: 340 两
威望: 69 点
贡献值: 0 点
综合积分: 116 分
注册时间: 2011-08-24
最后登录: 2015-01-21
2楼  发表于: 2012-03-10 19:50