我是靠谱客的博主 傲娇蜗牛,这篇文章主要介绍linux input输入子系统分析(input_dev和handler匹配分析),现在分享给大家,希望可以做个参考。

内核的输入子系统是相对分散的,对多种不同类别的输入设备(如键盘,鼠标,跟踪球,操纵杆,触摸屏,加速计和手写板)进行统一抽象处理。输入子系统包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。鼠标事件生成文件mousedev属于事件驱动程序,而PS/2鼠标驱动程序是设备驱动程序。事件驱动程序是标准的,对所有的输入类都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序。设备驱动程序可以利用一个已经存在的,合适的事件驱动程序通过输入核心和用户应用程序接口。(事件驱动程序不需要我们去实现)
输入子系统带来了如下好处: 1.统一了物理形态各异的相似的输入设备的处理功能 2.提供了用于分发输入报告给用户应用程序的简单的事件接口 3.抽取出了输入驱动程序的通用部分,简化了驱动,并引入了一致性 如下图,input子系统分三层,最上一层是event handler,中间是intput core,底层是input driver。input driver把event report到input core层。input core对event进行分发,传到event handler,相应的event handler层把event放到event buffer中,等待用户进程来取。
这里写图片描述
在上一篇中分析的设备驱动程序的probe函数中,先对input_dev结构体进行了属性的配置,然后调用了input_register_device函数向input_core注册这个input_dev设备。下面看看input_register_device这个函数在匹配中做了什么:
首先看input_register_device源码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; const char *path; int error; /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); if (!dev->hint_events_per_packet) dev->hint_events_per_packet = input_estimate_events_per_packet(dev); /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ //rep主要是处理重复按键,如果没有定义dev->rep[REP_DELAY]和dev->rep[REP_PERIOD], //则将其赋值为默认值。dev->rep[REP_DELAY]是指第一次按下多久算一次,这里是250ms, //dev->rep[REP_PERIOD]指如果按键没有被抬起,每33ms算一次。 init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250;//这两个设置和按键的灵敏度有关 dev->rep[REP_PERIOD] = 33; } if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); error = device_add(&dev->dev);//将dev加入到linux的设备模型中 if (error) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); pr_info("%s as %sn", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) { device_del(&dev->dev); return error; } //这个函数主要功能时将input_dev与input_handler_list中的handler进行匹配, //匹配过程看input_attach_handler源码 list_add_tail(&dev->node, &input_dev_list); list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return 0; }

下面 input_attach_handler源码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { //先执行input_match_device函数对dev和handler进行匹配, //若匹配成功后,执行handler的connect函数,先看input_match_device函数的匹配 //工程,匹配成功后再看connect函数。 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; }

input_match_device函数匹配过程

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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; //这边的input_dev的各项属性,是在设备的驱动程序中注册的,这里的handler的各项属性是在evdev.c(事件驱动程序) //中赋值的,关于handler的注册将在下面的文章中分析,这里暂时认为这个handler是可以匹配所有的dev 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; }

匹配成功后,调用handler->connect(handler, dev, id)函数,这个函数的定义也是在evdev.c中进行定义的,先
贴出源码进行分析这个函数:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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; 首先补充几个知识点:(这些知识点也会在下一篇的handler注册中介绍) // static struct input_handler *input_table[8]; //#define INPUT_DEVICES 256 // 一共有8个input_handler,对应256个设备,所以一个handler对应32个设备。 // 这个问题在我参加的一次linux驱动的面试中被问到,当时真是汗啊!!! // static struct evdev *evdev_table[EVDEV_MINORS]; // #define EVDEV_MINORS 32 // evdev理论上可对应32个设备,其对应的设备节点一般位于/dev/input/event0~/dev/input/event4 // 下边的for循环,在evdev_table数组中找一个未使用的地方 // evdev_table用来记录此handler下的所有的evdev 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 = kzalloc(sizeof(struct evdev), GFP_KERNEL); if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); //匹配成功后 还在/dev/input下生成eventX节点 dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = true; evdev->minor = minor; //evdev这个结构体中含有handle这个结构体变量,handle中包含了dev结构体和handler结构体 //dev用来记录input_dev,handler用来记录与dev相匹配的handle,并把evdev作为handle //的私有变量 evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; //设置evdevX这个设备的主设备号和次设备号 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); // input_register_handle完成的主要功能是: // list_add_tail_rcu(&handle->d_node, &dev->h_list);将handle->d_node加到dev->h_list中 // list_add_tail(&handle->h_node, &handler->h_list);将handle->h_node加到handler->h_list中 //这样以来无论是先得到dev还是先得到handler都可以通过handle找到与之匹配的handle,handle将dev和handler //紧密联合起来 error = input_register_handle(&evdev->handle); if (error) goto err_free_evdev; //evdev_install_chrdev完成的功能是evdev_table[evdev->minor]=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; }

在connect函数中的evdev结构体,其有结构体变量handle,在handle中又有handler和dev这两个结构体
用来描述相匹配的handler和dev。然后向设备模型添加evdev结构体,完成了整个input_dev和handler的匹配过程

最后

以上就是傲娇蜗牛最近收集整理的关于linux input输入子系统分析(input_dev和handler匹配分析)的全部内容,更多相关linux内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部