【linux kernel】linux media子系统分析之media控制器设备

文章目录

    • 一、抽象媒体设备模型
    • 二、抽象媒体设备
    • 三、Entity
    • 四、Interfaces
    • 五、Pad
    • 六、Link
    • 七、Media图遍历
    • 八、使用计数和电源处理
    • 九、link设置
    • 十、Pipeline和Media流
    • 十一、链接验证
    • 十二、媒体控制器设备的分配器API

本文基于linux内核 4.19.4,抽象媒体设备模型框架的相关源码文件如下:

(1)include/media/media-device.h

(2)include/media/media-entity.h

(3)/drivers/media/media-device.c

(4)/drivers/media/media-devnode.c

(5)/drivers/media/media-entity.c

在linux内核6.2.7版本中,抽象媒体设备框架描述文件放在了/drivers/media/mc目录中(也就是单独开了一个目录存放),且文件命名也发生了变化:

  • mc-dev-allocator.c - 媒体控制器设备分配器API

  • mc-device.c:该文件中的代码包含了媒体设备子系统的核心逻辑和数据结构的定义,以及与之相关的函数实现。它提供了用于媒体设备的注册、初始化、配置和控制的接口。

  • mc-devnode.c:文件中的代码包含了媒体设备子系统中设备节点相关的逻辑和函数实现。它负责创建和管理媒体设备子系统中的设备节点,以便应用程序可以通过设备节点与媒体设备进行通信。

  • mc-entry.c:文件中的代码包含了媒体设备子系统中媒体实体相关的逻辑和函数实现。它定义了媒体实体的数据结构以及用于管理和操作媒体实体的接口函数。

  • mc-request.c:该文件为新添文件,该文件中为Media控制器和V4L2接口提供了Request API。目前一些无状态编解码器驱动程序需要使用该文件中实现的函数。

一、抽象媒体设备模型

media控制器设备本文称为媒体控制器框架,该框架的一个设计目标是:发现设备内部拓扑并在运行时对其进行配置,为了实现这一目标,硬件设备被建模为定向图,由pad连接的实体共同组成。即媒体设备被抽象成包含entitypadlink的对象。

Linux内核的Media Controller是一个通用的多媒体框架,它提供了一种标准化的方法来处理多种媒体类型,包括音频、视频、图像等。Media Controller主要用于描述媒体设备的拓扑结构,即描述多个媒体设备之间的连接关系

在Media Controller中,每个媒体设备都被描述为一个媒体实体(Media Entity),每个媒体实体都有一个或多个媒体端口(Media Pad),每个媒体端口都可以连接到另一个媒体设备的媒体端口上,从而形成一个媒体设备的拓扑结构。

Media Controller还提供了一组API,使得用户可以方便地访问媒体设备的拓扑结构,并且可以使用相同的API来访问不同类型的媒体设备。

Media Controller的主要组成设备包括:

  1. Media Controller设备:Media Controller设备是一个虚拟的设备,它代表了整个媒体设备的拓扑结构,包括所有的媒体实体、媒体端口、连接关系等。在开发中可以通过Media Controller设备来访问整个媒体设备的拓扑结构。

  2. Media Entity设备:Media Entity设备是一个实际的媒体设备,它可以是一个摄像头、一个麦克风、一个视频采集卡等。每个Media Entity设备都被描述为一个媒体实体,它包括一个或多个媒体端口和一些属性信息。

  3. Media Pad设备:Media Pad设备是一个媒体端口,它描述了一个Media Entity设备的输入或输出端口。每个Media Pad设备包括一些属性信息,如媒体类型、格式、方向等。

  4. Media Link设备:Media Link设备描述了两个媒体端口之间的连接关系。每个Media Link设备包括一些属性信息,如连接的源端口、目标端口、媒体类型、格式等。

二、抽象媒体设备

一个媒体设备由struct media_device实例表示,定义在include/media/media-device.h中。该结构的分配由媒体设备驱动程序完成,通常将media_device实例嵌入到一个更大的特定驱动程序的结构中。

struct media_device {
    struct device *dev;  //父设备
    struct media_devnode *devnode;  //媒体设备节点
    char model[32];  //设备模型的名称
    char driver_name[32]; //设备驱动名称(可选)。如果没有设置,调用MEDIA_IOC_DEVICE_INFO将返回dev->driver->name。
    char serial[40];  // 设备序列号 (可选)
    char bus_info[32]; //唯一和稳定的设备位置标识符
    u32 hw_revision; //硬件设备修订
    u64 topology_version; //单调计数器用于存储图形拓扑的版本,
    u32 id; //在最后注册的图对象上使用的唯一ID
    struct ida entity_internal_idx; //图遍历算法使用的唯一内部实体ID
    int entity_internal_idx_max; //分配的内部实体索引
    struct list_head entities; //用于存储已注册的entity的链表
    struct list_head interfaces; //用于存储已注册的接口的链表
    struct list_head pads;  //用于存储已注册的pad的链表
    struct list_head links; //用于存储已注册的link的链表
    struct list_head entity_notify; //用于存储已注册的entity_notify回调的链表
    struct mutex graph_mutex; //用于保护对struct media_device数据的访问
    struct media_graph pm_count_walk;
    void *source_priv; //驱动程序用于启用/禁用源处理程序的私有数据
    int (*enable_source)(struct media_entity *entity, struct media_pipeline *pipe);//启用源处理程序函数指针
    void (*disable_source)(struct media_entity *entity); //禁用源处理程序函数指针
    const struct media_device_ops *ops; //media设备的操作回调集合
    struct mutex req_queue_mutex;  //互斥锁
    atomic_t request_id;  //用于生成唯一的请求ID
};

驱动程序通过调用media_device_init()来初始化媒体设备实例,在初始化媒体设备实例后,通过media_device_register()宏函数调用__media_device_register()向linux内核注册媒体设备。

通过调用media_device_unregister()来取消注册的媒体设备实例,初始化的媒体设备最后必须通过调用media_device_cleanup()来进行清理操作。

注意,不允许取消未注册的媒体设备实例的注册,也不允许清除未初始化的媒体设备实例。

三、Entity

实体由struct media_entity实例表示,定义在include/media/media-entity.h中:

struct media_entity {
    struct media_gobj graph_obj; //包含媒体对象通用数据的嵌入结构。
    const char *name; //实体的名称。
    enum media_entity_type obj_type; //实现media_entity的对象类型。
    u32 function;   //实体主函数,定义在include/uapi/linux/media.h (命名类似于MEDIA_ENT_F_*)。
    unsigned long flags;//实体标志,在include/uapi/linux/media.h定义(命名类似于MEDIA_ENT_FL_*)。
    u16 num_pads; //sink和source pad的数量。
    u16 num_links;  //link的总数,转发和返回,启用和禁用。
    u16 num_backlinks;  //反向的backlink的数量。
    int internal_idx;  //唯一的内部实体特定编号,如果实体未注册或重新注册,这些号码将被重用。
    struct media_pad *pads; //数组的大小由num_pads定义。
    struct list_head links; //用于存储数据link的链表。
    const struct media_entity_operations *ops;  //实体操作。
    int use_count;  //对实体使用count。
    union {
        struct {
            u32 major;
            u32 minor;
        } dev; /包含设备major和minor信息。
    } info;
};

在驱动程序中可以直接分配entity,但该结构通常会嵌入到更大的结构中,例如v4l2_subdevvideo_device实例中。

驱动程序通过调用media_entity_pads_init()初始化entity pad,在初始化pad后,驱动程序通过调用media_device_register_entity()向媒体设备注册实体。

如果想要注销已注册的media_entity,可调用media_device_unregister_entity()取消注册。

四、Interfaces

接口由struct media_interface实例表示,定义在include/media/media-entity.h中。目前只定义了一种类型的接口:设备节点,该接口由struct media_intf_devnode表示。

struct media_interface {
    struct media_gobj     graph_obj;  //嵌入式的图对象
    struct list_head      links;  //指向图实体的link列表
    u32 type;   //在include/uapi/linux/media.h中定义的接口类型
    u32 flags;  //在include/uapi/linux/media.h中定义的接口标志。(以MEDIA_INTF_FL_*进行命名)
};

驱动程序通过调用media_devnode_create()初始化和创建设备节点接口。

通过调用media_devnode_remove()来删除设备节点。

五、Pad

Pad由struct media_pad实例表示,定义在include/media/media-entity.h中。

struct media_pad {
    struct media_gobj graph_obj; //包含媒体对象通用数据的嵌入式结构。
    struct media_entity *entity; //本pad所属的Entity。
    u16 index;   //Pad在实体Pad数组中的索引,编号从0到n。
    enum media_pad_signal_type sig_type;  //media pad的信号类型。
    unsigned long flags; //在include/uapi/linux/media.h中定义的Pad标志。
    struct media_pipeline *pipe;  //该pad属于的Pipeline。可以使用media_entity_pipeline()访问该字段。
};

每个entity将其Pad存储在由entity驱动程序管理的Pad数组中,驱动程序通常将Pad数组嵌入到特定驱动程序的结构中。Pads由它们的entity和它们在Pads数组中基于0的索引来标识,这两个信息都存储在struct media_pad中,使得struct media_pad指针成为存储和传递链接引用的规范方法。Pad具有描述pad功能和状态的标志:MEDIA_PAD_FL_SINK表示pad支持sink数据,MEDIA_PAD_FL_SOURCE表示pad支持源数据。

每个pad必须设置MEDIA_PAD_FL_SINKMEDIA_PAD_FL_SOURCE中的一个且只能设置一个标志。

六、Link

Link由struct media_link实例表示,定义在include/media/media-entity.h中:

struct media_link {
    struct media_gobj graph_obj; //包含媒体对象通用数据的嵌入式结构。
    struct list_head list;  //与拥有该链路的实体或接口相关联的链表。
    union {
        struct media_gobj *gobj0;     //用于获取链接的第一个graph_object的指针。
        struct media_pad *source;     //仅当第一个对象(gobj0)是pad时使用。在这种情况下,它表示源pad。
        struct media_interface *intf; //仅当第一个对象(gobj0)是pad时使用。在这种情况下,它表示源pad。
    };
    union {
        struct media_gobj *gobj1;  //用于获取链接的第二个graph_object的指针。
        struct media_pad *sink;  //仅在第二个对象(gobj1)是pad时使用。在这种情况下,它表示sink pad。
        struct media_entity *entity; //仅在第二个对象(gobj1)是实体时使用。
    };
    struct media_link *reverse; //指向pad到pad link的反向链接的指针。
    unsigned long flags; //link标志,在uapi/media.h中定义。
    bool is_backlink; //表示link是否是反向链路。
};

Link有两种类型:

  • (1)Pad到Pad的链接
    通过它们的pad关联两个实体,每个实体都有一个指向所有链接的列表,这些链接来自或指向其任何一个pad。因此,给定的链接存储两次,一次在源实体中,一次在目标实体中。

  • (2)接口到实体的链接

该种链接用于将一个接口关联到Link。在驱动程序中通过调用media_create_intf_link()创建接口到实体的链接,并使用media_remove_intf_links()删除已创建的链接。

驱动程序通过调用media_create_pad_link()创建pad到pad的链接,并使用media_entity_remove_links()删除已创建的链接。

Link必须在两端都已创建的情况下才能创建。

链接具有描述其功能和状态的标志:有效值在media_create_pad_link()media_create_intf_link()中描述。

七、Media图遍历

在媒体框架中,提供了API来迭代图中的entity。要遍历属于媒体设备的所有entity,驱动程序可以使用media_device_for_each_entity宏,该宏定义在include/media/media-device.h中:

#define media_device_for_each_entity(entity, mdev)			\
	list_for_each_entry(entity, &(mdev)->entities, graph_obj.list)

例如下列使用代码:

struct media_entity *entity;

media_device_for_each_entity(entity, mdev) {
//entity will point to each entity in turn
...
}

在驱动程序中可能还需要遍历图中的所有entity,这些entity只能通过从给定entity开始的链接访问,因此媒体框架为此提供了一个深度优化的图遍历API。

注意:图遍历API不支持带有循环的图(无论是有向的还是无向的)。为了防止出现无限循环,图遍历代码将最大深度限制为MEDIA_ENTITY_ENUM_MAX_DEPTH,当前值定义为16。

在驱动程序中,通过调用media_graph_walk_start()来开始一个图遍历。驱动程序可以通过调用media_graph_walk_next()来检索下一个entity,当图遍历完成时,函数将返回NULL。

图遍历操作可以在任何时刻中断,不需要调用清理函数,可以正常释放图结构。

以下助手函数可用于查找两个给定pad之间的link,或者通过link查找连接到另一个pad的pad:

media_entity_find_link()  //用于查找两个pad之间的连接。

media_pad_remote_pad_first() //在连接的远端查找第一个pad。

media_entity_remote_source_pad_unique() //查找一个连接到实体的远程 source pad。

media_pad_remote_pad_unique() //查找一个连接到pad的远程pad。

八、使用计数和电源处理

由于在驱动程序之间关于电源管理需求存在巨大的差异,所以媒体控制器框架中不实现电源管理。但是,struct media_entity结构中包括一个use_count字段,媒体驱动程序可以使用该字段来跟踪每个实体的用户数量,以满足电源管理需求。

media_entity.use_count字段由媒体驱动程序拥有,entity驱动程序不能使用它,除此之外,对该字段的访问必须受到media_device.graph_mutex锁的保护。

九、link设置

可以通过调用media_entity_setup_link()在运行时修改链接(struct media_link)的属性。该函数原型如下:

int media_entity_setup_link(struct media_link *link, u32 flags)
  • link : 指向struct media_link的指针。

  • flags :标志。

十、Pipeline和Media流

媒体流是源自一个或多个源设备(如传感器)的像素或元数据流,流经媒体实体pad,流向最终接收器。媒体流可以在路由上被设备修改(例如:缩放或像素格式转换),也可以被分割成多个分支,或者进行多分支合并。

媒体管道是一组相互依赖的媒体流,这种相互依赖可能是由硬件引起的(例如,如果第一个流已启用,则无法更改第二个流的配置),也可能是由驱动程序引起的。最常见的是:媒体管道由一个没有分支的流组成。

当开始流时,驱动程序必须通知管道中的所有entity,以防止在流期间调用media_pipeline_start()进而修改链接状态,在该函数中会将所有作为管道的pad标记为流。

pipe参数所指向的struct media_pipeline实例将存储在管道中的每个pad中。驱动程序应该在更高级的管道结构中嵌入struct media_pipeline,然后可以通过struct media_pad pipe字段访问管道。

media_pipeline_start()的调用可以嵌套。对于函数的所有嵌套调用,管道指针必须相同。

media_pipeline_start()可能返回错误,在这种情况下,它将清除自己所做的所有更改。

当停止流时,驱动程序必须用media_pipeline_stop()通知entity。如果多次调用media_pipeline_start(),则需要调用相同次数的media_pipeline_stop()来停止流。media_entity.pipe字段将在最后一个嵌套调用停止时重置为NULL。

默认情况下,如果link的任何一端是流实体,则使用-EBUSY配置链路将失败。

可以在流式传输时修改的链接必须使用MEDIA_LNK_FL_DYNAMIC标志进行标记。

如果需要禁止流实体上的其他操作(例如:更改实体配置参数),驱动程序可以显式检查media_entity stream_count字段,以确定entity是否正在进行流操作,该操作必须在持有media_device graph_mutex的情况下完成。

十一、链接验证

media_pipeline_start()函数中对pipeline中有sink pad的实体执行链接验证。media_entity.link_validate()回调用于实现验证操作,在link_validate()回调中,实体的驱动程序应该检查被连接实体的source pad和它自己的sink pad的属性是否匹配。匹配的实际含义取决于实体的类型(最终取决于硬件的属性)。

子系统应该通过提供特定子系统的辅助函数来访问通常需要的信息,并最终提供一种使用特定于驱动程序的回调的方法,从而促进链接验证。

十二、媒体控制器设备的分配器API

当媒体设备属于多个驱动程序时,共享媒体设备被分配为共享的struct device以作为查找的键,共享媒体设备应该保持注册状态,直到最后一个驱动程序注销它。此外,媒体设备应在所有引用都释放时释放,在probe期间,当分配媒体设备时,每个驱动程序都获得对媒体设备的引用,如果媒体设备已经被分配,那么allocate API会增加refcount并返回现有的媒体设备。

调用media_device_delete()函数,媒体设备将被取消注册并从kref put处理程序中清除,以确保媒体设备保持在已注册状态,直到最后一个驱动程序取消对媒体设备的注册。

在驱动程序中的使用方法
驱动程序应该使用适当的媒体核心来管理共享媒体设备的生命周期,处理以下两种状态:

  • 1、 allocate -> register -> delete

  • 2、 获取已注册设备的引用 -> delete

调用media_device_delete()函数确保能正确处理共享媒体设备的删除操作。

对于驱动.probe操作:如果media devnode没有注册,需调用media_device_usb_allocate()来分配或获取一个对媒体设备的引用。

对于驱动disconnect的操作:调用media_device_delete()释放media_device,释放由kref put处理程序完成。

【参考链接】

https://docs.kernel.org/driver-api/media/mc-core.html#c.media_device

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/30768.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python如何查找特定名称文件

Python如何查找特定名称文件 在计算机文件管理和互联网网络应用程序中,查找特定文件往往是一项必要的任务。在使用Python编程时,我们可以使用Python内置的os模块来查找特定名称的文件。本文将介绍如何使用Python查找特定名称的文件,并提供实…

013:解决vue中不能加载.geojson的问题

第013个 查看专栏目录: VUE — element UI 本文章目录 问题状态造成这个结果的原因:解决办法Vue Loader 其他特性:专栏目标 问题状态 在做vue项目的时候,碰到这样一个问题,vue页面中引用一个.geojson文件,提示如下错误…

【C++篇】字符串:标准库string类

友情链接:C/C系列系统学习目录 知识总结顺序参考C Primer Plus(第六版)和谭浩强老师的C程序设计(第五版)等,内容以书中为标准,同时参考其它各类书籍以及优质文章,以至减少知识点上的…

面试篇:Java基础

目录 一、HashMap 的底层结构和原理 1、JDK7 2、JDK8 3、扩容问题 二、讲一下你对动态代理的理解 1、JDK动态代理 2、CGLIB动态代理 三、Java 集合体系的划分、List、Set、Map 的区别 四、ArrayList 和 LinkedList 的区别 1、数据结构实现: 2、随机访问&a…

Python-Selenium-定位详解

目录 前言: 一、id定位 二、name定位 三、class_name定位 四、xpath定位 五、css_selector定位 六、tag_name定位 七、link_text 定位 八、Xpath&Css定位方法速查表 九、By定位 十、elements复数定位 十一、JS的定位 前言: Python是一种…

pikachu靶场-PHP反序列化

在理解这个漏洞前,你需要先搞清楚php中serialize(),unserialize()这两个函数。 序列化serialize() 序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象: class S{public $test"pikachu";}$snew S(); //创建一个对象serialize($s); //…

eclipse中创建一个maven父工程和几个模块(子工程)

示例:创建一个父工程和几个模块(子工程) 1)、先创建一个父工程 注意:下面的Packaging选择pom: 点击Finish,父工程就创建好了: 2)、再创建模块(module&am…

RuntimeError: launcher ‘pdsh‘ not installed解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

麒麟系统在线安装docker(x86/arm)

文章目录 一、查看系统版本,确认版本二、查看系统架构三、下载安装docker-ceX86架构安装aarch64架构 一、查看系统版本,确认版本 [rootlocalhost ~]# cat /etc/kylin-release Kylin Linux Advanced Server release V10 (Sword)二、查看系统架构 [root…

基于STM32的四旋翼无人机项目(二):MPU6050姿态解算(含上位机3D姿态显示教学)

前言:本文为手把手教学飞控核心知识点之一的姿态解算——MPU6050 姿态解算(飞控专栏第2篇)。项目中飞行器使用 MPU6050 传感器对飞行器的姿态进行解算(四元数方法),搭配设计的卡尔曼滤波器与一阶低通滤波器…

软件设计模式之原型模式

一.定义 原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多,其定义如下: Specify the kinds of objects to create using a prototypical instance, and create new objects by copyingthis protot…

第七章 测试

文章目录 第七章 测试7.1 编码7.1.1 选择程序设计语言1. 计算机程序设计语言基本上可以分为汇编语言和高级语言2. 从应用特点看,高级语言可分为基础语言、结构化语言、专用语言 7.1.2 编码风格 7.2 软件测试基础7.2.1 软件测试的目标7.2.2 软件测试准则7.2.3 测试方…

边缘智能:边缘计算驱动实时深度学习

边缘智能 作为人工智能领域的当红炸子鸡,深度学习技术近年来得到了学术界与产业界的大力追捧。目前,深度学习技术已在计算机视觉、自然语言处理以及语音识别等领域大放异彩,相关产品正如雨后春笋般涌现。由于深度学习模型需要进行大量的计算…

Delta 一个新的 git diff 对比显示工具

目录 介绍git diff 介绍delta介绍 一、安装1.下载 Git2.下载 delta3.解压4.修改配置文件5. 修改主题6.其他配置和说明 二、对比命令1.在项目中 git diff 常用命令2.对比电脑上两个文件3.对比电脑上的两个文件夹 三、在Git 命令行中使用效果四、在idea 的Terminal命令行中使用效…

linux 内核版本和发行版本

当要明确自己的Linux系统的版本号时,大多数情况是用命令确定Linux内核版本的。不过这个还是要与CentOS的版本号(就是你使用的Linux系统的发行版本)区分开来,这两个不是一个东西。 一、发行版本号 比如当时安装CentOS时&#x…

MySQL是什么,如何整合SpringBoot,以及使用优势

目录 一、MySQL是什么 二、如何整合SpringBoot 三、MySQL使用优势 一、MySQL是什么 MySQL是一种开源的关系型数据库管理系统,采用客户机/服务器模式实现数据存储和管理。其最初由瑞典的MySQL AB公司开发,后来被Sun Microsystems收购,最终…

【Python 随练】寻找完数

题目: 一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如 61+2+3.编程找出 1000 以内的所有完数。 简介: 在本篇博客中,我们将解决一个数学问题:如何找出 1000 以内的所有…

AI绘画基于 Kaggle 10 分钟搭建 Stable Diffusion(保姆级教程)

AI绘画基于 Kaggle 10 分钟搭建 Stable Diffusion(保姆级教程) 一、引言二、安装教程1. 注册 Kaggle2. Edit My Copy3. 进行手机号的验证4. 打开 “internet off” 开关,并选择显卡5. 开启 session,运行脚本 三、主界面介绍四、注…

Windows提示“找不到rgss202j.dll”怎么办?

Rgss202j.dll文件是Windows操作系统最重要的系统文件之一,它包含了一组程序和驱动函数。如果此文件丢失或损坏,驱动程序将无法正常工作,并且相应的应用程序也将无法正常启动且运行。通常情况下,造成Rgss202j.dll文件无法找到的原因…

计算机的工作过程和主要性能指标

一、计算机的工作过程 为使计算机按预定要求工作,首先要编制程序。 程序是一个特定的指令序列,它告诉计算机要做哪些事,按什么步骤去做。 指令是一组二进制信息的代码,用来表示计算机所能完成的基本操作。 编制好的程序放在主存中…