Linux驱动学习—输入子系统

1、什么是输入子系统?

输入子系统是Linux专门做的一套框架来处理输入事件的,像鼠标,键盘,触摸屏这些都是输入设备,但是这邪恶输入设备的类型又都不是一样的,所以为了统一这些输入设备驱动标准应运而生的。

统一了以后,在节点/dev/input下面则是我们输入设备的节点,如下图所示:

这些节点对应的则是我们当前系统的输入设备,我们要怎么查看当前系统都有哪些输入设备呢?我们可以使用命令来查看:

cat /proc/bus/input/devices

如下图所示:

2、如何确定哪个设备对应那个节点呢?

可以使用hexdump确定,hexdump命令是Linux下查看二进制文本的工具。

举例:如果想确定键盘对应是哪个节点,可以使用命令:

hexdump /dev/input/event0 或者
hexdump /dev/input/event1 或者
hexdump /dev/input/event2 或者
...

输入完一条命令之后,按键盘上的按键,如果有数据打印出来,则证明当前查看的这个节点就是键盘这个设备对应 的节点。

比如,我现在在Ubuntu上输入命令:

hexdump /dev/input/event1

然后按键盘的按键,这时候有打印信息的出现,则证明/dev/input/event1为键盘对应的节点,如下图所示:

3、hedump出来的打印信息都是什么意思呢?

上报的数据要按照具体的格式上百给输入子系统的核心层,我们的应用就可以通过设备节点来获得按照具体格式上报来的数据了。

封装数据是输入子系统的核心层来帮我们完成的,我们只需要按照指定的类型和这个类型对应的数据来上报给输入子系统的核心层即可。

那怎么指定类型呢?这个就需要了解struct input_event这个结构体,这个结构体在include/uapi/linux/input.h,如下所示:

struct input_event {struct timeval time;//上报事件的事件__u16 type;//类型__u16 code;//编码__s32 value;//值
};

这里值得注意的是,当type不同的时候,code和value所代表的意义也是不一样的。include/uapi/linux/input-event-codes.h:这个头文件里面找到type的定义,每一个定义都是一个类型,如下所示:

#define EV_SYN          0x00 //同步事件
#define EV_KEY          0x01 //按键事件
#define EV_REL          0x02 //相对坐标事件
#define EV_ABS          0x03 //相对坐标事件
#define EV_MSC          0x04 //杂项(其他)事件
#define EV_SW           0x05 //开关事件

按下按键1:

4、实验:在应用层读取键盘按下的键值

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/input.h>
​
int main(int argc, char *argv[])
{int fd;struct input_event test_event;fd = open("dev/input/event1",O_RDWE\R);if (fd < 0) {perror("open error");return fd;}while (1) {read(fd, &test_event, sizeof(test_event));if (test_event.type == EV_KEY) {printf("type is %#x\n", test_event.type);printf("value is %#x\n", test_event.value);}}return 0;
}

在ubuntu上编译运行:

按下回车以及长按回车:

5、使用输入子系统设计按键驱动

5.1 申请和释放input_dev结构体的函数

将开发板上的按键值设置为input.h文件中宏定义的任意一个,比如这次实验将开发板上的KEY按键设置为KEY_0。

在编写input设备驱动的时候我们需要先申请一个ibput_dev结构体变量,使用input_allocate_device函数来申请一个input_dev。此函数原型如下所示:

struct input_dev *input_allocate_device(void);
返回值:申请到的input_dev。

如果要注销niput设备的话需要使用input_free_device函数来释放掉前面申请到的input_dev,input_free_device函数原型如下:

void input_free_device(struct input_dev *dev);
dev:需要释放的input_dev。

5.2注册及注销input_dev的函数

申请好一个input_dev以后就需要初始化这个input_dev,需要初始化的内容主要为事件类型(evbit)和事件值(keybit)这两种。初始化完成之后就需要向Linux内核注册input_dev,需要用到input_register_device函数,原型如下:

int input_register_device(struct input_dev *dev);
dev:要注册的input_dev。
返回值:0,input_dev注册成功;负值,input_dev注册失败。

注销input驱动的时候也需要使用input_unregister_device,函数原型如下:

void input_unregister_device(struct input_dev *dev);
dev:要注销的input_dev。
返回值:无

5.3 事件上报函数

最终我们需要把事件上报上去,上报事件我们使用的函数要针对具体的时间来上报。比如,按键我们使用input_report_key函数。同样的含有一些其他的事件上报函数,函数如下所示:

void input_report_key(struct input_dev *dev, 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_sync(struct input_dev *dev);
void input_mt_sync(struct input_dev *dev);

当上报事件以后还需要使用input_sync函数来告诉Linux内核input子系统上报结束,input_sync函数本质上是上报一个同步事件,函数原型如下:

void input_sync(struct input_dev *dev);
dev:需要上报同步事件的input_dev。

5.4 实验驱动代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/timer.h>
#include <linux/input.h>
​
struct device_node *test_device_node;
struct property *test_node_property;
int gpio_num;
int irq = 0;
struct input_dev *test_dev;
​
static void timer_funtion(ubsigned long data);
​
DEFINE_TIMER(test_timer, timer_funtion, 0, 0);
​
static void timer_funtion(ubsigned long data)
{int value;value = !gpio_get_value(gpio_num);input_repo(rt_key(test_dev, KEY_1, value);input_sync(test_dev);
}
​
static const of_device_id of_match_table_test[] = {//匹配表{.compatible = "keys"},
};
​
static const platform_device_id beep_id_table ={.name = "beep_test",
};
​
irqreturn_t test_key(int irq, void *args)
{printk("test_key\n");test_timer.expires = jiffies + msec_to_jiffies(20);add_timer(&test_timer);return IRQ_HANDLED;
}
​
/*设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入该函数,pdev是匹配成功后传入的设备树节点*/
int beep_probe(struct platform_device *pdev)
{int ret = 0;printk("beep_probe\n");//查找要查找的节点test_device_node = of_find_node_by_path("/test_key");if (test_device_node == NULL) {printk("test_device_node find error\n");return -1;}printk("test_device_node name is %s\n",test_device_node->name);//test_keygpio_num = of_get_named_gpio(test_device_node, "gpios", 0);if (gpio_num < 0) {printk("of_get_named_gpio error\n");return -1;}printk("gpio_num name is %d\n",gpio_num);gpio_direction_input(gpio_num);//获取中断号//irq = gpio_to_irq(gpio_num);irq = irq_of_parse_and_map(test_device_node, 0);printk("irq is %d\n",irq);//申请中断ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "test_key", NULL);//if (ret < 0) {printk("request_irq error\n");return -1;}test_dev = input_allocate_device();test_dev->name = "test_key";__set_bit(EV_KEY, test_dev->ebit);__set_bit(KEY_1, test_dev->keybit);ret = input_register_device(test_dev);if(ret < 0) {printk("input_register_device is error");goto error_input_register;}return 0;error_input_register:input_unregister_device(test_dev);
}
int beep_remove(struct platform_device *pdev)
{pritnk("beep_remove \n");return 0;
}
​
strcut platform_driver beep_device = {.probe = beep_probe,.remove = beep_remove,.driver = {.owner = THIS_MODULE,.name  = "123",.of_match_table = of_match_table_test,//匹配表 },.id_table = &beep_id_table,
};
​
static int beep_driver_init(void)
{int ret = -1;ret = platform_driver_register(&beep_device);if(ret < 0) {printk("platform_driver_register error \n");}printk("platform_driver_register ok\n");return 0;
}
​
static void  beep_driver_exit(void)
{free_irq(irq, NULL);input_unregister_device(test_dev);platform_driver_unregister(&beep_device);printk("beep_driver_exit \n");
}
​
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL"); 

编译加载驱动:

查看当前系统输入设备有哪些:

ls可以看到出现新的event3:

再敲这个命令,每按一次按键会打印相应信息:

5.5 应用层程序

#incldue <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/input.h>
​
int main(int argc, char *argv[])
{int fd; struct input_event test_event;fd = open("/dev/input/event3", O_RDWR);if (fd < 0) {perror("open error");return fd;}while (1) {read(fd, &test_event, sizeof(test_event));if (test_event.type == EV_KEY) {printf("type is %#x\n",test_event.type);printf("code is %#x\n",test_event.code);printf("value is %#x\n",test_event.value);}}return 0;
}

编译app且拷贝到开发板上:

运行app且按下开发板的按键:

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

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

相关文章

springCould中的Bus-从小白开始【11】

目录 &#x1f9c2;1.Bus是什么❤️❤️❤️ &#x1f32d;2.什么是总线❤️❤️❤️ &#x1f953;3.rabbitmq❤️❤️❤️ &#x1f95e;4.新建模块3366❤️❤️❤️ &#x1f373;5.设计思想 ❤️❤️❤️ &#x1f37f;6.添加消息总线的支持❤️❤️❤️ &#x1f9…

世邦IP网络对讲广播系统 uploadjson.php接口处存在任意文件上传漏洞

产品简介 SPON世邦IP网络对讲广播系统是一种先进的通信解决方案&#xff0c;旨在提供高效的网络对讲和广播功能。 漏洞概述 SPON世邦IP网络对讲广播系统 uploadjson.php接口处存在任意文件上传漏洞&#xff0c;未经身份验证的攻击者可利用此漏洞上传恶意后门文件&#xff0c…

【UE Niagara学习笔记】05 - 喷射火焰顶部的蓝色火焰

在上一篇博客&#xff08;【UE Niagara学习笔记】04 - 火焰喷射时的黑烟效果&#xff09;的基础上继续实现在火焰喷射的起点位置生成蓝色火焰的效果。 目录 效果 步骤 1. 创建新的发射器 2. 减少粒子生成数量 3. 减小粒子初始大小 4. 减少粒子喷射距离 5. 减少粒子初始…

Jenkins持续集成

1. Jenkins插件 Jenkins做持续集成很好用&#xff0c;这里只是为了列一下我们经常使用的插件。目前加的比较少&#xff0c;以后可以逐步完善。 必备插件&#xff1a; 1. Credentials Plugin授权插件&#xff0c;不解释。 2. Matrix Authorization Strategy Plugin 矩阵式授权…

前端(angular)在谷歌(chrome)浏览器使用高德地图api定位报错超时geolocation time out ,能定位但不安全的方法

已知信息整合 正如大家搜到的大佬说的原因是chrome浏览器本身的问题。我换成edge就可以。高德地图给出的地图定位api的常见问题&#xff0c;这是另外还有个别浏览器&#xff08;如google Chrome浏览器等&#xff09;本身的定位接口是黑洞 以下是能定位但不安全的方法 连接上…

网络协议攻击与模拟_02ARP协议

一、arp协议简介 一个工作在二层的三层协议&#xff0c;事一个2.5层协议 ARP协议地址解析协议&#xff0c;将一个已知的Ip地址解析为MAC地址&#xff0c;从而进行二层数据交互 二、工作流程 1、两个阶段 ARP请求ARP响应 两台主机IP地址主机A和主机B&#xff0c;IP地址和MAC…

固乔快递查询助手:一键批量查询,省时省力

在快递行业飞速发展的今天&#xff0c;高效、准确地掌握快递信息对于电商卖家、物流公司管理者以及经常需要查询快递信息的消费者来说至关重要。固乔快递查询助手正是为此而生&#xff0c;它的批量查询功能为用户带来前所未有的便利和高效。 首先&#xff0c;如何下载固乔快递查…

深入理解 Flink(四)Flink Time+WaterMark+Window 深入分析

Flink Window 常见需求背景 需求描述 每隔 5 秒&#xff0c;计算最近 10 秒单词出现的次数 —— 滑动窗口 每隔 5 秒&#xff0c;计算最近 5 秒单词出现的次数 —— 滚动窗口 关于 Flink time 种类 TimeCharacteristic ProcessingTimeIngestionTimeEventTime WindowAssign…

解析c++空指针解引用奔溃

空指针解引用引起程序奔溃是c/c中最常见的稳定性错误之一。 显然并非所有使用空指针的语句都会导致奔溃&#xff0c;那什么情况下使用空指针才会引起程序奔溃呢&#xff1f;有一个判断标准&#xff1a;判断空指针是否会导致访问非法内存的情况&#xff0c;如果会导致访问非法内…

大模型LLM在 Text2SQL 上的应用实践

一、前言 目前&#xff0c;大模型的一个热门应用方向Text2SQL&#xff0c;它可以帮助用户快速生成想要查询的SQL语句&#xff0c;再结合可视化技术可以降低使用数据的门槛&#xff0c;更便捷的支持决策。本文将从以下四个方面介绍LLM在Text2SQL应用上的基础实践。 Text2SQL概…

k8s的存储卷、数据卷

容器内的目录和宿主机目录进行挂载。 容器在系统上的生命周期是短暂的。 k8s用控制器创建的pod。delete相当于重启。容器的状态也会恢复到初始状态。一旦恢复到初始状态&#xff0c;所有的后天编辑的文件都会消失 容器和节点之间创建一个可以持久化保存容器内文件的存储卷。…

斯坦福Mobile ALOHA机器人(智能佳)爆火!会烹饪、洗衣等多项家务功能

最近&#xff0c;斯坦福大学 Mobile ALOHA 机器人炒菜的视频和文章火出了圈&#xff01;虽然机器人技术日新月异&#xff0c;能做菜早已是意料之中的事情&#xff0c;但把这么多技术集成到一起&#xff0c;用廉价的机械臂做出了丝滑的动作&#xff0c;还能够洗衣做饭&#xff0…

GoLang:gRPC协议的介绍以及详细教程,从Protocol开始

目录 ​编辑 引言 一、安装相关Go语言库和相关工具 1. 安装Go 2. 安装Protocol Buffers Compiler 2.1 Windows 2.1.1 下载 2.1.2 解压 2.1.3 环境变量 2. macOS 3. Linux 4. 验证安装 3. 安装gRPC-Go 4. 安装Protocol Buffers的Go插件 二、定义服务 三、生成Go…

【Java集合篇】ConcurrentHashMap是如何保证fail- safe的

ConcurrentHashMap是如何保证fail-safe的 ✅典型解析✅拓展知识仓✅分段锁☑️分段锁适用于什么情况&#x1f7e1;分段锁的锁争用情况,是否会带来一定的性能影响✔️分段锁的优缺点&#x1f7e2; 还有哪些其他的线程安全哈希表实现&#x1f7e0;Hashtable和 Collections区别&am…

Python从入门到网络爬虫(控制语句详解)

前言 做任何事情都要遵循一定的原则。例如&#xff0c;到图书馆去借书&#xff0c;就需要有借书证&#xff0c;并且借书证不能过期&#xff0c;这两个条件缺一不可。程序设计亦是如此&#xff0c;需要使用流程控制实现与用户的交流&#xff0c;并根据用户需求决定程序“做什么…

如何实现两台Linux虚拟机ssh免密登录

实验开始前 1.准备好两台虚拟机&#xff08;下载好镜像文件的&#xff09; 2.实验步骤 公钥验证&#xff1a;&#xff08;免密登陆验证方式&#xff09; &#xff08;1&#xff09;生成非对称秘钥 [rootclient ~]# ssh-keygen -t rsa Generating public/private rsa key pai…

秒变办公达人,只因用了这5款在线协同文档app!

在日常工作中&#xff0c;我们不可避免地需要处理各种文档&#xff0c;有时你可能会为如何高效地管理这些文档而感到烦恼&#xff0c;或是不知道如何挑选合适的在线文档工具&#xff1f; 不用担心&#xff01;在这篇文章中&#xff0c;我们将介绍5个好用的在线文档工具App&…

SpringBoot+Hutool实现图片验证码

图片验证码在注册、登录、交易、交互等各类场景中都发挥着巨大作用&#xff0c;能够防止操作者利用机器进行暴力破解、恶意注册、滥用服务、批量化操作和自动发布等行为。 创建一个实体类封装&#xff0c;给前端返回的验证码数据&#xff1a; Data public class ValidateCodeV…

一、数据结构基本概念

数据结构基本概念 一、数据结构基本概念1.基本概念和术语1.1数据&#xff08;Data&#xff09;1.2 数据元素&#xff08;Data element&#xff09;1.3 数据项 &#xff08;Data Item&#xff09;1.4 数据对象 &#xff08;Data Object&#xff09;1.5 数据结构 &#xff08;Dat…

基于 Validator 类实现 ParamValidator,用于校验函数参数

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 使用 TypeV…