Android7 Input(二)Linux 驱动层输入事件管理

概述

        在Linux系统中,将键盘,鼠标,触摸屏等这类交互设备交由Linux Input子系统进行管理,Linux Input驱动子系统由于具有良好的和用户空间交互的接口。因此Linux Input驱动子系统,不止于只管理输入类型的设备。也可以将其他类型的设备归纳于Linux Input进行管理, 比如Rockchip 的Android系统平台上,将传感器数据的处理,就归纳到Linux Input进行管理。

        本文主要描述了输入设备在Linux Input驱动子系统中的管理,Input设备事件管理框架和产生事件时如何上报给Linux Input子系统。本文是基于RK3288 Android7的Linux内核进行讲解,该版本使用的是Linux-4.4.y的版本,读者在阅读的时候,请注意,不同版本的内核,部分源码可能与文章涉及到的源码不一样。

Input 驱动框架

Linux input驱动框架,如下所示:

注: 本文主要描述侧重点在键盘和触摸这类输入设备,因此,框图中描述的主要涉及这两类设备的驱动框架;

Input驱动数据结构

在Input驱动子系统中, 输入设备有input_dev数据结构进行管理,核心成员如下所示:

struct input_dev {const char *name;......struct input_id id;......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)];......int (*open)(struct input_dev *dev);void (*close)(struct input_dev *dev);int (*flush)(struct input_dev *dev, struct file *file);int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);......struct input_handle __rcu *grab;......struct list_head    h_list;struct list_head    node;};

主要成员:

name:设备名称

id:输入设备身份标识,比如厂家信息,设备总线类型等信息;

evbit:支持的事件类型掩码

keybit:按键事件掩码

relbit:相对坐标事件掩码

absbit:绝对坐标掩码

grab:事件Handler(这个后面进行讲解)

h_list:输入设备输入事件处理handle链表

node: 输入设备连接节点,系统将所有注册的输入设备通过一个全局的链表进行管理;

Input设备的创建和注册

1、Linux Input设备的创建可以使用input_allocate_device和devm_input_allocate_deviced两种类型API,目前推荐使用带有devm字样的接口,因为它可以在驱动probe过程中如果发生失败,自动完成内存回收,防止驱动程序处理不当,导致的物理内存的泄露。devm_input_allocate_deviced函数的实现,最终的核心部分还是input_allocate_device,如下图所示:

struct input_dev *input_allocate_device(void)
{static atomic_t input_no = ATOMIC_INIT(-1);struct input_dev *dev;dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);if (dev) {dev->dev.type = &input_dev_type;dev->dev.class = &input_class;device_initialize(&dev->dev);mutex_init(&dev->mutex);spin_lock_init(&dev->event_lock);init_timer(&dev->timer);INIT_LIST_HEAD(&dev->h_list);INIT_LIST_HEAD(&dev->node);dev_set_name(&dev->dev, "input%lu",(unsigned long)atomic_inc_return(&input_no));__module_get(THIS_MODULE);}return dev;
}

input_allocate_device完成主要如下的功能:

a、创建input_dev管理结构,并初始化部分成员变量;

b、设置Input设备的名字;

2、input设备的注册,有input_register_device()接口完成,下面只展示核心代码:

int input_register_device(struct input_dev *dev)
{....../* Every input device generates EV_SYN/SYN_REPORT events. */__set_bit(EV_SYN, dev->evbit);/* KEY_RESERVED is not supposed to be transmitted to userspace. */__clear_bit(KEY_RESERVED, dev->keybit);/* Make sure that bitmasks not mentioned in dev->evbit are clean. */input_cleanse_bitmasks(dev);......error = device_add(&dev->dev);if (error)goto err_free_vals;......list_add_tail(&dev->node, &input_dev_list);list_for_each_entry(handler, &input_handler_list, node)input_attach_handler(dev, handler);......return 0;......
}

input_register_device函数完成如下功能:

1)设置EV_SYN事件掩码,这是事件同步掩码,表示当前事件上报完成标记;

2)将input设备注册到Linux设备驱动管理框架中device_add;

3)将input设备加入到系统全局输入设备管理链表中;

4)为输入设备绑定事件处理Handler。不同的输入设备可以根据自己的特性,绑定不同的事件处理Handler;

Input Handler

        在上一个章节,描述注册input设备时,会为输入设备绑定一个事件处理handler。在Input驱动子系统中,对输入设备产生的事件,由input的Handler进行处理。目前在linux-4.4.y内核中,使用input_register_handler() 接口注册事件处理Handler,因此Input子系统可以灵活开发自己的事件处理Handler, 可以将输入设备产生的事件进行灵活处理,input_register_handler函数的核心实现如下所示:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{const struct input_device_id *id;int error;id = input_match_device(handler, dev);if (!id)return -ENODEV;error = handler->connect(handler, dev, id);if (error && error != -ENODEV)pr_err("failed to attach handler %s to device %s, error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);return error;
}

input_attach_handler完成的主要如下功能:

1) 将系统handler与输入设备dev进行匹配,只有匹配的handler,才能当做input设备的事件处理器;

2) dev和handler匹配完成,调用handler的connect方法绑定input 设备;

3)input对触摸屏,键盘等这类设备,使用evdev_hander进行事件处理;

evdev Handler

上一节,我们讲解了Input hander的注册方法,这一章节,我们主要描述evdev的实现。evdev handler的初始化,主要如下所示:

static struct input_handler evdev_handler = {.event      = evdev_event,.events     = evdev_events,.connect    = evdev_connect,.disconnect = evdev_disconnect,.legacy_minors  = true,.minor      = EVDEV_MINOR_BASE,.name       = "evdev",.id_table   = evdev_ids,
};static int __init evdev_init(void)
{return input_register_handler(&evdev_handler);}

首先,我们讲解connect的处理,因为,他是input设备和handler建立联系的过程。在evdev中connect由evdev_connect实现,核心代码如下所示:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);if (minor < 0) {error = minor;pr_err("failed to reserve new minor: %d\n", error);return error;}evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);if (!evdev) {error = -ENOMEM;goto err_free_minor;}INIT_LIST_HEAD(&evdev->client_list);spin_lock_init(&evdev->client_lock);mutex_init(&evdev->mutex);init_waitqueue_head(&evdev->wait);evdev->exist = true;dev_no = minor;/* Normalize device number if it falls into legacy range */if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)dev_no -= EVDEV_MINOR_BASE;dev_set_name(&evdev->dev, "event%d", dev_no);evdev->handle.dev = input_get_device(dev);evdev->handle.name = dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);evdev->dev.class = &input_class;evdev->dev.parent = &dev->dev;evdev->dev.release = evdev_free;device_initialize(&evdev->dev);error = input_register_handle(&evdev->handle);if (error)goto err_free_evdev;cdev_init(&evdev->cdev, &evdev_fops);evdev->cdev.kobj.parent = &evdev->dev.kobj;error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);if (error)goto err_unregister_handle;error = device_add(&evdev->dev);if (error)goto err_cleanup_evdev;return 0;
}

evdev_connect函数完成的功能有:

1) 创建evdev设备,并初始化;

2) 初始化evdev设备的handle,并进行注册。在input驱动框架中,handler和handle要注意区分,这个地方的命名并不太好;

3)注册evdev字符设备,字符设备的路径为/dev/input/event*, 该设备文件与用户空间打交道,重点关注evdev_fops这个方法集合,他对应了用户空间操作输入设备文件操作对用的内核空间字符设备的函数实现;

Handle

前面在讲解evdev的connect函数中,我们提到了input handle的注册,handle主要作用就是input dev和input handler建立联系的桥梁,数据结构如下所示:

struct input_handle {......struct input_dev *dev;struct input_handler *handler;struct list_head    d_node;struct list_head    h_node;
};

Handle的注册由input_register_handle完成,这里不在详细描述;

Input事件上报

本章节,主要描述常用输入设备产生事件,Input子系统如何进行事件的上报。在linux input驱动子系统中:

1)按键事件使用input_report_key()接口进行上报

2)相对坐标事件使用input_report_rel()接口进行上报;

3)绝对坐标事件使用input_report_abs()接口进行上报;

4)事件同步input_sync()和input_mt_sync(),这两个事件,可以当做是一个当前事件上报完成的标记,用户层程序,通过这个事件,输入设备产生的事件,是否上报完成;

注: 其他输入类型设备的事件上报接口,这里不再描述,感兴趣的同学,可以去阅读Linux源码。

Input设备驱动开发

本章节,主要描述开发一个input设备驱动的大概步骤,以下的描述基于ft6206这个i2c接口的多点触控芯片,开发步骤如下描述.

1、创建管理ft6206 Input设备驱动结构体, 将input_dev封装在里面。

struct ft6206_data {struct i2c_client *client;struct input_dev *input;struct gpio_desc *reset_gpio;u32 max_x;u32 max_y;
};

2、创建input设备

 input = devm_input_allocate_device(dev);if (!input)return -ENOMEM;ft6206->input = input;input->name = client->name;input->id.bustype = BUS_I2C;

3、设置该触摸设备支持的坐标事件

 input_set_abs_params(input, ABS_MT_POSITION_X, 0,ft6206->max_x, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_Y, 0,ft6206->max_y, 0, 0);error = input_mt_init_slots(input, FT6206_MAX_TOUCH_POINTS,INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);if (error)return error;

4、触摸设备被按下,会出发硬件中断,我们注册中断处理函数,接收硬件触发的事件

  error = devm_request_threaded_irq(dev, client->irq, NULL,ft6206_interrupt, IRQF_ONESHOT,client->name, ft6206);if (error) {dev_err(dev, "request irq %d failed: %d\n", client->irq, error);return error;}

5、注册input设备

 error = input_register_device(input);if (error) {dev_err(dev, "failed to register input device: %d\n", error);return error;}

6、在中断处理函数中,处理硬件触发的事件,并将该事件,封装为input事件,上报给input core处理

static irqreturn_t ft6206_interrupt(int irq, void *dev_id)
{......error = ft6206_read(ft6206->client, FT6206_REG_DEV_MODE, sizeof(buf), &buf);if (error) {dev_err(dev, "read touchdata failed %d\n", error);return IRQ_HANDLED;}touches = buf.touches & 0xf;if (touches > FT6206_MAX_TOUCH_POINTS) {dev_dbg(dev,"%d touch points reported, only %d are supported\n",touches, FT6206_MAX_TOUCH_POINTS);touches = FT6206_MAX_TOUCH_POINTS;}for (i = 0; i < touches; i++) {struct ft6206_touchpoint *point = &buf.points[i];u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;u8 event = point->event >> 6;u8 id = point->id >> 4;bool act = (event == FT6206_EVENT_PRESS_DOWN ||event == FT6206_EVENT_CONTACT);input_mt_slot(input, id);input_mt_report_slot_state(input, MT_TOOL_FINGER, act);if (!act)continue;input_report_abs(input, ABS_MT_POSITION_X, x);input_report_abs(input, ABS_MT_POSITION_Y, y);}input_mt_sync_frame(input);input_sync(input);return IRQ_HANDLED;
}

该中断处理程序完成的功能有:

1、读取硬件上报的触摸坐标;

2、将每一个坐标点封装为一个多点触控inpt事件(该设备支持多点触控硬件A协议,感兴趣的同学,可以研究下硬件多点触控协议);

3、上报多点触控事件;

4、坐标事件上报完成,最后上报一个同步事件,表示本次触摸事件上报完成;

总结

本文主要描述了在Linux系统中对于像键盘,触摸这类设备的管理和事件处理。下一篇讲解Andoid eventHub ,用户空间如何获取Input事件并进行处理;

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

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

相关文章

Java内存中的Heap(堆)的作用

Java内存中的Heap&#xff08;堆&#xff09;的作用 在 Java 的内存模型中&#xff0c;Heap&#xff08;堆&#xff09; 是 JVM&#xff08;Java Virtual Machine&#xff09;管理的运行时数据区域之一&#xff0c;主要用于存储程序运行过程中动态分配的对象和数据。它是 Java…

自行车模型与汽车模型的混合策略在自动驾驶中的多维度协同优化

基于动态架构与智能调度的自动驾驶系统设计 #mermaid-svg-1yvF1EzG07ktndY6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1yvF1EzG07ktndY6 .error-icon{fill:#552222;}#mermaid-svg-1yvF1EzG07ktndY6 .error-tex…

mysql.8.4.4--初始化报错--libnuma.so.1缺失

错误 mysqld: error while loading shared libraries: libnuma.so.1: cannot open shared object file: No such file or directory解决办法&#xff1a;下载相关依赖 sudo apt update sudo apt install numactl然后重新初始化 mysqld --initialize

【区块链安全 | 第三篇】主流公链以太坊运行机制

文章目录 1. 以太坊账户类型2. 以太坊网络架构2.1 节点类型2.2 交易流程 3. 共识机制4. Gas 机制4.1 Gas 计算方式4.2 以太坊 EIP-1559 交易机制 5. EVM&#xff08;以太坊虚拟机&#xff09;5.1 EVM 结构5.2 EVM 指令5.3 EVM 运行机制 6. 智能合约7. ERC 代币标准7.1 ERC-207.…

计算机三级信息安全部分英文缩写

eip&#xff0c;指令寄存器&#xff0c;用于存放指向下一条将执行指令的指针&#xff0c;即返回地址栈顶指针esp基址指针寄存器EBP&#xff0c;基地址数据执行保护DEP(Data Execute Prevention)技术可以设置内存堆栈区的代码为不可执行状态&#xff0c;从而防范溢出后代码的执行…

【Goalng】第九弹-----文件操作、JSON处理

&#x1f381;个人主页&#xff1a;星云爱编程 &#x1f50d;所属专栏&#xff1a;【Go】 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 长风破浪会有时&#xff0c;直挂云帆济沧海 目录 1.文件操作 1.1文件介绍 1.2.文件流 1.3.打开和关闭文件 1…

C#高级:启动、中止一个指定路径的exe程序

一、启动一个exe class Program {static void Main(string[] args){string exePath "D:\测试\Test.exe";// 修改为你要运行的exe路径StartProcess(exePath);}private static bool StartProcess(string exePath){// 创建一个 ProcessStartInfo 对象来配置进程启动参…

猜猜我用的是哪个大模型?我的世界游戏界面简单的模拟效果

我的罗里吧嗦的&#xff0c;根据小朋友的要求&#xff0c;边听边写边输入的提示词&#xff1a; 请生成一段完整的在网页中用html5和javascript代码模拟“我的世界”中游戏场景的互动画面&#xff0c;要求提供若干人物选项可以选择&#xff0c;请自行选择需要使用哪些库或框架来…

AI知识补全(八):多模态大模型是什么?

名人说&#xff1a;人生如逆旅&#xff0c;我亦是行人。 ——苏轼《临江仙送钱穆父》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知识补全&#xff08;七&#xff09;&#xff1a;AI Agent 智能…

更新docker 容器时,提前换后端jar 包,为什么会存在异常

我们现场更新时&#xff0c;通常都是提前将后端jar 包替换了&#xff0c;然后到了更新的时间&#xff0c;只需要更新相关的前端文件和修改各种配置&#xff0c;就行了。 但是最近一次更新操作中&#xff0c;忽然发现&#xff0c;提前更新后端包&#xff0c;会存在依赖丢失问题…

LoRA 模型微调框架核心原理及实现步骤

LoRA&#xff08;Low-Rank Adaptation&#xff09;模型微调框架通过低秩矩阵分解原理&#xff0c;实现了对大型预训练模型的高效微调。其核心原理是&#xff1a;在冻结预训练模型权重的基础上&#xff0c;向特定层注入可训练的低秩矩阵&#xff0c;以极少量参数&#xff08;通常…

XHR.readyState详解

XHR.readyState详解 引言 XHR.readyState是XMLHttpRequest对象的一个属性,它反映了当前请求的状态。在Ajax编程中,正确理解和使用XHR.readyState对于调试和确保异步请求的正确执行至关重要。本文将详细介绍XHR.readyState的属性值、含义以及在Ajax请求中的具体应用。 XHR.…

MySQL8.4 InnoDB Cluster高可用集群使用指南

简介 高可用方案 Orchestrator&#xff1a; 可视化 Web 界面管理 MySQL 拓扑结构&#xff0c;并且兼容多种复制架构&#xff08;异步、半同步、GTID&#xff09;&#xff0c;提供自动和手动的故障转移。但是8.0.21后 MySQL 更新了主从复制相关命令&#xff0c;Orchestrator无…

扩散模型总结

目录 定义与原理 发展历程 正向扩散过程 反向扩散过程 噪声预测网络 离散时间模型 连续时间模型 条件扩散模型 生成质量 训练稳定性 采样灵活性 图像生成 音频合成 文本生成 计算效率 模型复杂度 定义与原理 扩散模型是一种新型的生成模型,其核心原理源于热力…

【Java】Java核心知识点与相应面试技巧(七)——类与对象(二)

Java 类与对象篇 1.上期面试题解析&#xff1a; 上文链接&#xff1a;https://blog.csdn.net/weixin_73492487/article/details/146607026 创建对象时的内存分配过程&#xff1f; ① 加载类 ② 堆内存分配空间 ③ 默认初始化 ④ 显式初始化 ⑤ 构造器执行 this和super能否同时…

笔记:遇见未来——6G协同创新技术研讨会

https://www.cww.net.cn/article?id564308 研讨会由中国移动研究院首席科学家易芝玲博士主持。来自清华大学-中国移动联合研究院、北京邮电大学-中国移动研究院联合创新中心、东南大学-中国移动研究院联合创新中心、中关村泛联移动通信技术创新应用研究院等合作载体的知名教授…

Python Cookbook-4.14 反转字典

任务 给定一个字典&#xff0c;此字典将不同的键映射到不同的值。而你想创建一个反转的字典&#xff0c;将各个值反映射到键。 解决方案 可以创建一个函数&#xff0c;此函数传递一个列表推导作为dict的参数以创建需要的字典。 def invert_dict(d):return dict([(v,k) for …

深度学习在测距模型中的应用

一、单目视觉测距和双目视觉测距简介 1、单目视觉测距 模型&#xff1a;深度估计&#xff08;Depth Estimation&#xff09; 原理&#xff1a;通过深度学习模型&#xff08;如MonoDepth2、MiDaS&#xff09;或传统的计算机视觉方法&#xff08;如单目相机结合物体大小推断&am…

Linux Mem -- Slub内存分配器的几点疑问及解答

目录 1 怎样通过object地址获取其对应的struct slab&#xff1f; 2 struct page、struct folio和struct slab类型之间转换&#xff0c;怎么保证内部关键数据的传递&#xff1f; 3 怎样判断一个内存空间是属于slab、page管理&#xff1f; 4 struct page 结构中 __mapcou…

pip install cryptacular卡住,卡在downloading阶段

笔者安装pip install cryptacular卡在downloading阶段&#xff0c;但不知道为何 Collecting cryptacularCreated temporary directory: /tmp/pip-unpack-qfbl8f08http://10.170.22.41:8082 "GET http://repo.huaweicloud.com/repository/pypi/packages/42/69/34d478310d6…