2. V4L2框架入门:核心数据结构与设备模型

V4L2(Video for Linux 2)是Linux内核中视频设备驱动的标准框架。理解其核心数据结构是编写Camera驱动的第一步。本章将深入剖析video_device、v4l2_device和v4l2_subdev三个核心结构体,以及它们之间的协作关系。

V4L2 核心数据结构关系图 v4l2_device 顶层设备实例(一个物理设备一个) video_device 字符设备 /dev/videoX v4l2_subdev 子设备(sensor/ISP/decoder) 关键说明: • v4l2_device 是顶层容器,管理所有子设备。 • video_device 负责与用户空间交互,提供 /dev/videoX 接口。 • v4l2_subdev 代表硬件组件(如 sensor、ISP),通过 v4l2_device 注册。

2.1 v4l2_device:顶层设备容器

v4l2_device 是V4L2框架中最顶层的结构体,代表一个物理视频设备。它作为容器,管理该设备下的所有子设备(subdev)和视频节点(video_node)。

成员 类型 说明
dev struct device * 指向内核通用设备结构体,用于设备模型集成
v4l2_dev struct list_head 用于将本设备链接到全局v4l2设备列表
subdevs struct list_head 挂载所有注册到本设备的v4l2_subdev
name char[32] 设备名称,用于调试和用户空间识别
ctrl_handler struct v4l2_ctrl_handler * 控制句柄,管理曝光、增益等控制参数
notify void (*notify)() 子设备通知回调函数
实战要点: 在驱动probe函数中,通常先调用 v4l2_device_register() 注册顶层设备,再注册子设备和视频节点。一个物理Camera模组通常对应一个 v4l2_device。

2.2 video_device:用户空间接口

video_device 是V4L2框架中与用户空间直接交互的结构体。它创建 /dev/videoX 字符设备节点,并实现 open()、close()、ioctl() 等文件操作。

video_device 结构体关键成员 video_device fops: v4l2_file_operations v4l2_dev: 所属顶层设备 queue: vb2_queue ctrl_handler: 控制句柄 minor: 次设备号

video_device 的核心成员及其作用:

  • fops:指向 v4l2_file_operations 结构体,实现 open/close/ioctl/mmap/poll 等系统调用。
  • v4l2_dev:指向所属的顶层 v4l2_device,建立父子关系。
  • queue:指向 vb2_queue(videobuf2队列),管理视频缓冲区的分配与流转。
  • ctrl_handler:控制句柄,处理用户空间的 VIDIOC_S_CTRL 等控制命令。
  • minor:次设备号,用于在 /dev 下创建设备节点。
常见陷阱: 注册 video_device 前必须确保 v4l2_device 已经注册成功,否则会导致设备节点无法正确关联到顶层设备。

2.3 v4l2_subdev:子设备抽象

v4l2_subdev 是V4L2框架中用于表示硬件子组件的结构体。一个Camera模组通常包含多个子设备:图像传感器(sensor)、ISP(图像信号处理器)、自动对焦控制器等。

成员 类型 说明
name char[32] 子设备名称,如 "imx219 0-0010"
grp_id u32 分组ID,用于将多个子设备归为一组
ops struct v4l2_subdev_ops * 子设备操作函数集(core/video/pad等)
ctrl_handler struct v4l2_ctrl_handler * 子设备级别的控制句柄
v4l2_dev struct v4l2_device * 指向所属的顶层v4l2_device
owner struct module * 所属内核模块

2.4 三者协作流程

在Camera驱动初始化过程中,三个结构体的协作顺序如下:

  1. 注册顶层设备:调用 v4l2_device_register(),创建 v4l2_device 实例。
  2. 注册子设备:调用 v4l2_subdev_register(),将sensor、ISP等子设备挂载到顶层设备。
  3. 注册视频节点:调用 video_register_device(),创建 video_device 并关联到顶层设备。
  4. 建立数据通路:通过 media_entity 框架连接子设备的pad,形成完整的图像数据流。
Camera驱动初始化流程 1. v4l2_device_register 2. v4l2_subdev_register 3. video_register_device 4. media_link_setup 初始化顺序说明: • 必须先注册顶层设备,子设备才能找到归属。 • 子设备注册后,video_device 才能通过 v4l2_dev 指针访问子设备。 • media_link_setup 建立sensor→ISP→video_node的数据路由。

2.5 代码示例:驱动初始化骨架

以下是一个典型的Camera驱动probe函数中三个核心结构体的注册顺序:

static int camera_probe(struct platform_device *pdev)
{
    struct camera_dev *cam;
    int ret;

    // 1. 分配设备私有数据结构
    cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
    if (!cam)
        return -ENOMEM;

    // 2. 注册顶层 v4l2_device
    ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
    if (ret < 0) {
        dev_err(&pdev->dev, "Failed to register v4l2_device\n");
        return ret;
    }

    // 3. 注册子设备(例如 sensor)
    ret = v4l2_subdev_register(&cam->v4l2_dev, &cam->sensor_subdev);
    if (ret < 0) {
        dev_err(&pdev->dev, "Failed to register sensor subdev\n");
        goto err_unregister_v4l2;
    }

    // 4. 初始化 video_device
    cam->vdev = video_device_alloc();
    if (!cam->vdev) {
        ret = -ENOMEM;
        goto err_unregister_subdev;
    }

    strscpy(cam->vdev->name, "camera", sizeof(cam->vdev->name));
    cam->vdev->v4l2_dev = &cam->v4l2_dev;
    cam->vdev->fops = &camera_fops;
    cam->vdev->ioctl_ops = &camera_ioctl_ops;
    cam->vdev->release = video_device_release;

    // 5. 注册 video_device
    ret = video_register_device(cam->vdev, VFL_TYPE_VIDEO, -1);
    if (ret < 0) {
        dev_err(&pdev->dev, "Failed to register video device\n");
        goto err_free_vdev;
    }

    dev_info(&pdev->dev, "Camera driver initialized successfully\n");
    return 0;

err_free_vdev:
    video_device_release(cam->vdev);
err_unregister_subdev:
    v4l2_subdev_unregister(&cam->sensor_subdev);
err_unregister_v4l2:
    v4l2_device_unregister(&cam->v4l2_dev);
    return ret;
}
实战建议: 在编写驱动时,务必按照 v4l2_device → v4l2_subdev → video_device 的顺序注册,反序注销。使用 devm_ 系列API可以简化资源管理,但要注意 v4l2_device 的注销时机。

2.6 本章小结

V4L2框架通过三个核心结构体实现了清晰的设备分层模型:

  • v4l2_device:顶层容器,代表物理设备,管理子设备和视频节点。
  • video_device:用户空间接口,提供 /dev/videoX 设备节点。
  • v4l2_subdev:硬件子组件抽象,如sensor、ISP等。