Linux驱动学习—输入子系统

news2024/12/27 12:29:46

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_key
    
    gpio_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.coloradmin.cn/o/1372441.html

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

相关文章

使用FFmpeg+EasyDarwin搭建音视频推拉流测试环境

1. 前言 在上一篇文章《使用VS2017在win10 x64上编译调试FFmpeg&#xff08;附源码和虚拟机下载&#xff09;》中&#xff0c;我们讲解了如何搭建FFmpeg源码编译和调试环境。 调试FFmpeg&#xff0c;还需要搭建流媒体服务器。流媒体服务器的作用是通过网络对外提供音视频服务…

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

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

Win10子系统Ubuntu实战(二)

在 Windows 10 中安装 Ubuntu 子系统&#xff08;Windows Subsystem for Linux&#xff0c;简称 WSL&#xff09;有几个主要的用途和好处&#xff1a;Linux 环境的支持、跨平台开发、命令行工具、测试和验证、教育用途。总体而言&#xff0c;WSL 提供了一种将 Windows 和 Linux…

统信UOS虚拟机安装VirtualBox扩展使用USB功能

为什么要安装VirtualBox扩展包&#xff1f; 安装 Oracle VM VirtualBox 扩展包的原因是&#xff0c;它提供了对 USB 2.0、USB 3.0、远程桌面协议 VRDP&#xff08;VirtualBox Remote Desktop Protocol&#xff09;等实用功能的支持&#xff0c;以增强 VirtualBox 的功能。这些…

一文读懂大型语言模型参数高效微调:Prefix Tuning与LLaMA-Adapter

芝士AI吃鱼 在快速发展的人工智能领域中&#xff0c;高效、有效地使用大型语言模型变得日益重要&#xff0c;参数高效微调是这一追求的前沿技术&#xff0c;它允许研究人员和实践者在最小化计算和资源占用的同时&#xff0c;重复使用预训练模型。这还使我们能够在更广泛的硬件范…

STK 特定问题建模(五)频谱分析(第一部分)

文章目录 简介一、GEO星地通信收发机设计1.1 GEO卫星仿真1.2 地面接收机仿真 二、LEO星座及天线设计 简介 本篇对卫星通信中的频谱利用率、潜在干扰对频谱的影响进行分析&#xff0c;以LEO卫星信号对GEO通信链路影响为例&#xff0c;分析星地链路频谱。 建模将从以下几个部分…

PandoraNext—一个让你呼吸顺畅的ChatGPT

博客地址 PandoraNext—一个让你呼吸顺畅的ChatGPT-雪饼 (xue6ing.cn)https://xue6ing.cn/archives/pandora--yi-ge-rang-ni-hu-xi-shun-chang-de-chatgpt 项目 项目地址 pandora-next/deploy 项目介绍 支持多种登录方式&#xff1a; 账号/密码 Access Token Session To…

基于YOLOv5+单目的物体距离和尺寸测量

目录 1&#xff0c;YOLOv5原理介绍 2,单目测尺寸以及距离原理 2.1单目测物体距离 2.2单目测物体尺寸 3&#xff0c;成果展示 3.3测距离 3.2测尺寸&#xff1a; 1&#xff0c;YOLOv5原理介绍 YOLOv5是目前应用广泛的目标检测算法之一&#xff0c;其主要结构分为两个部分&a…

地理科学数据库近千个网站合集(2024年最新版)

树谷online&#xff0c;还有更多的直接获取的数据 树谷资料库大全&#xff08;2024年1月4日更新&#xff09; 地理科学数据库网站导航收集近1000个相关网站&#xff0c;由刘洪老师&#xff08;成都地质调查中心高级工程师&#xff09;长期维护&#xff0c;定期更新&#xff0…

宏景eHR fileDownLoad SQL注入漏洞复现

0x01 产品简介 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。 0x02 漏洞概述 宏景eHR fileDownLoad 接口处存在SQL注入漏洞,未经过身份认证的远程攻击者可利用此漏洞执行任意SQL指令,从而窃取数据库敏感…

【Linux】通过两台linux主机配置ssh实现互相免密登陆

以下是通过两台Linux主机配置SSH实现互相免密登录的代码及操作流程&#xff1a; node1主机IP&#xff1a;192.168.48.129 server主机IP&#xff1a;192.168.48.130 1、在node1主机上生成密钥对&#xff1a; ssh-keygen -t rsa 2、将node1主机的公钥发送到server主机&#x…

Python 编写不同时间格式的函数

该代码是一个时间相关的功能模块&#xff0c;提供了一些获取当前时间的函数。 Report_time() 函数返回当前时间的格式化字符串&#xff0c;例如 "20240110114512"。Y_M_D_h_m_s_time() 函数返回当前时间的年、月、日、时、分、秒的元组格式。Y_M_D_h_m_s() 函数返回…

乐鑫ESP32与SD NAND的协同应用|MK-米客方德

SD NAND在乐鑫ESP32上的作用 SD NAND是贴片式TF卡&#xff0c;可以用于存储数据&#xff0c;比如视频图片或者代码 乐鑫ESP32一颗具有双核处理器的嵌入式系统芯片&#xff0c;有丰富的外设接口&#xff0c;包括Wi-Fi、蓝牙、UART、SPI、I2C等&#xff0c;使其适用于各种物联网…

Vulnhub靶机:Corrosion1

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;corrosion:1&#xff08;10.0.2.12&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/c…

鸿蒙原生应用再添新丁!京东入局鸿蒙

鸿蒙原生应用再添新丁&#xff01;京东入局鸿蒙 来自 HarmonyOS 微博1月10日消息&#xff0c;#京东启动鸿蒙原生应用开发#&#xff01;优惠信息、派送进度都可以随时随地便捷查询。双方将携手为消费者带来全场景“多快好省”购物体验&#xff0c;更智能&#xff0c;更贴心&…

分享两个概念:非受检异常和受检异常

分享两个概念&#xff1a;非受检异常和受检异常 愿你的每一天都充满阳光和笑声&#xff0c;愿每一步都是轻松与愉快。在新的旅程中&#xff0c;愿你找到勇气攀登高峰&#xff0c;找到智慧化解困境。 愿你的心中充满温暖和善意&#xff0c;愿你的梦想如彩虹般美丽且真实。愿你发…

1.10 力扣回溯中等题

93. 复原 IP 地址 代码随想录 (programmercarl.com) 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1"…

SpringBoot:详解Bean装配

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《SpringBoot从官方文档学习》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、IoC容…

【QML COOK】- 006-用C++定义一个QML元素类型

Qt原本是一个C图形框架&#xff0c;因此QML也少不了C。QML通常只负责显示&#xff0c;而后台逻辑由C实现&#xff0c;因此掌握C和QML之间的交互非常必要。 本例实现一个最简单的例子&#xff0c;用C定义一个QML的元素类型并在QML使用它。 需求是在窗口上显示鼠标点击的次数。…

知识】分享几个摄像头的选型相关知识

【知识】分享几个摄像头的选型相关知识 目录 【知识】分享几个摄像头的选型相关知识一、前言二、正文1、先了解一下监控摄像头的种类1.1、云台型&#xff08;云台型一体摄像机&#xff09;1.2、枪机型&#xff08;枪型摄像机&#xff09;1.3、球机型&#xff08;球型摄像机&…