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了
同时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
那就是/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的代码 这个地方也要回顾为啥是到这里!!!
因为这里又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

调用到 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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 微信小程序登录 新手专用 引入即可

预览 第一步导入插件 在引入的页面的登录按钮下拷贝一下代码 <template><view class"content"><button type"primary" click"login">微信登录</button></view><TC-WXlogin :wxloginwxlogin /> </templ…

YOLOv5 学习记录

文章目录 整体概况数据增强与前处理自适应Anchor的计算Lettorbox 架构SiLU激活函数YOLOv5改进点SSPF 模块 正负样本匹配损失函数 整体概况 YOLOv5 是一个基于 Anchor 的单阶段目标检测&#xff0c;其主要分为以下 5 个阶段&#xff1a; 1、输入端&#xff1a;Mosaic 数据增强、…

SpirngBoot + Vue 前后端分离开发工具代码

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

MIB 6.S081 System calls(1)using gdb

难度:easy In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspecting the variables on the stack is helpful. To learn more about how to run GDB and the common iss…

linux系统环境下mysql安装和基本命令学习

此篇文章为蓝桥云课--MySQL的学习记录 块引用部分为自己的实验部分&#xff0c;其余部分是课程自带的知识&#xff0c;链接如下&#xff1a; MySQL 基础课程_MySQL - 蓝桥云课 本课程为 SQL 基本语法及 MySQL 基本操作的实验&#xff0c;理论内容较少&#xff0c;动手实践多&am…

云课五分钟-0Cg++默认版本和升级-std=c++17

前篇&#xff1a; 云课五分钟-0B快速排序C示例代码-注释和编译指令 视频&#xff1a; 云课五分钟-0Cg默认版本和升级-stdc17 文本&#xff1a; 在Linux系统中&#xff0c;可以通过以下步骤升级g&#xff1a; 打开终端&#xff0c;使用root权限或者sudo权限登录。输入以下命令…

Linux操作文件的底层系统调用

目录 1.概述 2.open的介绍 3.write 的介绍 4.read 5.close的介绍 6.文件描述符 1.概述 C语言操作文件的几个库函数:fopen,fread,fwrite,fclose; 系统调用:open,read,write,close; 系统调用方法实现在内核中;(陷入内核,切换到内核) 2.open的介绍 open重载:两个参数用于打…

Android图片涂鸦,Kotlin(1)

Android图片涂鸦&#xff0c;Kotlin&#xff08;1&#xff09; import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.PointF import android.…

用GPT 搭建一个占星术、解梦、塔罗牌占卜和命理学服务

今天来尝试我们的占星术、解梦、塔罗牌占卜和命理学服务&#xff0c;揭开宇宙的奥秘并获得自我认识 聊天 GPT API 集成的 HTML5 模板。我们的目标是提供易于使用且高度可定制的 API 代码&#xff0c;使您能够训练自己的人工智能解决方案并将其添加到提示中。 我们的产品是可定…

蓝桥杯每日一题2023.11.19

题目描述 “蓝桥杯”练习系统 (lanqiao.cn) 题目分析 首先想到的方法为dfs去寻找每一个数&#xff0c;但发现会有超时 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int n, cnt, a[N]; void dfs(int dep, int sum, int start) {if(dep 4){if(s…

数据结构:红黑树的插入实现(C++)

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 一、红黑树二、红黑树的插入三、代码实现总结 一、红黑树 红黑树的概念&#xff1a; 红黑树是一颗二叉搜索树&#xff0c;但在每个节点上增加一个存储位表示节点的颜色&…

redis三种集群方式

redis有三种集群方式&#xff1a;主从复制&#xff0c;哨兵模式和集群。 1.主从复制 主从复制原理&#xff1a; 从服务器连接主服务器&#xff0c;发送SYNC命令&#xff1b; 主服务器接收到SYNC命名后&#xff0c;开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所…