编写一个简单的Iinput_dev框架

往期内容

本专栏往期内容:

  1. input子系统的框架和重要数据结构详解-CSDN博客
  2. input device和input handler的注册以及匹配过程解析-CSDN博客
  3. input device和input handler的注册以及匹配过程解析-CSDN博客

I2C子系统专栏:

  1. 专栏地址:IIC子系统_憧憬一下的博客-CSDN博客
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序

前言

img
img

和之前的驱动程序有点差别(IIC专栏中编写的控制器驱动框架编写一个通用的i2c控制器驱动框架-CSDN博客),在driver中变成注册input_dev,file_operations字符驱程序的创建在input_handler层实现(原本是在platform_driver中实现的:file_operation、设备类的注册),实现了内核驱动程序的上层、中转层、下层的分离

下层驱动中,只需要去编写好设备的驱动程序,在程序中分配、设置、注册input_dev,发生中断时只需要上报中断事件即可,其余的中转层和上层的驱动程序内核已经做好了。

这个在之前对内核提供的源码示例进行讲解的时候也很清晰了,详见本专栏前3章内容。

1. 怎么编写input_dev驱动

这里参考内核提供的gpio_keys.c为例子,input_dev上层

\Linux-4.9.88\drivers\input\keyboard\gpio_keys.c:📎gpio_keys.c

1.1 分配、设置、注册input_dev

这一部分主要是probe完成:

img

在gpio_keys.c中,添加了自己理解的一点注释,如下:

static int gpio_keys_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;  // 获取设备结构体const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);  // 从平台设备获取平台数据struct gpio_keys_drvdata *ddata;  // 驱动私有数据结构struct input_dev *input;  // 输入设备结构体size_t size;  // 计算需要分配的内存大小int i, error;  // 循环计数器和错误码int wakeup = 0;  // 标志位,指示是否支持唤醒功能// 如果 pdata 为 NULL,则从设备树获取平台数据if (!pdata) {pdata = gpio_keys_get_devtree_pdata(dev);if (IS_ERR(pdata))return PTR_ERR(pdata);  // 返回错误码}// 计算 gpio_keys_drvdata 和按钮数据结构的大小size = sizeof(struct gpio_keys_drvdata) +pdata->nbuttons * sizeof(struct gpio_button_data);// 分配驱动私有数据内存ddata = devm_kzalloc(dev, size, GFP_KERNEL);if (!ddata) {dev_err(dev, "failed to allocate state\n");  // 分配失败,输出错误信息return -ENOMEM;  // 返回内存不足错误码}// 分配输入设备input = devm_input_allocate_device(dev);if (!input) {dev_err(dev, "failed to allocate input device\n");  // 分配失败,输出错误信息return -ENOMEM;  // 返回内存不足错误码}ddata->pdata = pdata;  // 保存平台数据指针ddata->input = input;  // 保存输入设备指针mutex_init(&ddata->disable_lock);  // 初始化互斥锁// 将驱动数据指针与平台设备相关联platform_set_drvdata(pdev, ddata);input_set_drvdata(input, ddata);  // 将驱动数据与输入设备关联// 设置输入设备名称和物理路径input->name = pdata->name ? : pdev->name;  // 如果 pdata 中有名称则使用,否则使用平台设备名称input->phys = "gpio-keys/input0";  // 设置物理路径input->dev.parent = &pdev->dev;  // 设置设备的父设备input->open = gpio_keys_open;  // 设置打开设备的函数input->close = gpio_keys_close;  // 设置关闭设备的函数// 设置输入设备的 IDinput->id.bustype = BUS_HOST;  // 设置总线类型input->id.vendor = 0x0001;  // 设置厂商 IDinput->id.product = 0x0001;  // 设置产品 IDinput->id.version = 0x0100;  // 设置版本号// 启用 Linux 输入子系统的自动重复功能if (pdata->rep)__set_bit(EV_REP, input->evbit);  // 设置 EV_REP 事件位// 遍历每个按钮并设置for (i = 0; i < pdata->nbuttons; i++) {const struct gpio_keys_button *button = &pdata->buttons[i];  // 获取当前按钮信息struct gpio_button_data *bdata = &ddata->data[i];  // 获取按钮数据error = gpio_keys_setup_key(pdev, input, bdata, button);  // 设置按键,里面包括设置了中断函数if (error)return error;  // 返回错误码if (button->wakeup)  // 如果按钮支持唤醒功能wakeup = 1;  // 设置唤醒标志}// 创建 sysfs 组,用于导出按键和开关error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);if (error) {dev_err(dev, "Unable to export keys/switches, error: %d\n", error);  // 输出错误信息return error;  // 返回错误码}// 注册输入设备error = input_register_device(input);if (error) {dev_err(dev, "Unable to register input device, error: %d\n", error);  // 输出错误信息goto err_remove_group;  // 错误处理,移除 sysfs 组}// 初始化唤醒设备功能device_init_wakeup(&pdev->dev, wakeup);return 0;  // 返回成功err_remove_group:// 在错误情况下移除 sysfs 组sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);return error;  // 返回错误码
}

可以看出来,分配、设置、注册input_dev,然后去注册中断函数,用于调用中断的input_event函数上报中断事件,尝试写一个:

static struct input_dev *g_input_dev;
static int g_irq;
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{/* read data *//* report data */input_event(g_input_dev, EV_KEY, XX, 0);//通过 input_event() 上报事件,input_sync() 用于同步报告的输入事件。input_sync(g_input_dev);return IRQ_HANDLED;
}static int input_dev_demo_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;int error;struct resource *irq;/* 从设备树中获取硬件信息 *//* 分配、设置并注册 input_dev */g_input_dev = devm_input_allocate_device(dev);// 设置输入设备的基本信息g_input_dev->name = "input_dev_demo";g_input_dev->phys = "input_dev_demo";g_input_dev->dev.parent = dev;g_input_dev->id.bustype = BUS_HOST;g_input_dev->id.vendor = 0x0001;g_input_dev->id.product = 0x0001;g_input_dev->id.version = 0x0100;/* 设置 1: 支持的事件类型 */__set_bit(EV_KEY, g_input_dev->evbit); // 键盘或按钮事件__set_bit(EV_ABS, g_input_dev->evbit); // 绝对坐标事件/* 设置 2: 支持的具体事件 */__set_bit(BTN_TOUCH, g_input_dev->keybit); // 触摸按钮事件__set_bit(ABS_MT_SLOT, g_input_dev->absbit); // 多点触摸槽位__set_bit(ABS_MT_POSITION_X, g_input_dev->absbit); // 触摸屏 X 轴坐标__set_bit(ABS_MT_POSITION_Y, g_input_dev->absbit); // 触摸屏 Y 轴坐标/* 设置 3: 事件参数 */input_set_abs_params(g_input_dev, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); // X 坐标范围input_set_abs_params(g_input_dev, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); // Y 坐标范围// 注册输入设备error = input_register_device(g_input_dev);/* 硬件操作: 获取中断资源并注册中断 */irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);g_irq = irq->start;request_irq(irq->start, input_dev_demo_isr, IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);return 0;
}

1.2 硬件相关操作

  • 申请中断
  • 在中断服务程序里
    • 读取硬件获得数据
    • 上报数据
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);static inline void input_sync(struct input_dev *dev); // 实质也是 input_event

input子系统中读取流程解析-CSDN博客
具体内容在该章节的 event 处已经讲解过。

2. 代码

img

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>/* 定义指向输入设备结构体的指针 */
static struct input_dev *input_device_demo;
/* 用于保存输入设备的中断号 */
static int irq_num;/* 中断服务程序,处理输入设备的事件 */
static irqreturn_t input_device_demo_isr(int irq, void *dev_id)
{/* 可在此处添加数据读取和事件处理逻辑 *//* 向输入子系统报告按键事件 */input_event(input_device_demo, EV_KEY, KEY_TOUCH, 0);input_sync(input_device_demo);return IRQ_HANDLED;
}/* 分配、配置和注册平台驱动 */
static int input_device_demo_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;int error;struct resource *irq;/* 从设备树获取硬件信息 *//* 分配并初始化输入设备结构体 */input_device_demo = devm_input_allocate_device(dev);if (!input_device_demo)return -ENOMEM;/* 设置设备名称和物理位置 */input_device_demo->name = "input_device_demo";input_device_demo->phys = "input_device_demo";input_device_demo->dev.parent = dev;/* 设置设备的总线类型和ID */input_device_demo->id.bustype = BUS_HOST;input_device_demo->id.vendor = 0x0001;input_device_demo->id.product = 0x0001;input_device_demo->id.version = 0x0100;/* 设置输入设备支持的事件类型 */__set_bit(EV_KEY, input_device_demo->evbit); // 支持按键事件__set_bit(EV_ABS, input_device_demo->evbit); // 支持绝对位置事件/* 设置输入设备支持的具体事件 */__set_bit(BTN_TOUCH, input_device_demo->keybit); // 支持触摸按键事件__set_bit(ABS_MT_SLOT, input_device_demo->absbit); // 多点触控槽位事件__set_bit(ABS_MT_POSITION_X, input_device_demo->absbit); // X轴位置事件__set_bit(ABS_MT_POSITION_Y, input_device_demo->absbit); // Y轴位置事件/* 设置具体事件的参数范围,例如X和Y坐标的最小值、最大值等 */input_set_abs_params(input_device_demo, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);input_set_abs_params(input_device_demo, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);/* 注册输入设备 */error = input_register_device(input_device_demo);if (error)return error;/* 硬件操作:从设备树获取中断资源并注册中断 */irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);irq_num = irq->start;error = request_irq(irq_num, input_device_demo_isr, IRQF_TRIGGER_RISING, "input_device_demo_irq", NULL);if (error)input_unregister_device(input_device_demo);return error;
}/* 驱动移除函数,释放中断资源并注销输入设备 */
static int input_device_demo_remove(struct platform_device *pdev)
{free_irq(irq_num, NULL);input_unregister_device(input_device_demo);return 0;
}/* 设备树匹配表,用于匹配设备树中描述的设备 */
static const struct of_device_id input_device_demo_of_match[] = {{ .compatible = "input,input_device_demo", },{ },
};/* 定义平台驱动结构体,指定probe和remove函数 */
static struct platform_driver input_device_demo_driver = {.probe = input_device_demo_probe,.remove = input_device_demo_remove,.driver = {.name = "input_device_demo",.of_match_table = input_device_demo_of_match,}
};/* 模块初始化函数,注册平台驱动 */
static int __init input_device_demo_init(void)
{return platform_driver_register(&input_device_demo_driver);
}/* 模块退出函数,注销平台驱动 */
static void __exit input_device_demo_exit(void)
{platform_driver_unregister(&input_device_demo_driver);
}module_init(input_device_demo_init);
module_exit(input_device_demo_exit);MODULE_LICENSE("GPL");

img

在以前的驱动程序模板中,platform_driver中不仅注册了file_operation等,也注册了中断处理函数,并且中断处理函数中是直接对中断事件进行处理

但是在内核驱动分层中,platform_input_dev,中断处理函数只上报中断事件给input.c,交由Input.c去调用input_dev对应的input_handler中的函数来处理中断(filter、events、event函数),同时input_handler层提供了app调用的接口函数,如file_operation中的read

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

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

相关文章

2024年CentOS镜像下载地址,包括CentOS官网、国内镜像下载,超详细也

这里给大家提供了4种镜像下载地址&#xff0c;包括CentOS官方镜像下载、阿里云开源镜像站下载、网易开源镜像下载搜狐开源镜像下载。 1.CentOS官网镜像下载 因为服务器在国外所以打开CentOS官方网站的时候可能会比较慢。大家可以选择后面几种国内镜像下载方式。 1.1进入CentO…

《决策思维:人人必备的决策口袋书》

本书干货很多&#xff0c;十分值得一读。但受众不是一线员工与一线管理者&#xff0c;更多的倾向于管理者的管理者。一线员工读完的最大收获是可以理解老板的决策逻辑与思维方式&#xff0c;便于更好的去做执行。同时&#xff0c;还能帮助判断老板的决策是否正确&#xff0c;是…

esp32学习:语音识别教程esp-skainet库的使用

乐鑫推出了基于esp_sr算法的语音识别应用esp-skainet。官方介绍&#xff1a;ESP-Skainet 以最便捷的方式支持基于乐鑫的 ESP32系列 芯片的唤醒词识别和命令词识别应用程序的开发。使用 ESP-Skainet&#xff0c;您可以轻松构建唤醒词识别和命令词识别应用程序。 支持的主要功能…

C#通过异或(^)运算符制作二进制加密(C#实现加密)

快速了解异或运算符&#xff1a; 异或运算符在C#中用 “^” 来表示 口诀&#xff1a;相同取0&#xff0c;相异取1 简单加密解密winform示例&#xff1a; /// <summary>/// 异或运算符加密实现/// </summary>/// <param name"p_int_Num">初始值<…

网络原理之 TCP解释超详细!!!

TCP 有连接的 可靠传输 面向字节流 全双工 其中最核心的是可靠传输 那么 TCP 如何使用可靠传输的 ??? 我们尽可能传过去, 如果传不过去,发送方至少知道自己没传过去, 所以在于接收方, 收到或者没有收到, 都会有应答的操作 1. 确认应答 实现可靠性最核心的机制!!! 引出 …

【2024最新】渗透测试工具大全(超详细),收藏这一篇就够了!

所有工具仅能在取得足够合法授权的企业安全建设中使用&#xff0c;在使用所有工具过程中&#xff0c;您应确保自己所有行为符合当地的法律法规。如您在使用所有工具的过程中存在任何非法行为&#xff0c;您将自行承担所有后果&#xff0c;所有工具所有开发者和所有贡献者不承担…

eks节点的网络策略配置机制解析

参考链接 vpc-cni网络策略最佳实践&#xff0c;https://aws.github.io/aws-eks-best-practices/security/docs/network/#additional-resourcesvpc cni网络策略faq&#xff0c;https://github.com/aws/amazon-vpc-cni-k8s/blob/0703d03dec8afb8f83a7ff0c9d5eb5cc3363026e/docs/…

IP数据报的 分片与组装技术 深度解析

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;计算机网络高效通关之路 欢迎大家点赞收藏评论&#x1f60a; 目录 IP 分片和组装分片与组装的过程分片组装 分片与组装过程的示意图分片组装过程 IP 分片和组装 16 位标识(id): 唯一的标识主机发…

Redis 事务 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 事务 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 事务 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis & 事务…

自旋锁原理及基于原子引用手写自旋锁

什么是自旋锁 自旋锁&#xff08;Spinlock&#xff09;是一种用于多线程同步的机制&#xff0c;在尝试获取锁时&#xff0c;如果锁已经被其他线程持有&#xff0c;则当前线程不会立即被阻塞&#xff0c;而是会进入一个循环中反复尝试获取锁&#xff0c;直到成功为止。这种机制通…

探索CRM功能:六个解决方案助力企业发展

在当前竞争激烈的市场环境中&#xff0c;企业面临着客户关系管理的诸多挑战&#xff0c;CRM&#xff08;客户关系管理&#xff09;系统能够有效解决客户数据孤岛、提升客户互动效率、增强销售预测准确性等问题。通过整合客户信息和优化业务流程&#xff0c;CRM帮助企业实现更高…

解决JeecgBoot微服务通过Gateway访问Swagger资源出现“Knife4j文档请求异常”

1.问题描述 基于jeecgboot单体版本,参照官方推荐的纯微服务项目拆分指南,对jeecgboot项目进行微服务拆分,将gateway和system模块启动成功后,通过gateway访问访问Swagger接口文档,出现“Knife4j文档请求异常”,如下图: 2.问题定位: 1.浏览器F12打开控制台,查看异常请…

Kafka-Eagle(可视化监控平台)安装教程

Kafka Eagle 1. Install Mysql Kafka-Eagle 的安装依赖于 MySQL&#xff0c;MySQL 主要用来存储可视化展示的数据。 2.Kafka 环境准备 调整Kafka内存占用等参数&#xff0c;暴露JMX JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和…

在GeoTools中的Shapefile属性表读取效率之Shp与Dbf对比

目录 前言 一、POI测试数据简介 1、选用的POI数据 2、关于数据的属性数据 二、属性数据读取的两种方式实现 1、基于DbaseFileReader的读取 2、基于SimpleFeatureSource的读取 三、实际运行对比 1、内存和CPU占用情况 2、运行耗时情况 四、总结 前言 众所周知&#x…

《向量数据库指南》——text-embedding-3-large与Mlivus Cloud打造语义搜索新纪元

使用text-embedding-3-large生成向量并将向量插入Mlivus Cloud实现高效语义搜索的深度解析与实战操作 在数字化时代,数据的处理和存储方式正在经历前所未有的变革。特别是随着大数据和人工智能技术的快速发展,向量数据库作为一种新型的数据存储和查询方式,正逐渐受到越来越…

系统架构设计师教程 第2章 2.6 计算机语言 笔记

2.6计算机语言 ★★★★☆ 2.6.1计算机语言的组成 计算机语言 (Computer Language) 是指用于人与计算机之间交流的一种语言&#xff0c;是人与计算机之间传递信息的媒介。 计算机语言主要由一套指令组成&#xff0c;指令一般包括表达式、流程控制和集合三大部分内容。 表达…

Selenium爬虫技术:如何模拟鼠标悬停抓取动态内容

介绍 在当今数据驱动的世界中&#xff0c;抓取动态网页内容变得越来越重要&#xff0c;尤其是像抖音这样的社交平台&#xff0c;动态加载的评论等内容需要通过特定的方式来获取。传统的静态爬虫方法难以处理这些由JavaScript生成的动态内容&#xff0c;Selenium爬虫技术则是一…

测试造数,excel转insert语句

目录 excel转sql的insert语句一、背景二、直接上代码 excel转sql的insert语句 一、背景 在实际测试工作中&#xff0c;需要频繁地进行测试造数并插入数据库验证&#xff0c;常规的手写sql语句过于浪费时间&#xff0c;为此简单写个脚本&#xff0c;通过excel来造数&#xff0…

Flink CDC系列之:调研应用Flink CDC将 ELT 从 MySQL 流式传输到 StarRocks方案

Flink CDC系列之&#xff1a;调研应用Flink CDC将 ELT 从 MySQL 流式传输到 StarRocks方案 准备准备 Flink Standalone 集群准备 docker compose为 MySQL 准备记录使用 Flink CDC CLI 提交作业 同步架构和数据更改路由变更清理 本教程将展示如何使用 Flink CDC 快速构建从 MySQ…

Rust 力扣 - 1. 两数相加

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们使用一个全局的备忘录&#xff0c;然后我们遍历数组&#xff0c;如果当前元素在备忘录里面找到了&#xff0c;就返回备忘录里面记录的下标和当前下标记录&#xff0c;没找到就把当前元素匹配的元素和当前元素…