【linux kernel】一文总结linux输入子系统

文章目录

    • 一、导读
    • 二、重要数据数据结构
      • (2-1)struct input_dev
      • (2-2)input_dev_list和input_handler_list
      • (2-3)struct input_handler
    • 三、input核心的初始化
    • 四、常用API
    • 五、输入设备驱动开发总结
      • (1)查看输入事件
      • (2)查看输入设备信息
      • (3)使用evtest查看输入事件
      • (4)使用hexdump直接查看输入事件内容

一、导读

linux内核的输入子系统是一个“一对多”的驱动程序,对上层,为应用程序提供统一的事件接口;对下层,统一形态各一的输入设备的处理功能,例如:各种鼠标,不管是PS/2、USB还是蓝牙的,都进行相同的处理。

👉相关源码文件:

  • drivers/input/input-core.c
  • drivers/input/input.c 输入子系统的核心
  • drivers/input/input-compat.c 输入子系统的32位兼容性包装器
  • drivers/input/input-mt.c 输入多点触控库
  • drivers/input/input-poller.c 支持输入设备的轮询模式。
  • drivers/input/ff-core.c 支持Linux输入子系统的强制反馈
  • drivers/input/touchscreen.c 用于触摸屏和其他二维指向设备的通用助手函数

二、重要数据数据结构

(2-1)struct input_dev

input_dev代表一个input设备,定义如下(include/linux/input.h):

struct input_dev {const char *name;  //输入设备的名称const char *phys;  //输入设备的物理地址const char *uniq;  //输入设备的唯一标识符struct input_id id; //输入设备的标识符信息,包括总线类型、供应商 ID、产品 ID 和版本号unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; //输入设备的属性位图,表示设备支持的属性。unsigned long evbit[BITS_TO_LONGS(EV_CNT)];        //事件类型的位图 ,表示设备支持的事件类型。unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];      //按键值的位图,表示设备支持的按键。unsigned long relbit[BITS_TO_LONGS(REL_CNT)];      //相对坐标的位图,表示设备支持的相对坐标事件。unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];      //绝对坐标的位图,表示设备支持的绝对坐标事件。unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];      //杂项事件的位图,表示设备支持的其他杂项事件。unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];      //LED 相关的位图,表示设备支持的 LED 灯。unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];      //sound 有关的位图,表示设备支持的声音事件。unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];        //压力反馈的位图,表示设备支持的力反馈功能。unsigned long swbit[BITS_TO_LONGS(SW_CNT)];        //开关状态的位图,表示设备支持的开关状态。unsigned int hint_events_per_packet;     //每个数据包中事件数量的提示值。unsigned int keycodemax; //按键编码的最大值。unsigned int keycodesize; //按键编码的大小。void *keycode; //按键编码数据的指针。int (*setkeycode)(struct input_dev *dev,    //设置按键编码的函数指针。const struct input_keymap_entry *ke,unsigned int *old_keycode);int (*getkeycode)(struct input_dev *dev,  //获取按键编码的函数指针。struct input_keymap_entry *ke);struct ff_device *ff;  //力反馈设备的指针。unsigned int repeat_key; //重复按键的标志。struct timer_list timer; //用于定时任务的定时器。int rep[REP_CNT]; //重复计数器数组。struct input_mt *mt; //多点触控设备信息的指针。struct input_absinfo *absinfo; //绝对坐标信息的指针。unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //按键状态的位图。unsigned long led[BITS_TO_LONGS(LED_CNT)]; //LED 灯状态的位图。unsigned long snd[BITS_TO_LONGS(SND_CNT)]; //声音状态的位图。unsigned long sw[BITS_TO_LONGS(SW_CNT)];   //开关状态的位图。int (*open)(struct input_dev *dev);       //打开设备的函数指针。void (*close)(struct input_dev *dev);     //关闭设备的函数指针。int (*flush)(struct input_dev *dev, struct file *file); //刷新设备的函数指针。int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); //事件处理函数的指针。struct input_handle __rcu *grab; //指向当前占用该设备的输入处理程序的指针。spinlock_t event_lock; //事件锁,用于保护事件数据的并发访问。struct mutex mutex; //互斥锁,用于保护设备的状态数据。unsigned int users;  //设备的用户数。bool going_away; //设备的用户数。struct device dev; //设备结构体,用于设备管理。struct list_head	h_list; //连接到输入处理程序链表的链表头。struct list_head	node;   //连接到全局输入设备链表的链表头。unsigned int num_vals;  //输入值的数量。unsigned int max_vals;  //输入值的最大数量。struct input_value *vals; //输入值数组的指针。bool devres_managed;  //标志,指示设备是否由设备资源管理器管理。
};

input_dev结构中定义了几个用于描述具体输入行为的类型,其中evbit表示输入事件类型,在linux内核中为许多常见的事件类型都进行了定义(/include/uapi/linux/input.h):

#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)

在/include/uapi/linux/input.h文件中也能找到输入子系统下许多的宏定义,这些宏定义可以在基于输入子系统的驱动程序中使用。

(2-2)input_dev_list和input_handler_list

在input输入子系统中定义了两个重要的全局链表:

static LIST_HEAD(input_dev_list);  //dev链表
static LIST_HEAD(input_handler_list);  //handler链表

input_dev_list表示input输入子系统中的所有设备,当使用input_register_device()这次input设备时,所注册的设备会被添加到input_dev_list链表中:

list_add_tail(&dev->node, &input_dev_list);

input_handler_list表示input输入子系统的handler,当使用input_register_handler()注册一个handler时,会将该handler添加到input_handler_list链表中:

list_add_tail(&handler->node, &input_handler_list);

(2-3)struct input_handler

input_handler结构用于描述一个输入设备的操作接口,定义如下:

struct input_handler {//驱动特殊数据void *private;void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);void (*events)(struct input_handle *handle,const struct input_value *vals, unsigned int count);bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);bool (*match)(struct input_handler *handler, struct input_dev *dev);int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);void (*disconnect)(struct input_handle *handle);void (*start)(struct input_handle *handle);bool legacy_minors;int minor;const char *name;const struct input_device_id *id_table;struct list_head	h_list;struct list_head	node;
};

input_handler结构体用于描述输入事件的处理程序,结构中各元素功能如下:

  • 1、void *private;:指向驱动特殊数据的指针,通常用于存储与该输入处理程序相关的私有数据。
  • 2、void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);:指向事件处理函数的指针。当输入事件发生时,此函数将被调用以处理事件。
  • 3、void (*events)(struct input_handle *handle, const struct input_value *vals, unsigned int count);:指向批量事件处理函数的指针。类似于 event 函数,但允许处理多个输入值。
  • 4、bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);:指向事件过滤器函数的指针。该函数用于确定是否应该处理特定的输入事件。
  • 5、bool (*match)(struct input_handler *handler, struct input_dev *dev);:指向匹配函数的指针。用于确定输入设备是否与此输入处理程序匹配。
  • 6、int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);:指向连接函数的指针。用于将输入设备与输入处理程序连接起来。
  • 7、void (*disconnect)(struct input_handle *handle);:指向断开连接函数的指针。用于在输入设备断开连接时执行清理操作。
  • 8、void (*start)(struct input_handle *handle);:指向启动函数的指针。用于启动输入处理程序的某些操作。
  • 9、bool legacy_minors;:指示是否使用传统的设备编号(例如 /dev/input/eventX 中的 X)。
  • 10、int minor;:输入处理程序的次设备号,用于确定其在系统中的唯一标识符。
  • 11、const char *name;:输入处理程序的名称。
  • 12、const struct input_device_id *id_table;:指向输入设备标识符表的指针。用于描述输入设备与输入处理程序之间的匹配关系。
  • 13、struct list_head h_list;:用于将输入处理程序连接到输入设备的处理程序链表。
  • 14、struct list_head node;:用于将输入处理程序连接到全局输入处理程序链表。

总而言之,struct input_handler结构体描述了输入事件处理程序的各种属性和操作函数指针,用于与输入设备交互并处理输入事件。

三、input核心的初始化

input输入子系统的核心由/drivers/input/input.c文件实现,该文件中内容基于linux内核驱动模型而设计,模块出口是input_init(),由subsys_initcall(input_init)语句导出,如下代码:

static int __init input_init(void)
{int err;//注册input类err = class_register(&input_class);if (err) {pr_err("unable to register input_dev class\n");return err;}//在proc文件系统中创建与input相关目录err = input_proc_init();if (err)goto fail1;//为input输入子系统注册主设备号err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");if (err) {pr_err("unable to register char major %d", INPUT_MAJOR);goto fail2;}return 0;fail2:	input_proc_exit();fail1:	class_unregister(&input_class);return err;
}//模块导出
subsys_initcall(input_init);

input_proc_init()定义如下:

static int __init input_proc_init(void)
{struct proc_dir_entry *entry;proc_bus_input_dir = proc_mkdir("bus/input", NULL);if (!proc_bus_input_dir)return -ENOMEM;entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fileops);if (!entry)goto fail1;entry = proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);if (!entry)goto fail2;return 0;fail2:	remove_proc_entry("devices", proc_bus_input_dir);fail1: remove_proc_entry("bus/input", NULL);return -ENOMEM;
}

上述代码将在 /proc/bus/input 目录下创建两个文件 “devices” 和 “handlers”,用于显示输入设备和输入事件处理程序的信息。例如:

四、常用API

//申请input_dev结构变量
struct input_dev *input_allocate_device(void)//注销input_dev设备
void input_free_device(struct input_dev *dev)//向linux内核注册input设备
int input_register_device(struct input_dev *dev)//从linux内核注销input设备
void input_unregister_device(struct input_dev *dev)//上报一个输入事件,包括事件类型 (type)、事件代码 (code) 和事件值 (value)。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)//上报一个相对坐标事件,即相对于上一个位置的变化。
void input_report_rel(struct input_dev *dev, unsigned int code, int value)//上报一个绝对坐标事件,即绝对位置的变化。
void input_report_abs(struct input_dev *dev, unsigned int code, int value)//上报一个力反馈状态事件,通常用于通知应用程序力反馈设备的状态。
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)//上报一个开关状态事件,通常用于通知应用程序开关设备的状态。
void input_report_switch(struct input_dev *dev, unsigned int code, int value)//上报一个多点触摸同步事件,用于通知输入子系统多点触摸操作的结束
void input_mt_sync(struct input_dev *dev)//上报一个同步事件,用于通知linux内核input子系统上报操作结束
void input_sync(struct input_dev *dev)
/*##############################################################*/

五、输入设备驱动开发总结

(1)查看输入事件

很多时候,我们需要获取系统目前存在哪些输入事件,这时候可以使用:

ls /dev/input/

该目录导出的信息是输入事件对应的文件节点。

(2)查看输入设备信息

获取到输入事件,在开发中可能还不够,还想更进一步获取到更详细的关于输入事件和输入设备的信息,这时候可以使用:

cat /proc/bus/input/devices

将会输出如下格式的数据:

I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="20cc000.snvs:snvs-powerkey"
P: Phys=snvs-pwrkey/input0
S: Sysfs=/devices/platform/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
U: Uniq=
H: Handlers=kbd event0 
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="iMX6UL TouchScreen Controller"
P: Phys=
S: Sysfs=/devices/platform/soc/2000000.aips-bus/2040000.tsc/input/input1
U: Uniq=
H: Handlers=mouse0 event1 
B: PROP=0
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=3

(3)使用evtest查看输入事件

通过上述两种方式查看输入事件获取到的信息是关于具体的输入事件和输入设备,如果想要获取到输入事件更为详细的信息(数据层),则可以使用evtest工具进行查看。

在发行版的linux系统中,例如(ubuntu),可以使用如下命令:

sudo apt install evtest

安装evtest,在安装完evtest后,就可以使用了(必需以root运行):

sudo evtest


将会列出目前系统中可用的输入设备,我们可以选择一个对应的事件号进行监听,上图中可选的事件为0-4,对应五个输入设备。

在嵌入式linux中,可能就没有evtest工具的直接二进制文件,这时候我们可以自行编译源码得到。

  • 源码URL:https://github.com/freedesktop-unofficial-mirror/evtest/tree/master


例如上图,笔者自行编译了一个运行在ARM32上的evtest,下图为监测hid鼠标输入设备的一个应用场景:首先会打印出输入hid设备的相关信息,和支持的事件:

在evtest运行的情况下,如果点击鼠标左键/右键、滑动鼠标滚轮和移动鼠标,evtest都会将监测到的事件打印出(下图为点击鼠标左键/右键输出的信息):

(4)使用hexdump直接查看输入事件内容

如果需要获取到更为直接的输入设备而数据,则可以使用hexdump命令直接查看/dev/input/eventx:

hexdump /dev/input/event2

例如:

参考链接:
https://docs.kernel.org/input/index.html

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

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

相关文章

【面试题】面试官:判断图是否有环?_数据结构复试问题 有向图是否有环

type: NODE;name: string;[x: string]: any; }; [x: string]: any;}; export type Data Node | Edge; 复制代码 * 测试数据如下const data: Data[] [ { id: ‘1’, data: { type: ‘NODE’, name: ‘节点1’ } }, { id: ‘2’, data: { type: ‘NODE’, name: ‘节点2’ } },…

猫头虎 AI 前沿科技探索之路(持续更新):ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略

猫头虎 AI 前沿科技探索之路(持续更新):ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略 背景介绍 随着人工智能技术的飞速发展,AI 的应用已经渗透到各个领域,从商业决策到医疗健康,再到日常生活中的…

猫头虎 分享已解决Error || Vanishing/Exploding Gradients: NaN values in gradients

猫头虎 分享已解决Error || Vanishing/Exploding Gradients: NaN values in gradients 🐯 摘要 📄 大家好,我是猫头虎,一名专注于人工智能领域的博主。在AI开发中,我们经常会遇到各种各样的错误,其中Vani…

React+TS 从零开始教程(3):useState

源码链接:下载 在开始今天的内容之前呢,我们需要先看一个上一节遗留的问题,就是给属性设置默认值。 我们不难发现,这个defaultProps已经被废弃了,说明官方并不推荐这样做。其实,这个写法是之前类组件的时候…

Kafka基础教程

Kafka基础教程 资料来源:Apache Kafka - Introduction (tutorialspoint.com) Apache Kafka起源于LinkedIn,后来在2011年成为一个开源Apache项目,然后在2012年成为一流的Apache项目。Kafka是用Scala和Java编写的。Apache Kafka是基于发布-订…

【Python/Pytorch 】-- K-means聚类算法

文章目录 文章目录 00 写在前面01 基于Python版本的K-means代码02 X-means方法03 最小二乘法简单理解04 贝叶斯信息准则 00 写在前面 时间演变聚类算法:将时间演变聚类算法用在去噪上,基本思想是,具有相似信号演化的体素具有相似的模型参数…

推荐一款AI修图工具,支持AI去水印,AI重绘,AI抠图...

不知道大家有没有这样的一个痛点,发现了一张不错的“素材”, 但是有水印,因此不能采用,但找来找去,还是觉得初见的那个素材不错,怎么办? 自己先办法呗。 二师兄发现了一款功能强大的AI修图工具…

使用Jetpack Compose为Android App创建自定义页面指示器

使用Jetpack Compose为Android App创建自定义页面指示器 在现代移动应用中,页面指示器在提供视觉导航提示方面发挥着重要作用,帮助用户理解其在应用内容中的当前位置。页面指示器特别适用于顺序展示内容的场景,如图片轮播、图像库、幻灯片放…

【Linux】Socket阻塞和非阻塞、同步与异步

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;Linux系列专栏&#xff1a;Linux基础 &#x1f525; 给大家…

【ajax核心02】底层原理-Promise对象

目录 一&#xff1a;promise对象是什么 二&#xff1a;语法&#xff08;Promise使用步骤&#xff09; 三&#xff1a;Promise-三种状态 一&#xff1a;promise对象是什么 Promise 对象代表异步操作最终的完成&#xff08;或失败&#xff09;以及其结果值。 即Promise对象是…

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来&#xff0c;无需把原生代码转换为uniapp&#xff0c;可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录&#xff0c;原生入口…

java基于ssm+jsp 医院远程诊断系统

1前台首页功能模块 医院远程诊断系统&#xff0c;在系统首页可以查看首页、医生信息、论坛信息、我的、跳转到后台、客服等内容&#xff0c;如图1所示。 图1前台首页功能界面图 用户登录&#xff0c;在用户登录页面可以填写用户名、密码、等信息进行用户登录&#xff0c;如图2…

安装Django Web框架

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Django是基于Python的重量级开源Web框架。Django拥有高度定制的ORM和大量的API&#xff0c;简单灵活的视图编写&#xff0c;优雅的URL&#xff0c;适…

近2年时间,华为手机上的卫星通信功能发展成怎样了?

自从Mate 50 系列支持北斗卫星短报文功能以来&#xff0c;已经过去了近2年的时间&#xff0c;卫星相关的功能也从最开始的摸索、罕见&#xff0c;逐渐变得成熟、在各品牌旗舰机上常见起来。 那么&#xff0c;这近两年的发展&#xff0c;卫星相关的功能都有了怎样的变化呢&…

史上最全整合nacos单机模式整合哈哈哈哈哈

Nacos 是阿里巴巴推出的一个新开源项目&#xff0c;它主要是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos提供了一组简单易用的特性集&#xff0c;帮助用户快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 的关键特性包括&#x…

简过网:公务员报班和不报班的有区别吗?

很多备考公务员的朋友可能都会有这种纠结&#xff0c;到底要不要报个培训班呢&#xff0c;报班了怕没什么用&#xff0c;不报班又怕自己考不上&#xff0c;如果你也有这个疑问&#xff0c;那么不妨来看看这篇文章&#xff01; ​ 先说一下&#xff0c;公务员报班和不报班的有区…

平方根的三种求法(袖珍计算器算法,二分查找,牛顿迭代)

一、袖珍计算器 袖珍计算器方法主要运用到了我们高数上所学的关于e底数转化的思想&#xff0c;即 一种用指数函数 exp⁡ 和对数函数 ln⁡ 代替平方根函数的方法 : 1、exp函数&#xff1a; exp是 C 标准库 <math.h> 中的一个函数&#xff0c;用于计算 e 的 x 次幂&…

微服务(服务治理)

服务远程调用时存在的问题 注册中心原理 服务治理中的三个角色分别是什么&#xff1f; 服务提供者&#xff1a;暴露服务接口&#xff0c;供其它服务调用服务消费者&#xff1a;调用其它服务提供的接口注册中心&#xff1a;记录并监控微服务各实例状态&#xff0c;推送服务变更信…

【乐吾乐2D可视化组态编辑器】图表动态显示

1. 添加数据 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 图表动态展示是指一个图表图元的数据属性&#xff08;一般是dataY&#xff09;绑定多个变量&#xff0c;建立通信后数据动态变化展示。 官网默认Echarts图表拖拽到画布中是已经添加了图元的da…

【ChatBI】超轻量Python库Vanna快速上手,对接oneapi

oneapi 准备 首先确保你有oneapi &#xff0c;然后申请 kimi的api 需要去Moonshot AI - 开放平台 然后添加一个api key 然后打开oneapi的渠道界面&#xff0c;添加kimi。 然后点击 测试&#xff0c; 如果能生成响应时间&#xff0c;就是配置正确。 然后创建令牌 http:…