linux的自定义input,Linux Input子系统之第一篇(input_dev/input_handle/input_handler)
Input子系统是linux kernel中与部分外围器件驱动联系比较紧密的模块,常用于Sensor,TP(touch panel),power key等器件的驱动。这类模块有个共同特点:字符设备,且数据量都不大,比如sensor一般最多只有xyz三个维度的数据。
整体来看,Input子系统有一个主线,那就是题目中这三个结构体的关系(下面简称为三方关系),input_dev对应于实际的device端,input_handler从名字也可以猜出来是对device的处理。“处理”这个词语不单单指的是对device数据的处理,比如report等;它其实可以包括系统在该device事件发生时想做的任何动作。至于input_handle,它是连接input_dev与input_handler的,该设计后面也会详细分析。在这里请记住,我们最终的目的是,通过input_dev,可以遍历所有与它有关的input_handler;通过input_handler,也可以遍历所有与它有关的input_dev。
为了更加透彻地讲述Input子系统,本博文将分两篇介绍,第一篇就来分析上面这个主线,第二篇分析Input子系统的A/B两个协议(B协议又称为Slot Protocol)。下面从input_device说起。
驱动端通过input_allocate_device来allocate对应的input_dev结构体,之后持有该指针,并完成对应的初始化(name, set_bit等等)。
点击(此处)折叠或打开
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
...//I deleted many lines here.
INIT_LIST_HEAD(&dev->h_list);//this is the head of the list which consists of related input_handles.
...
}
return dev;
}
结合这段代码,看下input_dev结构体(这里只关心主线相关的结构体成员)
点击(此处)折叠或打开
struct input_dev {
...//detailed info of this device
struct list_head h_list;//这是与input_dev相关联的input_handle的链表的表头
struct list_head node;//链入全局链表
};
驱动完成初始化后,调用input_register_device来注册已经初始化的input_dev,这个函数是三方关系的核心,它调用了input_attach_handler,而恰恰是在input_attach_handler这个函数内dev, handler和handle这三者确定了关系。看下这个函数究竟做了哪些关键的事情,
点击(此处)折叠或打开
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
...//此次省略一千行O(∩_∩)O~
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);//set the dev name here: input0,input1,...
list_add_tail(&dev->node, &input_dev_list);//input_dev_list is a global list! So every input_dev will be listed.
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);//dev and handler, we fould it.
...//同上
return 0;
}
上面的代码,对dev做进一步的设置,同时也将dev链接到全局链表中;但它最重要的功能还是体现在11、12行。
到了这里不得介绍下input_handler_list与input_handler,它是系统中所有handler挂载的链表的表头,自定义的handler必须挂载到该链表才有可能被系统所用(调用input_register_handler)。input_handler的作用上面简单提了,定义如下
点击(此处)折叠或打开
struct input_handler {
...
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//important
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
...
const struct input_device_id *id_table;
struct list_head h_list;//这是与input_handler相关联的input_handle的链表的表头
struct list_head node;//链入全局链表 h_list node~是否注意到这与input_dev的最后两个一模一样呢,事实上他们名字与作用都一样
};
完成了必要的背景介绍,继续input_attach_handler,这个函数的逻辑与具体的handler是强相关的,下面就以input子系统默认的evdev_handler为例进行分析。
点击(此处)折叠或打开
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 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);//判断handler是否与dev match.通过handler的id_table、match等实现,比较简单,不做展开。
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);//这之前dev与handler还是彼此独立的,connect直接产生我们关注的三方关系
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
看看evdev_handler的connect究竟为这个三方关系做了什么吧,
点击(此处)折叠或打开
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
...
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
...
evdev->handle.dev = input_get_device(dev);//这里的handle就是input_handle,它的dev成员指向input_dev
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;//它的handler成员指向input_handler
evdev->handle.private = evdev;
...
error = input_register_handle(&evdev->handle);//10、12行完成了handle到dev和handler,这个函数完善了所有的三方关系
return 0;
...//error handling
}
点击(此处)折叠或打开
struct input_handle {
...
struct input_dev *dev;//上段代码的第10行
struct input_handler *handler;//上段代码的第12行
struct list_head d_node;//链入input_dev的h_list代表的链表
struct list_head h_node;//链入input_handler的h_list代表的链表
};
点击(此处)折叠或打开
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
...
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);//上段代码的第5行的注释
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
...
list_add_tail_rcu(&handle->h_node, &handler->h_list);//上段代码的第6行的注释
...
return 0;
}
至此,三方关系形成完毕。我们实现了最终的目的,通过input_dev,可以遍历所有与它有关的input_handler;通过input_handler,也可以遍历所有与它有关的input_dev。
图解如下:图中单向箭头表示指针,双向箭头表示list_head。可以看出,从任何一个双向箭头出发,通过handle的过度,完全实现了我们的最终目标。掌握了这点,再看input_report那些流程的时候就非常容易了,dev想要report数据的时候无非是调用了handler的event函数指针指向的函数,我们可以在这个函数里定义任何想让系统去做的任务,比如cpu调频等,而不仅限于数据上报。熟悉面向对象编程的人可能想到了,其实这个设计运用了面向对象的observer设计模式。
至此,本文主要内容完结。掌握了这些知识固然重要,但还需要简单分析下这个三方关系的设计思路。
从本质上讲,input_dev与input_handler是一个多对多的关系,一个dev可以对应多个handler,一个handler也可以对应多个dev(参考上图)。
针对这种多对多的关系,也许有人会想,为什么不将input_handle的dnode,hnode分别内嵌到dev和handler内,这样也可以节省空间;实际上,内嵌的方式最终实现的是混乱的一对多的关系,因为指针的指向是唯一的,所以当两个不同的dev有一个共同的handler的时候,两个链表相交,那么后注册的dev会改变前一个dev的链表,导致混乱。
实际上,input_handle可以拆成两部分,dnode一部分,hnode一部分,dnode来表达一个dev可以对应多个handler,hnode来表达一个handler也可以对应多个dev。这两部分独立存在也一样可以实现input子系统的功能,而且理解起来更加简单;将他们合并起来节省了空间。
希望本文能让看了它的人,遇到多对多模型的类似问题时,能够记起这个三方关系。
相关文章:

为什么不记录慢速查询?
㈠ 底:2014/8/18 13点37分收到前端说反馈有玩家掉线情况,检查CPU、慢查询、DB请求量,并未发现异常,DB表现一如往常。㈡ 定位原因:INSERT INTO t (col1, col2, col3, col4, col5, col6, col7) VALUES (3532082239485507…

docker常用命令详解
docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来。 根据自己的理解,总的来说分为以下几种: Docker环境信息 — docker…

Unity3D脚本属性
Unity3D的脚本属性用法: // JavaScriptscript AddComponentMenu ("Transform/Follow Transform") // CSharp [AddComponentMenu("Transform/Follow Transform")] 以下是具体说明(部分无关紧要的不翻译): Add…

Linux下查看.so和可执行文件是否debug编译
如何判断一个.so是否是debug编译的? 如果用此方法:用file来查看一个.so, 根据是否包含”not stripped”来判断该.so是否是debug编译的。然而stripped/not stripped并不是debug/release编译的判断标准. 对debug和release的.so运行file后可得出几乎相同的输…

UE商城资源 Motion Symphony 运动匹配插件
UE商城资源 Motion Symphony 运动匹配插件 Unreal Engine虚幻游戏引擎素材资源 Unreal Engine Marketplace –Motion Symphony 1.05 4.26运动交响曲插件 插件大小解压后:346M 资源大小共 2G 含官方文档 和官方使用视频教程(共100分钟 1920X1080 mp4 中…

linux下出现重定义,Oracle Online Redefinition在线重定义
在线重定义特性进行数据表Online的结构变动操作。本篇我们从一个较复杂的案例出发,讨论复杂变化情况下如何进行Online Redefinition,以及dbms_redefinition包各个关键方法的作用。3、一个分区表的重定义动作我们定义一个数据表T。SQL> create table t…

Lr IP欺骗设置
IP欺骗设置IP工具:IP Wizard 开启IP欺骗时会关闭DHCP(也就是关闭IP自动获取 更改为手动设置IP) 注:添加IP欺骗,和释放IP,都要重启机器后才会生效,IP Wizard要管理员身份运行; 在con…

2022-2028年中国异戊二烯橡胶产业竞争现状及发展规模预测报告
【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新(交付时间约3个工作日) 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国异戊二烯橡胶行业市场行业相关概述、中国异戊二烯橡胶行业市场行业运行环境、分析了中国异…

Mysql新安装服务启动失败
#备注如果新安装的mysql启动报错,请检查my.cnf文件的innodb_buffer_pool_size设置的值,最好为内存的总大小的70%。转载于:https://blog.51cto.com/azhuang/1553167

js实现图片上传本地预览
演示地址:https://xibushijie.github.io/static/uploadImg.html <!DOCTYPE> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title>图片上传本地预览</title><style…

Unity增强现实初学者指南视频教程 A Beginner’s Guide to Augmented Reality with Unity
Unity增强现实初学者指南视频教程 A Beginner’s Guide to Augmented Reality with Unity MP4 |视频:h264,1280720 (部分1920X1080) |音频:AAC,44100 Hz 语言:英语中英文字幕(根据原英文字幕机译更准确&a…

c语言中变量有什么作用是什么,C语言里面局部变量和临时变量有什么区别?
typedefexternstatic_Thread_localregister其中,除了 typedef (放在这里仅仅是为了描述语法方便),其它几个(配合变量声明的位置)描述了的变量的 linkage 和 storage duration。但是 storage class specifier 跟 linkage / storage duration 并不是一一对…

Android Acitivity 生命周期
Fragment 的生命周期: Android Fragment 生命周期及其API使用(建议使用自定义View替换Fragment) Activity的生命周期: (1)启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用…

Docker入门六部曲——基本引导
原文链接:http://www.dubby.cn/detail.html?id8733 预备知识 虽然我们接下来还是会介绍很多概念,但是最好还是提前了解什么是Docker,和为什么你会使用Docker。 我们假设你对下面这些知识比较熟悉: IP地址和端口虚拟机编辑配置…

fragment切换事件
2019独角兽企业重金招聘Python工程师标准>>> 我使用fragment fragmenttabhost的时候,如果切换tab,对应的Fragment就会执行onDestroyView ,再切换回来又会执行onCreateView(),如此反反复复。destroyView ,c…

quartz关闭DBUG日志
使用quartz调度任务,每次启动产生大量debug日志,机器都要被累死了。 试过很多方法都不好使,包括在log4j.properties里配置 quartz源代码,发现它的日志输出用的是slf4j,而不是log4j,所以想到用logback.xml来控制。 把他…

UE卡通风格游戏场景制作视频教程
UE卡通风格游戏场景制作视频教程 UE卡通风格游戏场景制作视频教程 教程大小:4.53G 含项目文件 3840X2160 mp4 语言:英语中英字幕(机译) 本教程是关于UE4卡通渲染游戏环境场景制作训练视频教程,时长:4小时…

c语言顺序表有效元素长度,用C语言描述的顺序表类型
2.2.1 顺序表用C语言描述的顺序表类型如下所示:// 存储结构const int MAXLISTSIZE80; // 预设的存储空间最大容量typedef struct {ElemType *elem; // 存储空间基址int length; // 当前长度int listsize; //允许的最大存储容量(以sizeof(E…

css样式之边框和内外边距
1、css样式之边框:border 实心的边框: <!DOCTYPE html><html> <head> <meta http-equiv"content-type" content"text/html;charsetutf-8"> <title>页面一</title> </head> <body>…

2022-2028年中国乙烷行业投资分析及前景预测报告
【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新(交付时间约3个工作日) 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国乙烷行业市场行业相关概述、中国乙烷行业市场行业运行环境、分析了中国乙烷行业市场行业的…

SQL Server 中master..spt_values的应用
今天在做数据分析报表的时候遇到一个这样的问题。表结构如下。部门编码、部门名称、部门人员ID(中间用逗号分割)我想通过和人员表链接,查询出一个新的数据集,查询出的结果集格式如下:人员信息(ID或者姓名&a…

ora-1031解决一例
今天建立了一个测试环境,打算再次测试logical standby的建制。在建制物理standby时,发现archive log无法传递到standby,手工可以。察看log,发现如下错误: Errors in file c:\oracle\product\10.2.0\admin\it\bdump\it_arcp_2116.trc: ORA-010…

Revit:概念建模环境技能学习 Revit: Conceptual Modeling Environment
Revit:概念建模环境技能学习 Revit: Conceptual Modeling Environment MP4 |视频:h264,1280720 |音频:AAC,44.1 KHz,2 Ch 云桥网络 平台huo取 教程 技能水平:高级|语言:英语中英文字幕(根据原英文字幕机译更准确&…

1 23 456c语言,2014年计算机二级考试C语言模拟题(1)
2014年计算机二级考试C语言模拟题(1)21.下列程序的运行结果为( )。#includemain(){struct date{int year,month,day;}today;printf("%d\n",sizeof(struct date));}A.8B.6C.10D.1222.有以下程序:#include<struct s…

屏蔽浏览器默认样式 user agent stylesheet
user agent stylesheet <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> table{border-collapse: collapse;border-spacing: 0;} 转载于:https://www.cnblogs.com/jxk…

Docker入门六部曲——服务
原文链接:http://www.dubby.cn/detail.html?id8735 准备 已经安装好Docker 1.13或者以上的版本。安装好Docker Compose。如果你是用的是Docker for Mac和Docker for Windows,那么不用担心,这是自带的。如果是Linux,那么需要你去…

AngularJs学习笔记--Forms
控件(input、select、textarea)是用户输入数据的一种方式。Form(表单)是这些控件的集合,目的是将相关的控件进行分组。 表单和控件提供了验证服务,所以用户可以收到无效输入的提示。这提供了更好的用户体验…

天秤座的爱情(转)
若不是秤子们爱上了爱情,或许,也就不用独自承受那么多。他们的爱情就像在上演的一部电影,他们就在这出戏里,眼看着它从开始到结束。落幕并不可怕,秤子们在乎的是他们成就了一部电影。 秤子们不喜欢落入俗套中的爱情。与…

虚幻引擎C++终极射手教程 Unreal Engine C++ The Ultimate Shooter Course
虚幻引擎C终极射手教程 Unreal Engine C The Ultimate Shooter Course MP4 |视频:h264,1280720 |音频:AAC,44.1 KHz,2 Ch 语言:英语中英文字幕(根据原英文字幕机译更准确) |时长:55节课(8小时53分钟)|大小…
Nio得知3——该示范基地:多路复用器模式
Reactor模式和NIO 本文可以看作是Doug Lea Scalable IO in Java一文的翻译。当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作。他们都有一个共同的结构: 1. Read request 2. Decode request 3. Process service 4. Enco…