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

news2024/10/7 20:25:25

文章目录

    • 一、导读
    • 二、重要数据数据结构
      • (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 0

I: 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.coloradmin.cn/o/1858974.html

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

相关文章

20240507-招商证券 基于鳄鱼线的指数择时及轮动策略

动量震荡指标构造 动量震荡指标为交易者提供了获利的钥匙。动量震荡指标测算了5根价格柱相对于34根价格柱的动量变化。首先计算最近5根价格柱的最高价和最低价间的中点的简单移动平均值,即(最高价最低价)12的简单移动平均,将得出的值减去最近34根价格柱的最高价和最低价中点的…

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探…

Listary——最好用的电脑搜索文件软件

简易版: https://www.listary.com/download-completion?versionstable 完整功能版: Microsoft PowerToys | Microsoft Learn

【PA交易】BackTrader(一): 如何使用实时tick数据和蜡烛图

背景和需求 整合Tick数据是PA交易的回测与实盘基本需求。多数交易回测框架往往缺乏对大规模Tick数据直接而全面的支持。Tick数据因其体量庞大(例如,某棕榈油主力合约四年间的数据达8GB)为结合价格趋势与PA分析带来挑战,凸显了实时…

探索ChatTTS项目:高效的文字转语音解决方案

文章目录 📖 介绍 📖📒 ChatTTS 📒📝 项目介绍📝 项目亮点📝 UI 🎈 项目地址 🎈 📖 介绍 📖 在AI技术迅速发展的今天,文本到语音&…

[职场] 怎么写个人简历模板 #其他#知识分享

怎么写个人简历模板 怎么写个人简历模板1 姓名:xxx 性别:x 年龄:x岁 婚姻状况:x 最高学历:xx 政治面貌:xx 现居城市:xx 籍贯:xx 联系电话:xxxxxx 电子邮箱:xx…

CRMEB开源商城系统Java版:新零售时代的技术创新与实战案例

一、引言 随着新零售概念的兴起和电子商务的飞速发展,企业对商城系统的需求也日益多元化和个性化。CRMEB开源商城系统Java版,凭借其先进的技术架构、丰富的功能模块和灵活的扩展性,成为了众多企业构建和扩展自身电商业务的首选。本文将对CRM…

基于SpringBoot前后端分离在线骑行网站设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟感兴趣的可以先收藏起来,还…

我是如何在markdown编辑器中完成视频的插入和播放的

如果你有更好用的编辑器组件,请一定推荐给我!!!(最好附带使用说明🤓️) 介绍 在开发一个社区页面的时候,需要完成发帖、浏览帖子的能力。这里考虑接入markdown编辑器进行开发,也符合大多数用户的习惯。 …

MM-LLM:Internvl_chat.v1.5论文解读

这个模型在我自己测的结果上也是表现优异,和glm4v打得有来有回。是目前开源的效果最佳的模型之一。 官方的评测榜单:https://huggingface.co/spaces/opencompass/open_vlm_leaderboard 摘要: 直接说提出了一个拉近开源和商业多模态模型的开…

强强联合 极光推送(JPush)成为华为生态市场首家推送类SDK服务商

近日,中国领先的客户互动和营销科技服务商,极光(Aurora Mobile,纳斯达克股票代码:JG)的核心产品极光推送(JPush)顺利通过华为开发者联盟的多项测试及审核,成为首家在Harm…

RN开发搬砖经验之—“Calculated frame index should never be lower than 0“崩溃问题分析

问题重现 崩溃堆栈: Back traces starts. java.lang.RuntimeException: java.lang.IllegalStateException: Calculated frame index should never be lower than 0at com.facebook.react.animated.NativeAnimatedModule$1.doFrameGuarded(NativeAnimatedModule.ja…

【FFMPEG+Mediamtx】 本地RTSP测试推流记录

利用本地FFMPEGMediamtx 搭建本地RTSP测试推流电脑摄像头 起因 本来要用qt的qml的Video做摄像头测试。 😔但是,不在现场,本地测试,又要测试rtsp流,又因为搜了一圈找不到一个比较好的在线测试rtsp推流网址&#x1f6…

JAVA每日作业day6.24

ok了家人们今天学习了一些关键字,用法和注意事项,静态代码块这些知识,闲话少叙我们一起看看吧。 一,final关键字 1.1 final关键字的概述 final: 不可改变。可以用于修饰类、方法和变量。 类:被修饰的类&a…

Hex文件与BIn文件的关系

单片机中Hex文件与BIn文件的关系 前言 时间:2024/6/24 官方网站:.hex文件解析:Hexadecimal (Intel-Format) File (.hex) Definition 参考博客:实现STM32烧写程序-(3) Hex文件结构-CSDN博客 文件:《Hexfrmt.pdf》描述了…

地信大四,实习重要吗?怎么找实习岗位?

“地信怎么找实习啊?” “实习三个月以上?暑假只有两个月啊” “什么岗位实习比较有用?” “助理类岗位是做什么?” …… 同学们好啊,不知不觉24年已经是过完一小半了,24届毕业的同学们也差不多就要迎来…

JSON.parse(JSON.stringify())导致的响应式属性丢失

console.log("formdata赋值前", this.formdata);console.log("row",row);console.log("row序列化后", JSON.parse(JSON.stringify(row)));this.formdata JSON.parse(JSON.stringify(row)); console.log("formdata赋值后", this.formd…

0803功放3

1.甲乙类互补堆成功放, 理想12v t提供静态偏置,消去交越失真 2.12V Po(12)2/2RL 3.电压并联负反馈 并联减小输入电阻 电压减小输出电阻 4.-Rf/Ri 这个问题是工艺问题引起的,最早用PNP管用的锗管,后面硅工艺成熟后用的就是硅管&…

springboot 网上商城系统-计算机毕业设计源码08789

摘 要 随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设网上商城系统。 本设…

数据库攻防之MongoDB

MongoDB是一个安全性相对较高的非关系型数据库,它的安全问题主要出现在使用、配置过程当中。目前随着MongoDB的流行,它也成为了红队攻防领域不可忽视的数据库。 0x01 MongoDB简介 MongoDB 是一个由C编写、基于分布式文件存储的开源数据库系统&#xff…