Chinaunix首页 | 论坛 | 博客
  • 博客访问: 716410
  • 博文数量: 94
  • 博客积分: 2812
  • 博客等级: 少校
  • 技术积分: 1555
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-08 21:28
文章分类

全部博文(94)

文章存档

2012年(23)

2011年(39)

2010年(14)

2009年(18)

分类: LINUX

2011-09-30 10:16:47

drivers/input/input.c

就是所谓的input的核心程序。

分析这个文件,先从input_init开始。

   1:  static int __init input_init(void)

   2:  {

   3:  err = class_register(&input_class);

   4:  err = input_proc_init();

   5:  err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

   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.

了解了框架再去看具体操作的核心代码就不会迷糊了。


阅读(3418) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~