我是靠谱客的博主 孤独学姐,最近开发中收集的这篇文章主要介绍三、Linux input 子系统学习之register_input_handler 分析  1、看看入口函数 2、详细分析下input_register_handler 这个函数,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这一篇,我们来分析input_register_handler 这个非常重要的函数。就像之前说过的一样,这是注册一个驱动。

  我们去input目录下看看那些文件调用的这个函数,分别有:evdev.c(evdev驱动)、joydev.c(joydev驱动)、keychord.c(按键驱动)、mousedev.c(鼠标驱动)。
  也即是每个文件,对于一种驱动,并在里面进行注册驱动。

 好的,那么我们以evdev.c为例子分析就好了。因为,触摸屏设备也是对应这个驱动。
 

 1、看看入口函数

点击(此处)折叠或打开

  1. static const struct input_device_id evdev_ids[] = {
  2.     { .driver_info = 1 },    /* Matches all devices */
  3.     { },            /* Terminating zero entry */
  4. };

  5. MODULE_DEVICE_TABLE(input, evdev_ids);

  6. static struct input_handler evdev_handler = {
  7.     .event        = evdev_event,
  8.     .connect    = evdev_connect,
  9.     .disconnect    = evdev_disconnect,
  10.     .fops        = &evdev_fops,
  11.     .minor        = EVDEV_MINOR_BASE,
  12.     .name        = "evdev",
  13.     .id_table    = evdev_ids,
  14. };

  15. static int __init evdev_init(void)
  16. {
  17.     return input_register_handler(&evdev_handler);
  18. }

    入口函数里面,啥都没干,就直接注册了一个evdev_handler,够直接把。哈哈!
   struct input_handler   evdev_handler 这几个机构体里面的几个元素都比较重要,后面都会用到,现在先来个大概的了解。

    .event = evdev_event,                       : 当时间上报的时候,会调用到此函数。
    .connect = evdev_connect,                 :当驱动和设备匹配的时候会调用到
    .disconnect = evdev_disconnect,         :这个还没研究
    .fops = &evdev_fops,                         :这个handler的操作函数
    .minor = EVDEV_MINOR_BASE,           :EVDEV 此设备号的基值
    .name = "evdev",                               : 名字啦
    .id_table = evdev_ids,                         :驱动和设备是否匹配,需要通过id_table来验证。这里的evdev_ids 看到上面的注释没“match all devices ”,就是匹配所有设
                                                                备。

2、详细分析下input_register_handler 这个函数

点击(此处)折叠或打开

  1. /**
  2.  * input_register_handler - register a new input handler
  3.  * @handler: handler to be registered
  4.  *
  5.  * This function registers a new input handler (interface) for input
  6.  * devices in the system and attaches it to all input devices that
  7.  * are compatible with the handler.
  8.  */
  9. int input_register_handler(struct input_handler *handler)
  10. {
  11.     struct input_dev *dev;
  12.     int retval;

  13.     retval = mutex_lock_interruptible(&input_mutex);
  14.     if (retval)
  15.         return retval;
  16.   
  17.     INIT_LIST_HEAD(&handler->h_list);    //初始化h_list头部

  18.     if (handler->fops != NULL) {
  19.         if (input_table[handler->minor >> 5]) {
  20.             retval = -EBUSY;
  21.             goto out;
  22.         }
  23.         input_table[handler->minor >> 5] = handler;  //将handler插入数组,其位置是handler->minor >> 5,也就是除以32
  24.     }
  25.      
  26.     list_add_tail(&handler->node, &input_handler_list)// 将handler->node插入到input_handler_list链表,深入代码分析可知,是插在input_handler_list头部的后面

  27.     list_for_each_entry(dev, &input_dev_list, node//遍历input_dev_list链表,对每一个dev调用input_attach_handler函数,看是否匹配。
  28.         input_attach_handler(dev, handler);

  29.     input_wakeup_procfs_readers();

  30.  out:
  31.     mutex_unlock(&input_mutex);
  32.     return retval;
  33. }
  34. EXPORT_SYMBOL(input_register_handler);
   上面的注释也说的很清楚,就是注册一个input_handler。函数的分析,代码旁边的注释也说了。

2.1 我们将重点集中下面这句代码。

    list_for_each_entry ( dev ,   & input_dev_list ,  node
       
input_attach_handler ( dev ,  handler ) ;

  
先看看list_for_each_entry是怎么定义的
 

点击(此处)折叠或打开

  1. /**
  2.  * list_for_each_entry    -    iterate over list of given type
  3.  * @pos:    the type * to use as a loop cursor.
  4.  * @head:    the head for your list.
  5.  * @member:    the name of the list_struct within the struct.
  6.  */
  7. #define list_for_each_entry(pos, head, member)                
  8.     for (pos = list_entry((head)->next, typeof(*pos), member);    
  9.      &pos->member != (head);     
  10.      pos = list_entry(pos->member.next, typeof(*pos), member))
   注释说得很清楚,就是对于给定的某种类型的链表进行遍历,其实就是一个for 循环。我们带入就是下面这样
   

点击(此处)折叠或打开


  1. #define list_for_each_entry(dev, input_dev_list, node)                
  2.     for (dev= list_entry((input_dev_list)->next, typeof(*dev), node);    
  3.      &dev->node!= (input_dev_list);     
  4.      dev= list_entry(dev->node.next, typeof(*dev), node))
  5.     {
  6.          input_attach_handler(dev, handler);
  7.     }
  8.   

  再 看看, list_entry的定义,就是我们熟悉的container_of,通过结构体里面某个元素的指针,找到结构体本身的指针,这里要找到的当然就dev结构体本身了,也就是input_device (关于这个我们留在下一篇分析)

点击(此处)折叠或打开

  1. /**
  2.  * list_entry - get the struct for this entry
  3.  * @ptr:    the &struct list_head pointer.
  4.  * @type:    the type of the struct this is embedded in.
  5.  * @member:    the name of the list_struct within the struct.
  6.  */
  7. #define list_entry(ptr, type, member) 
  8.     container_of(ptr, type, member)
 
  到了这里很明了,就是 遍历 input_dev_list链表,对每一个dev(input_device)调用 input_attach_handler函数,看是否匹配

2.2 input_attach_handler分析

先看看函数的定义

点击(此处)折叠或打开

  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
  2. {
  3.     const struct input_device_id *id;
  4.     int error;

  5.     id = input_match_device(handler, dev);
  6.     if (!id)
  7.         return -ENODEV;

  8.     error = handler->connect(handler, dev, id);
  9.     if (error && error != -ENODEV)
  10.         pr_err("failed to attach handler %s to device %s, error: %dn",
  11.          handler->name, kobject_name(&dev->dev.kobj), error);

  12.     return error;
  13. }
小伙伴,惊呆了吧,看!首先是调用input_match_device函数,看驱动handler和dev是否匹配,如果匹配成功,就会返回相应的ID,并调用handler里面的connect函数

2.2.1  input_match_device 函数分析

点击(此处)折叠或打开

  1. static const struct input_device_id *input_match_device(struct input_handler *handler,
  2.                             struct input_dev *dev)
  3. {
  4.     const struct input_device_id *id;
  5.     int i;

  6.     for (id = handler->id_table; id->flags || id->driver_info; id++) {

  7.         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
  8.             if (id->bustype != dev->id.bustype)
  9.                 continue;

  10.         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
  11.             if (id->vendor != dev->id.vendor)
  12.                 continue;

  13.         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
  14.             if (id->product != dev->id.product)
  15.                 continue;

  16.         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
  17.             if (id->version != dev->id.version)
  18.                 continue;

  19.         MATCH_BIT(evbit, EV_MAX);
  20.         MATCH_BIT(keybit, KEY_MAX);
  21.         MATCH_BIT(relbit, REL_MAX);
  22.         MATCH_BIT(absbit, ABS_MAX);
  23.         MATCH_BIT(mscbit, MSC_MAX);
  24.         MATCH_BIT(ledbit, LED_MAX);
  25.         MATCH_BIT(sndbit, SND_MAX);
  26.         MATCH_BIT(ffbit, FF_MAX);
  27.         MATCH_BIT(swbit, SW_MAX);

  28.         if (!handler->match || handler->match(handler, dev))
  29.             return id;
  30.     }

  31.     return NULL;
  32. }

  因为evdev_handle->flag元素为空,并且 evdev_handle->match也为空,所以 evdev_handle->id_table,所以匹配是成功的。

2.2.2  分析下handler->connect这个函数

也就是下面的代码

点击(此处)折叠或打开

  1. /*
  2.  * Create new evdev device. Note that input core serializes calls
  3.  * to connect and disconnect so we don't need to lock evdev_table here.
  4.  */
  5. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
  6.              const struct input_device_id *id)
  7. {
  8.     struct evdev *evdev;
  9.     int minor;
  10.     int error;
  11.    //先在evdev_table[ ] 数组里面,找到一个还没有使用过的索引
  12.     for (minor = 0; minor < EVDEV_MINORS; minor++)  
  13.         if (!evdev_table[minor])
  14.             break;

  15.     if (minor == EVDEV_MINORS) {
  16.         pr_err("no more free evdev devicesn");
  17.         return -ENFILE;
  18.     }
  19.     //申请一个evdev对象
  20.     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  21.     if (!evdev)
  22.         return -ENOMEM;
  23.     //对evdev进行一些初始化
  24.     INIT_LIST_HEAD(&evdev->client_list);
  25.     spin_lock_init(&evdev->client_lock);
  26.     mutex_init(&evdev->mutex);
  27.     init_waitqueue_head(&evdev->wait);
  28.     //赋值一些元素
  29.     dev_set_name(&evdev->dev, "event%d", minor);
  30.     evdev->exist = true;
  31.     evdev->minor = minor;
  32.     //特别注意到evdev里面有一个handle的元素,以及对它的赋值,特别是handle.dev和handle.handler非常重要,这样才能把handle、dev、handler关联起来
  33.     evdev->handle.dev = input_get_device(dev);  //指向我们的dev
  34.     evdev->handle.name = dev_name(&evdev->dev);
  35.     evdev->handle.handler = handler; //指向我们的handler
  36.     evdev->handle.private = evdev;
  37.     //也要特别注意到evdev里面有一个dev的元素,设备节点的生产就是靠它生成的
  38.     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);  //得到设备号
  39.     evdev->dev.class = &input_class; //第二篇里面分析,有提到这个input_class哦!!
  40.     evdev->dev.parent = &dev->dev;
  41.     evdev->dev.release = evdev_free;
  42.     device_initialize(&evdev->dev);
  43.     //注册evdev里面的handle
  44.     error = input_register_handle(&evdev->handle);
  45.     if (error)
  46.         goto err_free_evdev;
  47.     //其实就是把evdev放入到evdev_table[]数组里面
  48.     error = evdev_install_chrdev(evdev);
  49.     if (error)
  50.         goto err_unregister_handle;
  51.    //生成设备节点
  52.     error = device_add(&evdev->dev);
  53.     if (error)
  54.         goto err_cleanup_evdev;

  55.     return 0;

  56.  err_cleanup_evdev:
  57.     evdev_cleanup(evdev);
  58.  err_unregister_handle:
  59.     input_unregister_handle(&evdev->handle);
  60.  err_free_evdev:
  61.     put_device(&evdev->dev);
  62.     return error;
  63. }
  如上面的注释所述,先在evdev_talble[ ]数组里面找到一个没有使用过的索引,然后申请一个evdev,再就是初始化里面的元素,最后这个通过evdev_install_chrdev函数,将evdev放入到
  evdev_talble[ ]数组里面。当然最后还要创建设备节点。

2.2.2.1  input_register_handle 分析


点击(此处)折叠或打开

  1. /**
  2.  * input_register_handle - register a new input handle
  3.  * @handle: handle to register
  4.  *
  5.  * This function puts a new input handle onto device's
  6.  * and handler's lists so that events can flow through
  7.  * it once it is opened using input_open_device().
  8.  *
  9.  * This function is supposed to be called from handler's
  10.  * connect() method.
  11.  */
  12. int input_register_handle(struct input_handle *handle)
  13. {
  14.     struct input_handler *handler = handle->handler;
  15.     struct input_dev *dev = handle->dev;
  16.     int error;

  17.     /*
  18.      * We take dev->mutex here to prevent race with
  19.      * input_release_device().
  20.      */
  21.     error = mutex_lock_interruptible(&dev->mutex);
  22.     if (error)
  23.         return error;

  24.     /*
  25.      * Filters go to the head of the list, normal handlers
  26.      * to the tail.
  27.      */
  28.     if (handler->filter)
  29.         list_add_rcu(&handle->d_node, &dev->h_list);
  30.     else
  31.         list_add_tail_rcu(&handle->d_node, &dev->h_list);

  32.     mutex_unlock(&dev->mutex);

  33.     /*
  34.      * Since we are supposed to be called from ->connect()
  35.      * which is mutually exclusive with ->disconnect()
  36.      * we can't be racing with input_unregister_handle()
  37.      * and so separate lock is not needed here.
  38.      */
  39.     list_add_tail_rcu(&handle->h_node, &handler->h_list);

  40.     if (handler->start)
  41.         handler->start(handle);

  42.     return 0;
  43. }
  函数的头部说的很明白了,将handle放到device's list 和 handler list ,这样通过dev或者handler 就可以找到 handle ,结合之前的分析,通过handle 也可以找到dev和handler,这样他们就都关联起来了。

2.2.2.1  input_register_handle 分析

点击(此处)折叠或打开

  1. static int evdev_install_chrdev(struct evdev *evdev)
  2. {
  3.     /*
  4.      * No need to do any locking here as calls to connect and
  5.      * disconnect are serialized by the input core
  6.      */
  7.     evdev_table[evdev->minor] = evdev;
  8.     return 0;
  9. }
很简单。

好了,evdev.c这个驱动就分析完了。下一篇我们来分析设备是如何注册的也就是input_regiter_device()。

最后

以上就是孤独学姐为你收集整理的三、Linux input 子系统学习之register_input_handler 分析  1、看看入口函数 2、详细分析下input_register_handler 这个函数的全部内容,希望文章能够帮你解决三、Linux input 子系统学习之register_input_handler 分析  1、看看入口函数 2、详细分析下input_register_handler 这个函数所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部