1. V4L2 框架概述

V4L2(Video for Linux 2)是 Linux 内核中用于视频设备驱动的标准框架,广泛应用于摄像头、电视调谐器、视频采集卡等设备。本章从架构分层、核心数据结构、视频采集流程三个维度,为后续设备树配置与 pinctrl/时钟参数解析奠定基础。

V4L2 软件架构分层 用户空间 应用程序(GStreamer / FFmpeg / OpenCV / v4l2-ctl) 系统调用接口(open / ioctl / mmap / poll / close) V4L2 核心框架(videodev.ko / v4l2-dev.c / v4l2-ioctl.c) 设备节点管理 / IOCTL 分发 / 缓冲区管理 / 事件通知 V4L2 设备驱动(Sensor / ISP / MIPI CSI / Video Mux) v4l2_subdev / v4l2_device / video_device / vb2_ops 硬件设备(Camera Sensor / CSI 控制器 / SoC 外设)

1.1 V4L2 架构分层

V4L2 框架采用分层设计,从上到下依次为:

层次 职责 关键模块/文件
用户空间 应用程序通过标准文件 I/O 和 ioctl 与 V4L2 设备交互 libv4l2, v4l2-ctl, GStreamer v4l2src
系统调用层 提供 open/close/ioctl/mmap/poll 等 POSIX 接口 VFS (Virtual File System)
V4L2 核心 设备节点注册、ioctl 命令分发、缓冲区管理框架 videodev.ko, v4l2-dev.c, v4l2-ioctl.c
设备驱动 实现具体的硬件操作:初始化、流控、格式配置、中断处理 sensor 驱动, CSI 驱动, video_device 回调
硬件层 物理摄像头传感器、MIPI CSI-2 接收器、SoC 视频接口 I2C 总线、MIPI D-PHY、时钟/Pinctrl 外设
要点提示: V4L2 核心层与驱动层之间通过 v4l2_device、video_device、v4l2_subdev 三个核心结构体进行解耦。驱动开发者主要关注驱动层的实现,但必须理解核心层的缓冲区管理机制。

1.2 核心数据结构

V4L2 框架围绕三个核心结构体组织驱动逻辑,它们之间的关系如下:

V4L2 核心数据结构关系 v4l2_device 顶层设备实例 管理 subdev 列表 video_device 字符设备节点 fops / ioctl 分发 v4l2_subdev 子设备抽象 sensor / CSI / 编解码器 关键关系 v4l2_device 包含 video_device 和多个 v4l2_subdev;subdev 通过 media_entity 连接 扩展数据结构 vb2_queue(缓冲区队列) | media_pad(媒体垫) | v4l2_pix_format(像素格式) v4l2_ctrl_handler(控制处理器) | v4l2_fh(文件句柄)

1.2.1 v4l2_device

顶层设备结构体,代表一个完整的 V4L2 硬件设备实例。驱动初始化时首先注册 v4l2_device,然后在其上挂载 video_device 和 v4l2_subdev。

struct v4l2_device {
    struct device *dev;                /* 指向 struct device */
    struct list_head subdevs;          /* 子设备链表头 */
    spinlock_t lock;
    char name[V4L2_DEVICE_NAME_SIZE];
    /* ... */
};

1.2.2 video_device

字符设备节点抽象,用户空间通过 /dev/videoX 访问。核心成员包括文件操作集 fops 和 ioctl 操作集 ioctl_ops。

struct video_device {
    const struct v4l2_file_operations *fops;   /* open/release/read/write/poll */
    const struct v4l2_ioctl_ops *ioctl_ops;     /* 所有 ioctl 命令处理 */
    struct device dev;                          /* 内核设备结构 */
    struct v4l2_device *v4l2_dev;               /* 所属顶层设备 */
    /* ... */
};

1.2.3 v4l2_subdev

子设备抽象,用于表示摄像头传感器、MIPI CSI 接收器、视频解码器等独立硬件模块。subdev 通过 v4l2_subdev_ops 暴露操作接口。

struct v4l2_subdev {
    struct media_entity entity;          /* 媒体实体,用于 pipeline 管理 */
    struct v4l2_device *v4l2_dev;        /* 所属顶层设备 */
    const struct v4l2_subdev_ops *ops;   /* 核心操作集 */
    struct v4l2_ctrl_handler *ctrl_handler; /* 控制处理器 */
    struct device *dev;                  /* 对应 struct device */
    /* ... */
};
实践要点: 在设备树配置中,每个 v4l2_subdev 通常对应一个设备树节点。pinctrl 和时钟参数正是在这些节点中通过 pinctrl-* 和 clocks 属性进行配置,驱动通过 devm_clk_get() 和 pinctrl_select_state() 获取。

1.3 视频采集流程

一个标准的 V4L2 视频采集流程包含以下步骤,驱动开发者需要理解每个阶段对应的内核回调:

  1. 打开设备:应用程序调用 open("/dev/video0"),触发驱动 v4l2_fops->open 回调,初始化硬件并申请资源。
  2. 查询能力:VIDIOC_QUERYCAP ioctl 返回 v4l2_capability,告知驱动支持的设备类型和功能。
  3. 设置格式:VIDIOC_S_FMT 设置图像宽度、高度、像素格式(如 YUYV、MJPEG、NV12)。
  4. 申请缓冲区:VIDIOC_REQBUFS 分配视频缓冲区,驱动通过 vb2_queue 管理内存。
  5. 查询/映射缓冲区:VIDIOC_QUERYBUF + mmap() 将内核缓冲区映射到用户空间。
  6. 入队缓冲区:VIDIOC_QBUF 将空闲缓冲区放入驱动采集队列。
  7. 开始采集:VIDIOC_STREAMON 启动视频流,驱动开始从 sensor 获取数据。
  8. 出队缓冲区:VIDIOC_DQBUF 获取已填充数据的缓冲区,应用程序处理图像。
  9. 停止采集:VIDIOC_STREAMOFF 停止视频流。
  10. 关闭设备:close(fd) 释放所有资源。
V4L2 视频采集流程 1. open() 2. QUERYCAP 3. S_FMT 4. REQBUFS 5. QUERYBUF+mmap 6. QBUF (入队) 7. STREAMON
常见陷阱: 在 STREAMON 之前,驱动必须确保 pinctrl 已切换到正确状态(通常是 "active" 状态),且所有相关时钟已使能。如果设备树中 pinctrl 或时钟配置错误,STREAMON 后的第一个 DQBUF 可能返回超时或空数据。

1.4 缓冲区管理机制

V4L2 使用 videobuf2 (vb2) 框架管理缓冲区,支持三种内存类型:

内存类型 V4L2 标识 适用场景 性能特点
用户指针 V4L2_MEMORY_USERPTR 应用程序自行分配内存 需额外拷贝,延迟较高
MMAP V4L2_MEMORY_MMAP 驱动分配,用户映射 零拷贝(需硬件支持),最常用
DMA 缓冲 V4L2_MEMORY_DMABUF 跨设备共享缓冲区 零拷贝,适合 ISP/GPU 流水线

驱动需要实现 vb2_ops 回调:

struct vb2_ops {
    int (*queue_setup)(struct vb2_queue *q,
                       unsigned int *nbuffers,
                       unsigned int *nplanes,
                       unsigned int sizes[],
                       struct device *alloc_devs[]);
    int (*buf_prepare)(struct vb2_buffer *vb);
    int (*buf_queue)(struct vb2_buffer *vb);
    int (*start_streaming)(struct vb2_queue *q, unsigned int count);
    int (*stop_streaming)(struct vb2_queue *q);
};
设备树关联: start_streaming 回调是 pinctrl 和时钟参数生效的关键位置。驱动在此函数中调用 pinctrl_select_state() 切换引脚功能,并通过 clk_prepare_enable() 使能 MCLK、PCLK 等时钟。这些资源的设备树属性将在后续章节详细解析。

1.5 本章小结

  • V4L2 采用五层架构,驱动开发者主要工作在驱动层,但需理解核心层的缓冲区管理。
  • 三个核心结构体 v4l2_device、video_device、v4l2_subdev 构成驱动骨架。
  • 视频采集流程包含 10 个标准步骤,其中 STREAMON 是 pinctrl 和时钟生效的触发点。
  • vb2 缓冲区框架支持 MMAP/USERPTR/DMABUF 三种模式,驱动需实现 vb2_ops 回调。