编写一个简单的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;  // 设置关闭设备的函数

	// 设置输入设备的 ID
	input->id.bustype = BUS_HOST;  // 设置总线类型
	input->id.vendor = 0x0001;  // 设置厂商 ID
	input->id.product = 0x0001;  // 设置产品 ID
	input->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

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

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

相关文章

使用 NumPy 和 Matplotlib 进行高级数据可视化:实践指南

使用 NumPy 和 Matplotlib 进行高级数据可视化&#xff1a;实践指南 数据科学和工程实践中&#xff0c;NumPy 和 Matplotlib 是强大的组合工具。本文将进一步展示如何借助这两个库进行更复杂的可视化任务&#xff0c;例如创建多曲线、叠加图、动态可视化等场景。 一、环境准备…

Crowd Counting 系列NO4.—SwitchCNN(CVPR 2017)网络复现

文章目录 引言简介环境配置1、numpy 安装2、matplotlib 安装3、cv2 安装&#xff0c;即opencv-python安装4、scipy 安装5、theano安装7、flip_filters不再支持 数据问题密度图生成注意 引言 SwitchCNN是我看的比较早的一篇多列密集计数网络了&#xff0c;但是其网络实现因各种…

漏洞挖掘 | 基于mssql数据库的sql注入

前记 今天挖edu随意点开个站&#xff0c;发现存在mssql数据库的sql注入&#xff0c;在此分享下整个挖掘过程 目录 0x1 判断网站数据库类型 0x2 了解mssql数据库的主要三大系统表 0x3 了解mssql的主要函数 0x4 判断注入点及其注入类型 0x5 联合查询之判断列数 0x6 联合查询之…

Redis 哨兵 总结

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

【成长day】NeRF学习记录1:预备知识nerf论文算法学习

个人知乎文章链接&#xff1a;https://zhuanlan.zhihu.com/p/3383996241 预备知识 NeRF重建 NeRF的全称是Neural Radiance Fields&#xff0c;即将场景表示为视场合成的神经辐射场&#xff0c;用神经网络来拟合辐射场&#xff0c;实现对三维场景的隐式表示。本质是完成了图形…

[项目详解][boost搜索引擎#2] 建立index | 安装分词工具cppjieba | 实现倒排索引

目录 编写建立索引的模块 Index 1. 设计节点 2.基本结构 3.(难点) 构建索引 1. 构建正排索引&#xff08;BuildForwardIndex&#xff09; 2.❗构建倒排索引 3.1 cppjieba分词工具的安装和使用 3.2 引入cppjieba到项目中 倒排索引代码 本篇文章&#xff0c;我们将继续项…

Android——事件冲突处理

当我们给列表的item设置了点击事件后&#xff0c;又给item中的按钮设置了点击事件&#xff0c;此时item的点击事件会失效。 解决 给item的布局xml中设置以下属性 android:descendantFocusability"blocksDescendants"<LinearLayout xmlns:android"http://sc…

005:航空力学基础、无人机操纵、飞机性能

摘要&#xff1a;本文详细介绍无人机稳定性、操控性、飞机性能等概念。 一、飞机的稳定性 概念&#xff1a; 飞机的稳定性&#xff08;安定性&#xff09;&#xff0c;是指在飞机受到扰动后&#xff0c;不经飞行员操纵&#xff0c;能恢复到受扰动前的原始状态&#xff08;即原…

Android系统架构

Android系统架构&#xff1a; Android系统架构是一个复杂的、分层的结构&#xff0c;旨在提供高度的灵活性和可扩展性。这个架构可以大致分为以下几个主要层次&#xff1a; Linux Kernel&#xff08;Linux内核&#xff09;&#xff1a; Linux内核是Android系统的底层&#xff0…

OAK相机的RGB-D彩色相机去畸变做对齐

▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去&#xff0c;或者产生的…

OpenSSL

OpenSSL 概述 OpenSSL 是一个开源的、安全传输协议实现工具&#xff0c;广泛应用于数据加密与解密、证书生成与管理以及其他安全性相关的任务。在现代网络安全中&#xff0c;OpenSSL 被用于构建和维护 SSL/TLS 通信&#xff0c;确保数据在传输过程中的机密性和完整性。 简单来…

西圣和绿联电容笔哪款好用?西圣、绿联和uhb电容笔真实避坑测评!

现在市面上的平替电容笔种类非常多&#xff0c;在购买的时候大多数人都没有非常确定的目标&#xff0c;这主要是因为大多数人对电容笔的认识程度不够。 作为一个有着多年数码产品测评经验的测评员&#xff0c;我刚好对电容笔也有比较深刻的理解&#xff0c;也借着大家问我关于…

ASP.NET MVC-font awesome-localhost可用IIS不可用

环境&#xff1a; win10, .NET 6.0&#xff0c;IIS 问题描述 本地IIS正常显示&#xff0c;但放到远程服务器上&#xff0c;每个icon都显示?。同时浏览器的控制台报错&#xff1a; fontawesome-webfont.woff2:1 Failed to load resource: the server responded with a statu…

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)

绪论​ “没有那么多天赋异禀&#xff0c;优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构&#xff0c;通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…

java实现的音视频格式转化器

一、前言 最近写了一款图形界面版的音视频格式转化器&#xff0c;可以实现将多种视频之间进行转化&#xff0c;非常好用&#xff0c;如将AVI转换为&#xff0c;TS&#xff0c;FLV&#xff0c;MP4等。音频可将MP3转成WAV。 二、实现 1.需引入相关maven依赖。 <!-- 核心包 -…

群控系统服务端开发模式-应用开发-业务架构逻辑开发准备工作

安装与仓库已经调整完毕&#xff0c;现在开发业务架构逻辑&#xff0c;其次再开发功能逻辑。业务架构逻辑开发与功能逻辑开发不是一回事&#xff0c;一定要明白。业务架构指的是做某一件事或是某一种类型的事的逻辑&#xff0c;在互联网web应用中通常指一套系统的外在逻辑&…

知识见闻 - 磁力片原理

磁力片是一种利用磁性原理设计的玩具&#xff0c;它的工作原理和磁性方向的排列方式非常有趣。让我们深入了解一下磁力片的核心原理和磁性方向的特点。 磁力片的基本原理 磁力片的工作原理基于磁铁的基本特性。每个磁力片都包含多个小磁铁&#xff0c;这些磁铁被精心排列&#…

强化学习数学原理学习(一)

前言 总之开始学! 正文 先从一些concept开始吧,有一个脉络比较好 state 首先是就是状态和状态空间,显而易见,不多说了 action 同理,动作和动作空间 state transition 状态转换,不多说 policy 策略,不多说 reward 奖励,不多说 MDP(马尔科夫) 这里需要注意到就是这个是无…

小程序视频SDK解决方案,提供个性化开发和特效定制设计

美摄科技作为视频处理技术的领航者&#xff0c;深知在这一变革中&#xff0c;每一个细微的创新都能激发无限可能。因此&#xff0c;我们精心打造了一套小程序视频SDK解决方案&#xff0c;旨在满足不同行业、不同规模客户的多元化需求&#xff0c;携手共创视频内容的璀璨未来。 …

守护头顶安全——AI高空抛物监测,让悲剧不再重演

在城市的喧嚣中&#xff0c;我们享受着高楼林立带来的便捷与繁华&#xff0c;却往往忽视了那些隐藏在高空中的危险。近日&#xff0c;震惊全国的高空抛物死刑案件被最高院核准并执行。案件中被告人多次高空抛物的举动&#xff0c;夺去了无辜者的生命&#xff0c;也让自己付出了…