Linux驱动开发笔记(八)输入子系统

文章目录

  • 前言
  • 一、输入子系统
    • 1. 子系统的引入
    • 2. 组成部分
    • 3. 事件处理流程
    • 4. 相关数据结构
  • 二、程序编写
    • 1. 相关API函数
      • 1.1 input_allocate_device ( )
      • 1.2 input_free_device ( )
      • 1.3 input_register_device ( )
      • 1.4 input_unregister_device ( )
      • 1.5 input_event ( )
      • 1.6 input_report_key ( )和input_sync ( )
    • 2. probe函数的编写
    • 3. remove函数
    • 4. 按键消抖


前言

  Linux 的 input 子系统是一个用于处理和管理输入设备(例如键盘、鼠标、触摸屏、游戏控制器等)的框架。它的作用是将硬件输入设备产生的原始输入数据转换成系统可以识别和使用的输入事件,并将这些事件传递给用户空间的应用程序。


一、输入子系统

1. 子系统的引入

  引入子系统是操作系统设计中一个重要的实践,它有助于管理复杂性、提高可扩展性和维护性。子系统将操作系统的不同功能模块化,每个子系统专注于特定的功能。例如,input子系统专门处理各种输入设备的数据。这种模块化设计可以简化内核的复杂性,使得每个子系统更易于开发、测试和维护。
  子系统提供标准化的接口和框架,简化了驱动程序的开发。开发人员只需关注如何与具体设备交互,而不必关心数据如何传递到用户空间。
  这种模块化和标准化设计有助于增强系统的稳定性。每个子系统都是相对独立的模块,出问题时可以单独调试和修复,而不影响其他部分。例如,input子系统出问题时,主要影响输入设备,而不会波及网络、存储等其他子系统。

2. 组成部分

  linux为了统一各个输入设备,将输入子系统分为了Drivers(驱动层)、Input Core(输入子系统核心层)、Handlers(事件处理层)三部分:
在这里插入图片描述

  Drivers
  设备驱动程序负责与实际的硬件设备进行交互,将设备的原始输入数据传递给 input core。每种输入设备(如键盘、鼠标)通常都有相应的驱动程序。
  Input Core
  这是 input 子系统的核心部分,为Drivers提供了规范及接口并通知Handlers对事件进行处理。负责管理输入设备和事件,处理设备注册、事件分发等工作。
  Handlers
  事件处理程序负责接收来自 input core 的输入事件,并将其传递给用户空间的应用程序或其他内核子系统。常见的事件处理程序包括键盘事件处理器、鼠标事件处理器等。

3. 事件处理流程

  1. 驱动程序检测到输入事件:
    当输入设备(如键盘或鼠标)产生输入数据时,设备驱动程序会检测到这些事件。
  2. 驱动程序生成输入事件:
    驱动程序将这些原始数据转换成标准的 input 事件结构,并将其传递给 input core。
  3. Input core 分发事件:
    input core 接收到驱动程序传递的事件后,将其分发给相应的事件处理程序。
  4. 事件处理程序处理事件:
    事件处理程序(如键盘或鼠标事件处理程序)接收到事件后,将其转换成用户空间可以理解的格式,并通过 input 事件接口传递给用户空间的应用程序。
  5. 用户空间应用程序接收事件:
    用户空间的应用程序通过读取 /dev/input/eventX 设备文件来接收和处理这些输入事件。

4. 相关数据结构

  我们可以将所有输入设备的输入信息将被抽象成以下结构体:

//输入事件
struct input_event{struct timeval time;//事件产生的时间__u16 type;         //输入设备的类型,鼠标、键盘、触摸屏__u16 code;			//设备类型的不同其含义也不同,如其类型是按键表示为按键号__s16 value;		//设备类型的不同其含义也不同,如其类型是按键表示为按键值
}

  我们可以利用struct input_dev结构体来代表一个具体的输入设备, 后面将会根据具体的设备来初始化这个结构体。

struct input_dev {const char *name;   //提供给用户的输入设备的名称const char *phys;   //提供给编程者的设备节点的名称const char *uniq;   //指定唯一的ID号struct input_id id; //输入设备标识IDunsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];unsigned long evbit[BITS_TO_LONGS(EV_CNT)];   //指定设备支持的事件类型unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //记录支持的键值unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //记录支持的相对坐标位图unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //记录支持的绝对坐标位图unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];unsigned long swbit[BITS_TO_LONGS(SW_CNT)];/*----------以下结构体成员省略----------------*/
};
  • evbit:用于指定支持的事件类型,这要根据实际输入设备能够产生的事件来选择,可选选项如下所示。
输入子系统事件类型取值描述
EV_SYN0x00同步事件
EV_KEY0x01用于描述键盘、按钮或其他类似按键的设备
EV_REL0x02用于描述相对位置变化,例如鼠标移动
EV_ABS0x03用于描述绝对位置变化,例如触摸屏的触点坐标
EV_MSC0x04其他事件类型
EV_SW0x05用于描述二进制开关类型的设备,例如拨码开关
EV_LED0x11用于 LED 事件,表示 LED 灯的状态
EV_SND0x12用于声音事件,表示声音的播放相关事件
EV_REP0x14用于重复事件,表示键盘重复发送事件
EV_FF0x15用于力反馈事件,表示力反馈设备的输出事件
EV_PWR0x16用于电源事件,表示电源状态变化
EV_FF_STATUS0x17用于力反馈状态事件,表示力反馈设备的状态变化
EV_MAX0x1f输入事件类型的最大值
EV_CNT(EV_MAX+1)输入事件类型的数量
  • keybit:记录支持的键值,“键值”在程序中用于区分不同的按键,可选“键值”如下所示。
按键键值描述
KEY_RESERVED0
KEY_ESC1
KEY_12
KEY_23
KEY_34
KEY_45

二、程序编写

1. 相关API函数

1.1 input_allocate_device ( )

//申请input_dev结构体
struct input_dev *input_allocate_device(void);
  • 参数:无
  • 返回值:申请到的 input_dev。

1.2 input_free_device ( )

//释放掉申请到的input_dev
void input_free_device(struct input_dev *dev);
  • 参数
    • dev:需要释放的input_dev。
  • 返回值:无

1.3 input_register_device ( )

//初始化input_dev
int input_register_device(struct input_dev *dev);
  • 参数
    • dev:要注册的 input_dev 。
  • 返回值:0,input_dev 注册成功;负值,input_dev 注册失败

1.4 input_unregister_device ( )

//注销input_dev
void input_unregister_device(struct input_dev *dev);
  • 参数
    • dev:要注销的 input_dev
  • 返回值:无

1.5 input_event ( )

//上报事件
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
  • 参数:
    • dev:指定输设备(input_dev结构体)。
    • type:事件类型。
    • code:编码。
    • value:指定事件的值。
  • 返回值: 无

1.6 input_report_key ( )和input_sync ( )

//发送上报结束事件
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{input_event(dev, EV_KEY, code, !!value);
}
//上报按键事件
static inline void input_sync(struct input_dev *dev)
{input_event(dev, EV_SYN, SYN_REPORT, 0);
}

2. probe函数的编写

  本次实验采用平台设备的写法,考虑到在input子系统中,我们不需要进行设备号的申请和类的创建,这里便不进行module_init和module_exti的编写。代码如下(示例):

//这里还用到了上章讲到的tasklet和work软中断,在宏定义通过更改DEFER_TEST实现切换
static int button_probe(struct platform_device *pdev)
{struct button_data *priv;struct gpio_desc *gpiod;struct input_dev *i_dev;int ret;pr_info("button_probe\n");priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);if (!priv)return -ENOMEM;i_dev = input_allocate_device();if (!i_dev) {devm_kfree(&pdev->dev, priv);return -ENOMEM;}i_dev->open = btn_open;i_dev->close = btn_close;i_dev->name = "key input";i_dev->dev.parent = &pdev->dev;priv->button_input_dev = i_dev;priv->pdev = pdev;set_bit(EV_KEY, i_dev->evbit);set_bit(KEY_1, i_dev->keybit);gpiod = gpiod_get(&pdev->dev, "button", GPIOD_IN);if (IS_ERR(gpiod)) {ret = PTR_ERR(gpiod);devm_kfree(&pdev->dev, priv);input_free_device(i_dev);return ret;}priv->irq = gpiod_to_irq(gpiod);priv->button_input_gpiod = gpiod;ret = input_register_device(priv->button_input_dev);if (ret) {pr_err("Failed to register input device\n");gpiod_put(priv->button_input_gpiod);devm_kfree(&pdev->dev, priv);input_free_device(i_dev);return ret;}platform_set_drvdata(pdev, priv);// 初始化去抖动工作INIT_DELAYED_WORK(&priv->debounce_work, button_debounce_work);ret = request_any_context_irq(priv->irq, button_input_irq_handler, IRQF_TRIGGER_FALLING, "input-button", priv);if (ret < 0) {dev_err(&pdev->dev, "Request GPIO IRQ failed\n");input_unregister_device(priv->button_input_dev);gpiod_put(priv->button_input_gpiod);devm_kfree(&pdev->dev, priv);return ret;}#if (DEFER_TEST == 0)tasklet_init(&button_tasklet, button_tasklet_handler, 0);
#elif (DEFER_TEST==1)/*初始化button_work*/INIT_WORK(&button_work, button_work_hander);
#endifreturn 0;
}
}

3. remove函数

static int button_remove(struct platform_device *pdev)
{struct button_data *priv = platform_get_drvdata(pdev);#if (DEFER_TEST == 0)tasklet_kill(&button_tasklet);
#endifcancel_delayed_work_sync(&priv->debounce_work);free_irq(priv->irq, priv);input_unregister_device(priv->button_input_dev);gpiod_put(priv->button_input_gpiod);devm_kfree(&pdev->dev, priv);return 0;
}

4. 按键消抖

//按键消抖
static void button_debounce_work(struct work_struct *work)
{struct button_data *priv = container_of(work, struct button_data, debounce_work.work);int button_status;button_status = gpiod_get_value(priv->button_input_gpiod);input_report_key(priv->button_input_dev, KEY_1, button_status);input_sync(priv->button_input_dev);
}//中断服务函数
static irqreturn_t button_input_irq_handler(int irq, void *dev_id)
{struct button_data *priv = dev_id;// 调试信息:记录去抖动工作被执行pr_info("Debounce work executed\n");// 调度去抖动工作schedule_delayed_work(&priv->debounce_work, msecs_to_jiffies(DEBOUNCE_DELAY_MS));#if (DEFER_TEST==0)tasklet_schedule(&button_tasklet);
#elif (DEFER_TEST==1)schedule_work(&button_work);  //触发工作
#endif	return IRQ_HANDLED;
}

免责声明:本文参考了野火的部分资料,仅供学习参考使用,若有侵权或勘误请联系笔者。

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

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

相关文章

Prometheus写入influxDB:中间件remote_storage_adapter

Prometheus写入influxDB&#xff1a;中间件remote_storage_adapter prometheus默认采用的是本地磁盘做数据存储&#xff0c;本地存储的优势就是运维简单但是缺点就是无法海量的metrics持久化和数据存在丢失的风险,数据写入可能造成wal文件损坏导致采集数据无法再写入的问题。 …

【嵌入式DIY实例】-Nokia 5110显示DS18B20传感器数据

Nokia 5110显示DS18B20传感器数据 文章目录 Nokia 5110显示DS18B20传感器数据1、硬件准备2、代码实现本文将介绍如何使用 ESP8266 NodeMCU 板和 DS18B20 数字温度传感器实现简单的温度测量站。 NodeMCU 微控制器 (ESP8266EX) 从 DS18B20 传感器读取温度值,并将其打印在诺基亚 …

LeetCode 2786.访问数组中的位置使分数最大:奇偶分开记录(逻辑还算清晰的题解)

【LetMeFly】2786.访问数组中的位置使分数最大&#xff1a;奇偶分开记录&#xff08;逻辑还算清晰的题解&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/visit-array-positions-to-maximize-score/ 给你一个下标从 0 开始的整数数组 nums 和一个正整数 …

如何使⽤C语⾔填充封闭图形?

一、问题 如要对封闭图形&#xff08;如两个区域的交集&#xff09;进⾏填充&#xff0c;那么怎么实现呢&#xff1f; 二、解答 填充就是⽤指定的颜⾊和图案填满⼀个封闭图形。 TC 提供了⼀个可对任意封闭图形填充的函数&#xff0c;即 floodfill( ) 。其调⽤格式如下&#xf…

Nginx部署Vue项目css文件能加载但是不生效

目录 问题描述问题解决 问题描述 Nginx部署打包后的Vue项目css文件能加载但是不生效&#xff0c; 问题解决 查看响应标头&#xff0c;发现不对劲&#xff0c; Content-Type: text/plain正确的应该是 Content-Type: text/css根本原因是nginx没有告诉浏览器正确的文件类型 所…

Kubernetes面试整理-Kubernetes 如何工作?

1. 部署应用: ● 开发者或管理员定义一组期望的状态(通常通过 YAML 文件),描述了应用包括的 pods、容器镜像、网络设置和存储要求。 ● 这些定义文件会提交给 API 服务器,存储在 etcd 中。 2. 调度: ● 当创建 pod 请求提交给 Kubernetes 时,调度器会选择一个节点来部署…

Postman接口测试/接口自动化实战教程

一、API 自动化测试 Postman 最基本的功能用来重放请求&#xff0c;并且配合良好的 response 格式化工具。 高级点的用法可以使用 Postman 生成各个语言的脚本&#xff0c;还可以抓包&#xff0c;认证&#xff0c;传输文件。 仅仅做到这些还不能够满足一个系统的开发&#x…

springboot学习小结

背景 业务上需要开发&#xff0c;组里一位前辈给我指路 spring基础 什么是spring spring提供一个容器称为spring应用上下文&#xff0c;容器里可以创建和管理组件&#xff0c;组件会在容器里装配好&#xff0c;组件也可以叫bean。 装配不由组件创建他依赖的组件&#xff0…

Python学习打卡:day05

day5 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day538、函数的初体验39、函数的基础定义语法函数的定义注意事项 40、函数的基础定义案例练习41、函数的传入参数42、函数的传入参数案例练习——升…

GEE数据融合——Landsat (collection 2,level 2 )4、5、7、8、9长时间序列影像数据融合和视频导出分析

本次我们使用Landsat (collection 2,level 2 )4、5、7、8、9数据的地标反射率数据进行融合,来实现指定区域的影像导出分析。 简介 长时间序列影像数据融合是指将Landsat影像数据集合2级2(Level 2)中的4、5、7、8和9这五个卫星的数据进行融合。具体来说,这包括将同一地…

毕业年薪30W起!25届最近5年浙江大学自动化考研院校分析

浙江大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、历年真题PDF 七、初试大纲复试大纲 八、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试科目…

揭秘招生简章的制作方法

一年一度的招生季即将来临&#xff0c;各大院校纷纷摩拳擦掌&#xff0c;准备迎接新的学子。对于学校而言&#xff0c;招生简章是其对外宣传的重要窗口&#xff0c;它直接关系到学校的招生效果和声誉。那么&#xff0c;如何制作一份既吸引人又实用的招生简章呢&#xff1f;下面…

web前端翻页:技术探秘与未来趋势

web前端翻页&#xff1a;技术探秘与未来趋势 Web前端翻页&#xff0c;作为网页交互体验的重要组成部分&#xff0c;始终吸引着开发者的关注。其设计原理、实现技巧以及未来趋势&#xff0c;都是我们在探索前端技术时不可忽视的方面。本文将从四个方面、五个方面、六个方面和七…

GIS之arcgis系列09:arcpy实现克里金差值

矢量点数据经过克里金差值后可以转换成栅格数据&#xff0c;那么就需要了解一下什么是克里金差值。 什么是克里金法? IDW(反距离加权法)和样条函数法插值工具被称为确定性插值方法&#xff0c;因为这些方法直接基于周围的测量值或确定生成表面的平滑度的指定数学公式。第二类…

【leetcode--字母异位词分组】

class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:np collections.defaultdict(list)for st in strs:name "".join(sorted(st))np[name].append(st)return list(np.values()) collections.defaultdict(list)创建字典类型&#xff…

Git代码冲突原理与三路合并算法

Git代码冲突原理 Git合并文件是以行为单位进行一行一行合并的&#xff0c;但是有些时候并不是两行内容不一样Git就会报冲突&#xff0c;这是因为Git会帮助我们进行分析得出哪个结果是我们所期望的最终结果。而这个分析依据就是三路合并算法。当然&#xff0c;三路合并算法并不…

Flowable-决策表设计器

✨✨✨ 最好用的Flowable决策表设计器 ✨✨✨ 最好用的Flowable流程设计器 本文中内容和案例出自贺波老师的书《深入Activiti流程引擎&#xff1a;核心原理与高阶实战》&#xff0c;书中的介绍更全面、详细&#xff0c;推荐给大家。 深入Activiti流程引擎

C++ 31 之 静态成员变量

#include <iostream> #include <string.h> using namespace std;// 特点: // 1.在编译阶段就分配了内存空间 // 2.类内声明&#xff0c;在类外进行初始化 // 3.所有对象共享一份静态成员数据 class Students01{ public:static int s_a; // 静态成员变量int s_b; };…

LSS 和 BEVDepth算法解读

前言 当前BEV的研究大都基于深度学习的方法&#xff0c;从组织BEV特征信息的方式来看&#xff0c;主流方法分属两类&#xff1a;自底向上方法和自顶向下方法。 自底向上方法比较早的代表工作是LSS&#xff0c;后来BEVDet、BEVDepth等也是基于LSS的框架来进行优化。自底向上方…

ArcGIS Pro SDK 样例 - 内容

ArcGIS Pro SDK 内容 目录 ArcGIS Pro SDK 内容1 项目1.1 创建一个空项目1.2 创建具有指定名称的新项目1.3 使用 Pro 的默认设置创建新项目1.4 使用自定义模板文件新建项目1.5 使用 ArcGIS Pro 提供的模板创建工程1.6 打开现有项目1.7 获取当前项目1.8 获取当前项目的位置1.9 获…