RK3568驱动指南|第十三篇 输入子系统-第150章 通用事件处理层event函数分析

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第十三篇 输入子系统_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第150章 通用事件处理层event函数分析

接下来,我们将继续学习event和events函数,如下图所示:

evdev_events函数如下所示:

static void evdev_events(struct input_ handle *handle, const struct input_value *vals, unsigned int count)
{
    struct evdev *evdev = handle->private;  // 获取输入句柄的私有数据,这里假设是evdev结构体类型
    struct evdev_client *client;  // 定义evdev客户端指针
    ktime_t *ev_time = input_get_timestamp(handle->dev);  // 获取输入设备的时间戳

    rcu_read_lock();  // 开始读取RCU保护区域

    client = rcu_dereference(evdev->grab);  // RCU安全地获取当前的evdev客户端

    if (client)
        evdev_pass_values(client, vals, count, ev_time);  // 如果存在抢占的客户端,则将值传递给抢占的客户端
    else
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            evdev_pass_values(client, vals, count, ev_time);  // 否则,将值传递给所有注册的客户端

    rcu_read_unlock();  // 结束读取RCU保护区域
}

该函数用于处理输入设备的事件。它接受一个输入句柄、一个输入值数组以及值的数量作为参数。

首先,它从输入句柄的私有数据中获取一个指向evdev结构体的指针。

然后,它获取输入设备的时间戳。

接下来,它通过使用RCU(Read-Copy-Update)机制来保护数据访问。通过调用rcu_read_lock()函数,它开始读取RCU保护区域。

然后,它通过RCU安全地获取当前的evdev客户端。如果存在抢占的客户端(即非空),它将调用evdev_pass_values函数将值传递给抢占的客户端。否则,它使用list_for_each_entry_rcu宏遍历evdev->client_list链表中的每个客户端,并调用evdev_pass_values函数将值传递给每个注册的客户端。

最后,它通过调用rcu_read_unlock()函数结束对RCU保护区域的读取操作。

我们学习一下evdev_pass_values函数,如下所示:

static void evdev_pass_values(struct evdev_client *client, const struct input_value *vals, unsigned int count, ktime_t *ev_time)
{
    struct evdev *evdev = client->evdev;  // 获取evdev客户端所属的evdev结构体
    const struct input_value *v;  // 当前处理的输入值
    struct input_event event;  // 输入事件结构体
    struct timespec64 ts;  // 时间戳
    bool wakeup = false;  // 是否需要唤醒等待线程

    if (client->revoked)
        return;  // 如果客户端已被撤销,则直接返回

    ts = ktime_to_timespec64(ev_time[client->clk_type]);  // 将ev_time转换为struct timespec64类型的时间戳
    event.input_event_sec = ts.tv_sec;  // 输入事件的秒字段设置为时间戳的秒值
    event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;  // 输入事件的微秒字段设置为时间戳的纳秒值除以1000得到的值

    /* 关中断,只需获取锁即可。 */
    spin_lock(&client->buffer_lock);  // 获取客户端的缓冲区锁

    for (v = vals; v != vals + count; v++) {
        if (__evdev_is_filtered(client, v->type, v->code))
            continue;  // 如果输入值被过滤,则跳过当前值的处理

        if (v->type == EV_SYN && v->code == SYN_REPORT) {
            /* 丢弃空的SYN_REPORT */
            if (client->packet_head == client->head)
                continue;  // 如果客户端的数据包头和数据头相同,则跳过当前值的处理

            wakeup = true;  // 设置唤醒标志为真
        }

        event.type = v->type;  // 设置输入事件的类型字段为当前值的类型
        event.code = v->code;  // 设置输入事件的代码字段为当前值的代码
        event.value = v->value;  // 设置输入事件的值字段为当前值的值
        __pass_event(client, &event);  // 将输入事件传递给客户端的事件处理函数
    }

    spin_unlock(&client->buffer_lock);  // 释放客户端的缓冲区锁

    if (wakeup)
        wake_up_interruptible(&evdev->wait);  // 如果需要唤醒等待线程,则唤醒等待队列中的线程
}

该函数用于将输入值传递给evdev客户端进行处理。它接受一个evdev_client结构体指针,一个输入值数组以及值的数量作为参数。

首先,它从客户端结构体中获取所属的evdev结构体。

然后,它检查客户端是否已被撤销,如果是,则直接返回。接下来,它将ev_time转换为struct timespec64类型的时间戳,并将其赋值给ts变量。然后,它将时间戳的秒值赋给输入事件结构体的input_event_sec字段,将时间戳的纳秒值除以1000得到的值赋给输入事件结构体的input_event_usec字段。然后,它获取客户端的缓冲区锁,以确保在处理输入值时不会有竞争条件。

接下来,它遍历输入值数组中的每个值。对于每个值,它首先检查是否被客户端过滤。如果被过滤,则跳过当前值的处理。

然后,它检查当前值是否为EV_SYN类型且代码为SYN_REPORT。如果是,它进一步检查客户端的数据包头和数据头是否相同。如果相同,则说明是一个空的SYN_REPORT,可以丢弃。否则,将唤醒标志设置为真。

接下来,它设置输入事件的类型字段为当前值的类型,代码字段为当前值的代码,值字段为当前值的值,并调用__pass_event函数将输入事件传递给客户端的事件处理函数。

完成对所有输入值的处理后,它释放客户端的缓冲区锁,解除对缓冲区的访问限制。

最后,如果需要唤醒等待线程(即唤醒标志为真),则调用wake_up_interruptible函数唤醒等待队列中的线程,以通知它们有新的事件可用。

   __pass_event函数将输入事件传递给客户端的事件处理函数,我们来学习一下。

static void __pass_event(struct evdev_client *client, const struct input_event *event)
{
    client->buffer[client->head++] = *event;  // 将事件复制到客户端的缓冲区中,然后将缓冲区头指针递增

    client->head &= client->bufsize - 1;  // 将缓冲区头指针掩码处理,确保其在缓冲区范围内

    if (unlikely(client->head == client->tail)) {
        /*
         * 这实际上"丢弃"了所有未消耗的事件,只保留了EV_SYN/SYN_DROPPED加上最新的事件。
         */
        client->tail = (client->head - 2) & (client->bufsize - 1);  // 更新缓冲区尾指针,使其指向倒数第二个事件

        client->buffer[client->tail] = (struct input_event) {
            .input_event_sec = event->input_event_sec,
            .input_event_usec = event->input_event_usec,
            .type = EV_SYN,
            .code = SYN_DROPPED,
            .value = 0,
        };  // 在缓冲区尾指针位置插入一个EV_SYN/SYN_DROPPED事件,表示丢弃了事件

        client->packet_head = client->tail;  // 更新数据包头指针为缓冲区尾指针
    }

    if (event->type == EV_SYN && event->code == SYN_REPORT) {
        client->packet_head = client->head;  // 更新数据包头指针为缓冲区头指针
        kill_fasync(&client->fasync, SIGIO, POLL_IN);  // 向注册的异步通知处理函数发送SIGIO信号,通知有新的事件可读取
    }
}

该函数用于将输入事件传递给evdev客户端的缓冲区进行存储。它接受一个evdev_client结构体指针和一个输入事件指针作为参数。

首先,它将输入事件复制到客户端的缓冲区中,并递增缓冲区头指针。

然后,它对缓冲区头指针进行掩码处理,以确保其在缓冲区范围内。

接下来,它检查缓冲区头指针是否等于缓冲区尾指针。如果相等,表示缓冲区已满,需要丢弃一些事件。

在这种情况下,它将缓冲区尾指针更新为缓冲区头指针减去2,并进行掩码处理,以确保其在缓冲区范围内。这样做是为了给丢弃的事件留出空间。

然后,它在缓冲区尾指针的位置插入一个EV_SYN/SYN_DROPPED事件,表示丢弃了事件。该事件具有与最新事件相同的时间戳。

接下来,它将数据包头指针更新为缓冲区尾指针,以确保数据包头始终指向最新的事件。

最后,如果输入事件的类型为EV_SYN且代码为SYN_REPORT,表示一个数据包的结束,它将数据包头指针更新为缓冲区头指针,并向注册的异步通知处理函数发送SIGIO信号,通知有新的事件可读取。

至此,通用事件处理层event函数分析完毕。

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

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

相关文章

python程序打包

目录 1. 命令2. 安装2.1 PyInstaller2.2 cx_Freeze(笔者未用过) 3. 打包示例3.1 在 pycharm 中执行3.2 若使用打包命令时报错3.3 路径问题 python打包成可执行文件,用于在没有Python环境的地方运行该程序,与qt打包类似。(笔者写的qt打包地址&…

Jenkins的快速入门

文章目录 一、Jenkins是什么?二、Jenkins安装和持续集成环境配置1.持续集成流程说明2.Gitlab代码托管服务器安装Gitlab简介:Gitlab安装Gitlab的使用切换中文添加组创建用户将用户添加到组创建项目idea中代码上传Gitlab 3.Jenkins持续集成环境服务器安装J…

SiteServer 学习笔记 Day02 添加站点的样式、脚本文件、图片资源

1、今天上传一些CSS、JavaScript、Image等资源文件,方便学习的时候使用。这些资源文件是SSCMS提供的模版文件中的可以到官网上去找,也可以从我上传的资源中下载。 https://download.csdn.net/download/xingchengaiwei/89030060 2、选择显示管理->资…

国产大模型KimiChat起飞了!200万字内测开启,AI助手能力大提升!

会议之眼 快讯 Kimi Chat是北京月之暗面科技有限公司推出的支持输入20万汉字的智能助手产品。其背后的技术是一个体量为千亿参数的大模型。Kimi Chat的推出是月之暗面“登月计划”的一部分,旨在为未来的多模态版本产品提供基础,并最终在大模型领域打造面…

MySQL运维实战之ProxySQL(9.1)ProxySQL介绍

作者:俊达 mysql通过复制技术实现了数据库高层面的可用,但是对于应用来说,当后端MySQL发生高可用切换时,应该怎么处理? 我们考虑几种方案: 1、使用域名绑定。应用通过dns连接后端实例,当后端发…

Bi-LSTM-CRF:其结合了 BI-LSTM 的上下文捕获能力和 CRF 的标签关系建模

Bi-LSTM-CRF:其结合了 BI-LSTM 的上下文捕获能力和 CRF 的标签关系建模 提出背景LSTM网络双向LSTM网络 (BI-LSTM)CRF网络LSTM-CRF网络双向LSTM-CRF网络 (BI-LSTM-CRF) 效果对比结构对比 论文:https://arxiv.org/pdf/1508.01991.pdf 代码:htt…

Tether CEO力挺波场TRON,直言其在一定程度实现了惠普金融

近期,加密媒体Bankless对Tether CEO Paolo Ardoino进行了深度专访。在专访中,Tether CEO Paolo Ardoino详细且深入地向听众们介绍了USDT,并对波场TRON的成就给予了高度认可。他更是直接表示,“我们不应该讨厌波场TRON,更应该换位思考站在其他人的角度考虑,尤其是那些无法负担起…

Hashtable 是如何保证线程安全的?

1、典型回答 Hashtable 保证线程安全主要是通过给关键方法,例如 put 添加方法、remove 删除方法,添加 synchronized 加锁来保证线程安全的。 2、全面剖析 Hashtable 保证线程安全的方法实现非常简单粗暴,就是给关键方法整体添加 synchroni…

力扣HOT100 - 49. 字母异位词分组

解题思路&#xff1a; 排序 注意&#xff1a; 返回时不能用List&#xff0c;因为List是抽象类&#xff0c;return的必须是List的具体实现&#xff0c;如ArrayList class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, Lis…

谈谈我对 AIGC 趋势下软件工程重塑的理解

作者&#xff1a;陈鑫 今天给大家带来的话题是 AIGC 趋势下的软件工程重塑。今天这个话题主要分为以下四大部分。 第一部分是 AI 是否已经成为软件研发的必选项&#xff1b;第二部分是 AI 对于软件研发的挑战及智能化机会&#xff0c;第三部分是企业落地软件研发智能化的策略…

c++之类与对象<二>

目录 前言 一&#xff1a;类的六个成员默认函数 二&#xff1a;构造函数 1.概念 2.特征 3.全缺省调用构造函数 4. 类中无构造函数 三&#xff1a;析构函数 1.概念 2.特征 3.类中无析构函数 四&#xff1a;拷贝构造函数 1.概念 2.特征 3.类中无拷贝构造函数 4.…

“一根盲杖,扫清前进道路”视障人士关爱行动中

近期&#xff0c;红枫林义警服务发展中心联合暨南街道社工站&#xff0c;面向暨南街道辖区内的视障人群&#xff0c;开展了一系列服务&#xff0c;送去了我们的关爱。 首先&#xff0c;我们成功为视障人群链接到了价值1万的爱心物资&#xff0c;捐赠仪式即为本次我们关爱行动的…

Zabbix 配置使用

目录 配置流程 添加组机组 添加模板 添加主机 配置图形 配置大屏 Monitoring 配置地图 最新数据 故障 使用IT服务 使用报表 资产管理 全局搜索 导入导出 用户权限 用户组权限 用户 匿名用户 调试模式 与 LDAP 对接 维护模式 故障确认 批量更新 配置流程…

Stable Diffusion WebUI 生成参数:脚本(Script)——提示词矩阵、从文本框或文件载入提示词、X/Y/Z图表

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 在本篇文章中&#xff0c;我们将深入探讨 Stable Diffusion WebUI 的另一个引人注目的生成参数——脚本&#xff08;S…

Java中的多线程详解(超级简单理解)(上篇)

使用工具 IntelliJ IDEA Community Edition 2023.1.4 使用语言 Java8 代码能力快速提升小方法&#xff0c;看完代码自己敲一遍&#xff0c;十分有用 目录 1.多线程概述 1.1 进程与线程 1.2 多线程的运行机制 1.3 多线程的优势 2.多线程编程 2.1 Thread类介绍 2.2 …

基于Java中的SSM框架实现电能计量与客户服务管理系统项目【项目源码+论文说明】

基于Java中的SSM框架实现电能计量与客户服务管理系统演示 摘要 当前时代的两个突出特征是世界经济一体化和以计算机为代表的信息技术的迅速发展。为了使组织在激烈的竞争中保持实力和发展&#xff0c;它必须对迅速变化的环境做出有效而有效的响应。 管理信息系统的应用可以提供…

Linux系统安装openGauss结合内网穿透实现公网访问本地数据库管理系统——“cpolar内网穿透”

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…

6SL3126-1TE21-8AA4单电机模块质保一年

商品编号(市售编号) 6SL3126-1TE21-8AA4 SINAMICS S120 单电机模块 输入&#xff1a;600V DC 输出&#xff1a;3AC 400V,18A 结构形式&#xff1a;书本尺寸 冷板冷却 优化的脉冲图形和 支持扩展 安全集成功能 包含 DRIVE-CLiQ 电缆 列表价&#xff08;不含税&#xff09…

查询正在运行的Top SQL的脚本(建议收藏)

这篇文章提供了一些现成的SQL脚本&#xff0c;通过查询V$SQLSTATS视图找到正在运行的TOP SQL&#xff0c;用于后续的优化。建议大家收藏&#xff0c;需要查询TOP SQL时直接复制和粘贴即可。 之前的一篇文章解释了为什么要使用V$SQLSTATS视图。 当数据库表现出各种不同的性能问…

MATLAB环境基于健康指标(Health indicator)的滚动轴承故障诊断

轴承的剩余使用寿命RUL预测过程一般包括以下三个步骤&#xff1a;&#xff08;1&#xff09;数据采集&#xff0c;&#xff08;2&#xff09;健康指标HI构建&#xff0c;&#xff08;3&#xff09;RUL预测。在预测过程中&#xff0c;RUL并不能直接依靠观测得到&#xff0c;其主…