4. RK V4L2框架入门:核心数据结构与ioctl流程
在RK平台Sensor驱动开发中,V4L2(Video for Linux 2)是贯穿始终的核心框架。本章将深入剖析V4L2的三大核心数据结构——video_device、v4l2_device、v4l2_subdev,并详细解析ioctl调用在用户空间与内核驱动之间的完整流转路径。理解这些内容是编写正确、高效的Sensor驱动的基石。
学习提示: V4L2框架采用“设备-子设备”分层模型。在RK平台上,
v4l2_device代表整个摄像头子系统,v4l2_subdev代表Sensor、ISP等独立硬件单元,而video_device则是用户空间操作/dev/video*节点的接口。三者协同工作,缺一不可。
4.1 V4L2核心数据结构全景
V4L2框架围绕三个核心结构体构建,它们之间的关系可以用下面的架构图清晰展示:
4.2 三大核心数据结构详解
4.2.1 v4l2_device —— 子系统根容器
v4l2_device是V4L2框架的顶层结构体,代表一个完整的视频采集设备。在RK平台上,通常一个Camera Sensor对应一个v4l2_device实例。它的主要职责是管理所有关联的v4l2_subdev和video_device。
| 成员 | 类型 | 说明 |
|---|---|---|
dev |
struct device * |
指向内核通用设备结构体,用于设备模型集成 |
subdevs |
struct list_head |
链表头,挂载所有注册到此设备的v4l2_subdev |
ctrl_handler |
struct v4l2_ctrl_handler * |
控制句柄,管理曝光、增益等控制参数 |
name |
char[32] |
设备名称,用于标识和调试 |
notify |
void (*notify)() |
通知回调,子设备通过此回调向主设备发送事件 |
RK平台实践: 在RK驱动中,
v4l2_device通常在probe函数中通过v4l2_device_register()注册。例如RKISP驱动会创建一个v4l2_device,然后将Sensor的v4l2_subdev和ISP的v4l2_subdev都注册到它下面。
4.2.2 video_device —— 用户空间接口
video_device是用户空间与内核驱动交互的桥梁。它创建一个字符设备节点(如/dev/video0),并注册一组file_operations,其中最重要的就是ioctl操作。
// video_device 核心结构(简化版)
struct video_device {
const struct v4l2_file_operations *fops; // 文件操作函数集
struct v4l2_device *v4l2_dev; // 所属的v4l2_device
struct device *dev; // 字符设备
int index; // 设备节点编号
unsigned long flags; // 设备标志
// ... 其他成员
};
// v4l2_file_operations 定义
struct v4l2_file_operations {
struct module *owner;
long (*ioctl) (struct file *file, unsigned int cmd, unsigned long arg);
int (*mmap) (struct file *file, struct vm_area_struct *vma);
__poll_t (*poll) (struct file *file, struct poll_table_struct *wait);
// ... 其他操作
};
4.2.3 v4l2_subdev —— 硬件抽象层
v4l2_subdev是V4L2框架中最重要的抽象层,用于表示一个独立的硬件子模块。在RK Sensor驱动中,Sensor本身就是一个v4l2_subdev。它通过v4l2_subdev_ops结构体向框架暴露操作接口。
| 操作组 | 回调函数 | 功能描述 |
|---|---|---|
core |
s_power / ioctl |
电源管理、自定义ioctl |
video |
s_stream / g_frame_interval |
视频流控制、帧率获取 |
pad |
get_fmt / set_fmt / enum_mbus_code |
媒体总线格式协商 |
ctrl |
s_ctrl / g_ctrl |
控制参数(曝光、增益等)读写 |
常见误区: 初学者容易混淆
video_device和v4l2_subdev。记住:video_device是用户空间看到的“设备文件”,而v4l2_subdev是内核内部管理的“硬件单元”。一个video_device可以对应多个v4l2_subdev(如Sensor + ISP)。
4.3 V4L2 ioctl调用流程详解
ioctl是用户空间与V4L2驱动交互的主要方式。理解ioctl的完整调用流程,对于调试驱动问题至关重要。下面是一个典型的ioctl调用流程图:
4.3.1 ioctl调用详细步骤
- 用户空间发起调用: 应用程序调用
ioctl(fd, VIDIOC_S_FMT, &fmt),其中fd是/dev/video0的文件描述符。 - VFS层路由: 内核VFS根据
fd找到对应的struct file,调用其file_operations中的unlocked_ioctl函数指针。 - video_device分发:
video_device注册的v4l2_ioctl被调用。该函数根据命令号(如VIDIOC_S_FMT)进行初步分类。 - V4L2核心处理:
video_ioctl2()函数接管,通过查表找到对应的处理函数。对于需要子设备参与的命令,会遍历v4l2_device下的所有v4l2_subdev。 - 驱动实现: 最终调用到Sensor驱动注册的
v4l2_subdev_ops中的具体回调函数,如set_fmt、s_stream等。
4.3.2 关键ioctl命令分类
| 命令类别 | 示例命令 | 功能描述 |
|---|---|---|
| 格式协商 | VIDIOC_S_FMT / VIDIOC_G_FMT |
设置/获取图像格式(分辨率、像素格式) |
| 流控制 | VIDIOC_STREAMON / VIDIOC_STREAMOFF |
启动/停止视频流 |
| 缓冲区管理 | VIDIOC_REQBUFS / VIDIOC_QBUF / VIDIOC_DQBUF |
请求缓冲区、入队/出队缓冲区 |
| 控制参数 | VIDIOC_S_CTRL / VIDIOC_G_CTRL |
设置/获取曝光、增益等控制参数 |
| 查询能力 | VIDIOC_QUERYCAP / VIDIOC_ENUM_FMT |
查询设备能力、枚举支持的格式 |
调试技巧: 在RK平台开发中,可以使用
v4l2-ctl 工具从用户空间直接发送ioctl命令,快速验证驱动功能。例如:v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12 可以测试格式设置是否正常。
4.4 RK平台V4L2驱动初始化流程
在RK平台上,Sensor驱动的初始化流程遵循标准的V4L2子设备注册模式。下面是一个典型的初始化序列:
// RK Sensor驱动 probe 函数核心流程
static int sensor_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct sensor_dev *sensor;
int ret;
// 1. 分配并初始化私有数据结构
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
// 2. 初始化 v4l2_subdev
v4l2_i2c_subdev_init(&sensor->sd, client, &sensor_subdev_ops);
sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
// 3. 设置 pad 和媒体总线格式
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
// 4. 注册控制句柄(曝光、增益等)
v4l2_ctrl_handler_init(&sensor->ctrl_handler, 5);
sensor->ctrl = v4l2_ctrl_new_std(&sensor->ctrl_handler,
&sensor_ctrl_ops,
V4L2_CID_EXPOSURE, 0, 65535, 1, 1000);
sensor->sd.ctrl_handler = &sensor->ctrl_handler;
// 5. 注册子设备到 V4L2 框架
ret = v4l2_async_register_subdev(&sensor->sd);
return ret;
}
RK平台差异点: 在RK平台上,Sensor驱动通常通过
v4l2_async_register_subdev() 进行异步注册,而不是直接调用 v4l2_device_register_subdev()。这是因为RK的ISP驱动会先注册 v4l2_device,然后等待所有Sensor子设备异步注册完成后,再进行绑定和初始化。
4.5 本章小结
- v4l2_device 是顶层容器,管理所有子设备和视频设备。
- video_device 提供用户空间接口,通过
/dev/video*节点暴露。 - v4l2_subdev 是硬件抽象层,Sensor驱动主要实现此接口。
- ioctl调用路径:用户空间 → VFS → video_device → V4L2核心 → subdev ops。
- RK平台使用异步注册机制,Sensor驱动通过
v4l2_async_register_subdev()注册。