驱动开发 —— 输入子系统(工作逻辑分析)

输入子系统的工作原理和代码分析
目的:
a,学会如何分析内核中子系统的代码,从而可以举一反三
b,整体把握框架思想,理解分层中各层的配合方式
c,掌握子系统,增强排错能力

 分析代码

1、input核心层:input.c

 1 subsys_initcall(input_init);  //优先级比module高
 2 module_exit(input_exit);
 3 
 4     input_init()
 5         |
 6         //注册类,类似于class_create();
 7         err = class_register(&input_class);
 8         //在/proc创建相关文件 bus/input/devices  handlers
 9         err = input_proc_init();
10         //申请设备号
11         err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
12                      INPUT_MAX_CHAR_DEVICES, "input");

第11行通过register_chrdev_region创建驱动设备,其中变量INPUT_MAJOR=13,所以就是创建

了一个主设备为13的“input”设备。在新版本的内核中,fops接口在input_handler层的evdev.c中。

总结:
  1),注册了主设备号
  2),注册input class

2、input handler层:evdev.c

进入evdev.c的初始化函数,

static int __init evdev_init(void)
{
       return input_register_handler(&evdev_handler);  //注册
}

在内核中搜索一下input_register_handler,看看这个函数都被谁调用:

 如上图所示,有evdev.c(事件设备),tsdev.c(触摸屏设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备) 这5个内核自带的设备处理函数注册到input子系统中

进入input_register_handler分析(这个函数在核心层input.c定义,提供接口给设备层使用):

 1 module_init(evdev_init);
 2 module_exit(evdev_exit);
 3 
 4 static struct input_handler evdev_handler = {
 5     .event         = evdev_event,
 6     .events        = evdev_events,
 7     .connect       = evdev_connect,
 8     .disconnect    = evdev_disconnect,
 9     .legacy_minors = true,
10     .minor         = EVDEV_MINOR_BASE,
11     .name          = "evdev",
12     .id_table      = evdev_ids,
13 };
14 
15         evdev_init(void)
16             |
17             input_register_handler(&evdev_handler);
18                     |
19                     //初始化h_list  
20                     INIT_LIST_HEAD(&handler->h_list);
21 
22                     //将当前的handler加入到一个input_handler_list
23                     list_add_tail(&handler->node, &input_handler_list);
24 
25                     //遍历链表input_dev_list
26                     list_for_each_entry(dev, &input_dev_list, node)
27                         input_attach_handler(dev, handler);
28                             |
29                             // 将当前的hanler和input dev进行匹配, event handler能够匹配所有的input dev
30                             id = input_match_device(handler, dev);
31 
32                             //匹配成功,之后要调用handler中connect方法
33                             // 实际就是event handler,实际调用了evdev_connect
34                             error = handler->connect(handler, dev, id);
35 
36                     //将当前的handler加入到/proc/bus/input/handlers文件中
37                     input_wakeup_procfs_readers();

在事件设备处理驱动中,调用了核心层的input_register_handler来注册evdev_handler。

1)我们先来看看这个evdev_handler变量是个什么东西?

 1 /**
 2  * struct input_handler - implements one of interfaces for input devices
 3  * @private: driver-specific data
 4  * @event: event handler. This method is being called by input core with
 5  *    interrupts disabled and dev->event_lock spinlock held and so
 6  *    it may not sleep
 7  * @events: event sequence handler. This method is being called by
 8  *    input core with interrupts disabled and dev->event_lock
 9  *    spinlock held and so it may not sleep
10  * @filter: similar to @event; separates normal event handlers from
11  *    "filters".
12  * @match: called after comparing device's id with handler's id_table
13  *    to perform fine-grained matching between device and handler
14  * @connect: called when attaching a handler to an input device
15  * @disconnect: disconnects a handler from input device
16  * @start: starts handler for given handle. This function is called by
17  *    input core right after connect() method and also when a process
18  *    that "grabbed" a device releases it
19  * @legacy_minors: set to %true by drivers using legacy minor ranges
20  * @minor: beginning of range of 32 legacy minors for devices this driver
21  *    can provide
22  * @name: name of the handler, to be shown in /proc/bus/input/handlers
23  * @id_table: pointer to a table of input_device_ids this driver can
24  *    handle
25  * @h_list: list of input handles associated with the handler
26  * @node: for placing the driver onto input_handler_list
27  *
28  * Input handlers attach to input devices and create input handles. There
29  * are likely several handlers attached to any given input device at the
30  * same time. All of them will get their copy of input event generated by
31  * the device.
32  *
33  * The very same structure is used to implement input filters. Input core
34  * allows filters to run first and will not pass event to regular handlers
35  * if any of the filters indicate that the event should be filtered (by
36  * returning %true from their filter() method).
37  *
38  * Note that input core serializes calls to connect() and disconnect()
39  * methods.
40  */
41 struct input_handler {
42 
43     void *private;
44 
45     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
46     void (*events)(struct input_handle *handle,
47                const struct input_value *vals, unsigned int count);
48     bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
49     bool (*match)(struct input_handler *handler, struct input_dev *dev);
50     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
51     void (*disconnect)(struct input_handle *handle);
52     void (*start)(struct input_handle *handle);
53 
54     bool legacy_minors;
55     int minor;
56     const char *name;
57 
58     const struct input_device_id *id_table;
59 
60     struct list_head    h_list;
61     struct list_head    node;
62 };
input.h -- input_handler

如上代码:evdev_handler是由input_handler初始化后的一个对象,描述了事件设备处理的相关信息:

  .minor:用来存放次设备号,EVDEV_MINOR_BASE=64;

     .id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev的id和某个input_handler的id_table相匹配,就会调用.connect连接函数

  .connect:连接函数,将设备input_dev和某个input_handler建立连接

2)进入input_register_handler

   初始化链表input_handler_list,将handler加入链表,遍历链表,匹配对眼的handler与dev,匹配成功后调用connect;

总结:
  1,注册了evdev_handler
  2, 遍历input dev list,并行匹配,恒匹配成功,自动会调用handler中connect方法--- evdev_connect

3、input dev层:simple_input.c

 1 input_register_device(inputdev);
 2         |
 3         //将input dev加入到链表input_dev_list
 4         list_add_tail(&dev->node, &input_dev_list);
 5 
 6         //遍历input handler链表,进行匹配
 7         list_for_each_entry(handler, &input_handler_list, node)
 8             input_attach_handler(dev, handler);
 9                     |
10                 //匹配成功,之后要调用handler中connect方法
11                 // 实际就是event handler,实际调用了evdev_connect
12                 error = handler->connect(handler, dev, id);

在设备注册函数input_register_device中做着与处理函数input_register_handler相同的事:

将要注册的设备放在input_dev_list链表中,遍历handler链表,调用匹配函数对两者id_table进行

判断,若支持就connect。

     所以,不管先注册input_dev还是input_handler最终都会调用到匹配函数进行判断与connect。对于内核自带的handler驱动,一般都是先注册进去的。

4、分析evdev_handler->connect函数是怎样建立连接的,如下:

 1 evdev_connect(struct input_handler *handler, struct input_dev *dev,
 2              const struct input_device_id *id)
 3     |
 4     //找到一个没有被使用的次设备号, 从64开始, 65,66 
 5     minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
 6 
 7     // 实例化 一个evdev对象
 8     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
 9     //初始化evdev对象
10     INIT_LIST_HEAD(&evdev->client_list);
11     spin_lock_init(&evdev->client_lock);
12     mutex_init(&evdev->mutex);
13     //等待队列是完成阻塞
14     init_waitqueue_head(&evdev->wait);
15     evdev->exist = true;
16 
17     dev_no = minor;
18     dev_no -= EVDEV_MINOR_BASE; //减去了64
19 
20     // 创建设备文件/dev/event0,1,2
21     dev_set_name(&evdev->dev, "event%d", dev_no);
22     evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// 12, 64
23     evdev->dev.class = &input_class;
24     evdev->dev.parent = &dev->dev;
25     evdev->dev.release = evdev_free;
26     device_initialize(&evdev->dev)
27     device_add(&evdev->dev); 
28     //以上代码和device_create的内部实现是一样的
29 
30     //利用handle记录input device和input handler
31     evdev->handle.dev = input_get_device(dev);
32     evdev->handle.name = dev_name(&evdev->dev);
33     evdev->handle.handler = handler;  //将handler存入handle的成员handler中
34     //你中有我,我中有你,指向自己,可以通过此成员找到结构体
35     evdev->handle.private = evdev;
36 
37     
38     //将儿子handle关联到父亲(input handler)和母亲(input dev)
39     error = input_register_handle(&evdev->handle);
40                     |
41                 list_add_tail_rcu(&handle->d_node, &dev->h_list);
42                 list_add_tail_rcu(&handle->h_node, &handler->h_list);
43 
44     
45     //初始化了cdev,完成了fops, 为用户提供文件io
46     cdev_init(&evdev->cdev, &evdev_fops);
47     evdev->cdev.kobj.parent = &evdev->dev.kobj;
48     error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);

  

总结:
  1,分配evdev,并初始化,记录input device和handler的关系
  2,创建设备节点/dev/event0
  3, 注册cdev,并实现fops

  4,关系:
    多个input device可以对应一个event handler
    一个input device对应一个 evdev,对应于一个设备节点/dev/event0,1,2
  5, 所有的设备节点调用open,read,write文件io的时候
    实际是调用cdev中fops中各个接口,最终都调用了

 1 static const struct file_operations evdev_fops = {
 2                     .owner        = THIS_MODULE,
 3                     .read        = evdev_read,
 4                     .write        = evdev_write,
 5                     .poll        = evdev_poll,
 6                     .open        = evdev_open,
 7                     .release    = evdev_release,
 8                     .unlocked_ioctl    = evdev_ioctl,
 9                 #ifdef CONFIG_COMPAT
10                     .compat_ioctl    = evdev_ioctl_compat,
11                 #endif
12                     .fasync        = evdev_fasync,
13                     .flush        = evdev_flush,
14                     .llseek        = no_llseek,
15                 };

着重分析一下input_dev和input_handler与handle之间的关系:

结合connect的代码中可以看到:

 1 struct input_handle {   //input.h
 2     void *private;
 3     int open;
 4     const char *name;
 5 
 6     struct input_dev *dev;
 7     struct input_handler *handler;
 8 
 9     struct list_head    d_node;
10     struct list_head    h_node;
11 };
12 
13 struct input_handle handle;   //evdev.c
14 //我们在这把handle比作input_handler和input_dev的儿子,
15 //handle的存在意义就是作为父母间的联结,通过儿子,父母间可
16 //以相互沟通寻找。
17 //可以看到handle体内有着指向父母dev与handler的指针,通过指
18 //针可以记录父母的信息
19 //两者的链表h_list都指向同一个handle结构体,即分别指向
20 //d_node,h_node,然后通过h_list来找到handle的成员dev和
21 //handler,便能找到双方,建立了连接
22 //eg:input_dev:h_list-> handle.d_node ->handle.handler ->input_handler
1 //将儿子handle关联到父亲(input handler)和母亲(input dev)
2      error = input_register_handle(&evdev->handle);
3                      |
4                  list_add_tail_rcu(&handle->d_node, &dev->h_list);
5                  list_add_tail_rcu(&handle->h_node, &handler->h_list);
6 
7 //在第4行中, 因为handle->dev指向input_dev驱动设备,所以就是将handle->d_node放入到
//input_dev驱动设备的h_list链表中,即input_dev驱动设备的h_list链表就指向handle->d_node
8 //在第5行中, 同样, input_handler驱动处理结构体的h_list也指向了handle->h_node

最终如下图所示:

更加直观的图示:

5、应用程序是如何调用到输入子系统中去的

 以应用程序调用open为例:

在open之后会返回一个文件描述符,文件描述符实际上是一个fd_table数组的一个下标,

其中0/1/2被系统的标准输入输出与出错占用了。数组里面存放着struct file *file。

在每次open之后,系统都会创建一个struct file结构体,里面包含文件属性,IO接口等(path、flag、mode、fops、fpos等)。

cdev详解

1)每一个文件都会有inode,里面包含文件的权限、类型、大小以及主次设备号等;

当应用层调用open一个设备节点时,系统会找到对应的inode,进而读取到主次设备号,

遍历cdev链表,找到设备号对应的cdev;

2)在输入子系统中,会通过register_chrdev和cdev_add将cdev插入到链表中,cdev中包含

了设备号与在输入子系统中初始化的xx_ops操作函数;

3)若应用层找到了对应的cdev,就会调用在输入子系统中的xx_open;

4)由上可以看到,应用层open调用过程:先找到inode获取设备号,在VFS的cdev链表中遍历

  进而调用到cdev中的ops操作函数,这个过程是比较长的。如果接下来要调用read,又得重复以上过程。

  内核为了在后面几次调用ops操作函数时,能快速调用,就在第一次调用open时,把cdev->ops复制到

  struct file结构体里面的f_ops中,并将fd与file结构体关联。这样在下次调用read(fd)等其他函数时,就可以通过

  fd_table[]的下标 --- fd,直接调用file中的fops就可以了。

 由上可以应用层调用open最终调用到cdev->ops中的xx_open。对于输入子系统,cdev在哪里定义?

对于evdev事件驱动,input_handler层的evdev.c中,

evdev.c

 1 //匹配成功,调用connect建立input_dev与input_handler的连接
 2 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 3              const struct input_device_id *id)
 4 {
 5     .....
 6     cdev_init(&evdev->cdev, &evdev_fops);
 7     evdev->cdev.kobj.parent = &evdev->dev.kobj;
 8     error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
 9    .....
10 }
 1 input handler 层:evdev.c
 2         cdev;
 3        xxx_ops = {
 4             .open = xxx_open,
 5             .write = xxx_write,
 6        }
 7 
 8 
 9      static const struct file_operations evdev_fops = {
10                     .owner        = THIS_MODULE,
11                     .read        = evdev_read,
12                     .write        = evdev_write,
13                     .poll        = evdev_poll,
14                     .open        = evdev_open,
15      }
16     
17     实际最终调用了evdev_open();
18         |
19         // 实际cdev是谁,就是evdev_connect注册的那个
20         struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);   (1)
21 
22         // 通过儿子,找到老母input device  bufsize为handle里面dev的个数
23         unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
24 
25         // size就包含了很多input_event
26         unsigned int size = sizeof(struct evdev_client) +
27                         bufsize * sizeof(struct input_event);
28 
29         struct evdev_client *client;
30         //client不是客户端,而是一个内存
31         // 分配一个client对象,描述一个缓冲队列,存放的就是input_event
32         client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
33 
34         // client中有一个缓冲区
35         client->bufsize = bufsize;
36         spin_lock_init(&client->buffer_lock);
37         //在client中记录evdev
38         client->evdev = evdev;
39         // 将client加入到evdev中一个小链表
40         evdev_attach_client(evdev, client);
41                 |
42                 list_add_tail_rcu(&client->node, &evdev->client_list);
43 
44         // 将client记录到file,方面其他的接口使用
45         file->private_data = client;

总结:
  1,为输入设备分配一个缓冲区evdev_client,用户存放input device层上报的数据
  2,evdev_client记录到evdev中
  3,evdev_client记录到file中,方面其他的接口使用

 第20行(1);用程序在open时,会根据主次设备号找到cdev,cdev属于evdev对象的一部分,通过cdev就可找到evdev

由此可以了解到对应关系:一个设备节点/dev/evnet0对应一个cdev,一个cdev对应一个evdev,通过connect,evdev对应一个input_dev,

一个input_dev对应一个input_handler,input_handler可以对应多个input_dev

evdev里面包含:cdev、client、dev、handler、connect。它的作用就是将所有相关的信息记录下来,多个层次联系起来,相当于一个大的全局设备对象

结合下图理解:

6、应用程序中read,是如何获取到数据的

 1      read(fd, &event, sizeof(struct input_event));
 2     ---------------------------------------------
 3     vfs
 4         sys_read();
 5             file->f_ops->read();
 6     -------------------------------------------
 7     evdev.c
 8     static const struct file_operations evdev_fops = {
 9                     .read        = evdev_read,
10         evdev_read(struct file *file, char __user *buffer,
11               size_t count, loff_t *ppos)
12             |
13         // 获取到open中分配的缓冲区对象
14         struct evdev_client *client = file->private_data;
15         //获取到evdev
16         struct evdev *evdev = client->evdev;
17         //表示一个数据包,要给用户
18         struct input_event event;
19 
20         for (;;) {
21             // 实现非阻塞
22             if (client->packet_head == client->tail &&
23                 (file->f_flags & O_NONBLOCK))
24                 return -EAGAIN;
25 
26                 while (read + input_event_size() <= count &&
27                 // 1从缓冲区获取数据,存放在 input_event数据包
28                    evdev_fetch_next_event(client, &event)) {
29                             |
30                             *event = client->buffer[client->tail++];
31                 // 2, 将数据上报给用户
32                 if (input_event_to_user(buffer + read, &event))
33                             |
34                             copy_to_user(buffer, event, sizeof(struct input_event)
35 
36                 // 3,统计上报多少数据
37                 read += input_event_size();
38             }
39 
40             // 如果当前是阻塞模式
41             if (!(file->f_flags & O_NONBLOCK)) {
42                 //等待---休眠,需要被唤醒,有数据时候唤醒
43                 error = wait_event_interruptible(evdev->wait,
44                         client->packet_head != client->tail ||
45                         !evdev->exist || client->revoked);
46         }

总结:
  1,如果没有数据,就会休眠等待
  2,如果有数据,就从缓冲区client->buffer[client->tail++]拿数据
    通过copy_to_user上报给用户

疑问:
  数据到底是如何存放在缓冲区的
  等待队列是谁唤醒的
    input_report_key(inputdev, pdesc->key_code, 0);
    input_sync(inputdev);//上报数据结束

7、input_event上报数据的过程

在我们写的simple_input_drv.c中(input_dev层),上报数据的代码如下;

 1 //中断处理函数
 2 irqreturn_t input_irq_handler(int irqno, void *devid)
 3 {
 4     //区分不同的按键
 5     ...
 6     //在设备树中获取gpio号
 7     ...
 8     //通过gpio号获取gpio引脚状态
 9     ...
10     if(value){
11         //上报数据,输入子系统默认抬起为0
12         input_report_key(inputdev, pdesc->key_code, 0);  //为什么是1?
13             //调用:input_event(dev, EV_KEY, code, !!value);
14         input_sync(inputdev);  //同步,上报数据结束
15     }else{
16         //上报数据,输入子系统默认按下为1
17         input_report_key(inputdev, pdesc->key_code, 1);
18         input_sync(inputdev);  //同步,上报数据结束
19     }
20 
21     //阻塞的相关操作,上层会去实现
22     return IRQ_HANDLED;
23 }

上报数据的过程是怎么实现的?

进入input_report_key (最终调用到核心层input.c的input_event)

 1     input_report_key(inputdev, pdesc->key_code, 0);
 2     input_sync(inputdev);//上报数据结束
 3         |  //input_event(dev, EV_KEY, code, !!value);
 4         input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);// 上报数据
 5                 |
 6                 input_handle_event(dev, type, code, value);
 7                         |
 8                         // 如果将数据交给input handler去处理
 9                         if (disposition & INPUT_PASS_TO_HANDLERS) {    //disposition:处理
10                             struct input_value *v;
11                             //将input device获取到数据暂存一下input value
12                             v = &dev->vals[dev->num_vals++];
13                             v->type = type;
14                             v->code = code;
15                             v->value = value;
16 
17 
18                             input_pass_values(dev, dev->vals, dev->num_vals)
19                                 |
20                                 // 从input device中获取到input handle
21                             else {
22     
23                             list_for_each_entry_rcu(handle, &dev->h_list, d_node)
24                                 if (handle->open)
25                                     count = input_to_handler(handle, vals, count);
26                                             |
27                                         // 通过handle儿子找到handler父亲
28                                         struct input_handler *handler = handle->handler;
29                                         // 如果有events函数指针,那就调用
30                                         if (handler->events)
31                                             handler->events(handle, vals, count);
32                                         else if (handler->event)//否则就调用event();
33                                             for (v = vals; v != end; v++)
34                                                 handler->event(handle, v->type, v->code, v->value);                       
37                         }

总结: 如果将数据上报,最终是调用handler中events()或者event()

  其中evdev_event()是evdev.c(事件驱动) 的evdev_handler->.event成员,如下所示:

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 };
evdev_handler

8、evdev填充缓冲区数据的过程

 1 将数据上报,最终是调用handler中events()或者event()
 2 实际是evdev.c
 3 .event   = evdev_event,
 4 .events  = evdev_events,
 5 static void evdev_events(struct input_handle *handle,
 6              const struct input_value *vals, unsigned int count)
 7 {
 8     // 拿到evdev,肯定要拿到缓冲区
 9     struct evdev *evdev = handle->private;
10     struct evdev_client *client;
11 
12     // 获取到缓冲evdev_client
13     if (client)
14         evdev_pass_values(client, vals, count, time_mono, time_real);
15     else  //进入else
16     list_for_each_entry_rcu(client, &evdev->client_list, node)
17         evdev_pass_values(client, vals, count,
18                   time_mono, time_real);
19             |
20             // 通过client获取到evdev
21             struct evdev *evdev = client->evdev;
22             
23             const struct input_value *v;
24             struct input_event event;
25 
26             event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real);
27             for (v = vals; v != vals + count; v++) {
28                 // 将input device上报的数据获取到,并且封装成input event对象
29                 event.type = v->type;
30                 event.code = v->code;
31                 event.value = v->value;
32                 // 将input event数据存放到缓冲区中
33                 __pass_event(client, &event);
34                         |
35                         client->buffer[client->head++] = *event;
36                         client->head &= client->bufsize - 1;
37                 46                     // 如果调用 input_sync()
47                     if (v->type == EV_SYN && v->code == SYN_REPORT)
48                         wakeup = true;
49                 }
50 
51                     // 唤醒等待队列
52                     if (wakeup)
53                         wake_up_interruptible(&evdev->wait);

总结:
  input_report_key(inputdev, pdesc->key_code, 0);
    //将输入设备产生数据交给input handler,调用events();将数据存放在缓冲区client->buffer[client->head++] = *event;
  input_sync(inputdev);//上报数据结束
    // 唤醒等待队列,表示输入设备上报数据完毕

9、本节分析总结:

1.注册输入子系统,进入input_init():

  1)创建主设备号为13的"input"字符设备

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

 

2.open打开驱动,进入input_open_file():

  1)更新设备的file_oprations

file->f_op=fops_get(handler->fops);

  2)执行file_oprations->open函数

err = new_fops->open(inode, file);

3.注册input_handler,进入input_register_handler():

  1)添加到input_table[]处理数组中

input_table[handler->minor >> 5] = handler;

  2)添加到input_handler_list链表中

list_add_tail(&handler->node, &input_handler_list);

  3)判断input_dev的id,是否有支持这个驱动的设备

list_for_each_entry(dev, &input_dev_list, node)   //遍历查找input_dev_list链表里所有input_dev
input_attach_handler(dev, handler);         //判断两者id,若两者支持便进行连接。

4.注册input_dev,进入input_register_device():

   1)放在input_dev_list链表中

list_add_tail(&dev->node, &input_dev_list);

  2)判断input_handler的id,是否有支持这个设备的驱动

list_for_each_entry(handler, &input_handler_list, node)  //遍历查找input_handler_list链表里所有input_handler
input_attach_handler(dev, handler);                      //判断两者id,若两者支持便进行连接。

5.判断input_handlerinput_devid,进入input_attach_handler():

  1)匹配两者id,

input_match_device(handler->id_table, dev);        //匹配input_handler和dev的id,不成功退出函数

  2)匹配成功调用input_handler ->connect

handler->connect(handler, dev, id);              //建立连接

6.建立input_handlerinput_dev的连接,进入input_handler->connect():

    1)创建全局结构体,通过input_handle结构体连接双方

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //创建两者连接的input_handle全局结构体
list_add_tail(&handle->d_node, &handle->dev->h_list); //连接input_dev->h_list
list_add_tail(&handle->h_node, &handler->h_list);    // 连接input_handle->h_list

7.有事件发生时,比如按键中断,在中断函数中需要进入input_event()上报事件:

   1)找到驱动处理结构体,然后执行input_handler->event()

list_for_each_entry(handle, &dev->h_list, d_node)     // 通过input_dev ->h_list链表找到input_handle驱动处理结构体
if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式)
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数

  

原文地址:https://www.cnblogs.com/y4247464/p/12441502.html