Linux学习第32天:Linux INPUT 子系统实验(一):接纳

news2024/12/26 0:08:45

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


           题目中用了“接纳”俩字。其实学习就是一个接纳的过程。接纳新的知识,从而转化为自己知识宝库的一部分。那今天学习的input子系统和今天的主题接纳有什么关系呢?接纳就是一个从输入转为为内在并输出应用的过程。

        在日常的工作学习中,学会接纳,接纳同事同学朋友老师,更要学会接纳自己,接纳这个社会的不完美。    

        本笔记主要学习Linux input子系统实验,主要内容包括input子系统相关基础知识、原理图分析、驱动程序开发及测试、Linux自带按键驱动程序的使用。其中Linux自带按键驱动程序的使用将在下一节中进行。

一、input子系统

        Linux内核专门做了一个input子系统框架来处理输入事件。输入设备本质上是字符设备,只是在此基础上套上了input框架,用户只需上报输入事件,input核心层负责处理这些事件。

1、input子系统简介

        input子系统分为input驱动层、input核心层、input事件处理层,最终给用户空间提供可访问的设备节点。

        input子系统框架如下图:

        所有的输入设备以文件的形式供用户空间应用程序使用。

        编写驱动程序时只需要关注中间的驱动层、核心层和时间层。

        驱动层:输入设备具体的驱动程序。

        核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。

        事件层:主要和用户空间进行交互。

2、input驱动编写流程

        input核心层会向Linux内核注册一个字符设备,input.c就是input输入子系统的核心层。

err = class_register(&input_class);

        注册一个 input 类,这样系统启动以后就会在/sys/class 目录下有一个 input 子目录。

err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");

        注册一个字符设备,主设备号为 INPUT_MAJOR,INPUT_MAJOR定义如下:

#define INPUT_MAJOR 13

        input子系统所有设备的主设备号都是13,在使用input处理输入设备的时候就不需要去注册字符设备了。只需向系统注册一个input_device即可。

1)、注册input_dev

        在使用input子系统时,只需要注册一个input设备即可。input_dev结构体标识input设备。

unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
129 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
130 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
131 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
132 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
133 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
134 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
135 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
136 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
137 unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */
......

        evbit、 keybit、 relbit 等等都是存放不同事件对应的值。

        使用input_allocate_device 函数来申请一个 input_dev。

struct input_dev *input_allocate_device(void)

        使用 input_free_device 函数来释放掉前面申请到的input_dev:

void input_free_device(struct input_dev *dev)

        初始化这个 input_dev,需要初始化的内容主要为事件类型(evbit)事件值(keybit)这两种

        input_dev初始化完成以后,需要使用input_register_device函数向Linux内核注册input_dev。

int input_register_device(struct input_dev *dev)

        同样,使用input_unregister_device向Linux内核注销input_dev.

void input_unregister_device(struct input_dev *dev)

        终上所述,注册过程如下:

①、使用 input_allocate_device 函数申请一个 input_dev。
②、初始化 input_dev 的事件类型以及事件值。
③、使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
④、卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev,
然后使用 input_free_device 函数释放掉前面申请的 input_dev。
 

struct input_dev *inputdev; /* input 结构体变量 */

        定义一个 input_dev 结构体指针变量。

3
/* 驱动入口函数 */
4 static int __init xxx_init(void)
5 {
6 ......
7 inputdev = input_allocate_device(); /* 申请 input_dev */
8 inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */
9
10 /*********第一种设置事件和事件值的方法***********/
11 __set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */
12 __set_bit(EV_REP, inputdev->evbit); /* 重复事件 */
13 __set_bit(KEY_0, inputdev->keybit); /*设置产生哪些按键值 */
14 /************************************************/
15
16 /*********第二种设置事件和事件值的方法***********/
17 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
18 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=
BIT_MASK(KEY_0);
19 /************************************************/
20
21 /*********第三种设置事件和事件值的方法***********/
22 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
23 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
24 /************************************************/
25
26 /* 注册 input_dev */
27 input_register_device(inputdev);
28 ......
29 return 0;
30 }

        驱动入口函数,在此函数中完成 input_dev 的申请、设置、注册等工作。第 7行调用 input_allocate_device 函数申请一个 input_dev。第 10~23 行都是设置 input 设备事件和按键值,这里用了三种方法来设置事件和按键值。第 27 行调用 input_register_device 函数向 Linux内核注册 inputdev。

32 /* 驱动出口函数 */
33 static void __exit xxx_exit(void)
34 {
35 input_unregister_device(inputdev); /* 注销 input_dev */
36 input_free_device(inputdev); /* 删除 input_dev */
37 }

        驱动出口函数,第 35 行调用 input_unregister_device 函数注销前面注册的input_dev,第 36 行调用 input_free_device 函数删除前面申请的 input_dev。

2)、上报输入事件

        input_event 函数,此函数用于上报指定的事件以及对应的值。

void input_event(struct input_dev *dev,
unsigned int type,
unsigned int code,
int value)

dev:需要上报的 input_dev。

type: 上报的事件类型,比如 EV_KEY。
code: 事件码,也就是我们注册的按键值,比如 KEY_0、 KEY_1 等等。
value:事件值,比如 1 表示按键按下, 0 表示按键松开。
返回值: 无。

        上报按键事件的话建议使用 input_report_key 函数。

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

2 void timer_function(unsigned long arg)
3 {
4 unsigned char value;
5 6
value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
7 if(value == 0){ /* 按下按键 */
8 /* 上报按键值 */
9 input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1, 按下 */
10 input_sync(inputdev); /* 同步事件 */
11 } else { /* 按键松开 */
12 input_report_key(inputdev, KEY_0, 0); /* 最后一个参数 0, 松开 */
13 input_sync(inputdev); /* 同步事件 */
14 }
15 }

3、input_event结构体

        Linux 内核使用 input_event 这个结构体来表示所有的输入事件。

24 struct input_event {
25 struct timeval time;
26 __u16 type;
27 __u16 code;
28 __s32 value;
29 };

        time:时间,也就是此事件发生的时间,为 timeval 结构体类型。

        type: 事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。

        code: 事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如: KEY_0、 KEY_1
等等这些按键。此成员变量为 16 位。
        value: 值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的
话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。

二、硬件原理图

三、试验程序编写

1、修改设备树文件

1)、添加pinctrl节点

1 pinctrl_key: keygrp {
2 fsl,pins = <
3 MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */
4 >;
5 };

2)、添加KEY设备节点

1 key {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "atkalpha-key";
5 pinctrl-names = "default";
6 pinctrl-0 = <&pinctrl_key>;
7 key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
8 status = "okay";
9 };

3)、检查 PIN 为 UART1_CTS_B 这个 PIN 有没有被其他的 pinctrl 节点使用,如果有使用的话就要屏蔽掉,然后再检查 GPIO1_IO18这个 GPIO 有没有被其他外设使用,如果有的话也要屏蔽掉。

2、按键input驱动程序编写

struct input_dev *inputdev; /* input 结构体 */

        在设备结构体中定义一个 input_dev 指针变量。

93 if(value == 0){ /* 按下按键 */
94 /* 上报按键值 */
95 //input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
96 input_report_key(dev->inputdev, keydesc->value, 1);/*1, 按下*/
97 input_sync(dev->inputdev);
98 } else { /* 按键松开 */
99 //input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
100 input_report_key(dev->inputdev, keydesc->value, 0);
101 input_sync(dev->inputdev);
102 }

        在按键消抖定时器处理函数中上报输入事件,也就是使用 input_report_key函数上报按键事件以及按键值,最后使用 input_sync 函数上报一个同步事件!

155 /* 申请 input_dev */
156 keyinputdev.inputdev = input_allocate_device();
157 keyinputdev.inputdev->name = KEYINPUT_NAME;
158 #if 0
159 /* 初始化 input_dev,设置产生哪些事件 */
160 __set_bit(EV_KEY, keyinputdev.inputdev->evbit); /*按键事件 */
161 __set_bit(EV_REP, keyinputdev.inputdev->evbit); /* 重复事件 */
162
163 /* 初始化 input_dev,设置产生哪些按键 */
164 __set_bit(KEY_0, keyinputdev.inputdev->keybit);
165 #endif
166
167 #if 0
168 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
169 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=
BIT_MASK(KEY_0);
170 #endif
171

        使用 input_allocate_device 函数申请 input_dev,然后设置相应的事件以及事件码(也就是 KEY 模拟成那个按键,这里我们设置为 KEY_0)。最后使用 input_register_device函数向 Linux 内核注册 input_dev。

210 /* 释放 input_dev */
211 input_unregister_device(keyinputdev.inputdev);
212 input_free_device(keyinputdev.inputdev);

        当注销 input 设备驱动的时候使用 input_unregister_device 函数注销掉前面注册的 input_dev,最后使用 input_free_device 函数释放掉前面申请的 input_dev。

3、编写测试APP

static struct input_event inputevent;

        定义了一个 inputevent 变量,此变量为 input_event 结构体类型。

err = read(fd, &inputevent, sizeof(inputevent));

        当我们向 Linux 内核成功注册 input_dev 设备以后,会在/dev/input 目录下生成一个名为“eventX(X=0….n)”的文件,这个/dev/input/eventX 就是对应的 input 设备文件。我们读取这个文件就可以获取到输入事件信息,比如按键值什么的。使用read函数读取输入设备文件,也就是/dev/input/eventX,读取到的数据按照 input_event 结构体组织起来。获取到输入事件以后(input_event 结构体类型)使用 switch case 语句来判断事件类型,本章实验我们设置的事件类型
为 EV_KEY,因此只需要处理 EV_KEY 事件即可。比如获取按键编号(KEY_0 的编号为 11)、
获取按键状态,按下还是松开的?



四、运行测试

1、编译驱动程序和测试APP

obj-m := keyinput.o

make -j32
编译成功以后就会生成一个名为“keyinput.ko”的驱动模块文件。
arm-linux-gnueabihf-gcc keyinputApp.c -o keyinputApp
编译成功以后就会生成 keyinputApp 这个应用程序。
 

2、运行测试

        编译出来 keyinput.ko 和 keyinputApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15
目录中,重启开发板,进入到目录 lib/modules/4.1.15 中。

输入如下命令加载 keyinput.ko 这个驱动模块。

depmod //第一次加载驱动的时候需要运行此命令
modprobe keyinput.ko //加载驱动模块

        当驱动模块加载成功以后再来看一下/dev/input 目录:

        多了一个 event1 文件,因此/dev/input/event1 就是我们注册的驱动所对应的设备文件。

        输入如下命令:

./keyinputApp /dev/input/event1

        然后按下开发板上的 KEY 按键,结果如图:

        当我们按下或者释放开发板上的按键以后都会在终端上输出相应的内容,提示我们哪个按键按下或释放了,在 Linux 内核中 KEY_0 为 11。

五、总结:

        本笔记主要学习Linux input子系统实验,主要内容包括input子系统相关基础知识、原理图分析、驱动程序开发及测试、Linux自带按键驱动程序的使用。


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

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

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

相关文章

0基础2小时搭建自己的网站

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;0基础2小时搭建个人网站 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;宁静是一片强大而治愈的神奇海洋&#xff01; 目录 前言 第一步 环境…

DETR 论文精读【论文精读】End-to-End Object Detection with Transformers

DETR 这篇论文&#xff0c;大家为什么喜欢它&#xff1f;为什么大家说它是一个目标检测里的里程碑式的工作&#xff1f;而且为什么说它是一个全新的架构&#xff1f; 大家好&#xff0c;今天我们来讲一篇 ECC V20 的关于目标检测的论文。它的名字想必大家都不陌生&#xff0c;也…

系列十二、过滤器 vs 拦截器

一、过滤器 vs 拦截器 1.1、区别 &#xff08;1&#xff09;触发时机不一样&#xff0c;过滤器是在请求进入容器后Servlet之前进行预处理的&#xff0c;请求结束返回也是&#xff0c;是在Servlet处理完后&#xff0c;返回给前端之前&#xff1b; &#xff08;2&#xff09;过滤…

5.3有效的括号(LC20-E)

算法&#xff1a; 题目中&#xff1a;左括号必须以正确的顺序闭合。意思是&#xff0c;最后出现的左括号&#xff08;对应着栈中的最后一个元素&#xff09;&#xff0c;应该先找到对应的闭合符号&#xff08;右括号&#xff09; 比如:s"( [ ) ]"就是False&#xf…

个人服务器到期,项目下线,新的开始

告别旧服务器 2023.11.06服务器到期&#xff0c;所有项目正式下线 时间真的过的很快&#xff0c;从开始踏入编程的大门&#xff0c;到现在不知不觉已经陆续经手了两台服务器了&#xff0c;目前这台服务器是一年前的阿里云活动白嫖的嘿嘿嘿&#xff0c;该服务器上目前运行的项…

Docker与微服务实战——基础篇

Docker与微服务实战——基础篇 第一章 Docker 简介1.1 docker 理念1.2 容器与虚拟机比较 第二章 Docker 安装2.1 前提说明2.2 Docker的基本组成2.2.1 镜像&#xff08;image&#xff09;2.2.2 容器&#xff08;container&#xff09;2.2.3 仓库&#xff08;repository&#xff…

[MICROSAR Adaptive] --- Hello Adaptive World

Automotive E/E Architecture and AUTOSAR Adaptive Platform Vector Solution: MICROSAR Adaptive First project: Hello Adaptive World Summary 1 引言 1.1 AP诞生的历史背景 新一代电子电器架构通常将车内的节点分为三类。计算平台,预控制器和传感器执行器相关的节点,…

深度学习框架TensorFlow.NET之数据类型及张量2(C#)

环境搭建参考&#xff1a; 深度学习框架TensorFlow.NET环境搭建1&#xff08;C#&#xff09;-CSDN博客 由于本文作者水平有限&#xff0c;如有写得不对的地方&#xff0c;往指出 声明变量&#xff1a;tf.Variable 声明常量&#xff1a;tf.constant 下面通过代码的方式进行学…

用python写一个爆破网站管理员密码脚本

文章目录 拉取环境寻找登录界面寻找参数编写脚本试验效果 拉取环境 靶场环境是docker环境&#xff0c;把docker靶场映射到本机的7777端口&#xff0c;以便访问 docker pull docker.io/zksmile/vul docker run -d -p 7777:80 --name shop zksmile/vul:secshop_v2首页&#xff…

【嵌入式开发工具】TI+IAR建立工程与在线调试

IAR于1983 年在瑞典乌普萨拉由工程师Anders Rundgren创立&#xff0c;“IAR”是 Ingenjrsfirman Anders Rundgren 的缩写&#xff0c;意思是Anders Rundgren工程公司。笔者接触到这个开发工具主要是因为其对早期Ti的芯片支持很好&#xff0c;开发起来比较方便。不过现在TI公司官…

嵌入式中如何将BootLoader与APP合并成一个固件

1、前言 嵌入式固件一般分为BootLoader和App&#xff0c;BootLoader用于启动校验、App升级、App版本回滚等功能&#xff0c;BootLoader在cpu上电第一阶段中运行&#xff0c;之后跳转至App地址执行应用程序。 因此&#xff0c;在发布固件的时候&#xff0c;会存在BootLoader固件…

Redis02-持久化策略

目录 RDB&#xff08;Redis DataBase Backup file&#xff09; RDB执行原理 AOF&#xff08;Append-Only File&#xff09; RDB和AOF对比 Redis支持多种持久化方式&#xff0c;以确保数据在内存中持久存储&#xff0c;以便在Redis服务器重启时数据不会丢失。Redis中持久化的…

高效实用:批量重命名文件夹,提升工作效率

在日常生活和工作中&#xff0c;我们经常需要处理大量的文件夹&#xff0c;而文件夹的命名则直接关系到我们的管理效率和查找效率。如何快速、准确地批量重命名文件夹&#xff0c;提升工作效率呢&#xff1f;本文将提供一些实用的方法和技巧。现在一起来看看云炫文件管理器是如…

【Java 进阶篇】Java Cookie共享:让数据穿越不同应用的时空隧道

在Web开发中&#xff0c;Cookie是一种常见的会话管理技术&#xff0c;用于存储和传递用户相关的信息。通常&#xff0c;每个Web应用都会在用户的浏览器中设置自己的Cookie&#xff0c;以便在用户与应用之间保持状态。然而&#xff0c;有时我们需要在不同的应用之间共享Cookie数…

2.2整式的加减(第1课时)——合并同类项教学及作业设计

【学习目标】 1&#xff0e;理解同类项的概念&#xff0c;并能正确辨别同类项&#xff0e; 2&#xff0e;理解合并同类项的依据是乘法分配律&#xff0c;掌握合并同类项的方法&#xff0e; 知识点归纳&#xff1a; ★合并同类项后&#xff0c;所得的项的系数是___________…

Solidity数据类型之函数类型

solidity中函数的形式 function <function name>(<parameter types>) {internal|external|public|private} [pure|view|payable] [returns (<return types>)]每个关键字的意思&#xff08;方括号里面的写不写都可以&#xff09; function&#xff1a; 声明函…

基础知识:位运算

基础知识&#xff1a;位运算 1. 两类表达式 1. 两类表达式

【网安AIGC专题11.1】论文12:理解和解释代码,GPT-3大型语言模型学生创建的代码解释比较+错误代码的解释(是否可以发现并改正)

Comparing Code Explanations Created by Students and Large Language Models 写在最前面总结思考 背景介绍编程教育—代码理解和解释技能培养编程教育—解决方案研究问题研究结果 相关工作Code ComprehensionPedagogical Benifis of code explanationLarge Language Models i…

【监控指标】监控系统-prometheus、grafana。容器化部署。go语言 gin框架、gRPC框架的集成

文章目录 一、监控有哪些指标二、prometheus、grafana架构Prometheus 组件Grafana 组件架构优点 三、安装prometheus和node-exporter1. docker pull镜像2. 启动node-exporter3. 启动prometheus 四、promql基本语法五、grafana的安装和使用1. 新建空文件夹grafana-storage&#…

二维空间与三维空间的姿态表示法

二维空间与三维空间的姿态表示法 一、2D空间姿态表示法二、3D空间姿态表示法2.1 三个数表示空间姿态问题 2.2 九个数表示空间姿态问题 2.3 四个数表示空间姿态 结语Reference 假设&#xff0c;你有志成为我空军某航空旅歼-20飞行员&#xff0c;但要想开好飞机&#xff0c;那就得…