media子系统的注册与使用

1. 前言:导演的工作

在我们的“电影剧组”比喻中,subdev 是具体的工作人员,而 Media 子系统是“导演”。导演的核心工作有两件:

  1. 绘制分镜图 (构建拓扑): 导演需要了解所有工作人员的站位和他们之间的连接关系,形成一张精确的“分镜图”。这个过程发生在内核驱动加载时。
  2. 指挥拍摄 (响应应用): 导演需要提供一个接口,让“制片人”(APP)可以查看分镜图,并下达指令(比如“激活A机位到B录机的线路”)。这个过程发生在 APP 运行时。

这一讲,我们就来详细拆解导演的这两件核心工作。

2. 内核注册过程 —— 绘制“分镜图”

“分镜图”(硬件拓扑)的绘制不是由“导演”一个人完成的,而是由所有“工作人员(subdev)”和“导演助理”(桥接驱动)协同完成的。整个过程层次分明,可以概括为两个层次,四个步骤

2.1 两个层次

  1. 自我描述 (media_entity): 每个 subdev 驱动负责描述自己。知道自己有几个“输入/输出端口”(pads),但它不知道会和谁连接。
  2. 建立联系 (link): 更高一层的、统筹全局的桥接驱动扮演了“导演助理”的角色。它最清楚硬件的物理连接,因此由它来负责在各个 media_entity 之间建立“连接线”(links)。

2.2 瑞芯微(Rockchip)平台实例解析:谁是“桥接驱动”?

在这个平台上,rkisp1 驱动(Rockchip ISP1 Driver)就是一个典型的桥接驱动。它同时控制着 SoC 内部的 ISP、MIPI CSI-2 Host 等多个硬件模块。摄像头 Sensor (例如 Sony IMX378) 通常是通过 I2C 和 MIPI 总线连接到 RK3399 上的。

  • rkisp1 驱动知道自己有一个 MIPI CSI-2 接收器,可以接收外部 Sensor 的数据。
  • imx378 驱动(一个 subdev 驱动)知道自己是一个图像传感器,可以输出 MIPI 信号。

但是,imx378 驱动并不知道自己连接到了 rkisp1建立它们之间联系的责任,就落在了 rkisp1 这个桥接驱动身上。它会解析设备树(Device Tree),在设备树中会明确描述 imx378 的 MIPI 输出端口连接到了 rkisp1 的 MIPI 输入端口。rkisp1 驱动读取到这个信息后,就会调用 media_create_pad_link 来完成这个连接。

2.3 四个步骤

现在我们结合瑞芯微的例子,再来看这四个步骤,就会豁然开朗:

  • 第 1 步:描述自己 (Sensor 驱动, 如 imx378.c)
    • 做什么? imx378 驱动在初始化时,会调用 media_entity_pads_init,告诉“导演”:“我是一个 Sensor,我有一个 MIPI 输出端口(Pad)”。
  • 第 2 步:注册自己 (Sensor 驱动, 如 imx378.c)
    • 做什么? imx378 驱动调用 v4l2_device_register_subdev,将自己的 media_entity 注册到 rkisp1 创建的 media_device 中。相当于向导演报到:“摄影师 IMX378 加入剧组了”。
  • 第 3 步:和别人建立联系 (桥接驱动, 如 rkisp1.c)
    • 做什么? “导演助理” rkisp1 登场。它在自己的 probe 函数中,解析设备树,得知 imx378 连接到了自己身上。于是,它调用 media_create_pad_link,在 imx378 的输出 padrkisp1 的输入 pad 之间创建一条 link
    • 意义: 这条 link 就在内核中建立了从 Sensor 到 SoC 的数据流逻辑通路。
  • 第 4 步:暴露给 APP 使用 (桥接驱动, 如 rkisp1.c)
    • 做什么?rkisp1 把所有连接到它上面的 subdev (可能还有其他 ISP 相关的 subdev) 都注册并建立好 link 后,一张完整的“分镜图”就绘制好了。此时,rkisp1 驱动调用 media_device_register,创建 /dev/media0 设备节点,将这套完整的拓扑结构暴露给用户空间。

至此,整个 Media 子系统的内核注册过程完成。一个包含所有硬件模块及其连接关系的、可供 APP 查询和配置的 media_device 就绪。

3. APP 使用 Media 子系统 —— 与“导演”对话

APP 作为“制片人”,它不关心具体硬件细节,只关心如何通过“导演”来配置出想要的拍摄效果。APP 与 media 子系统的交互非常简洁,除了 open,几乎只涉及 5 个核心 ioctl 命令。

3.1 ioctl 调用核心机制

当 APP 对 /dev/media0 执行 ioctl 时,内核流程如下:

  1. APP: 调用 ioctl(fd, cmd, arg)
  2. 内核 VFS: 调用 media_devnode_fops 中的 media_ioctl 函数。
  3. media_ioctl: 这个函数是一个通用分发器。它不实现具体功能,而是通过一个名为 ioctl_info 的静态数组来查找 cmd 对应的处理函数。
  4. ioctl_info 数组: 这个数组的每一项都定义了一个 ioctl 命令的处理流程:
    • cmd: 命令号。
    • fn: 实际执行工作的函数指针。
    • arg_from_user / arg_to_user: 用于在用户空间和内核空间之间安全拷贝数据的函数指针。

现在我们来逐一解析这 5 个核心 ioctl

  1. MEDIA_IOC_DEVICE_INFO

    • 作用: 获取设备的基本信息,比如驱动名称、设备型号等。

    • APP 操作:

      struct media_device_info info;
      ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info);
      
    • 内核执行: ioctl_info 数组将此命令分发给 media_device_get_info 函数,该函数会从 media_device 结构体中复制信息到用户传入的 info 结构体中。

  2. MEDIA_IOC_ENUM_ENTITIES
    • 作用: 枚举设备中所有的 entity (工作人员)。
    • APP 操作: APP 通常在一个 for 循环中调用此 ioctl。通过在 media_entity_desc 结构体的 id 字段中设置 MEDIA_ENT_ID_FLAG_NEXT 标志,APP 可以告诉内核:“请给我下一个 entity 的信息”。
    • 内核执行: 分发给 media_device_enum_entities 函数。该函数根据 APP 传入的 id 找到对应的 entity,然后将其信息(名字、类型、pad 数量等)返回给 APP。
  3. MEDIA_IOC_ENUM_LINKS
    • 作用: 枚举指定 entity 的所有 link (连接线)。
    • APP 操作: 与枚举 entity 类似,APP 提供一个 entity 的 ID,然后内核返回该 entity 的所有 pad 信息和 link 信息。
    • 内核执行: 分发给 media_device_enum_links 函数。该函数首先找到指定的 entity,然后遍历其 pads 数组和 links 链表,将信息复制给用户空间。
  4. MEDIA_IOC_SETUP_LINK (最核心的控制接口)
    • 作用: 激活 (enable)闲置 (disable) 一条 link。这是配置 pipeline 的关键。

    • APP 操作:

      struct media_link_desc link_desc;
      // ... 填充 link_desc 的 source 和 sink pad 信息 ...
      link_desc.flags = MEDIA_LNK_FL_ENABLED; // 激活
      ioctl(fd, MEDIA_IOC_SETUP_LINK, &link_desc);
      
    • 内核执行: 分发给 media_device_setup_link 函数。该函数会:

      1. 找到 link_desc 描述的 link
      2. 检查该 link 是否是 MEDIA_LNK_FL_IMMUTABLE (不可变的)。
      3. 如果可变,就根据 APP 传入的 flags 来更新 link 的状态位。
      4. 最关键的一步: 调用 link 两端 subdev.pad->link_setup 回调函数,通知驱动程序:“这条线路的状态变了,请进行相应的硬件配置!”
  5. MEDIA_IOC_G_TOPOLOGY
    • 作用: 一次性获取整个拓扑图的快照信息,包括所有 entities, pads, links 的数量和大小。
    • APP 操作: APP 先调用一次此 ioctl 获取拓扑信息的大小,然后分配足够大的内存,再次调用以获取全部数据。
    • 内核执行: 分发给 media_device_get_topology 函数,该函数负责将整个拓扑图的统计信息返回给 APP。

总结

  • 内核注册 (绘制蓝图): 这是一个自下而上的协作过程。Subdev 驱动负责描述自己注册自己,而桥接驱动则负责建立连接并最终暴露给应用
  • APP 使用 (按图索骥): 这是一个自上而下的控制过程。APP 首先通过 ENUM 系列 ioctl 读取完整的拓扑图,然后根据需求,通过 SETUP_LINK ioctl 配置出一条有效的数据 pipeline
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值