免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 7097 | 回复: 20

[原]input core分析 [复制链接]

论坛徽章:
0
发表于 2011-04-08 21:42 |显示全部楼层
这两天做的点笔记,share一下,理解得不对的地方请指正,共同进步。

drivers/input/input.c
就是所谓的input的核心程序。
分析这个文件,先从input_init开始。

  1. 1:  static int __init input_init(void)
  2.    2:  {
  3.    3:  err = class_register(&input_class);
  4.    4:  err = input_proc_init();
  5.    5:  err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  6.    6:  }
复制代码
__init 把它放到了init.text段,初始化的时候会调用。函数去掉检查的情况,也就这么几行。class_register注册了一个device model里的class类型。具体会在__class_register里面调到
   1:  /*对class的私有数据进行了初始化工作*/
   2:  /*cp是一个class_private类型,class的私有类型*/   
   3:  cp = kzalloc(sizeof(*cp), GFP_KERNEL);
   4:  /*初始化了klist的get/put函数,实际调用了get_device和put_device*/
   5:  klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
   6:  INIT_LIST_HEAD(&cp->class_interfaces);
   7:  kset_init(&cp->class_dirs);
   8:  __mutex_init(&cp->class_mutex, "struct class mutex", key);
   9:  error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
  10:      
  11:  /* set the default /sys/dev directory for devices of this class */
  12:  if (!cls->dev_kobj)
  13:      cls->dev_kobj = sysfs_dev_char_kobj;
  14:  /*注册kset,里面要先初始化kset,在这里获取kobject,就建立了目录了*/
  15:  error = kset_register(&cp->class_subsys);
  16:  /*这里建立attribute文件,遍历它的attribute,熟悉device model的话就知道attribute其实就对应着文件*/
  17:  error = add_class_attrs(class_get(cls));
  18:   
再回来到input_init中,下一个input_proc_init,这个函数主要建立input字系统在proc文件系统中的目录,文件,注册相应的fops.
这个很简单:
   1:  /*在proc下建立了input的目录*/   
   2:  proc_bus_input_dir = proc_mkdir("bus/input", NULL);
   3:  /*在bus/input下建立两个文件devices和handlers,并注册了它们的fops,这两个fops是在input.c中定义的,fops的函数也是在input.c中定义.*/
   4:  entry = proc_create("devices", 0, proc_bus_input_dir,
   5:              &input_devices_fileops);
   6:  entry = proc_create("handlers", 0, proc_bus_input_dir,
   7:              &input_handlers_fileops);
   8:      
下面一个register_chardev注册了一个主设备号为INPUT_MAJOR,次设备号从0-256,文件操作符为input_fops的字符设备.
看完了input_init,再看看input.c中,后半部分就是fops的函数,前面是一些接口函数,中间是
handle和handler函数.

说到了input_fops,下面接上:
input_fops:

   1:    /*打开操作*/
   2:      input_open_file
   3:      /*不做操作*/
   4:      noop_llseek
input_open_file:
  
   1:   /*从inode获取minor,获得handler,取得handler的fops,并执行它的open操作*/
   2:      /*根据inode获取次设备号,再由次设备号算出它在input_table中的位置*/
   3:      handler = input_table[iminor(inode) >> 5];
   4:      if (handler)
   5:          new_fops = fops_get(handler->fops);
   6:   
   7:      old_fops = file->f_op;
   8:      file->f_op = new_fops;
   9:   
  10:      err = new_fops->open(inode, file);
  11:   
  12:      fops_put(old_fops);
  13:   
这里用到了handler,看handler是怎么注册的:
input_register_handler:
   
   1:  /*初始化handler的h_list*/
   2:      INIT_LIST_HEAD(&handler->h_list);
   3:      /*根据handler的minor将handler放到相应的input_table位置中*/
   4:      if (handler->fops != NULL) {
   5:          if (input_table[handler->minor >> 5]) {
   6:              retval = -EBUSY;
   7:              goto out;
   8:          }
   9:          input_table[handler->minor >> 5] = handler;
  10:      }
  11:      /*将handler通过node连接到input_handler_list链表中*/
  12:      list_add_tail(&handler->node, &input_handler_list);
  13:      /*遍历input_dev_list链表,找出与这个handler匹配的input_dev
  14:       *并和它connect,匹配和connect的操作就是input_attach_handler
  15:       *所做的事情
  16:       */
  17:      list_for_each_entry(dev, &input_dev_list, node)
  18:          input_attach_handler(dev, handler);
  19:      /*唤醒input_devices_poll_wait的等待队列*/
  20:      input_wakeup_procfs_readers();
这里说到注册handler的时候要遍历input_dev链表,那么input_dev是在哪里注册的呢?
这里当然必须提到input_dev的注册函数input_register_device,在用它注册input_dev
之前必须要分配一个input_dev并且设置它能够做的事情,这里要用input_allocate_device来分配,
用__set_bit,input_set_capability,input_set_abs_params等来设置input_dev的支持的事件evbit,
以及将所支持事件的bit数组中支持的值置相应位。这都是注册具体设备时根据设备支持的事件进行
设置了。设置完这些东西就可以注册这个input_dev了:

input_register_device:
  
   1:   /* 对每个输入设备都设置EV_SYN位 */
   2:      __set_bit(EV_SYN, dev->evbit);
   3:   
   4:      /* 清除保留位 */
   5:      __clear_bit(KEY_RESERVED, dev->keybit);
   6:   
   7:      /* 注册设备时没有提到evbit位,都把它们清除掉 */
   8:      input_cleanse_bitmasks(dev);
   9:   
  10:      /*如果没有设置get_keycode[_new]/set_keycode[_new],
  11:       *则使用input.c中定义的默认方法*/
  12:      if (!dev->getkeycode && !dev->getkeycode_new)
  13:          dev->getkeycode_new = input_default_getkeycode;
  14:   
  15:      if (!dev->setkeycode && !dev->setkeycode_new)
  16:          dev->setkeycode_new = input_default_setkeycode;
  17:   
  18:      dev_set_name(&dev->dev, "input%ld",
  19:               (unsigned long) atomic_inc_return(&input_no) - 1);
  20:      /*增加设备,这里会干很多设备core层的事情*/
  21:      error = device_add(&dev->dev);
  22:   
  23:      /*这里跟注册handler的时候相似,把input_dev加到input_dev_list
  24:       *链表中,然后遍历handler的链表,找到与这个input_dev匹配的handler
  25:       *再把它们connect*/
  26:      list_add_tail(&dev->node, &input_dev_list);
  27:   
  28:      list_for_each_entry(handler, &input_handler_list, node)
  29:          input_attach_handler(dev, handler);
  30:      /*唤醒等待队列*/
  31:      input_wakeup_procfs_readers();
  32:   
在input_allocate_device分配input_dev的时候,给它的设备type设置为
input_dev_type,文件中一大部分代码都是为了完成这个device_type的
成员。最重要的就是input_dev_attr_groups。
在proc_init的时候两个文件handlers,devices,给它们注册的文件操作符分别是
input_handlers_fileops,input_devices_fileops,文件中也有一大部分代码
是为了完成这两个文件操作符的实现。
余下的就是input提供的接口了,就不细说了,用到的时候看看就OK,主要是要对框架了解清楚。
====================================================
我们在写驱动的时候常常会用到input_event,input_sync等,这些都是通过事件层来做的,这就需要了解evdev.c,
看看这个事件设备是怎么搞的。
evdev.c
先看最下面evdev_init:
  
   1:   input_register_handler(&evdev_handler)
注册了一个input_handler:evdev_handler。
   1:  static struct input_handler evdev_handler = {
   2:      .event        = evdev_event,
   3:      .connect    = evdev_connect,
   4:      .disconnect    = evdev_disconnect,
   5:      .fops        = &evdev_fops,
   6:      .minor        = EVDEV_MINOR_BASE,
   7:      .name        = "evdev",
   8:      .id_table    = evdev_ids,
   9:  };
name不用多说,EVDEV_MINOR_BASE是64,按照minor >> 5的算法,它应该把这个handler指针放到
input_table[1]中,evdev_ids是input_device_id数组,在input_match_device,也就是input_attach_handler
中调用的用来匹配input_dev和handler的函数中用来进行匹配,根据id->flags还有handler里的那些*bit和dev->id
的相应成员进行匹配,这里evdev_ids中,只设置了driver_info,根据匹配函数,它是跟所有的device都匹配的,
It's a bitch.......
整个evdev.c实际上就是实现这个input_handler的各个具体操作函数。
evdev_connect:
    对evdev进行初始化,从evdev_table中找出第一个没有被用过的minor,做为它的minor
    初始化它的handle成员的dev和handler,初始化它的dev成员。
    然后调用input_register_handle,注册handle,它把handle的d_node加到input_dev的
    h_list链表,把h_node加到handler的h_list链表,这样就把这个handle同时跟input_dev和
    handler关联起来。evdev_install_chrdev把初始化好的这个evdev加到evdev_table中,最后
    调用device_add增加这个evdev设备。connect大功告成。
evdev_disconnect:
    不用解释。反向操作。
evdev_event:
    把input事件发给所有的client.
了解了框架再去看具体操作的核心代码就不会迷糊了。
冷月X<wanlong.gao@gmail.com>
2011.4.8

评分

参与人数 1可用积分 +10 收起 理由
Godbach + 10 感谢分享

查看全部评分

论坛徽章:
0
发表于 2011-08-06 00:26 |显示全部楼层
目有人顶

论坛徽章:
0
发表于 2011-08-06 00:31 |显示全部楼层
看到最近有童鞋问关于input 子系统的问题,so.......

论坛徽章:
0
发表于 2011-08-06 04:39 |显示全部楼层
顶下。有没有几句话的总结让我这样的懒人快速阅读啊?:)

论坛徽章:
5
摩羯座
日期:2014-07-22 09:03:552015元宵节徽章
日期:2015-03-06 15:50:392015亚冠之大阪钢巴
日期:2015-06-12 16:01:352015年中国系统架构师大会
日期:2015-06-29 16:11:2815-16赛季CBA联赛之四川
日期:2018-12-17 14:10:21
发表于 2011-08-06 08:25 |显示全部楼层
input_sync最后也是使用的input_event,只不过用SYNC的时间来做参数传递的
input_event

楼主,需要补充说明一下触摸屏的话,多点触摸怎么处理的,这个在input_event里面有实现

论坛徽章:
0
发表于 2011-08-06 09:49 |显示全部楼层
顶下。有没有几句话的总结让我这样的懒人快速阅读啊?:)
accessory 发表于 2011-08-06 04:39



    简单一点就是分层,lowlevel驱动层,event层,input core层。驱动基本都这样的分层思想。

论坛徽章:
0
发表于 2011-08-06 09:54 |显示全部楼层
input_sync最后也是使用的input_event,只不过用SYNC的时间来做参数传递的
input_event

楼主,需要补充 ...
T-Bagwell 发表于 2011-08-06 08:25



    多点的话,也是一个handle,在去年的时候Henrik Rydberg <rydberg@euromail.se>把它从其它的里面分离出来单独处理了,以前是跟别的一起处理的,因为后来觉得多点的触摸太多了。

具体你可以看看它的这个patch set    https://lkml.org/lkml/2010/11/29/24

论坛徽章:
0
发表于 2011-08-06 12:16 |显示全部楼层
本帖最后由 1jjk 于 2011-08-06 13:39 编辑

那多点按下,滑动,然后抬起一个手指,怎么作的?
好好讲讲吧
别发链接应付小妹呀,我可不懂英文

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2011-08-06 12:25 |显示全部楼层
回复 1# lengyuex

感谢 LZ 分享

论坛徽章:
0
发表于 2011-08-06 13:40 |显示全部楼层
那多点按下,滑动,然后抬起一个手指,怎么作的?
好好讲讲吧
别发链接应付事
1jjk 发表于 2011-08-06 12:16



    这样的多点按下,滑动,然后抬起一个手指,像你所说的这种情况 ,一般是按照触摸屏的芯片场家来规定的,像有些场家做得比较 好的话,就把初始化,等一些固定的动作都放到固件里面,这样的话你在驱动里面很好控制,比如挂在I2C上的话,只要读写I2C固定的地址就可以简单完成一切动作。也有做得比较差的场商,我以前遇到过一家,那做的,一堆一堆的寄存器,根本不是调驱动 ,而是调寄存器参数,这样就很头疼,几个的寄存器,值写得不对读出来的数据就对应不起来,遇到这样的就蛋疼吧。

像你说的这种复杂的操作,在core层是不会处理的,在具体的芯片驱动里面就会有对应的寄存器,多片芯片里面一般会设计几个寄存器,比如说每个手指一个寄存器,另外还会有事件寄存器,具体要看芯片场商的spec,当手指按下时,发中断,先去读事件寄存器里的事件,spec里会说读出来数是多少,对应哪个事件,比如手指按下,抬起,一个手指固定另一个抬起,一个手指固定另外手指转动等,具体能支持多么复杂的动作,要看场商硬件支持多复杂的动作。

实际上,不管怎么样,在内核驱动层只需要读出这些数据,包括按下类型,按下坐标等,都是以一个数据包的形式发送给用户层的接口。

这个时候,驱动的工作完成了,但是应用层要写出相应的程序来处理这些数据包,一般场商也会说清楚应用层应该怎么处理这样的数据包。有哪些动作正在发生等,还有一些与LCD的校准等。
具体应用层是如何处理的,我也没看过相应的代码,只知道应用层要与驱动层相互协调来调试。

说白白了,驱动层不管多么复杂的事件,它都是很简单地想法,收到中断,上报事件。

这说的只是我见过的情况 ,有没见过的情况,不对的地方,欢迎指正,嘻嘻。


这样说算不算是应付?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP