Linux input子系统简介
1.前言
本文主要对Linux下的input子系统进行介绍
2. 软件架构

图 input子系统结构图
input子系统主要包括三个部分:设备驱动层、核心层和事件层。我们可以分别理解为:具体的输入设备、过度设备和逻辑设备。对于用户空间来说与之直接交互的只有逻辑设备也就是事件层。
Input子系统主要包含两条路径(主要讲述第一条路径):
- 第一条路径
设备驱动层捕获事件并向核心层报告->核心层将事件交由事件层处理->用户空间读取事件层处理的数据
- 第二条路径
用户空间写入事件数据->事件层生成事件->调用核心层传递事件信息给驱动层->设备驱动层驱动硬件设备
3.系统组件之间的关联

图 input_dev input_handle input_handler之间的关联
通常input_dev、 input_handle、input_handler三者的关系如上图所示。
- input_dev
input_dev通过node链接进全局链表input_dev_list中;通过h_list将与其关联的所有input_handle链接起来,这说明每个input_dev可能会有多个handle与其连接
- input_handler
input_handler通过node节点链接进全局链表input_handler_list中;通过h_list将与其关联的所有input_handle链接起来,这说明每个input_handler可能会有多个handle与其连接
4. 系统组件
4.1 input_handle
| Elemete Name | input_handle |
| Path | include/linux/input.h |
| Responsiblities |
用于连接input_dev和input_handler,由input_handler创建 |
| Attributions |
|
| Operations |
4.2 input_dev
| Elemete Name | input_dev |
| Path | include/linux/input.h |
| Responsiblities |
代表一个input设备,如按键设备、触摸屏设备等 |
| Attributions |
|
| Operations |
4.3 input_handler
| Elemete Name | input_handler |
| Path | include/linux/input.h |
| Responsiblities |
input_dev的逻辑表示,用于和用户空间交互,为input_dev实现一堆接口 |
| Attributions |
|
| Operations |
4.4 evdev
| Elemete Name | evdev |
| Path | drivers/input/evdev.c |
| Responsiblities |
Evdev是最上层代表 input设备的结构体,它代表一个字符设备,用户可以open ,close等等fop操作 |
| Attributions |
|
| Operations |
4.5 evdev_client
| Elemete Name | evdev_client |
| Path | drivers/input/evdev.c |
| Responsiblities |
Evdev_client代表打开evdev设备的用户,用来管理input event buffer |
| Attributions |
|
| Operations |
5.关键流程
5.1 input_init
input_init->
class_register(&input_class)
input_proc_init()
register_chrdev_region->
__register_chrdev_region
主要完成了input子系统的注册(通过注册subsys->kobject),同时创建字符设备char_device_struct,并将指针插入到全局哈希表chrdevs中
- class_register
class_register实际上是分配struct subsys_private并放到全局的kobject层级结构中。
我们知道内核中任何一个设备或类的实体的元模型就是kobject,任何一个设备或类都将其kobject放入到全局kobject层级结构中。
而kset可以理解为是kobject的容器,一个子系统与一个kset是对应的。
subsys_private是对kset的进一步封装,其中的成员变量struct kset subsys就代表子系统,
class_register实际完成的工作就是将subsys->kobject放入到全局的kobject层级结构中,从而完成了子系统的注册。
- input_proc_init
创建/proc/bus/input/devices和/proc/bus/input/handlers两个目录。 cat /proc/bus/input/devices可以看到当前系统注册了哪些input设备; cat /proc/bus/input/handlers可以看到当前系统注册了哪些handlers
- register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input")
主要用来分配次设备号。从主设备号INPUT_MAJOR(),次设备号0开始,注册INPUT_MAX_CHAR_DEVICES()个设备,所有设备有一个共同的名字”input”。
核心是调用__register_chrdev_region,后者会对每一个字符设备分配一个char_device_struct结构体,并将结构体指针插入到全局chrdevs哈希表中。
5.2 input_register_device
input_register_device->
devres_alloc
__set_bit(EV_SYN, dev->evbit)
__clear_bit(KEY_RESERVED, dev->keybit)
input_cleanse_bitmasks(dev)
input_estimate_events_per_packet
init_timer(&dev->timer)
dev_set_name
device_add
kobject_get_path
list_add_tail
input_attach_handler->
input_match_device
input_wakeup_procfs_readers
input_register_device首先设置了相关的属性支持事件等,然后调用device_add将input_dev注册进设备驱动模型,同时连接进全局链表input_dev_list中,注册过程中会匹配handler,调用handler->connect,实际上handle的创建就是在connect 中完成的,后文会提到。
- devres_alloc
如果采用设备资源框架来分配则调用此函数分配input_dev,用此种方法分配的input_dev无需ungegister 或 free;
- __set_bit(EV_SYN, dev->evbit)
每个设备都支持EV_SYN事件;
- __clear_bit(KEY_RESERVED, dev->keybit)
KEY_RESERVED不支持发送给用户空间;
- input_cleanse_bitmasks(dev)
确保除了设置显示支持的事件以外,其它事件都被清空;
- input_estimate_events_per_packet
估算每个packet包含的事件长度,主要鼠标和触摸屏用到;
dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
- init_timer(&dev->timer)
初始化自动重复发送定时器,定时器处理函数为input_repeat_key,默认按下超过250ms开启自动开启重复发送按键,每33ms发送一次;
- dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
设置input设备的名字
- device_add
这个是 input设备注册的核心语句,完成向设备驱动模型的注册。
- kobject_get_path(&dev->dev.kobj, GFP_KERNEL)
获取sysfs中设备的节点路径
- list_add_tail(&dev->node, &input_dev_list)
向全局input_dev_list加入此节点
- list_for_each_entry(handler, &input_handler_list, node)
- input_attach_handler(dev, handler);
从全局的input_handler_list遍历每个handler执行input_attach_handler,完成input_device和input_handler的连接,一旦匹配成功,将调用handler->connect函数
- wait_wakeup_procfs_readers
5.3 input_register_handler
input_register_handler->
list_add_tail
input_attach_handler->
evdev_connect->
kzalloc(sizeof(struct evdev), GFP_KERNEL)
init_waitqueue_head
dev_set_name(&evdev->dev, "event%d", dev_no)
device_initialize(&evdev->dev)
input_register_handle(&evdev->handle)
cdev_init
cdev_add
device_add
以evdev handler为例,在注册input_handler过程中一旦与input_dev匹配将通过handler->connect创建handle并注册之,同时connect将完成evdev的初始化,并为整个input子系统添加一个字符设备,evdev作为一个device还将通过devcie_add注册进设备驱动模型
- list_add_tail
将handler加入到全局input_handler_list链表中,便于遍历;
- list_for_each_entry(dev, &input_dev_list, node)
- input_attach_handler(dev, handler);
对input_dev_list全局input_dev链表遍历,建立input_dev与input_handler的关联。实际上input_match_device匹配成功,会handler->connect来创建对应的handle
- evdev_connect
()kzalloc(sizeof(struct evdev), GFP_KERNEL)分配一个evdev结构体; ()init_waitqueue_head(&evdev->wait);初始化等待队列; ()dev_set_name(&evdev->dev, "event%d", dev_no);设置evdev的名字 ()初始化evdev->handle成员变量; ()device_initialize(&evdev->dev),evdev是一个device,需要加入设备驱动模型,此处初始化device; ()input_register_handle(&evdev->handle)注册input_handle,见下文 ()cdev_init(&evdev->cdev, &evdev_fops)初始化evdev->cdev字符设备的fos为evdev_fops ()cdev_add(&evdev->cdev, evdev->dev.devt, )将cdev设备注册进系统,此处可以看出整个input子系统只注册一个字符设备。
5.4 input_handle_register
input_handle注册,主要将handle->d_node连入对应的input_dev->h_list链表;主要将handle->d_node连入对应的input_handler->h_list链表;
5.5 evdev_open
evdev_open->
evdev_compute_buffer_size
kzalloc(size, GFP_KERNEL | __GFP_NOWARN)
evdev_attach_client
evdev_open_device
- evdev_compute_buffer_size
计算需要分配的存放事件的buffer大小
- kzalloc(size, GFP_KERNEL | __GFP_NOWARN)
创建evdev_client代表有一个用户打开了此设备。
- evdev_attach_client
建立evdev_client与evdev的关联
- evdev_open_device(evdev)
增加open的引用计数,调用input_open_device (&evdev->handle),后者最终会调用input_dev->open函数
5.6 evdev_read
evdev_read->
evdev_fetch_next_event
input_event_to_user
wait_event_interruptible
- evdev_fetch_next_event
for循环中通过此函数来从evdev_client->buffer取出一个input_event
- input_event_to_user
将input event解析完后发送给user buffer;
- wait_event_interruptible
如果file->f_flags定义为非O_NONBLOCK则代表是阻塞,如果buffer里面没有数据则会在此处阻塞等待,直到有数据将其唤醒(可参照后面的解释)
5.7 input_event
input_event->
input_handle_event->
input_pass_values->
input_to_handler->
handler->event, evdev_event->
evdev_events->
evdev_pass_values->
__pass_event
wake_up_interruptible
input_event会在input_report_key等被调用,不同的事件会进行不同的封装。此函数最终将input event上报存放在evdev_client的buffer里面供用户层取用,如果定义为阻塞,则此处需要唤醒阻塞的读。
5.8. evdev_write
evdev_write->
input_event_from_user
input_inject_event->
input_handle_event
- input_event_from_user
将用户空间数据拷贝到evdev_client->buffer中;
- input_inject_event
将根据事件方向调用input_handle_event进行写入,应用场景可以是震动马达,LED灯点亮等
Linux input子系统简介的更多相关文章
- Linux input子系统 io控制字段【转】
转自:http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html http://blog.csdn.net/guoshaobei/arc ...
- Linux input子系统分析
输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...
- Linux Input子系统浅析(二)-- 模拟tp上报键值【转】
转自:https://blog.csdn.net/xiaopangzi313/article/details/52383226 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...
- Linux Input子系统
先贴代码: //input.c int input_register_handler(struct input_handler *handler) { //此处省略很多代码 list_for_each ...
- Android驱动之 Linux Input子系统之TP——A/B(Slot)协议
将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围大多数用到input子系统的开发人员也不甚理解:另一方面是由于这部分知识一旦扩展到TP(触摸屏Touch Panel)的多点触 ...
- Android驱动之 Linux Input子系统之TP——A/B(Slot)协议【转】
转自:http://www.thinksaas.cn/topics/0/646/646797.html 将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围大多数用到input子 ...
- linux input子系统 — TP A/B(Slot)协议【转】
本文转载自:http://blog.csdn.net/u012719256/article/details/53609906 将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围 ...
- Linux input子系统编程、分析与模板
输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主设备号13,同时输入子系 ...
- Linux input子系统实例分析(二)
紧接着上一节的实例我们来分析调用的input子系统的接口: 1. input_dev,用来标识输入设备 1: struct input_dev { 2: const char *name; //设备名 ...
随机推荐
- 解析 Android Things 技术原理
2012 年 6 月,由 IoT-GSI(Global Standards Initiative on Internet of Things)发布的白皮书“ITU-T Y.4000/Y.2060”[1 ...
- 自学Python6.4-内置模块(2)
自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...
- python操作oracle实战
import cx_Oracle conn = cx_Oracle.connect('ua_test/ua_test@192.32.98.15/oracledb') cur1 = conn.curso ...
- Java -- JDBC 学习--数据库连接池
JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模式开 ...
- AtCoder Regular Contest 066 F Contest with Drinks Hard
题意: 你现在有n个题目可以做,第i个题目需要的时间为t[i],你要选择其中的若干题目去做.不妨令choose[i]表示第i个题目做不做.定义cost=∑(i<=n)∑(i<=j<= ...
- CF848E Days of Floral Colours——DP+多项式求逆/分治NTT
官方题解:http://codeforces.com/blog/entry/54233 就是由简入繁 1.序列处理,只考虑一个半圆 2.环形处理(其实这个就是多了旋转同构) 然后基于分割线邻居的跨越与 ...
- [luogu2114][起床困难综合症]
luogu2114 思路 因为位运算对于每一位是独立的,所以对每一位都对这n个数进行操作,然后观察最后得出的是1还是0.并且保证每一位拼起来之后要比m小. 代码 #include<cstdio& ...
- 【数学】NOIP数论内容整理
NOIP数论内容整理 注:特别感谢sdsy的zxy神仙以及lcez的tsr筮安帮助审稿 一.整除: 对于\(a,b~\in~Z\),若\(\exists~k~\in~Z\),\(s.t.~b~=~k~ ...
- 使用sass切图
明日复明日,明日何其多!我生待明日,万事成蹉跎!ruby挺可爱的.没有进入工程项目中使用sass. 尝试是一件快乐的事情.在页面中直接引进编译出来的css即可.
- ZOJ_3950_How Many Nines 解题报告及如何对程序进行测试修改
The 17th Zhejiang University Programming Contest Sponsored by TuSimple Solution: #include <stdio. ...