我是靠谱客的博主 孤独学姐,最近开发中收集的这篇文章主要介绍三、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、看看入口函数
点击(此处)折叠或打开
- static const struct input_device_id evdev_ids[] = {
- { .driver_info = 1 }, /* Matches all devices */
- { }, /* Terminating zero entry */
- };
-
- MODULE_DEVICE_TABLE(input, evdev_ids);
-
- static struct input_handler evdev_handler = {
- .event = evdev_event,
- .connect = evdev_connect,
- .disconnect = evdev_disconnect,
- .fops = &evdev_fops,
- .minor = EVDEV_MINOR_BASE,
- .name = "evdev",
- .id_table = evdev_ids,
- };
-
- static int __init evdev_init(void)
- {
- return input_register_handler(&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 这个函数
点击(此处)折叠或打开
- /**
- * input_register_handler - register a new input handler
- * @handler: handler to be registered
- *
- * This function registers a new input handler (interface) for input
- * devices in the system and attaches it to all input devices that
- * are compatible with the handler.
- */
- int input_register_handler(struct input_handler *handler)
- {
- struct input_dev *dev;
- int retval;
-
- retval = mutex_lock_interruptible(&input_mutex);
- if (retval)
- return retval;
- INIT_LIST_HEAD(&handler->h_list); //初始化h_list头部
-
- if (handler->fops != NULL) {
- if (input_table[handler->minor >> 5]) {
- retval = -EBUSY;
- goto out;
- }
- input_table[handler->minor >> 5] = handler; //将handler插入数组,其位置是handler->minor >> 5,也就是除以32
- }
- list_add_tail(&handler->node, &input_handler_list); // 将handler->node插入到input_handler_list链表,深入代码分析可知,是插在input_handler_list头部的后面
-
- list_for_each_entry(dev, &input_dev_list, node) //遍历input_dev_list链表,对每一个dev调用input_attach_handler函数,看是否匹配。
- input_attach_handler(dev, handler);
-
- input_wakeup_procfs_readers();
-
- out:
- mutex_unlock(&input_mutex);
- return retval;
- }
- EXPORT_SYMBOL(input_register_handler);
2.1 我们将重点集中下面这句代码。
list_for_each_entry ( dev , & input_dev_list , node )input_attach_handler ( dev , handler ) ;
先看看list_for_each_entry是怎么定义的
点击(此处)折叠或打开
- /**
- * list_for_each_entry - iterate over list of given type
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
- #define list_for_each_entry(pos, head, member)
- for (pos = list_entry((head)->next, typeof(*pos), member);
- &pos->member != (head);
- pos = list_entry(pos->member.next, typeof(*pos), member))
点击(此处)折叠或打开
-
- #define list_for_each_entry(dev, input_dev_list, node)
- for (dev= list_entry((input_dev_list)->next, typeof(*dev), node);
- &dev->node!= (input_dev_list);
- dev= list_entry(dev->node.next, typeof(*dev), node))
- {
- input_attach_handler(dev, handler);
- }
-
再 看看, list_entry的定义,就是我们熟悉的container_of,通过结构体里面某个元素的指针,找到结构体本身的指针,这里要找到的当然就dev结构体本身了,也就是input_device (关于这个我们留在下一篇分析)
点击(此处)折叠或打开
- /**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
- #define list_entry(ptr, type, member)
- container_of(ptr, type, member)
到了这里很明了,就是 遍历 input_dev_list链表,对每一个dev(input_device)调用 input_attach_handler函数,看是否匹配。
2.2 input_attach_handler分析
先看看函数的定义
点击(此处)折叠或打开
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;
- int error;
-
- id = input_match_device(handler, dev);
- if (!id)
- return -ENODEV;
-
- error = handler->connect(handler, dev, id);
- if (error && error != -ENODEV)
- pr_err("failed to attach handler %s to device %s, error: %dn",
- handler->name, kobject_name(&dev->dev.kobj), error);
-
- return error;
- }
2.2.1 input_match_device 函数分析
点击(此处)折叠或打开
- static const struct input_device_id *input_match_device(struct input_handler *handler,
- struct input_dev *dev)
- {
- const struct input_device_id *id;
- int i;
-
- for (id = handler->id_table; id->flags || id->driver_info; id++) {
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->bustype != dev->id.bustype)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
- if (id->vendor != dev->id.vendor)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
- if (id->product != dev->id.product)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
- if (id->version != dev->id.version)
- continue;
-
- MATCH_BIT(evbit, EV_MAX);
- MATCH_BIT(keybit, KEY_MAX);
- MATCH_BIT(relbit, REL_MAX);
- MATCH_BIT(absbit, ABS_MAX);
- MATCH_BIT(mscbit, MSC_MAX);
- MATCH_BIT(ledbit, LED_MAX);
- MATCH_BIT(sndbit, SND_MAX);
- MATCH_BIT(ffbit, FF_MAX);
- MATCH_BIT(swbit, SW_MAX);
-
- if (!handler->match || handler->match(handler, dev))
- return id;
- }
-
- return NULL;
- }
2.2.2 分析下handler->connect这个函数
也就是下面的代码
点击(此处)折叠或打开
- /*
- * Create new evdev device. Note that input core serializes calls
- * to connect and disconnect so we don't need to lock evdev_table here.
- */
- static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
- {
- struct evdev *evdev;
- int minor;
- int error;
- //先在evdev_table[ ] 数组里面,找到一个还没有使用过的索引
- for (minor = 0; minor < EVDEV_MINORS; minor++)
- if (!evdev_table[minor])
- break;
-
- if (minor == EVDEV_MINORS) {
- pr_err("no more free evdev devicesn");
- return -ENFILE;
- }
- //申请一个evdev对象
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- if (!evdev)
- return -ENOMEM;
- //对evdev进行一些初始化
- INIT_LIST_HEAD(&evdev->client_list);
- spin_lock_init(&evdev->client_lock);
- mutex_init(&evdev->mutex);
- init_waitqueue_head(&evdev->wait);
- //赋值一些元素
- dev_set_name(&evdev->dev, "event%d", minor);
- evdev->exist = true;
- evdev->minor = minor;
- //特别注意到evdev里面有一个handle的元素,以及对它的赋值,特别是handle.dev和handle.handler非常重要,这样才能把handle、dev、handler关联起来
- evdev->handle.dev = input_get_device(dev); //指向我们的dev
- evdev->handle.name = dev_name(&evdev->dev);
- evdev->handle.handler = handler; //指向我们的handler
- evdev->handle.private = evdev;
- //也要特别注意到evdev里面有一个dev的元素,设备节点的生产就是靠它生成的
- evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //得到设备号
- evdev->dev.class = &input_class; //第二篇里面分析,有提到这个input_class哦!!
- evdev->dev.parent = &dev->dev;
- evdev->dev.release = evdev_free;
- device_initialize(&evdev->dev);
- //注册evdev里面的handle
- error = input_register_handle(&evdev->handle);
- if (error)
- goto err_free_evdev;
- //其实就是把evdev放入到evdev_table[]数组里面
- error = evdev_install_chrdev(evdev);
- if (error)
- goto err_unregister_handle;
- //生成设备节点
- error = device_add(&evdev->dev);
- if (error)
- goto err_cleanup_evdev;
-
- return 0;
-
- err_cleanup_evdev:
- evdev_cleanup(evdev);
- err_unregister_handle:
- input_unregister_handle(&evdev->handle);
- err_free_evdev:
- put_device(&evdev->dev);
- return error;
- }
evdev_talble[ ]数组里面。当然最后还要创建设备节点。
2.2.2.1 input_register_handle 分析
点击(此处)折叠或打开
- /**
- * input_register_handle - register a new input handle
- * @handle: handle to register
- *
- * This function puts a new input handle onto device's
- * and handler's lists so that events can flow through
- * it once it is opened using input_open_device().
- *
- * This function is supposed to be called from handler's
- * connect() method.
- */
- int input_register_handle(struct input_handle *handle)
- {
- struct input_handler *handler = handle->handler;
- struct input_dev *dev = handle->dev;
- int error;
-
- /*
- * We take dev->mutex here to prevent race with
- * input_release_device().
- */
- error = mutex_lock_interruptible(&dev->mutex);
- if (error)
- return error;
-
- /*
- * Filters go to the head of the list, normal handlers
- * to the tail.
- */
- if (handler->filter)
- list_add_rcu(&handle->d_node, &dev->h_list);
- else
- list_add_tail_rcu(&handle->d_node, &dev->h_list);
-
- mutex_unlock(&dev->mutex);
-
- /*
- * Since we are supposed to be called from ->connect()
- * which is mutually exclusive with ->disconnect()
- * we can't be racing with input_unregister_handle()
- * and so separate lock is not needed here.
- */
- list_add_tail_rcu(&handle->h_node, &handler->h_list);
-
- if (handler->start)
- handler->start(handle);
-
- return 0;
- }
2.2.2.1 input_register_handle 分析
点击(此处)折叠或打开
- static int evdev_install_chrdev(struct evdev *evdev)
- {
- /*
- * No need to do any locking here as calls to connect and
- * disconnect are serialized by the input core
- */
- evdev_table[evdev->minor] = evdev;
- return 0;
- }
好了,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 这个函数所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复