10_6 input输入子系统,流程解析
简单分层

应用层
内核层
---------------------------
input handler 数据处理层 driver/input/evdev.c1.和用户空间交互,实现fops2.不知道数据怎么得到的,但是可以把数据上传给用户---------------------------
input core层1.维护上面和下面的两个链表2.为上下两层提供接口----------------------------
input device层---driver/input/input.c1.初始化硬件,获取硬件数据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中还注册了主设备号为13的类和fopsinput_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
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_opsint 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) //就是我们的handlerlist_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 ,也就是后面说的bufinit_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的作用是能指向handlerevdev->handle.private = evdev; //handle的作用是能指向evdev//注册这个handleinput_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的代码 这个地方也要回顾为啥是到这里!!!
有可能推理错了,应该是open直接调用evdev的open,需要回去看cdev那节
因为这里又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注册了fopcdev_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都能拿到了clientevdev_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文件的私有属性拿到clientevdev *evdev = client->evdev; //从clinet拿到evdevstruct input_event event;//构造一个要返换给用户空间的结构体 input_eventif (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里面拿一个bufferinput_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中拿到handlelist_for_each_entry_rcu(handle, &dev->h_list, d_node)//这个也是想办法拿到handlehandle_event(handle,type,code,value)
第十一步
input handler 数据处理层
input/evdev.c
第11步,就把中断传输来的 evdev_event数据放入了 event_client队列里面
就等着 app被唤醒后来拿数据就行了
调用到 input_handler evdev_handler->event = evdev_eventstruct evdev *evdev = handle->private; //通过private找到evdevstruct evdev_client *client;list_for_each_entry_rcu(client, &evdev->client_list, node)//也找到clinet
相关文章:
10_6 input输入子系统,流程解析
简单分层 应用层 内核层 --------------------------- input handler 数据处理层 driver/input/evdev.c1.和用户空间交互,实现fops2.不知道数据怎么得到的,但是可以把数据上传给用户--------------------------- input core层1.维护上面和下面的两个链表2.为上下两层提供接口--…...
竞赛选题 题目:垃圾邮件(短信)分类 算法实现 机器学习 深度学习 开题
文章目录 1 前言2 垃圾短信/邮件 分类算法 原理2.1 常用的分类器 - 贝叶斯分类器 3 数据集介绍4 数据预处理5 特征提取6 训练分类器7 综合测试结果8 其他模型方法9 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器学习的垃圾邮件分类 该项目…...
Web前端—移动Web第三天(移动Web基础、rem、less、综合案例—极速问诊)
版本说明 当前版本号[20231120]。 版本修改说明20231120初版 本课程的笔记已经更新完毕,各位可以通过点击《黑马程序员2023新版前端Web开发HTML5CSS3移动web视频教程,前端web入门首选》学习笔记总目录查看所有知识点,同时也能免费下载学习…...
MySQL--慢查询(一)
1. 查看慢查询日志是否开启 show variables like slow_query%; show variables like slow_query_log; 参数说明: 1、slow_query_log:这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。 2、long_query_time:当SQL语句执行…...
【大神支招】3步,打造一张BI报表
随着BI报表的高效直观、灵活分析的特点越来越被大家所熟知,很多BI零基础的用户可积极尝试制作BI报表,以达到灵活自助分析、高效智能分析的效果。那么BI报表零基础的小白们该怎么做BI报表,才能又快又好地做出来? 大神支招…...
【Linux】文件操作
欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析(3) 目录 👉🏻文件是什么?&am…...
(动手学习深度学习)第13章 实战kaggle竞赛:狗的品种识别
文章目录 1. 导入相关库2. 加载数据集3. 整理数据集4. 图像增广5. 读取数据6. 微调预训练模型7. 定义损失函数和评价损失函数9. 训练模型 1. 导入相关库 import os import torch import torchvision from torch import nn from d2l import torch as d2l2. 加载数据集 - 该数据…...
自定义注解+AOP
自定义注解与AOP(面向切面编程)的结合常常用于在应用程序中划定切面,以便在特定的方法或类上应用横切关注点。以下是一个简单的示例,演示了如何创建自定义注解,并使用Spring AOP来在被注解的方法上应用通知。 如何创建…...
Ribbon
在Spring Cloud中,Ribbon是一个用于客户端负载均衡的组件,它可以与其他服务发现组件(例如Eureka)集成,以提供更强大的负载均衡功能。Ribbon使得微服务架构中的客户端能够更加智能地调用其他服务的实例,从而…...
git -1
1.创建第一个仓库并配置local用户信息 git config git config --global 对当前用户所有仓库有效 git config --system 对系统所有登录的用户有效 git config --local 只对某个仓库有效 git config --list 显示配置 git config --list --global 所有仓库 git config --list…...
基于SSM+Vue的鲜花销售系统/网上花店系统
基于SSM的鲜花销售系统/网上花店系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringMyBatisSpringMVC工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 管理员界面 摘要 鲜花销售系统是一个基于SSM(Spring …...
安卓:Android Studio4.0~2023中正确的打开Android Device Monitor
Android Studio4.0~2023 中如何正确的打开Android Device Monitor(亲测有效) 前些天买了新电脑,安装了新版本的Android Studio4.0想试一试,结果就出现了一些问题。 问题引出: Android Device Monitor在工具栏中找不到,后来上网查…...
装备制造企业设备远程运维平台的建设-天拓四方分享
设备远程运维平台是一种基于互联网和物联网技术的设备管理平台,可以实现设备的远程监控、故障诊断、预警维护等功能。近年来,随着云计算、大数据、人工智能等技术的不断发展,设备远程运维平台的智能化程度越来越高,传统的设备运维…...
群晖NAS搭建WebDav服务做文件共享,可随时随地远程访问
文章目录 1. 在群晖套件中心安装WebDav Server套件1.1 安装完成后,启动webdav服务,并勾选HTTP复选框 2. 局域网测试WebDav服务2.1 下载RaiDrive客户端2.2 打开RaiDrive,设置界面语言可以选择中文2.3 点击添加按钮,新建虚拟驱动区2…...
c++调用Lua(table嵌套写法)
通过c调用lua接口将数据存储到虚拟栈中,就可以在lua脚本在虚拟栈中取得数据 c调用lua库,加载lua文件, lua_State* L;//定义一个全局变量***************************L luaL_newstate();luaL_openlibs(L);//打开Lua脚本文件std::string pat…...
算法复杂度分析
文章目录 有数据范围反推算法复杂度以及算法内容一般方法递归 有数据范围反推算法复杂度以及算法内容 c一秒可以算 1 0 7 10^7 107~ 1 0 8 10^8 108次 一般方法 看循环 有几层循环就可以初步分析O( n i n^i ni) 双指针算法除外O(n) 递归 公式法 根据公式的形式࿰…...
几款Java源码扫描工具(FindBugs、PMD、SonarQube、Fortify、WebInspect)
说明 有几个常用的Java源码扫描工具可以帮助您进行源代码分析和检查。以下是其中一些工具: FindBugs:FindBugs是一个静态分析工具,用于查找Java代码中的潜在缺陷和错误。它可以检测出空指针引用、资源未关闭、不良的代码实践等问题。FindBu…...
java springboot测试类鉴定虚拟MVC请求 返回内容与预期值是否相同
上文 java springboot测试类鉴定虚拟MVC运行值与预期值是否相同 中 我们验证了它HTTP的返回状态 简单说 校验了他 是否成功的状态 这次 我们来不对得到的内容 我们 直接改写测试类代码如下 package com.example.webdom;import org.junit.jupiter.api.Test; import org.springf…...
MongoDB随记
MongoDB 1、简单介绍2、基本术语3、shard分片概述背景架构路由功能chunk(数据分片)shard key(分片键值) 4、常用命令 1、简单介绍 MongoDB是一个分布式文件存储的数据库,介于关系数据库和非关系数据库之间,…...
839 - Not so Mobile (UVA)
题目链接如下: Online Judge 这道题刘汝佳的解法极其简洁,用了20来行就解决了问题。膜拜…… 他的解法如下:天平(UVa839紫书p157)_天平 uva 839_falldeep的博客-CSDN博客 我写了两个(都很冗长ÿ…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...
