1. V4L2 框架概述
V4L2(Video for Linux 2)是 Linux 内核中用于视频设备驱动的标准框架,广泛应用于摄像头、电视调谐器、视频采集卡等设备。本章从架构分层、核心数据结构、视频采集流程三个维度,为后续设备树配置与 pinctrl/时钟参数解析奠定基础。
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 框架围绕三个核心结构体组织驱动逻辑,它们之间的关系如下:
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 视频采集流程包含以下步骤,驱动开发者需要理解每个阶段对应的内核回调:
- 打开设备:应用程序调用
open("/dev/video0"),触发驱动v4l2_fops->open回调,初始化硬件并申请资源。 - 查询能力:
VIDIOC_QUERYCAPioctl 返回v4l2_capability,告知驱动支持的设备类型和功能。 - 设置格式:
VIDIOC_S_FMT设置图像宽度、高度、像素格式(如 YUYV、MJPEG、NV12)。 - 申请缓冲区:
VIDIOC_REQBUFS分配视频缓冲区,驱动通过vb2_queue管理内存。 - 查询/映射缓冲区:
VIDIOC_QUERYBUF+mmap()将内核缓冲区映射到用户空间。 - 入队缓冲区:
VIDIOC_QBUF将空闲缓冲区放入驱动采集队列。 - 开始采集:
VIDIOC_STREAMON启动视频流,驱动开始从 sensor 获取数据。 - 出队缓冲区:
VIDIOC_DQBUF获取已填充数据的缓冲区,应用程序处理图像。 - 停止采集:
VIDIOC_STREAMOFF停止视频流。 - 关闭设备:
close(fd)释放所有资源。
常见陷阱: 在
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回调。