10_6 input输入子系统,流程解析

news2024/9/25 7:15:54

简单分层

在这里插入图片描述

应用层
内核层
---------------------------
input handler 数据处理层 driver/input/evdev.c
	1.和用户空间交互,实现fops
	2.不知道数据怎么得到的,但是可以把数据上传给用户

---------------------------
input core层
	1.维护上面和下面的两个链表
	2.为上下两层提供接口
	
----------------------------
input device层---driver/input/input.c
	1.初始化硬件,获取硬件数据
	2.知道数据是什么样,不知道如何把数据给用户

具体

第一步

注册顺序最开始应该是 input_coer层,锁定函数 input/input.c
核心层,得创建链表把,图中的两个链表,方便进行匹配
class_register() //注册这个输入子系统类,同时这个类的主设备号都分配为13
同时这里两个链表
第一个是 input_handler_list 存放结构体 input_handler 可以理解是对这个输入dev的操作实例或方案
第二个是 input_dev_list 这么多输入设备,需要放进来和第一个链表进行匹配

1.input_coer应该是第一个核心层,得创建链表把,看看input_.c文件  !!!! 这里为啥跑到input.c了
同时input.c中还注册了主设备号为13的类和fops
	input_init(void)
		class_register(&input_class);
		err = input_proc_init();//感觉像bus总线的新玩法,注册bus总线上的input子系统
			proc_bus_input_dir = proc_mkdir("bus/input", NULL);
				entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fileops);
				//应该是bus总线里面的注册device文件夹
				static const struct file_operations input_devices_fileops = { //对这个文件夹里面的文件增加fops
					.owner		= THIS_MODULE,
					.open		= input_proc_devices_open,
					.poll		= input_proc_devices_poll,
					.read		= seq_read,
					.llseek		= seq_lseek,
					.release	= seq_release,
				};
				entry = proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);
				//应该是bus总线里面的注册handlers文件夹
		err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");//这里的主设备号是13
static LIST_HEAD(input_dev_list); //全局static 初始化链表
static LIST_HEAD(input_handler_list);//全局static 初始化链表
第二步

input handler 数据处理层
input/evdev.c

为了构建input_handler 先看看handler结构体里面有些什么
里面有主次设备号,还有fops操作参数,看起来就是能创建设备节点的 这里的主设备号次设备号还是64
那就是/dev/input/event 开始的设备号

//如果看struct input_handler 有下面这些成员 和/dev/input/event 13 64 里面次设备一致
struct input_handler(
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	file_operation *fops = evdev_ops
	int minor; =EVDEV_MINOR_BASE 64
	.connect	= evdev_connect,
	.event		= evdev_event,
	}
上面构造号后 就注册到 input_hadle_list 链表中  这里看图就不太对了  不是在中间层注册到嘛
input_handler层中 叫数据处理者  注册进core层的链表 就是看看哪些数据能被处理

假如上面的handler和下面的input_dev匹配成功 就直接调用 handler中的connect()方法
connect()方法会做以下事情,1 创建设备节点 如/dev/event0 主次设备编号13 64	
2 创建创建input_dev对象  
	1.input_dev里面有event clinet(描述的缓冲区对象)  这个缓冲区是个队列 每个队列都是struct input_dev结构体
	2.input_dev里面有handle 对象 里面放了handler指针和dev指针  我也画了图了  
		所以说 evdev对象就能有handle 就能找到input_dev* 和event_hadle*
	connect完就等下一层上报数据了
好开始读代码 一步一步来
static int __init evdev_init(void) //驱动程序的函数,自动注册
	return input_register_handler(&evdev_handler);//注册了一个 evdev_handler结构体

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.events		= evdev_events,
	.connect	= evdev_connect,

第三步

input_coer层
上面第二步调用了input_register_handler()函数
这个函数其实在 input_coer层 为了把上面的 evdev_handler 注册进链表

struct input_dev *dev;
input_register_handler(struct input_handler *handler) //就是我们的handler
	list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler);//遍历链表,就是把core层两个链表进行匹配
		id = input_match_device(handler, dev);//根据id进行匹配
		error = handler->connect(handler, dev, id); //这里调用了 handler的connect函数
第四步

input handler 数据处理层
input/evdev.c
匹配成功后,注册进入链表的 evdev_handler结构体的.connect函数被调用
好的又回去 input handler 层了
重点看图中 这里调用了connect函数后
1.生成了 对象 evdev
2.创建设备节点 /dev/input/event0
先说第一点生成了 对象 evdev

生成了 对象 evdev
这个evdev对象 里面会有两个对象 evdedv_client 和 input_handle 注意这个地方是handle

input_handler evdev_handler.connect	= evdev_connect,
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//初始化evdev结构体
	INIT_LIST_HEAD(&evdev->client_list); //初始化里面的client_list ,也就是后面说的buf
	init_waitqueue_head(&evdev->wait); //初始化里面的等待队列
	//下面是初始化evdev的handle 也就是用 handle 连接了handler层和input device层
	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler; //handle的作用是能指向handler
	evdev->handle.private = evdev; //handle的作用是能指向evdev
	//注册这个handle
	input_register_handle(&evdev->handle);
	
第五步

input handler 数据处理层
input/evdev.c
上面的connect的第二点还没说完
2.创建设备节点 /dev/input/event0

input_handler evdev_handler.connect	= evdev_connect,
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);//查找次设备号看哪个能用
	//注意这里的dev是device  就是字符设备哪个device
	//创建设备节点,之前我们都是用device_create(),其实就是做了下面的事情
	dev_set_name(&evdev->dev, "event%d", dev_no);
	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);//这里主设备号13 次设备号从65开始
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);
	device_add(&evdev->dev)
	cdev_init(&evdev->cdev, &evdev_fops);  //cdev的fops在这里
	cdev_device_add(&evdev->cdev, &evdev->dev);
note:以前用device_create()创建设备节点
device *device_create(struct class *class, struct device *parent,
     dev_t devt, void *drvdata, const char *fmt, ...)
	//这个函数要的参数上面竟然都有
	device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	device_initialize(dev);
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->groups = groups;
	dev->release = device_create_release;
	device_add(dev);
	//所以知道了 上面就是在创建设备节点		

做完那不就/dev/input/event0 就出来了

第六步

device层
注册自己写的函数

module_init(simple_btn_input_init);
static int __init simple_btn_input_init(void)
	//a, 分配一个input device对象
	btn_input = input_allocate_device();
	//b, 初始化input device对象
	//该设备能够产生哪种数据类型---EV_KEY表示产生按键数据
	btn_input->evbit[0] |= BIT_MASK(EV_KEY);
	//能够产生哪个按键---比如能够产生下键 KEY_DOWN, KEY_ESC
	// btn_input->keybit[108/32] |= 1<<(108%32);
	btn_input->keybit[BIT_WORD(KEY_DOWN)] |= BIT_MASK(KEY_DOWN);
	//c, 注册input device对象
	ret = input_register_device(btn_input);
		//这个函数里面最后也是调用了
		handler->connect(handler, dev, id);//匹配成功就是handle的connect方法,也就是 evdev_connect()

note: 可能这里要问了,有没有和palntfrom一样的匹配规则呢 啥设备树匹配 name匹配的
我们看到handler层的注册的结构体 input_handler evdev_handler
evdev_handler.id_table =evdev_ids
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices / //这里的意思是匹配所有设备,来了就匹配,我不要规则
{ }, /
Terminating zero entry */
};
那为啥还有个idtable 拿来匹配呢
是因为我们用的是公共驱动,所有都匹配 但是有其他的handler驱动,需要用支持哪些输入事件和键值对 来看是否能匹配这个设备了
举个例子在input_hadler这一层 我们看的是evdev.c这个万能驱动 起始还有mousedevhandler mousedev.c鼠标handler 和joydey_handle游戏杆的handler
所以鼠标设备会和 evdev.c匹配 也会和鼠标handler匹配 所以鼠标插入的时候 有个/dev/input/event0 和 /dev/input/mouse0
起始两个是同一个设备 所以用哪个都可以

第7步

应用程序调用open()
到vfsopen 根据设备号找到cdev
到驱动的open函数
我们之前是在core层 申请的设备号 所以找到了 input.c的代码 这个地方也要回顾为啥是到这里!!!
因为这里又register_chrdev_region()
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, “input”);
在input_open函数中 找到input_hadle 那么也能找到input_hadler层的fops
register_chrdev(Input_major,“input”,&input_fops)
input_fops.open()
new_fops = fops_get(hadler->fops)
//把文件节点的fop全改成了 handler层的fop了
file_fop = new_file

第八步

input handler 数据处理层
input/evdev.c
上一步找到了 input handler的open()

//当时的fop是这样注册的
 evdev_connect() //造connect注册了fop
	cdev_init(&evdev->cdev, &evdev_fops);
		evdev_fops.open = evdev_open() //open函数这里 初始化client 
			struct evdev_client *client;
			client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
			client->bufsize = bufsize;
			client->evdev = evdev;
			evdev_attach_client(evdev, client);
			//把文件节点的fop全改成了  handler层的fop了
			file_fop = new_file //使用file节点私有空间传输数据,那read,write都能拿到了client
			evdev_open_device() //查看第三层 input_dev xx层有没有open函数,有的话继续调用  但这里没有
第九步

app开始read
vfs_erad
到evdev.c 开始进行evdev_read 也就是调用到evdev_read()
从之前的client中 拿取最新的上报数据 返回给用户层

evdev_read()
	evdev_client *client = file->private_data;//从fd文件的私有属性拿到client
	evdev *evdev = client->evdev; //从clinet拿到evdev
	struct input_event event;//构造一个要返换给用户空间的结构体 input_event
	if (client->packet_head == client->tail &&(file->f_flags & O_NONBLOCK))
			//如果当前用非阻塞的方式,还没有数据,那应该马上返回
		return -EAGAIN;
	if (!(file->f_flags & O_NONBLOCK)) //正常的阻塞形式
			error = wait_event_interruptible(evdev->wait,client->packet_head != client->tail ||
					!evdev->exist || client->revoked);//这个进程丢进等待队列把,等中断唤醒继续往下走
	//下面是有中断了,阻塞解除,进程继续往下走
	//下面的进行用户空间数据发送
	while()
		evdev_fetch_next_event(client, &event)
			*event = client->buffer[client->tail++];//这里构造input_event,也就是从client里面拿一个buffer
		input_event_to_user(buffer + read, &event)
			copy_to_user(buffer, event, sizeof(struct input_event)//拿了buffer后给到用户空间
第十步

那么中断是谁发起的
就到了我们写的程序 input_device 层

//按下
input_event(btn_input, EV_KEY, KEY_DOWN,  1);
input_sync(btn_input);  //我们的中断函数执行上报数据

INPUT.C  //跑到中间层进行数据封装
	input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
		input_pass_values(dev, dev->vals, dev->num_vals);
				struct input_handle *handle; 
				struct input_value *v;
				handle = rcu_dereference(dev->grab);//从dev中拿到handle
				list_for_each_entry_rcu(handle, &dev->h_list, d_node)//这个也是想办法拿到handle
				handle_event(handle,type,code,value)

第十一步

input handler 数据处理层
input/evdev.c

调用到 input_handler evdev_handler->event = evdev_event
	struct evdev *evdev = handle->private; //通过private找到evdev
	struct evdev_client *client;
	list_for_each_entry_rcu(client, &evdev->client_list, node)//也找到clinet

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1227856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习:欠拟合与过拟合

1 定义 1.1 模型欠拟合 AI模型的欠拟合&#xff08;Underfitting&#xff09;发生在模型未能充分学习训练数据中的模式和结构时&#xff0c;导致它在训练集和验证集上都表现不佳。欠拟合通常是由于模型太过简单&#xff0c;没有足够的能力捕捉到数据的复杂性和细节。 1.2 模型…

Python 自动化(十七)ORM操作

ORM-查询操作 查询简介 数据库的查询需要使用管理器对象 objects 进行 通过 自定义模型类.objects 管理器调用查询方法 查询方法 all()方法 概念与理解 用法&#xff1a;自定义模型类.objects.all()作用&#xff1a;查询自定义模型实体中所有的数据等同于 select * fr…

第八部分:JSP

目录 JSP概述 8.1&#xff1a;什么是JSP&#xff0c;它有什么作用&#xff1f; 8.2&#xff1a;JSP的本质是什么&#xff1f; 8.3&#xff1a;JSP的三种语法 8.3.1&#xff1a;jsp头部的page指令 8.3.2&#xff1a;jsp中的常用脚本 ①声明脚本&#xff08;极少使用&#xf…

【Python仿真】基于EKF的传感器融合定位

基于EKF的传感器融合定位&#xff08;Python仿真&#xff09; 简述1. 背景介绍1.1. EKF扩展卡尔曼滤波1.1.1.概念1.1.2. 扩展卡尔曼滤波的主要步骤如下&#xff1a;1.1.3. 优、缺点 1.2. 航位推算1.3. 目前航位算法的使用通常与卡尔曼滤波相结合使用2. 分段代码 2.1. 导入需要的…

【Go入门】 Go如何使得Web工作

【Go入门】 Go如何使得Web工作 前面小节介绍了如何通过Go搭建一个Web服务&#xff0c;我们可以看到简单应用一个net/http包就方便的搭建起来了。那么Go在底层到底是怎么做的呢&#xff1f;万变不离其宗&#xff0c;Go的Web服务工作也离不开我们第一小节介绍的Web工作方式。 w…

Java --- JVM之垃圾回收相关算法

目录 一、垃圾标记算法 1.1、垃圾标记阶段&#xff1a;对象存活判断 1.2、引用计数算法 1.3、可达性分析算法 1.4、GC Roots 二、对象的finalization机制 2.1、生存还是死亡&#xff1f; 三、查看GC Roots 3.1、使用MAT查看 四、使用JProfiler分析OOM 五、清除阶段算…

系列五、怎么查看默认的垃圾收集器是哪个?

一、怎么查看默认的垃圾收集器是哪个 java -XX:PrintCommandLineFlags -version

SpringBoot项目连接linux服务器数据库两种解决方法(linux直接开放端口访问本机通过SSH协议访问,以mysql为例)

最近找个springboot脚手架重新熟悉一下springboot相关框架的东西&#xff0c;结果发现好像项目还不能直接像数据库GUI工具一样填几个SSH参数就可以了&#xff0c;于是就给他再整一下看看如何解决 linux开放3306&#xff08;可修改&#xff09;端口直接访问 此方法较为方便&am…

【数据结构&C++】二叉平衡搜索树-AVL树(25)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.AVL树的概念二.AVL树节点的定义(代码…

【嵌入式 – GD32开发实战指南(ARM版本)】第2部分 外设篇 - 第3章 温度传感器DS18B20

1 理论分析 1.1 DS18B20概述 DS18B20 是 DALLAS 最新单线数字温度传感器,新的"一线器件"体积更小、适用电压更宽、更经济。Dallas 半导体公司的数字化温度传感器 DS1820 是世界上第一片支持 "一线总线"接口的温度传感器。 DS18B20采用的单总线协议,也…

23.11.19日总结

经过昨天的中期答辩&#xff0c;其实可以看出来项目进度太慢了&#xff0c;现在是第十周&#xff0c;预计第十四周是终级答辩&#xff0c;在这段时间要把项目写完。 前端要加上一个未登录的拦截器&#xff0c;后端加上全局的异常处理。对于饿了么项目的商品建表&#xff0c;之前…

mybatis使用xml形式配置

以这个注解形式的查询代码为例 Select("select * from emp where name like concat(%,#{name},%) and gender #{gender} and entrydate between #{begin} and #{end} order by update_time desc ")public List<Emp> list(String name, Short gender, LocalDat…

SDUT OJ《算法分析与设计》搜索算法

A - 子集和问题 Description 子集和问题的一个实例为〈S,t〉。其中&#xff0c;S{ x1 &#xff0c; x2 &#xff0c;…&#xff0c;xn }是一个正整数的集合&#xff0c;c是一个正整数。子集和问题判定是否存在S的一个子集S1&#xff0c;使得&#xff1a; 。 试设计一个解子…

【Web】Ctfshow SSTI刷题记录1

目录 ①web361 362-无过滤 ②web363-过滤单双引号 ③web364-过滤单双引号和args ④web365-过滤中括号[]、单双引号、args ⑤web366-过滤单双引号、args、中括号[]、下划线 ⑦web367-过滤单双引号、args、中括号[]、下划线、os ⑧web368-过滤单双引号、args、中括号[]、下…

【Go入门】 Go搭建一个Web服务器

【Go入门】 Go搭建一个Web服务器 前面小节已经介绍了Web是基于http协议的一个服务&#xff0c;Go语言里面提供了一个完善的net/http包&#xff0c;通过http包可以很方便的搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由&#xff0c;静态文件&#xff0c…

卷积神经网络(VGG-16)海贼王人物识别

文章目录 前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;我的环境&#xff1a; 2. 导入数据3. 查看数据 二、数据预处理1. 加载数据2. 可视化数据3. 再次检查数据4. 配置数据集5. 归一化 三、构建VGG-16网络1. 官方模型&#xff08;已打包好&#xff…

【win32_000】视频截图

PPT 编译器不会自己添加unicode定义 v 函数 WinMain int __clrcall WinMain([in] HINSTANCE hInstance ,//应用程序的当前实例的句柄。[in, optional] HINSTANCE hPrevInstance ,//应用程序上一个实例的句柄。 此参数始终为 NULL。[in] …

文心一言 VS 讯飞星火 VS chatgpt (138)-- 算法导论11.4 2题

二、用go语言&#xff0c;试写出HASH-DELETE 的伪代码;修改 HASH-INSERT&#xff0c;使之能处理特殊值DELETED。 文心一言&#xff1a; 首先&#xff0c;让我们定义一个基本的哈希表数据结构。这个结构将包括一个存储键值对的哈希表和一个存储已删除键值对的队列。我们可以用…

mac控制台命令小技巧

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 hello伙伴们&#xff0c;作为忠实的mac骨灰级别的粉丝&#xff0c;它真的给我带来了很多效率上的提升。那作为接…

mysql练习1

-- 1.查询出部门编号为BM01的所有员工 SELECT* FROMemp e WHEREe.deptno BM01; -- 2.所有销售人员的姓名、编号和部门编号。 SELECTe.empname,e.empno,e.deptno FROMemp e WHEREe.empstation "销售人员";-- 3.找出奖金高于工资的员工。 SELECT* FROMemp2 WHE…