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.hqwc.cn/news/338031.html

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

相关文章

在IntelliJ IDEA上使用通义灵码(TONGYI Lingma)

参考链接&#xff1a; 通义灵码产品介绍_智能编码助手_AI编程_云效(Apsara Devops)-阿里云帮助中心 【IDEA如何使用通义灵码&#xff1f;】_idea 通义灵码-CSDN博客 1. 简介 1.1 定义 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提…

缓存代理服务器

一、代理的工作机制 1、代替客户机向网站请求数据&#xff0c;从而可以隐藏用户的直实IP地址 2、将获得的网页数据(静态 web 元素) 保存到缓存中并发送给客户机&#xff0c;以便下次请求相同的数据时快速响应 二、代理服务器的概念及其作用 代理服务器是一个位于客户端和原…

vue3引用类型和基础类型深度克隆

深度克隆失效的一个例子 import { cloneDeep } from "lodash"; import { ref } from "vue";const navArr ref(["recommend","hot","new", ]) const list1: any ref([]) const list2: any ref([]) const list3: any ref(…

强化学习求解TSP(三):Qlearning求解旅行商问题TSP(提供Python代码)

一、Qlearning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于奖励的决策问题。它是一种无模型的学习方法&#xff0c;通过与环境的交互来学习最优策略。Q-learning的核心思想是通过学习一个Q值函数来指导决策&#xff0c;该函数表示在给定状态下采取某个动作所获…

【Spring 篇】注解之舞:Spring AOP的优雅表演

欢迎来到Spring的代码舞台&#xff0c;在这里&#xff0c;我们将沉浸在一场注解之舞的盛宴中。今天我们将探讨如何使用注解方式实现Spring AOP&#xff0c;一种优雅而富有表现力的编程技术。 AOP的魅力 在编程的舞台上&#xff0c;AOP&#xff08;Aspect-Oriented Programmin…

CMU15-445-Spring-2023-Project #2 - 前置知识(lec07-010)

Lecture #07_ Hash Tables Data Structures Hash Table 哈希表将键映射到值。它提供平均 O (1) 的操作复杂度&#xff08;最坏情况下为 O (n)&#xff09;和 O (n) 的存储复杂度。 由两部分组成&#xff1a; Hash Function和Hashing Scheme&#xff08;发生冲突后的处理&…

[论文阅读]YOLO9000:Better,Faster,Stronger

摘要 我们引入了YOLO9000&#xff0c;一个可以检测超过9000种类别的先进的实时目标检测系统。首先我们提出了多种yolo检测方法的提升方式&#xff0c;既新颖又参考了 之前的工作。改进后的模型&#xff0c;YOLOV2在标准检测任务例如PASCAL VO 和COCO 上都取得了领先。使用一个…

每天学习一点点之 Spring Boot 1.x 升级 2.x 之 allowBeanDefinitionOverriding

最近组内大佬正在进行 Spring Boot 版本的升级&#xff0c;从 1.x 版本升级到 2.x 版本。在查看代码变更时&#xff0c;我注意到我之前编写的一个名为 ShardingRuleStrategy 的类被添加了 Primary 注解。这个类在原来的代码中被标记为 Component&#xff0c;同时也在 API 中被定…

Android开发基础(一)

Android开发基础&#xff08;一&#xff09; 本篇主要是从Android系统架构理解Android开发。 Android系统架构 Android系统的架构采用了分层的架构&#xff0c;共分为五层&#xff0c;从高到低分别是Android应用层&#xff08;System Apps&#xff09;、Android应用框架层&a…

Python基础知识:整理9 文件的相关操作

1 文件的打开 # open() 函数打开文件 # open(name, mode, encoding) """name: 文件名&#xff08;可以包含文件所在的具体路径&#xff09;mode: 文件打开模式encoding: 可选参数&#xff0c;表示读取文件的编码格式 """ 2 文件的读取 文…

Qt During startup program exited with code 0xc0000135

网上试了好多办法没有用&#xff0c;可以试试在pro目录下加入如图所示的.dll 可以下个everything搜索整个电脑查看是否有上述dll&#xff0c;如果没有也可以网上下载或者点击连接

文心大模型融入荣耀MagicOS!打造大模型“端云协同”创新样板

2024年1月10日&#xff0c;在荣耀MagicOS 8.0发布会及开发者大会上&#xff0c;荣耀终端有限公司CEO赵明宣布了“百模生态计划”&#xff0c;并与百度集团执行副总裁、百度智能云事业群总裁沈抖共同宣布&#xff0c;百度智能云成为荣耀大模型生态战略合作伙伴。 沈抖在现场演讲…