输入子系统的工作原理和代码分析
目的:
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 };
如上代码: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等)。
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 };
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_handler和input_dev的id,进入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_handler和input_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事件函数