2. V4L2框架基础:V4L2设备节点、ioctl命令集、videobuf2核心数据结构

V4L2(Video for Linux 2)是 Linux 内核中视频设备驱动的标准框架,也是 Camera HAL 与底层硬件交互的核心桥梁。理解 V4L2 的设备节点模型、ioctl 命令体系以及 videobuf2 缓冲区管理机制,是进行 Camera HAL Buffer 管理调试的前提。

2.1 V4L2 设备节点模型

在 Linux 系统中,V4L2 设备以字符设备的形式暴露给用户空间,设备节点通常位于 /dev 目录下。Camera HAL 通过打开这些节点来与驱动进行通信。

设备节点 主设备号 次设备号范围 典型用途
/dev/video0 81 0-63 主摄像头视频采集、M2M(Memory-to-Memory)设备
/dev/video1 81 64-127 副摄像头、深度传感器
/dev/media0 81 128-191 Media Controller 拓扑管理
/dev/v4l-subdev0 81 192-255 子设备(Sensor、ISP、CSI 接收器)控制
调试提示: 在调试 Camera HAL 时,首先通过 ls -l /dev/video* 确认设备节点是否存在,并使用 v4l2-ctl --list-devices 查看设备拓扑关系,这是定位驱动加载失败或节点权限问题的第一步。
V4L2 设备节点层次结构 用户空间 (Camera HAL / v4l2-ctl) /dev/video0 /dev/video1 /dev/media0 subdev 内核空间 (V4L2 核心 + 设备驱动) 硬件 (Camera Sensor / ISP / DMA)

2.2 V4L2 ioctl 命令集

V4L2 通过 ioctl() 系统调用实现用户空间与内核驱动的交互。ioctl 命令按照功能分为查询、设置、流控制、缓冲区管理四大类。Camera HAL 中最常用的 ioctl 命令如下:

分类 ioctl 命令 功能描述 HAL 调用时机
查询 VIDIOC_QUERYCAP 查询设备能力(驱动名、总线信息、Capabilities 标志) 打开设备后立即调用
VIDIOC_ENUM_FMT 枚举支持的像素格式(YUYV、NV12、MJPEG 等) 初始化格式协商阶段
VIDIOC_ENUM_FRAMESIZES 枚举支持的帧尺寸(分辨率列表) 配置流参数前
设置 VIDIOC_S_FMT 设置数据格式(宽、高、像素格式、bytesperline) 配置流参数时
VIDIOC_S_PARM 设置帧率参数(timeperframe) 配置帧率时
缓冲区管理 VIDIOC_REQBUFS 请求分配缓冲区(指定 count 和 memory 类型) 启动流前
VIDIOC_QBUF 将缓冲区入队(放入驱动待处理队列) 填充空 buffer 或 re-queue
VIDIOC_DQBUF 从驱动出队已填充数据的缓冲区 获取已采集帧
流控制 VIDIOC_STREAMON 启动视频流(通知硬件开始采集) 所有 buffer 入队后
VIDIOC_STREAMOFF 停止视频流(停止采集并清空队列) 停止预览/录像时
关键流程: Camera HAL 启动流的典型 ioctl 序列为:VIDIOC_S_FMT → VIDIOC_REQBUFS → VIDIOC_QBUF(多次) → VIDIOC_STREAMON → 循环 VIDIOC_DQBUF / VIDIOC_QBUF → VIDIOC_STREAMOFF。

2.3 videobuf2 核心数据结构

videobuf2(vb2)是 V4L2 框架中负责缓冲区管理的核心模块。它抽象了不同内存类型(MMAP、USERPTR、DMA_BUF)的缓冲区分配、映射、入队出队和同步操作。理解 vb2 的数据结构是分析 Buffer 问题的关键。

2.3.1 核心结构体关系

videobuf2 核心数据结构关系 struct vb2_queue 缓冲区队列管理:type / io_modes / buf_struct_size struct vb2_buffer index / state / planes[] struct vb2_v4l2_buffer field / sequence / timecode struct vb2_plane m.fd / mem_offset / length 数据结构关系说明 • vb2_queue 是驱动中唯一的队列实例,管理所有 buffer 的生命周期 • vb2_buffer 是通用 buffer 结构,包含状态机(VB2_BUF_STATE_*) • vb2_v4l2_buffer 是 V4L2 扩展,包含帧序号、时间戳等元数据 • vb2_plane 描述单个平面的内存信息(fd、偏移、长度)

2.3.2 缓冲区状态机

videobuf2 使用严格的状态机管理每个 buffer 的生命周期。理解状态转换对于调试 buffer 卡死、丢帧问题至关重要。

状态 枚举值 含义 典型触发操作
DEQUEUED VB2_BUF_STATE_DEQUEUED 缓冲区在用户空间,未被驱动使用 DQBUF 返回后
PREPARED VB2_BUF_STATE_PREPARED 缓冲区已准备,等待入队 QBUF 调用后、硬件处理前
QUEUED VB2_BUF_STATE_QUEUED 缓冲区在驱动队列中,等待硬件处理 QBUF 完成、硬件未开始
ACTIVE VB2_BUF_STATE_ACTIVE 硬件正在使用该缓冲区进行 DMA 硬件开始采集
DONE VB2_BUF_STATE_DONE 硬件完成填充,等待用户空间出队 硬件中断、DMA 完成
ERROR VB2_BUF_STATE_ERROR 硬件处理出错,buffer 无效 硬件错误、超时
调试陷阱: 如果 HAL 调用 DQBUF 时返回 EIO,通常表示 buffer 状态进入了 ERROR。此时应检查驱动中 vb2_buffer_done() 调用时传递的状态参数是否为 VB2_BUF_STATE_ERROR,以及硬件是否产生了 DMA 错误中断。

2.3.3 内存类型与操作回调

videobuf2 支持三种内存模型,每种模型对应不同的分配和映射策略。驱动通过 vb2_queue 中的 io_modes 字段声明支持的模式。

内存类型 ioctl 标志 分配方式 HAL 侧操作 典型场景
MMAP V4L2_MEMORY_MMAP 内核分配物理连续/非连续内存,用户空间通过 mmap 映射 调用 mmap() 获取虚拟地址 简单预览、拍照
USERPTR V4L2_MEMORY_USERPTR 用户空间分配内存,将用户地址传递给驱动 分配 malloc 或 dma_buf 映射地址 GPU/Display 共享内存
DMA_BUF V4L2_MEMORY_DMABUF 通过 dma-buf 文件描述符传递,驱动导入 exporter 的内存 获取 dma-buf fd 并传递给驱动 Android HAL 标准路径
Android HAL 实践: 在 Android Camera HAL3 中,buffer 管理使用 DMA_BUF 模式。HAL 从 Gralloc 获取 dma-buf fd,通过 VIDIOC_QBUF 的 m.fd 字段传递给 V4L2 驱动。驱动通过 dma_buf_get() 获取 sg_table 进行 DMA 操作。这是性能最优、内存拷贝最少的路径。

2.3.4 驱动侧回调函数接口

驱动开发者需要实现 vb2_ops 结构体中的回调函数,videobuf2 核心会在合适的时机调用这些回调:

struct vb2_ops {
    int (*queue_setup)(struct vb2_queue *q,
                       unsigned int *nbuffers,
                       unsigned int *nplanes,
                       unsigned int sizes[],
                       struct device *alloc_devs[]);
    void (*wait_prepare)(struct vb2_queue *q);
    void (*wait_finish)(struct vb2_queue *q);
    int (*buf_prepare)(struct vb2_buffer *vb);
    void (*buf_queue)(struct vb2_buffer *vb);
    int (*start_streaming)(struct vb2_queue *q, unsigned int count);
    void (*stop_streaming)(struct vb2_queue *q);
};
回调函数 调用时机 驱动职责 常见调试问题
queue_setup REQBUFS 时 确认 buffer 数量和大小,分配 plane 数 返回的 sizes 与硬件需求不匹配
buf_prepare QBUF 时(入队前) 验证 buffer 参数,准备 DMA 映射 dma_map_sg 失败导致 -ENOMEM
buf_queue QBUF 完成时 将 buffer 加入硬件处理队列 未正确添加到链表导致 buffer 丢失
start_streaming STREAMON 时 启动硬件采集,使能 DMA 硬件未就绪导致 streamon 失败
stop_streaming STREAMOFF 时 停止硬件,返回所有 buffer 到 DONE 状态 未正确调用 vb2_buffer_done 导致 buffer 泄漏
调试技巧: 当遇到 VIDIOC_REQBUFS 返回 EBUSY 时,检查驱动中 queue_setup 回调是否在流未停止时被重复调用。在 stop_streaming 中务必调用 vb2_buffer_done(vb, VB2_BUF_STATE_ERROR) 将未处理的 buffer 全部归还,否则会导致后续 REQBUFS 失败。