4. Linux V4L2框架入门:核心数据结构与调用流程

做Sensor驱动,绕不开V4L2。说实话,我刚接触这个框架时也觉得头大——结构体套结构体,ioctl满天飞。但摸清楚几个核心概念后,你会发现它其实挺规整的。

这一章,咱们就拆开V4L2的内核,看看它到底怎么组织设备、怎么处理用户请求、怎么把Sensor和ISP串成一条流水线。

4.1 两大核心数据结构:video_device 与 v4l2_device

V4L2框架里有两个“大管家”:v4l2_device 和 video_device。它们的关系,我习惯这么理解:

  • v4l2_device:整个视频设备的顶层抽象。它管理所有子设备(subdev)、时钟、GPIO等资源。一个硬件模块(比如一个摄像头模组)通常对应一个 v4l2_device。
  • video_device:用户空间看到的“节点”。每个 /dev/videoX 背后就是一个 video_device。它负责处理open、close、ioctl这些系统调用。

关键区别:v4l2_device 是内核内部的管理单元,video_device 是面向用户空间的接口。一个 v4l2_device 可以挂载多个 video_device(比如一个Sensor同时输出RAW图和YUV图)。

来看一个典型的注册流程:

// 1. 分配并初始化 v4l2_device
struct v4l2_device v4l2_dev;
v4l2_device_init(&v4l2_dev, NULL);

// 2. 分配 video_device
struct video_device vdev;
vdev = video_device_alloc();
vdev->v4l2_dev = &v4l2_dev;  // 绑定关系

// 3. 设置文件操作回调
vdev->fops = &my_camera_fops;
vdev->ioctl_ops = &my_camera_ioctl_ops;

// 4. 注册到系统
video_register_device(vdev, VFL_TYPE_VIDEO, -1);

嗯,这里要注意:video_device_alloc 和 video_register_device 的配对使用。我见过有人只注册不释放,导致rmmod时内核崩溃。

4.2 ioctl调用流程:从用户态到硬件

用户程序调用 ioctl(fd, VIDIOC_QBUF, ...),这中间经历了什么?我画了个简图:

ioctl 调用流程 用户程序 sys_ioctl v4l2_ioctl video_device 驱动ioctl_ops I2C/SPI/CSI 关键路径: 1. 用户调用 ioctl → 2. 内核 sys_ioctl 分发 → 3. V4L2 核心 v4l2_ioctl 解析命令 4. 查找 video_device 对应的 ioctl_ops → 5. 调用驱动实现的回调函数 → 6. 操作硬件寄存器

说白了,V4L2核心层就是个“路由器”。它根据ioctl命令号,找到对应 video_device 里注册的 ioctl_ops 回调。你想想看,如果没有这层抽象,每个驱动都得自己解析 VIDIOC_QBUF、VIDIOC_DQBUF……那代码得多乱。

我的调试习惯:在驱动里加 dev_dbg 打印每个ioctl的进入和退出。这样配合 strace 看用户态调用,能快速定位是上层传参错了,还是驱动没处理。

4.3 subdev与pipeline概念

现代摄像头模组,早就不只是一个Sensor了。它可能包含:

  • Sensor:感光元件,输出RAW数据
  • ISP:图像信号处理器,做AWB/AE/去噪
  • MIPI桥片:把MIPI CSI-2转成并行接口
  • AF驱动:自动对焦马达

每个硬件单元都是一个 subdev(子设备)。V4L2用 v4l2_subdev 结构体来描述它们。多个subdev串联起来,就形成了 pipeline(流水线)。

我曾经踩过的坑:在移植IMX290时,Sensor输出格式是SRGGB12,但ISP只支持SRGGB10。我忘了在pipeline里插入一个格式转换subdev,结果图像全是花的。后来老老实实把每个subdev的 get_fmt 回调都打印出来,才发现中间缺了一环。

Pipeline的典型配置流程:

  1. 枚举:遍历所有subdev,获取它们支持的格式、分辨率
  2. 设置:从源端(Sensor)到宿端(Video节点),逐级配置格式
  3. 启动:调用 s_stream(1) 让每个subdev开始工作
  4. 停止:反向调用 s_stream(0)

来看一个典型的pipeline拓扑:

Camera Pipeline 拓扑示例 Sensor (IMX335) ISP (TI DS90UB) MIPI 桥片 Video 节点 Pipeline 配置顺序: 1. 设置 Sensor 输出格式(如 1920x1080 SRGGB12) 2. 设置 ISP 输入/输出格式(格式转换、裁剪) 3. 设置 MIPI 桥片通道映射 → 4. 设置 Video 节点采集格式

4.4 核心数据结构关系一览

我把这几个关键结构体的关系整理成了一张表,方便你对照:

结构体 作用 所属层次 典型成员
v4l2_device 顶层设备管理 驱动核心 subdev列表、时钟、电源
video_device 用户空间接口 字符设备层 fops、ioctl_ops、v4l2_dev指针
v4l2_subdev 子设备抽象 硬件单元层 pad、ops、ctrl_handler
media_pad 子设备间连接点 media框架 entity、index、flags
media_link pad之间的连接 media框架 source、sink、flags

个人建议:刚开始写驱动时,别急着把所有subdev都塞进pipeline。先让Sensor直接输出到Video节点,图像能出来,再逐步加入ISP、桥片。每加一个subdev,就验证一次图像质量。这样出了问题,你至少知道是哪个环节引入的。

嗯,V4L2框架的核心逻辑其实不复杂:设备分层、接口统一、流水线化。搞懂了 video_device 和 v4l2_device 的分工,理解了ioctl怎么从用户态一路跑到硬件,再掌握了subdev和pipeline的组装方式,你就能看懂大部分Sensor驱动的代码结构了。