input子系统
Posted 5念since
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了input子系统相关的知识,希望对你有一定的参考价值。
Linux系统支持的输入设备繁多,例如键盘、鼠标、触摸屏、手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件处理层)、中层(输入核心层)、下层(输入设备驱动层)
输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。即将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报;
核心层为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。承上启下。为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息;
事件处理层是用户编程的接口(设备节点),并处理驱动层提交的数据处理。和用户空间交互。(Linux中在用户空间将所有的设备都当初文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)
input输入子系统分为3层,图中Drivers对应的就是下层设备驱动层,对应各种各样不同的输入设备,Input Core对应的就是中层核心层,Handlers对应的就是上层输入事件驱动层,最右边的代表的是用户空间。
evdev.c、mousedev.c、joydev.c属于输入事件驱动层,input.c输入核心层
(1)从图中可以看出,系统中可以注册多个输入设备,每个输入设备的可以是不同的,例如一台电脑上可以带有鼠标,键盘…。
(2)上层中的各个handler(Keyboard/Mouse/Joystick/Event)是属于平行关系,他们都是属于上层。不同的handler下对应的输入设备在应用层中的接口命名方式不一样,例如Mouse下的输入设备在应用层的接口是 /dev/input/mousen (n代表0、1、2…),Joystick下的输入设备在应用层的接口是 /dev/input/jsn(n代表0、1、2…),Event下的输入设备在应用层的接口是 /dev/input/eventn(n代表0、1、2…),这个是在input输入子系统中实现的。
(3)输入核心层其实是负责协调上层和下层,使得上层和下层之间能够完成数据传递。当下层发生输入事件的时候,整个系统就被激活了,事件就会通过核心层传递到上层对应的一个/多个handler中,最终会传递到应用空间。
输入子系统解决了什么问题?
(1)在GUI界面中,用户的自由度太大了,可以做的事情太多了,可以响应不同的输入类设备,而且还能够对不同的输入类设备的输入做出不同的动作。例如window中的一个软件既可以响应鼠标输入事件,也可以相应键盘输入事件,而且这些事件都是预先不知道的。
(2)input子系统解决了不同的输入类设备的输入事件与应用层之间的数据传输,使得应用层能够获取到各种不同的输入设备的输入事件,input输入子系统能够囊括所有的不同种类的输入设备,在应用层都能够感知到所有发生的输入事件。
input输入子系统如何工作?
例如以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ---->将按键信息上报给input core层 —> input core层处理好了之后就会上报给input event层,在这里会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中 —> 应用层read就会将缓冲区中的数据读取出去。
应用层如何解析input事件?
首先驱动加载好之后会在生成/dev/input/event*,我们读取解析event事件
event事件上传结构体组成,以按键事件为例分析,相关结构体存放位置usr/include/linux/input.h
struct input_event {
struct timeval time; //按键时间
__u16 type; //类型,在下面有定义
__u16 code; //要模拟成什么按键
__s32 value;//是按下还是释放
};
/*
* Event types
*/
#define EV_SYN 0x00 //同步事件
#define EV_KEY 0x01 //
#define EV_REL 0x02 //相对坐标事件,用于鼠标
#define EV_ABS 0x03 //绝对坐标事件,用于摇杆
#define EV_MSC 0x04 //其他事件
#define EV_SW 0x05 //具备两种状态的输入开关
#define EV_LED 0x11 //
#define EV_SND 0x12 //声音事件
#define EV_REP 0x14 //重复按键事件
#define EV_FF 0x15 //受力事件
#define EV_PWR 0x16 //电源事件
#define EV_FF_STATUS 0x17 //受力状态事件
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
应用demo
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <linux/input.h>
#include <time.h>
#include <sys/time.h>
#define KEY_BOARD "/dev/input/event1" //修改为自己设备对应注册的event
/*
struct timeval {
__kernel_time_t tv_sec; // seconds
__kernel_suseconds_t tv_usec; // microseconds
};
struct input_event {
struct timeval time; //按键时间
__u16 type; //类型,在下面有定义
__u16 code; //要模拟成什么按键
__s32 value;//是按下还是释放
};*/
int main() {
int fd = -1;
int ret = -1;
struct input_event ev;
time_t t;
struct tm* local;
char buf[128] = {0};
fd = open(KEY_BOARD, O_RDONLY);
if(fd<0) {
printf("open /dev/input/event2 error!\\n");
}
while(1) {
memset(&ev, 0, sizeof(struct input_event));
ret = read(fd, &ev, sizeof(struct input_event));
if(ret< sizeof(struct input_event)) {
printf("read event2 failed!");
close(fd);
}
printf("------------------------------\\n");
printf("tv_sec:%ld.\\n", ev.time.tv_sec);
printf("tv_usec:%ld.\\n", ev.time.tv_usec);
local = localtime(&ev.time.tv_sec); //转为本地时间
strftime(buf, 64, "%Y-%m-%d %H:%M:%S", local);
printf("%s\\n", buf);
printf("type:%d.\\n", ev.type);
printf("code:%d.\\n", ev.code);
printf("value:%d.\\n", ev.value);
printf("------------------------------\\n");
}
return 0;
}
打印值:
鼠标 左键按下抬起
------------------------------
tv_sec:1619272008.
tv_usec:169119.
2021-04-24 21:46:48
type:1.
code:272.
value:1.
------------------------------
------------------------------
tv_sec:1619272008.
tv_usec:169119.
2021-04-24 21:46:48
type:0.
code:0.
value:0.
------------------------------
------------------------------
tv_sec:1619272008.
tv_usec:369216.
2021-04-24 21:46:48
type:1.
code:272.
value:0.
------------------------------
------------------------------
tv_sec:1619272008.
tv_usec:369216.
2021-04-24 21:46:48
type:0.
code:0.
value:0.
------------------------------
键盘按下w
w------------------------------
tv_sec:1619271682.
tv_usec:409305.
2021-04-24 21:41:22
type:4.
code:4.
value:17.
------------------------------
------------------------------
tv_sec:1619271682.
tv_usec:409305.
2021-04-24 21:41:22
type:1.
code:17.
value:0.
------------------------------
------------------------------
tv_sec:1619271682.
tv_usec:409305.
2021-04-24 21:41:22
type:0.
code:0.
value:0.
------------------------------
框架分析:
分析函数为input.c ecdev.c gpio_keys.c
数据结构:
实现input驱动程序的流程 API在input.c中实现
input_allocate_device 必要初始化(一些泛型的初始化)
input_set_capability 初始化设备类型及具备的能力
input_register_device 注册设备
input core的分析
input_init
class_register /sys/class/input/
CONFIG_PROC_FS /proc 文件系统宏
register_chrdev_region 注册字符驱动设备
handler和device的匹配
input_attach_handler
input_match_device 匹配device和handler
handler->connect 连接device和handler
input_attach_handler的调用位置为input_register_device和input_register_handler,在handler和device注册时都会进行匹配,避免注册先后顺序不一致导致未能匹配到。
事件驱动层的接口函数
input_register_handler //register a new input handler
注册input子系统的输入处理程序
input_register_handle //register a new input handle
记录一次handler与device的匹配
以上是关于input子系统的主要内容,如果未能解决你的问题,请参考以下文章