4. V4L2框架核心数据结构:struct video_device、struct v4l2_device、struct vb2_queue
V4L2(Video for Linux 2)框架是Linux内核中视频设备驱动的标准抽象层。理解其三大核心数据结构——struct video_device、struct v4l2_device和struct vb2_queue——是编写任何视频驱动(包括JPEG编码器和MIPI CSI接收器)的基础。这三者分别负责设备节点管理、顶层设备实例化以及缓冲区队列管理,构成了驱动程序的骨架。
4.1 核心数据结构关系总览
下图展示了三个核心数据结构在V4L2驱动中的协作关系:v4l2_device作为顶层容器,包含video_device用于注册字符设备节点,而vb2_queue则挂载在video_device中,负责视频帧缓冲区的分配与流转。
4.2 struct v4l2_device — 顶层设备实例
struct v4l2_device是V4L2驱动的根结构体,代表一个物理设备实例。它负责管理该设备下的所有子设备(subdev)和视频节点,并提供设备级别的初始化与注销接口。
4.2.1 核心字段解析
| 字段名 | 类型 | 说明 |
|---|---|---|
dev |
struct device * |
指向Linux内核通用设备结构体,用于电源管理、sysfs等 |
v4l2_dev |
struct list_head |
用于将本设备链接到全局v4l2_device_list链表 |
subdevs |
struct list_head |
挂载该设备下所有V4L2子设备的链表头 |
name |
char[32] |
设备名称,用于调试和日志输出 |
notify |
void (*notify)() |
子设备通知回调,用于subdev向主设备报告事件 |
ctrl_handler |
struct v4l2_ctrl_handler * |
设备级别的控制处理器,管理亮度、对比度等通用控制 |
4.2.2 初始化与注销流程
// 初始化 v4l2_device
struct v4l2_device v4l2_dev;
v4l2_device_register(parent_dev, &v4l2_dev);
// 注册子设备或 video_device
// ...
// 注销时释放资源
v4l2_device_unregister(&v4l2_dev);
实战要点:在MIPI CSI驱动中,
v4l2_device通常由主控端(如ISP或Video Mux)实例化,而每个CSI摄像头传感器作为子设备(struct v4l2_subdev)挂载到该设备下。JPEG编码器如果作为独立硬件模块,也应当拥有自己的v4l2_device实例。
4.3 struct video_device — 字符设备节点
struct video_device是用户空间与内核驱动交互的桥梁。它注册为/dev/videoX字符设备,用户程序通过open()、ioctl()、mmap()等系统调用与驱动通信。
4.3.1 关键字段与用途
| 字段名 | 类型 | 说明 |
|---|---|---|
fops |
const struct v4l2_file_operations * |
文件操作函数集,定义open/release/ioctl等回调 |
ioctl_ops |
const struct v4l2_ioctl_ops * |
V4L2专用ioctl操作集,如VIDIOC_QUERYCAP、VIDIOC_S_FMT等 |
v4l2_dev |
struct v4l2_device * |
指向所属的顶层v4l2_device实例 |
queue |
struct vb2_queue * |
指向vb2缓冲区队列,用于流式I/O |
minor |
int |
次设备号,由内核自动分配 |
device_caps |
u32 |
设备能力标志,如V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING |
ctrl_handler |
struct v4l2_ctrl_handler * |
节点级别的控制处理器,可覆盖设备级控制 |
4.3.2 注册与注销示例
struct video_device vdev;
// 设置文件操作和ioctl操作
vdev.fops = &my_fops;
vdev.ioctl_ops = &my_ioctl_ops;
vdev.v4l2_dev = &v4l2_dev;
vdev.queue = &vb2_q;
vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
// 注册video_device
video_register_device(&vdev, VFL_TYPE_VIDEO, -1);
// 注销
video_unregister_device(&vdev);
常见错误:忘记设置
v4l2_dev指针会导致内核崩溃;device_caps必须与ioctl_ops中实现的功能一致,否则用户空间查询能力时会得到矛盾结果。在JPEG编码驱动中,通常需要设置V4L2_CAP_VIDEO_M2M(内存到内存)标志。
4.4 struct vb2_queue — 缓冲区队列管理
struct vb2_queue是V4L2框架中视频缓冲区管理的核心,它实现了从用户空间申请缓冲区、内核态填充数据、再返还给用户空间的完整生命周期管理。对于JPEG编码器,vb2_queue负责管理输入原始帧和输出编码数据的缓冲区。
4.4.1 核心字段与回调函数
| 字段/回调 | 类型 | 说明 |
|---|---|---|
type |
enum v4l2_buf_type |
缓冲区类型,如V4L2_BUF_TYPE_VIDEO_CAPTURE |
io_modes |
u32 |
支持的I/O模式,如VB2_MMAP、VB2_USERPTR、VB2_DMABUF |
ops |
struct vb2_ops * |
驱动必须实现的回调函数集 |
mem_ops |
struct vb2_mem_ops * |
内存分配操作,通常使用vb2_dma_contig_memops |
buf_struct_size |
int |
每个缓冲区私有数据的大小,驱动可扩展 |
min_buffers_needed |
int |
驱动正常工作所需的最小缓冲区数量 |
4.4.2 struct vb2_ops 回调函数集
驱动必须实现以下回调,vb2框架会在合适的时机调用它们:
| 回调函数 | 调用时机 | 驱动职责 |
|---|---|---|
queue_setup |
用户调用REQBUFS时 | 验证请求的缓冲区数量和大小,分配内存 |
buf_prepare |
缓冲区入队前 | 验证缓冲区参数,准备硬件访问(如cache同步) |
buf_queue |
缓冲区入队后 | 将缓冲区加入驱动内部队列,启动硬件处理 |
start_streaming |
流开始时 | 启动硬件(如MIPI CSI接收或JPEG编码器) |
stop_streaming |
流停止时 | 停止硬件,返回所有未处理缓冲区 |
buf_finish |
缓冲区出队后 | 清理工作,如解除DMA映射 |
4.4.3 vb2_queue 初始化示例
struct vb2_queue vb2_q;
vb2_q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vb2_q.io_modes = VB2_MMAP | VB2_DMABUF;
vb2_q.ops = &my_vb2_ops;
vb2_q.mem_ops = &vb2_dma_contig_memops;
vb2_q.buf_struct_size = sizeof(struct my_buffer_private);
vb2_q.min_buffers_needed = 2;
vb2_q.dev = &pdev->dev; // DMA设备
vb2_queue_init(&vb2_q);
4.5 三者协作流程:从设备注册到缓冲区流转
下图展示了从驱动初始化到用户空间获取一帧数据的完整协作流程:
4.6 三者在JPEG编码与MIPI CSI场景中的角色
| 场景 | v4l2_device |
video_device |
vb2_queue |
|---|---|---|---|
| MIPI CSI 接收 | 代表CSI主机控制器 | 注册为/dev/video0,提供RAW帧捕获 | 管理RAW12/RAW10缓冲区队列 |
| JPEG 编码器 | 代表JPEG硬件编码器 | 注册为/dev/video1,提供M2M编码 | 管理输入YUV和输出JPEG两个队列 |
| ISP 处理 | 代表ISP处理器 | 注册为/dev/video2,提供处理后的图像 | 管理输入RAW和输出RGB/YUV队列 |
实战提示:在MIPI CSI + JPEG编码的完整链路中,通常需要至少两个
video_device节点:一个用于从CSI接收原始帧(capture),另一个用于JPEG编码(m2m)。它们可以共享同一个v4l2_device,但必须拥有独立的vb2_queue实例,因为缓冲区类型和内存管理策略不同。
4.7 本章小结
struct v4l2_device:顶层容器,管理设备生命周期和子设备链表,是驱动的“根”。struct video_device:用户空间接口,注册为字符设备节点,承载ioctl_ops和fops。struct vb2_queue:缓冲区管理引擎,通过回调函数集与硬件交互,实现高效的流式I/O。- 三者通过指针相互关联,形成“设备 → 节点 → 队列”的层次化架构,是V4L2驱动设计的基石。