4. V4L2 Subdev ioctl 框架:v4l2_subdev_core_ops、v4l2_subdev_video_ops、v4l2_subdev_pad_ops
V4L2 (Video for Linux 2) 子设备驱动通过三个核心操作结构体来组织 ioctl 命令的实现:v4l2_subdev_core_ops、v4l2_subdev_video_ops 和 v4l2_subdev_pad_ops。这三个结构体分别管理通用控制、视频流控制和 pad(管脚/接口)级配置,构成了 subdev ioctl 命令分发的骨架。
核心概念: V4L2 框架将 subdev 的 ioctl 命令按功能域划分为三类操作集。应用层或桥驱动通过统一的 video_device 节点下发命令,V4L2 核心根据命令类型自动路由到对应的 ops 回调函数。
4.1 v4l2_subdev_core_ops — 核心操作集
v4l2_subdev_core_ops 是 subdev 最基础的操作结构体,负责设备无关的通用控制。它包含电源管理、扩展控制、日志、ioctl 直通等回调函数。
| 回调函数 | 功能描述 | 对应 ioctl / 使用场景 |
|---|---|---|
s_power |
控制子设备电源状态(on/off) | 系统休眠 / 流开启前上电 |
ioctl |
直通 ioctl 命令,处理私有命令 | 厂商自定义控制命令 |
g_ext_ctrls |
批量获取扩展控制值 | VIDIOC_G_EXT_CTRLS |
s_ext_ctrls |
批量设置扩展控制值 | VIDIOC_S_EXT_CTRLS |
log_status |
打印子设备当前状态到内核日志 | 调试 / VIDIOC_LOG_STATUS |
init |
子设备初始化回调 | 设备探测阶段调用 |
load_fw |
加载固件到子设备 | 需要固件的传感器/编解码器 |
实践建议: 大多数 sensor 驱动至少需要实现
s_power 和 ioctl 回调。s_power 通常配合 PM runtime 框架使用,在流开启前上电、流关闭后下电,以降低功耗。
4.2 v4l2_subdev_video_ops — 视频操作集
v4l2_subdev_video_ops 专注于视频流相关的控制,包括流启停、帧间隔、帧参数等。这是 sensor 和 decoder 驱动最常实现的操作集之一。
| 回调函数 | 功能描述 | 对应 ioctl / 使用场景 |
|---|---|---|
s_stream |
开启或停止视频流(enable/disable) | VIDIOC_STREAMON / STREAMOFF |
g_frame_interval |
获取当前帧间隔(帧率倒数) | VIDIOC_SUBDEV_G_FRAME_INTERVAL |
s_frame_interval |
设置帧间隔 | VIDIOC_SUBDEV_S_FRAME_INTERVAL |
g_parm |
获取视频流参数(帧率、模式等) | VIDIOC_G_PARM |
s_parm |
设置视频流参数 | VIDIOC_S_PARM |
g_std |
获取当前视频标准(如 NTSC/PAL) | 模拟视频 decoder |
s_std |
设置视频标准 | 模拟视频 decoder |
注意:
s_stream 是 video_ops 中最重要的回调。在开启流时,驱动应配置硬件寄存器启动 MIPI 或并行接口输出;在关闭流时,应停止输出并进入低功耗模式。务必实现流状态机保护,防止重复调用导致硬件异常。
4.3 v4l2_subdev_pad_ops — Pad 操作集
v4l2_subdev_pad_ops 是 V4L2 子设备框架中最重要的操作集之一,负责 pad(管脚/接口)级别的媒体总线格式、裁剪、选择器配置。它直接对应 VIDIOC_SUBDEV_* 系列 ioctl 命令。
| 回调函数 | 功能描述 | 对应 ioctl 命令 |
|---|---|---|
enum_mbus_code |
枚举 pad 支持的媒体总线格式码 | VIDIOC_SUBDEV_ENUM_MBUS_CODE |
get_fmt |
获取 pad 当前格式 | VIDIOC_SUBDEV_G_FMT |
set_fmt |
设置 pad 格式(包括分辨率、像素编码) | VIDIOC_SUBDEV_S_FMT |
enum_frame_size |
枚举 pad 支持的帧尺寸 | VIDIOC_SUBDEV_ENUM_FRAME_SIZE |
enum_frame_interval |
枚举 pad 支持的帧间隔 | VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL |
get_selection |
获取 pad 的裁剪/组合选择器 | VIDIOC_SUBDEV_G_SELECTION |
set_selection |
设置 pad 的裁剪/组合选择器 | VIDIOC_SUBDEV_S_SELECTION |
link_validate |
验证两个 pad 之间的链路配置是否有效 | 媒体框架链路验证 |
关键设计: pad_ops 中的
set_fmt 回调通常实现“协商-调整”模式:驱动接收应用层请求的格式,根据硬件能力调整到最接近的可用格式,并将实际配置写回 format 结构体。应用层读取返回的格式即可知道硬件实际采用的配置。
4.4 三种 ops 的协作关系
在实际驱动中,三个操作集并非孤立工作,而是通过 subdev 核心结构体 v4l2_subdev 组织在一起。桥驱动或应用层通过不同的 ioctl 命令触发对应的 ops 回调。
// subdev 驱动中注册三个 ops 的典型代码
static const struct v4l2_subdev_core_ops my_core_ops = {
.s_power = my_sensor_s_power,
.ioctl = my_sensor_ioctl,
.log_status = my_sensor_log_status,
};
static const struct v4l2_subdev_video_ops my_video_ops = {
.s_stream = my_sensor_s_stream,
.g_frame_interval = my_sensor_g_frame_interval,
.s_frame_interval = my_sensor_s_frame_interval,
};
static const struct v4l2_subdev_pad_ops my_pad_ops = {
.enum_mbus_code = my_sensor_enum_mbus_code,
.get_fmt = my_sensor_get_fmt,
.set_fmt = my_sensor_set_fmt,
.get_selection = my_sensor_get_selection,
};
// 组合成 v4l2_subdev_ops
static const struct v4l2_subdev_ops my_sensor_ops = {
.core = &my_core_ops,
.video = &my_video_ops,
.pad = &my_pad_ops,
};
最佳实践: 在实现
set_fmt 时,建议同时更新内部状态并调用 get_selection 的默认值,确保裁剪窗口与格式分辨率保持一致。对于不支持裁剪的 sensor,get_selection 应返回与格式相同的尺寸。
4.5 ioctl 命令到 ops 的路由规则
V4L2 核心根据 ioctl 命令编号自动将调用路由到对应的 ops 结构体。理解路由规则对调试和开发至关重要。
| ioctl 命令范围 | 路由目标 ops | 典型命令示例 |
|---|---|---|
VIDIOC_SUBDEV_* (0x5600 ~ 0x56FF) |
pad_ops |
VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT |
VIDIOC_STREAMON/OFF |
video_ops → s_stream |
VIDIOC_STREAMON, VIDIOC_STREAMOFF |
VIDIOC_G/S_PARM |
video_ops → g_parm / s_parm |
VIDIOC_G_PARM, VIDIOC_S_PARM |
VIDIOC_G/S_EXT_CTRLS |
core_ops → g_ext_ctrls / s_ext_ctrls |
VIDIOC_G_EXT_CTRLS, VIDIOC_S_EXT_CTRLS |
VIDIOC_LOG_STATUS |
core_ops → log_status |
VIDIOC_LOG_STATUS |
| 私有 / 厂商自定义命令 | core_ops → ioctl |
自定义命令码 |
常见错误: 初学者容易将 pad_ops 和 video_ops 混淆。记住:
pad_ops 处理的是“格式/裁剪/选择器”等静态配置,而 video_ops 处理的是“流启停/帧率”等运行时控制。如果 sensor 不支持流控制(如纯静态配置设备),video_ops 可以为 NULL。
4.6 驱动实现要点总结
- 最小实现集: 大多数 sensor 驱动至少需要实现
core_ops.s_power、video_ops.s_stream、pad_ops.set_fmt和pad_ops.get_fmt。 - 格式协商:
set_fmt必须实现“请求-调整-返回”模式,不能直接拒绝应用层请求。 - 流状态机: 使用原子变量或互斥锁保护
s_stream的调用状态,防止重复开启/关闭。 - 错误处理: 所有 ops 回调应返回标准的 Linux 错误码(如 -EINVAL、-ENOMEM、-EIO),避免返回未定义值。
- 调试支持: 实现
log_status回调,输出当前格式、流状态、电源状态等信息,便于调试。
总结: v4l2_subdev_core_ops、v4l2_subdev_video_ops 和 v4l2_subdev_pad_ops 构成了 V4L2 子设备 ioctl 命令分发的三大支柱。理解每个 ops 的职责边界和路由规则,是编写正确、高效 subdev 驱动的基础。在下一节中,我们将深入分析每个 ops 中关键回调的实现细节和常见陷阱。