当前位置: 首页 > news >正文

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且按下开发板的按键:

相关文章:

Linux驱动学习—输入子系统

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

计算机网络(2)

计算机网络&#xff08;2&#xff09; 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 计算机网络和因特网&#xff08;2&#xff09;分组交换网中的时延、丢包和吞吐量时延丢包吞吐量总结 协议层次及其服务模型模型类型OSI模型分析TCP/IP模型分析 追溯历史 小程一言 我…...

什么是预训练Pre-training—— AIGC必备知识点,您get了吗?

Look&#xff01;&#x1f440;我们的大模型商业化落地产品&#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;关注Free三天集训营助教在线为您火热答疑&#x1f469;&#x1f3fc;‍&#x1f3eb; 随着人工智能(AI)不断重塑我们的世界&#xff0c;其发展的一个关键方面已经…...

bat脚本sqlserver 不同数据库同步

如果你想使用批处理脚本&#xff08;.bat&#xff09;在 SQL Server 中同步不同数据库的数据&#xff0c;你可以考虑以下步骤&#xff1a; 设置环境变量&#xff1a; 确保你的系统环境变量中已经设置了 SQLCMD 和 BCP 的路径。 编写批处理脚本&#xff1a; 使用 sqlcmd 来执行…...

阶段十-分布式-Redis02

第一章 Redis 事务 1.1 节 数据库事务复习 数据库事务的四大特性 A&#xff1a;Atomic &#xff0c;原子性&#xff0c;将所以SQL作为原子工作单元执行&#xff0c;要么全部执行&#xff0c;要么全部不执行&#xff1b;C&#xff1a;Consistent&#xff0c;一致性&#xff0…...

微信小程序实战-02翻页时钟-2

微信小程序实战系列 《微信小程序实战-01翻页时钟-1》 文章目录 微信小程序实战系列前言计时功能实现clock.wxmlclock.wxssclock.js 运行效果总结 前言 接着《微信小程序实战-01翻页时钟-1》&#xff0c;继续完成“6个页面的静态渲染和计时”功能。 计时功能实现 clock.wxm…...

每天刷两道题——第十一天

1.1滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 输出&…...

Git提交规范

一. 修改类型 每个类型值都表示了不同的含义&#xff0c;类型值必须是以下的其中一个&#xff1a; feat&#xff1a;提交新功能fix&#xff1a;修复了bugdocs&#xff1a;只修改了文档style&#xff1a;调整代码格式&#xff0c;未修改代码逻辑&#xff08;比如修改空格、格式…...

apache2的虚拟主机的配置

APACHE2的虚拟主机配置 本章中心概括&#xff1a; 虚拟web主机的初步认识&#xff0c;在redhat系列系统中如何配置&#xff0c;在Debian系列系统中如何配置。 什么是apache2虚拟主机&#xff1a; 简单点讲&#xff0c;就是在同一个物理机中配置多个虚拟主机&#xff0c;从而达…...

Provide/Inject 依赖注入(未完待续)

父组件传递给子组件数据&#xff0c;通过props&#xff0c;但是需要逐层传递 provide/Inject 的推出就是为了解决这个问题&#xff0c;它提供了一种组件之间共享此类值的方式,不必通过组件树每层级显示地传递props 目的是为了共享那些被 认为对于一个组件树而言是全局的数据 p…...

力扣173. 二叉搜索树迭代器

深度优先搜索 思路&#xff1a; 遍历二叉搜索树&#xff0c;左子树总比根节点小&#xff0c;右子树总比根节点大&#xff1b;先深度遍历左子树&#xff0c;然后返回其父节点&#xff0c;然后遍历其右子树节点&#xff1b;使用栈数据结构存储节点数据&#xff0c;借用其“后进先…...

电脑找不到d3dcompiler43.dll怎么修复,教你5个可靠的方法

d3dcompiler43.dll是Windows操作系统中的一个重要动态链接库文件&#xff0c;主要负责Direct3D编译器的相关功能。如果“d3dcompiler43.dll丢失”通常会导致游戏无法正常运行或者程序崩溃。为了解决这个问题&#xff0c;我整理了以下五个解决方法&#xff0c;希望能帮助到遇到相…...

5.3 Android BCC环境搭建(eadb版 上)

写在前面 eadb即eBPF Android Debug Bridge,它是基于adeb的重构。后者曾随aosp 10发布在platform/external目录下。 一,root权限 这里再HighLight下,当前整个专栏都是基于开发环境来展开的,也就是Android设备需要具有root权限。因此该专栏下每一篇博客都是默认了当前开发…...

【算法题】44. 通配符匹配

题目 给你一个输入字符串 (s) 和一个字符模式 (p) &#xff0c;请你实现一个支持 ? 和 * 匹配规则的通配符匹配&#xff1a; ? 可以匹配任何单个字符。 * 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 判定匹配成功的充要条件是&#xff1a;字符模式必须能…...

vscode配置与注意事项

中文设置 https://zhuanlan.zhihu.com/p/263036716 应用搜索输入“Chinese (Simplified) Language Pack for Visual Studio Code”并敲回车键 底部信息窗没有的话 首先使用快捷键ctrlshiftp&#xff0c;Mac用户使shiftcommandp&#xff0c;然后输入settings.json 将下面的选…...

设计模式篇章(3)——七种结构型模式

结构型设计模式主要思考的是如何将对象进行合理的布局来组成一个更大的功能体或者结构体&#xff0c;这个现在讲有点抽象&#xff0c;用大白话讲就是利用现有的对象进行组合或者配合&#xff0c;使得组合后的这个系统更加好。好是相对于不使用设计模式&#xff0c;按照自己的堆…...

Window端口占用处理

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…...

算法实战(二)

基础算法编程 题目来源([PAT题目](https://pintia.cn/problem-sets/14/exam/problems/type/6))7-2 然后是几点7-3 逆序的三位数7-6 混合类型数据格式化输入 题目来源(PAT题目) 7-2 然后是几点 有时候人们用四位数字表示一个时间&#xff0c;比如 1106 表示 11 点零 6 分。现在…...

网工内推 | 上市公司网工,NP认证优先,最高15薪+项目奖金

01 广东轩辕网络科技股份有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、主要负责教育行业园区网的有线及无线网络项目的实施、维护、巡检等工作&#xff1b; 2、协助windows/linux平台服务器OS的安装、部署、配置与维护&#xff1b; 3、协助服务器、存储、…...

【LLM 论文阅读】NEFTU N E: LLM微调的免费午餐

指令微调的局限性 指令微调对于训练llm的能力至关重要&#xff0c;而模型的有用性在很大程度上取决于我们从小指令数据集中获得最大信息的能力。在本文中&#xff0c;我们提出在微调正向传递的过程中&#xff0c;在训练数据的嵌入向量中添加随机噪声&#xff0c;论文实验显示这…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

链式法则中 复合函数的推导路径 多变量“信息传递路径”

非常好&#xff0c;我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题&#xff0c;统一使用 二重复合函数&#xff1a; z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y))​ 来全面说明。我们会展示其全微分形式&#xff08;偏导…...

结构化文件管理实战:实现目录自动创建与归类

手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题&#xff0c;进而引发后续程序异常。使用工具进行标准化操作&#xff0c;能有效降低出错概率。 需要快速整理大量文件的技术用户而言&#xff0c;这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB&#xff0c;…...

Python的__call__ 方法

在 Python 中&#xff0c;__call__ 是一个特殊的魔术方法&#xff08;magic method&#xff09;&#xff0c;它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时&#xff08;例如 obj()&#xff09;&#xff0c;Python 会自动调用该对象的 __call__ 方法…...