linux input子系统学习笔记
程序员文章站
2024-02-23 22:30:40
...
1.什么是子系统?
内核是操作系统的核心。linux内核把不同功能分成不同的子系统。如进程管理、内存管理、文件系统、网络等等。
2.input子系统原理
硬件驱动层:
其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
子系统核心层:
子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
事件处理层:
事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
3.input 子系统有如下优点 :
(1) 统一了各种各样的输入设备的处理方法.
(2) 提供了用于分布给用户使用的简单接口
(3) 提炼了输入驱动程序的通用部分 , 简化了驱动程序的开发和移植工作.
4.open、read、write这些系统调用是如何和设备驱动程序关联起来的?
ile_operation就是把系统调用和驱动程序关联起来的关键数据结构,以鼠标设备文件为例,我们对设备文件调用open系统调用,实际上内部的调用是mousedev_open.
static const struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
.poll = mousedev_poll,
.open = mousedev_open,
.release = mousedev_release,
.fasync = mousedev_fasync,
.llseek = noop_llseek,
};
顺便说下fs.h中的两个文件相关的数据结构:
struct inode结构是用来在内核表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.
5.对鼠标设备文件调用open系统调用的流程
open系统调用 (以鼠标为例)
--> mousedev_open()
--> mousedev::open_device()
--> mousedev_open_device()
--> input_open_device()
--> input_dev::open()
6.鼠标事件上报流程
mousedev_init 内核加载的时候会调用该函数,主要做两个事情:
(1)mousedev_create()创建一个mousedev结构体对象
(2)input_register_handler()将设备对应的input_handler对象注册到输入子系统(dev与handler建立绑定关系)
static struct input_handler mousedev_handler = {
.event = mousedev_event,
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.legacy_minors = true,
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
input_event是核心层input.c提供给设备驱动层上报设备数据的接口
input_event()
--> input_handle_event()
--> input_pass_values()
--> input_to_handler()
--> handler->event()
--> mousedev_event() //最终调用到mousedev_handler中的mousedev_event
7.两个例子
/* 读取键盘设备文件 */
#include <stdio.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#define DEV_PATH "/dev/input/event4" //键盘对应的设备文件(后面数字可能不同)
int main(int argc, char* argv[])
{
int keys_fd;
char ret[2];
struct input_event t; //读取到的input设备数据是一个结构体
keys_fd = open(DEV_PATH, O_RDONLY);
if (keys_fd <= 0) {
printf("open device error!\n");
return -1;
}
while (1) {
if (read(keys_fd, &t, sizeof(t)) == sizeof(t)) {
if (t.type == EV_KEY) {
if (t.value == 0 || t.value == 1) {
//t.code值所对应的按键在/usr/include/linux/input-event-codes.h可以查到
printf("key %d %s\n", t.code, (t.value) ? "Pressed" : "Released");
}
}
}
}
close(keys_fd);
return 0;
}
/*
* 模拟鼠标按键和键盘按键事件
* https://www.cnblogs.com/zhoug2020/p/6404767.html
*/
#include <stdio.h>
#include <linux/input.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
//按键模拟,按键包含按下和松开两个环节
void simulate_key(int fd, int kval)
{
struct input_event event;
gettimeofday(&event.time, 0);
//按下kval键
event.type = EV_KEY;
event.value = 1;
event.code = kval;
write(fd, &event, sizeof(event));
//同步,也就是把它报告给系统
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
//松开kval键
event.type = EV_KEY;
event.value = 0;
event.code = kval;
write(fd, &event, sizeof(event));
//同步,也就是把它报告给系统
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
}
//鼠标移动模拟
void simulate_mouse(int fd, int rel_x, int rel_y)
{
struct input_event event;
gettimeofday(&event.time, 0);
//x轴坐标的相对位移
event.type = EV_REL;
event.value = rel_x;
event.code = REL_X;
write(fd, &event, sizeof(event));
//y轴坐标的相对位移
event.type = EV_REL;
event.value = rel_y;
event.code = REL_Y;
write(fd, &event, sizeof(event));
//同步
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
}
int main(int argc, char *argv[])
{
int fd_mouse = -1;
int fd_kbd = -1;
int i = 0;
fd_kbd = open("/dev/input/event4", O_RDWR);
if(fd_kbd <= 0)
{
printf("Can not open keyboard input file\n");
return -1;
}
fd_mouse = open("/dev/input/event3", O_RDWR);
if(fd_mouse <= 0)
{
printf("Can not open mouse input file\n");
return -1;
}
//simulate_key(fd_mouse, BTN_RIGHT); //模拟按下鼠标左键
simulate_key(fd_kbd, KEY_A); //模拟按下键盘A键
//模拟鼠标相对上次x和y轴相应移动10个像素
//simulate_mouse(fd_mouse, 10, 10);
close(fd_kbd);
close(fd_mouse);
}