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

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自带按键驱动程序的使用。


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机设计的自动门控制系统

一、项目介绍 随着科技的不断发展&#xff0c;自动门成为公共场所、商业建筑和住宅社区等地的常见设施。自动门的出现使得进出门的操作更加便捷&#xff0c;提高了人们的生活质量和工作效率。为了实现自动门的开关控制&#xff0c;本项目基于单片机设计了一套自动门控制系统。…

分布式服务框架设计

目录 服务框架的设计 服务框架的功能 服务框架的性能指标 服务治理需要哪些功能 服务框架的设计 尽管不同的分布式服务框架实现细节存在差异&#xff0c;但是核心功能差异不大&#xff0c;下面的架构图描绘了一个分布式服务框架的整体逻辑架构 总共分为 3 层&#xff1a;1…

GNU ld链接器 lang_process()(二)

一、ldemul_create_output_section_statements() 位于lang_process()中11行 。 该函数用于创建与目标有关的输出段的语句。这些语句将用于描述输出段的属性和分配。 void ldemul_create_output_section_statements (void) {if (ld_emulation->create_output_section_sta…

MySQL数据库之表的增删查改

目录 表的操作1.创建表创建表案例 2.查看表结构3.修改表4.删除表 表的操作 1.创建表 语法&#xff1a; CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;说明&#xff1a; field 表示列…

C# 基类中的虚函数调用基类的虚函数执行的是派生类实现的对应函数吗

答案 &#xff1a; 是的。 比如基类Base中有两个virtual 函数A和B&#xff0c;然后派生类为Derive&#xff0c;override了函数A记为A&#xff0c;override了函数B记为B&#xff0c;且B之中会执行base.B的逻辑&#xff1b; 在Base中&#xff0c;B调用了A的逻辑&#xff0c;那么外…

C++ 实现红黑树

红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因…

通过环境变量实现多个JDK切换

前文: 由于jdk版本需要升级为jdk17,因为jdk8比较常用且稳定,本人又不想卸载掉安装的jdk8,在经过查找资料后找到了可以通过修改环境变量在本地任意切换jdk版本 环境变量配置 网上教程一堆,直接跳过了,这里主要说明怎么通过配置环境变量切换 电脑->属性->高级系统设置-&g…

centos7中多版本go安装

安装go的方式 官网下载tar.gz包安装 # 1.下载tar包 wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz # 2.解压tar包到指定路径 tar -xvf go1.18.1.linux-amd64.tar.gz -C /usr/local/go1.18 # 3.配置环境变量&#xff0c;打开 /etc/profile 文件添加以下文件每次开机时…

【MATLAB源码-第65期】基于matlab的OFDM/OTFS通信系统性能对比,输处误码率曲线;对比是否采用LDPC编码。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OTFS&#xff08;Orthogonal Time Frequency Space&#xff09;是一种无线通信调制技术&#xff0c;它利用时间、频率和空间的正交性来传输数据&#xff0c;目的是提高无线通信系统的性能&#xff0c;尤其是在多径和高移动性环…

selenium自动化测试入门 —— 定位frame和iframe中的元素对象

< frame> <iframe> 标签&#xff0c;浏览器会在标签中打开一个特定的页面窗口&#xff08;框架&#xff09;&#xff0c;它在本窗口中嵌套进入一个网页&#xff0c;当用selenium定位页面元素的时候会遇到定位不到frame框架内的元素的问题。 定位frame中的元素前我…

node插件express(路由)的插件使用(二)——body-parser和ejs插件的基本使用

文章目录 前言一、express使用中间件body-parser获取请全体的数据1. 代码2. 效果 二、express使用ejs&#xff08;了解即可&#xff09;1.安装2.作用3.基本使用&#xff08;1&#xff09;代码&#xff08;2&#xff09;代码分析和效果 4.列表渲染&#xff08;1&#xff09;代码…

同步网盘与云盘:哪个更好用?

同步网盘、同步云盘现在是热门的文件管理工具&#xff0c;在回答“同步网盘云盘哪个好用”这个问题之前&#xff0c;我们需要知道什么样的同步网盘、同步云盘算好用&#xff1f; 什么样的同步网盘云盘好用&#xff1f; 1、存储空间大 对于文件管理工具而言&#xff0c;存储空…

从零开始学习PX4源码0(固件下载及编译)

目录 文章目录 目录摘要1.重点学习网址2.固件下载1.下载最新版本固件2.下载之前版本固件 摘要 本节主要记录从零开始学习PX4源码1(固件下载)的过程&#xff0c;欢迎批评指正&#xff01;&#xff01;&#xff01; 下载固件主要分为两个版本&#xff0c;之前稳定版本和最新官网…

3、Sentinel 动态限流规则

Sentinel 的理念是开发者只需要关注资源的定义&#xff0c;当资源定义成功后可以动态增加各种流控降级规则。Sentinel 提供两种方式修改规则&#xff1a; • 通过 API 直接修改 (loadRules) • 通过 DataSource 适配不同数据源修改 通过 API 修改比较直观&#xff0c;可以通…

C#中LINQtoSQL只能在.NetFramework下使用,不能在.net 下使用

目录 一、在net7.0下无法实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 二、在.NetFramework4.8下成功实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 三、结论 四、理由 本文是个人观点&#xff0c;因为我百般努力在.net7.0下无法实现LINQtoSQL的…

如何将 XxlJob 集成达梦数据库

1. 前言 在某些情况下&#xff0c;你的项目可能会面临数据库选择的特殊要求&#xff0c;随着国产化的不断推进&#xff0c;达梦数据库是一个常见的选择。本篇博客将教你如何解决 XxlJob 与达梦数据库之间的 SQL 兼容性问题&#xff0c;以便你的任务调度系统能够在这个数据库中…

Idea去掉显示的测试覆盖率

一.启东时 误点击了 快捷键调出 【Ctrl 】【Alt】【F6】

npm ERR! code ELIFECYCLE

问题&#xff1a; 一个老项目&#xff0c;现在想运行下&#xff0c;打不开了 npm install 也出错 尝试1 、使用cnpm npm install -g cnpm --registryhttps://registry.npm.taobao.org cnpm install 还是不行 尝试2、 package.json 文件&#xff0c;去掉 那个插件 chorm…

Unity中Shader的GI的直接光实现

文章目录 前言一、在上一篇文章中&#xff0c;得到GI相关数据后&#xff0c;需要对其进行Lambert光照模型计算二、在准备好上面步骤后&#xff0c;我们需要准备缺少的数据1、准备上图中的 s.Normal2、准备上图中的 s.Albedo 前言 Unity中Shader的GI的直接光实现&#xff0c;基…

基于springboot实现在线考试平台项目【项目源码+论文说明】

基于springboot实现在线考试平台管理系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把在线考试管理与现在网络相结合&#xff0c;利用java技术建设在线考试系统&#xff0c;实现在线考试的信息化。则对于进一步提高在线考试管理发展&#xff0c;丰富在线考试管理经…