当前位置: 首页 > news >正文

v4l2_subdev 与 /dev/videoX 的关联

1. 核心组件关联流程
步骤1:传感器驱动注册 v4l2_subdev

传感器驱动(如 ov5640.c)在 probe 阶段创建并注册 v4l2_subdev

// drivers/media/i2c/ov5640.c
static int ov5640_probe(struct i2c_client *client)
{struct v4l2_subdev *sd;struct ov5640 *ov5640;// 分配subdev结构sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);// 初始化subdevv4l2_i2c_subdev_init(sd, client, &ov5640_subdev_ops);// 设置subdev名称和IDsnprintf(sd->name, sizeof(sd->name), "ov5640 %s", dev_name(&client->dev));// 注册subdev到V4L2核心err = v4l2_async_register_subdev(sd);// 实现subdev操作集static const struct v4l2_subdev_ops ov5640_subdev_ops = {.core = &ov5640_core_ops,.video = &ov5640_video_ops,.pad = &ov5640_pad_ops,};
}

关键点

  • 通过 v4l2_i2c_subdev_init() 绑定 I2C 客户端
  • 实现 v4l2_subdev_ops 提供传感器控制接口
  • 最终通过 v4l2_async_register_subdev() 注册到 V4L2 子系统

步骤2:桥梁驱动创建 video_device

SoC 的 CSI 接收器驱动(如 sun6i_csi.c)创建顶层设备:

// drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
static int sun6i_csi_probe(struct platform_device *pdev)
{struct sun6i_csi *csi;// 分配video_devicecsi->vdev = video_device_alloc();// 配置video设备属性csi->vdev->fops = &sun6i_csi_fops;csi->vdev->ioctl_ops = &sun6i_csi_ioctl_ops;csi->vdev->v4l2_dev = &csi->v4l2_dev;csi->vdev->queue = &csi->queue;strscpy(csi->vdev->name, "sun6i-csi", sizeof(csi->vdev->name));// 注册video设备ret = video_register_device(csi->vdev, VFL_TYPE_VIDEO, -1);
}

关键点

  • video_device_alloc() 分配设备结构
  • 实现 fopsioctl_ops 处理用户空间调用
  • video_register_device() 生成 /dev/videoX 节点

步骤3:建立 subdev 与 video_device 的关联

通过 media controllerasync subdev 机制建立连接:

// 异步匹配示例 (drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c)
static int sun6i_csi_subdev_notifier_bound(struct v4l2_async_notifier *notifier,struct v4l2_subdev *subdev,struct v4l2_async_subdev *asd)
{struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi, notifier);// 保存传感器subdev引用csi->sensor_sd = subdev;// 建立数据流链接media_create_pad_link(&subdev->entity, SENSOR_PAD_SRC,&csi->vdev->entity, CSI_PAD_SINK,MEDIA_LNK_FL_ENABLED);
}

步骤4:用户操作传递路径

当用户操作 /dev/videoX 时:

// 用户调用ioctl(VIDIOC_S_FMT)
sun6i_csi_ioctl_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
{struct sun6i_csi *csi = video_drvdata(file);// 通过subdev调用传感器驱动struct v4l2_subdev_format fmt = {.which = V4L2_SUBDEV_FORMAT_ACTIVE,.format = /* 转换格式 */,};// 关键调用链:video_device -> subdevv4l2_subdev_call(csi->sensor_sd, pad, set_fmt, NULL, &fmt);
}

2. 关键关联机制
(1) 媒体控制器(Media Controller)
pad链接
pad链接
传感器实体
CSI实体
video_device实体
  • 实体通过 media_entity 结构表示
  • 使用 media_create_pad_link() 建立数据流路径
(2) 异步子设备注册
// 桥梁驱动配置异步匹配
static const struct v4l2_async_notifier_operations csi_async_ops = {.bound = sun6i_csi_subdev_notifier_bound,
};// 添加异步描述符
v4l2_async_notifier_add_subdev(&csi->notifier, &asd);
(3) 操作转发机制
// 核心转发函数 (drivers/media/v4l2-core/v4l2-subdev.c)
long v4l2_subdev_call(struct v4l2_subdev *sd, int o, int f, ...)
{// 通过函数指针调用具体实现if (sd && sd->ops->o && sd->ops->o->f)return sd->ops->o->f(sd, ...);
}

4. 典型场景示例

用户空间打开摄像头:

  1. open("/dev/video0") → 调用 video_device->fops.open()
  2. 桥梁驱动通过 v4l2_subdev_call(sensor_sd, core, s_power, 1) 开启传感器
  3. 传感器驱动通过 I2C 发送上电序列

设置分辨率:

  1. ioctl(VIDIOC_S_FMT)video_device->ioctl_ops.vidioc_s_fmt_vid_cap()
  2. 桥梁驱动调用 v4l2_subdev_call(sensor_sd, video, s_fmt, &fmt)
  3. 传感器驱动通过 I2C 配置寄存器实现分辨率切换

5. 关键数据结构关系
struct video_device {const struct v4l2_file_operations *fops; // 文件操作struct v4l2_ioctl_ops *ioctl_ops;        // ioctl操作struct vb2_queue *queue;                  // 缓冲区队列struct media_entity entity;               // 媒体实体
};struct v4l2_subdev {const struct v4l2_subdev_ops *ops;       // 子设备操作集struct v4l2_subdev_video_ops *video;     // 视频操作struct media_entity entity;               // 媒体实体struct v4l2_async_subdev *asd;           // 异步描述
};

总结:关联本质

  1. 层级关系
    /dev/videoXvideo_device (桥梁驱动) → v4l2_subdev (传感器)

  2. 控制流
    用户空间操作 → V4L2 核心 → 桥梁驱动 → 通过 v4l2_subdev_call() 转发 → 传感器驱动

  3. 数据流
    传感器 → MIPI CSI-2 → CSI 接收器 → DMA → vb2 缓冲区 → 用户空间

  4. 配置依赖
    所有传感器控制(分辨率/帧率等)最终通过 v4l2_subdev_ops 中的 I2C 操作实现

http://www.xdnf.cn/news/989803.html

相关文章:

  • Git不能更新以及提交代码,提示链接超时,本地凭证无问题
  • 6.11 MySQL面试题 日志 性能 架构
  • 深入理解TCP以及三次握手与四次挥手
  • 面对多个项目并行,协作机制如何建立?
  • Java 8 Stream 流详细教程 - 全面指南
  • 重塑未来的力量:人工智能的技术演进与产业变革
  • git的5种分支分别是干嘛的,git常用指令
  • git的常用方法
  • 交换机上抓包
  • 大模型与人工智能
  • 了解shell中的Fork炸弹
  • Java并发编程实战 Day 17:CompletableFuture高级应用
  • 种子音乐携手RBW 精心打造Solar颂乐首支中文单曲《Floating Free》
  • 单端的时钟阻抗设置为50欧姆,差分的时钟阻抗设置为100欧姆
  • echarts 地图 海南省全岛完整展示
  • Vuex 中Mutation 和Action介绍
  • Android SDK Manager 2025镜像,亲测好用
  • Windows为何总在不停更新补丁
  • EtherCAT转CANopen网关实现与伺服系统连通的配置实例探究
  • linux虚拟机磁盘容量不足?一个指令解决问题
  • 国内各种AI工具库
  • 大模型笔记_检索增强生成(RAG)
  • ABC 337
  • Arduino入门教程:0、课程介绍认识Arduino
  • 餐厅商家怎么做元宵节活动宣传海报?
  • C++ 精简知识点
  • 推荐算法介绍-基础算法
  • python打卡第49天
  • Unity | AmplifyShaderEditor插件基础(第九集:旗子进阶版)
  • Nginx完全学习指南 - 从入门到实战