瑞芯微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主板
-
第151章 通用事件处理层read和write函数分析
本章节我们继续分析通用事件处理层evdev.c文件中的read和write函数。
151.1 read函数分析
接下来我们继续分析read函数,如下图所示:
evdev_read函数如下所示
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data; // 获取文件私有数据中的evdev客户端结构体指针
struct evdev *evdev = client->evdev; // 获取客户端结构体中的evdev结构体指针
struct input_event event; // 定义一个输入事件结构体
size_t read = 0; // 已读取的字节数
int error;
if (count != 0 && count < input_event_size())
return -EINVAL; // 如果count不为0且小于输入事件大小,返回无效参数错误
for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV; // 如果evdev设备不存在或客户端已撤销,则返回设备不存在错误码
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN; // 如果数据包头等于尾且文件标志中设置了非阻塞标志,返回暂无数据可读错误码
/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)
break; // 如果count为0,退出循环,不执行IO操作,但仍检查错误条件
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + read, &event))
return -EFAULT; // 将输入事件数据复制到用户空间缓冲区中,如果复制失败,返回错误码
read += input_event_size(); // 更新已读取的字节数
}
if (read)
break; // 如果已读取的字节数大于0,退出循环
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked); // 等待事件的发生,阻塞当前线程
if (error)
return error; // 如果等待被中断,返回错误码
}
}
return read; // 返回已读取的字节数
}
此函数的目的是从evdev设备中读取输入事件。下面是逐行解释:
1 struct evdev_client *client = file->private_data; 从文件的私有数据中获取evdev客户端的指针。
2 struct evdev *evdev = client->evdev; 从evdev客户端结构体中获取evdev设备的指针。
3 struct input_event event; 创建一个输入事件结构体。
4 size_t read = 0; 用于记录已读取的字节数。
5 int error; 用于保存错误码。
接下来是一些错误检查和条件判断:
6 if (count != 0 && count < input_event_size()) return -EINVAL; 检查count是否为0且小于输入事件的大小,如果是,则返回无效参数错误码。
7 (!evdev->exist || client->revoked) return -ENODEV; 检查evdev设备是否存在或客户端是否已撤销,如果是,则返回设备不存在错误码。
8 if (client->packet_head == client->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; 检查数据包头是否等于尾,并且文件标志中是否设置了非阻塞标志,如果是,则返回暂无数据可读错误码。
接下来是主要的读取逻辑:
9 if (count == 0) break; 如果count为0,表示不执行IO操作,但仍检查错误条件,此时跳出循环。
10 while (read + input_event_size()<= count && evdev_fetch_next_event(client, &event)) { 在已读取的字节数小于等于count且还能获取到下一个输入事件的情况下循环执行。
11 if (input_event_to_user(buffer + read, &event)) return -EFAULT; 将输入事件复制到用户空间缓冲区中,如果复制失败,返回错误码。
12 read += input_event_size(); 更新已读取的字节数。
接下来是处理没有读取到任何事件的情况:
13 if (read) break; 当已读取的字节数大于0时,跳出循环。
接下来是处理阻塞等待事件的情况:
14 if (!(file->f_flags & O_NONBLOCK)) { 如果文件标志中没有设置非阻塞标志,则执行以下操作。
15error = wait_event_interruptible(evdev->wait, client->packet_head != client->tail || !evdev->exist || client->revoked); 等待事件的发生,阻塞当前线程,直到数据包头不等于尾或evdev设备不存在或客户端已撤销。
16 if (error) return error; 如果等待被中断,返回错误码。
最后返回已读取的字节数。
这段代码是一个用于从evdev设备读取输入事件的函数。它首先进行一些错误检查和条件判断,然后通过循环读取输入事件并将其复制到用户空间缓冲区中,直到满足退出循环的条件。如果没有读取到任何事件,则根据阻塞标志执行相应操作。最后返回已读取的字节数。
151.2 write函数分析
接下来我们继续分析write函数,如下图所示:
evdev_write函数如下所示:
static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data; // 获取文件私有数据中的evdev客户端结构体指针
struct evdev *evdev = client->evdev; // 获取客户端结构体中的evdev结构体指针
struct input_event event; // 定义一个输入事件结构体
int retval = 0; // 返回值变量,默认为0
if (count != 0 && count < input_event_size())
return -EINVAL; // 如果count不为0且小于输入事件大小,返回无效参数错误
retval = mutex_lock_interruptible(&evdev->mutex); // 对evdev的互斥锁进行上锁,可中断
if (retval)
return retval; // 如果上锁失败,返回错误码
if (!evdev->exist || client->revoked) {
retval = -ENODEV;
goto out;
} // 如果evdev设备不存在或客户端已撤销,则返回设备不存在错误码
while (retval + input_event_size() <= count) {
if (input_event_from_user(buffer + retval, &event)) {
retval = -EFAULT;
goto out;
} // 从用户空间复制输入事件到event结构体中,如果复制失败,返回错误码
retval += input_event_size(); // 更新retval,增加一个输入事件的大小
input_inject_event(&evdev->handle,
event.type, event.code, event.value); // 将输入事件注入到evdev事件处理器中
cond_resched(); // 条件调度,让出CPU给其他线程执行
}
out:
mutex_unlock(&evdev->mutex); // 解锁evdev的互斥锁
return retval; // 返回retval作为写入的字节数或错误码
}
该函数用于将输入事件写入到evdev设备的缓冲区中。它接受一个文件结构体指针、一个指向用户空间缓冲区的指针、写入的字节数以及文件偏移量指针作为参数。
首先,它从文件的私有数据中获取evdev客户端结构体指针和evdev结构体指针。
然后,它检查count是否为0且小于输入事件的大小。如果是,则返回无效参数错误。
接下来,它对evdev的互斥锁进行上锁,可中断。如果上锁失败,返回错误码。
然后,它检查evdev设备是否存在且客户端是否已撤销。如果设备不存在或客户端已撤销,则返回设备不存在错误码。
接下来,它进入一个循环,直到写入的字节数达到count或超过count。
在循环中,它从用户空间复制输入事件数据到event结构体中。如果复制失败,返回错误码。
然后,它增加一个输入事件的大小到retval中,以跟踪已写入的字节数。
接下来,它将输入事件注入到evdev事件处理器中,使用event结构体中的type、code和value字段。
最后,它进行条件调度,让出CPU给其他线程执行。
循环结束后,它解锁evdev的互斥锁,并返回retval作为写入的字节数或错误码。
至此,通用事件处理层read和write函数分析完毕。