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

news2024/11/18 14:32:00

文章目录

    • 一、抽象媒体设备模型
    • 二、媒体设备
    • 三、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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day11_类中成员之方法

成员变量是用来存储对象的数据信息的,那么如何表示对象的行为功能呢?就要通过方法来实现 方法 概念: 方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。把一个功能封装为方法的目的是,可…

信息安全原理与技术期末复习(如学)

文章目录 一、前言(开卷打印)二、选择题三、简答题1、简述端口扫描技术原理(P136)2、分组密码工作方式(P61)3、木马攻击(P176)4、消息认证码(P84)5、非对称密…

华为OD机试真题B卷 JavaScript 实现【删除字符串中出现次数最少的字符】,附详细解题思路

一、题目描述 删除字符串中出现次数最少的字符,如果多个字符出现次数一样则都删除。 二、输入描述 一个字符串。 三、输出描述 删除字符串中出现次数最少的字符,如果多个字符出现次数一样则都删除,如果都被删除 则换为empty。 四、解题…

机器视觉初步5-1:图像平滑专题

在计算机视觉领域,图像平滑处理是一个重要的任务,用于降低噪声,提高图像质量。常见的图像平滑算法有均值滤波、中值滤波、高斯滤波等。本文将介绍这些算法的原理,并分别给出使用Python与Halcon实现的代码。(当前版本&a…

libface 人脸检测

于老师的项目地址GitHub - ShiqiYu/libfacedetection: An open source library for face detection in images. The face detection speed can reach 1000FPS. 关于如何使用,于老师写得很清楚: 测试代码 CMakeList.txt 和 三个face开头的cpp文件都是于老…

有趣的数学 数学建模入门一 从几个简单的示例入手

一、“变量”的概念 一个代数表达式(通常只有一个字母:x,y,z…,如果它取代了一个未知值(物理、经济、时间等),则称为“变量”。 变量的作用是占据一个值所在的位置,如果该…

设计模式之工厂方法模式笔记

设计模式之工厂方法模式笔记 说明Factory Method(工厂方法)目录UML抽象工厂示例类图咖啡抽象类美式咖啡类拿铁咖啡类 咖啡工厂接口美式咖啡工厂类拿铁咖啡工厂类 咖啡店类测试类 说明 记录下学习设计模式-工厂方法模式的写法。 Factory Method(工厂方法) 意图:定义一个用于创…

深度学习图像分类、目标检测、图像分割源码小项目

​demo仓库和视频演示: 银色子弹zg的个人空间-银色子弹zg个人主页-哔哩哔哩视频 卷积网路CNN分类的模型一般使用包括alexnet、DenseNet、DLA、GoogleNet、Mobilenet、ResNet、ResNeXt、ShuffleNet、VGG、EfficientNet和Swin transformer等10多种模型 目标检测包括…

Sourcetree 打开闪退怎么处理

问题描述:Sourcetree打开闪退,已管理员身份运行仍然闪退 解决方法; 在Sourcetree图标上右键,然后打开文件所在位置: 找到目录 xxxx\AppData\Local\Atlassian 删除箭头所指向文件即可。

2023年怎么移除微博粉丝 微博怎么批量移除粉丝方法

2023最新微博批量粉丝移除_手机微博粉丝怎么批量删除 使用微博粉丝移除工具:可以帮助用户快速批量移除粉丝。在微博管理工具中,用户可以根据自己的需要设置移除粉丝的数量,可以一键批量移除多个粉丝。此外,管理工具还提供了粉丝管…

Linux下使用Samba做域控

AI画妹子的工作先暂告一段落。毕竟戗行也是要有门槛的。 企业中使用Windows Server使用活动目录集中管理PC、服务器是很成熟的方案。突然想到,如果有一天出于某种原因不再使用微软方案了,AD该如何替代?问了一下chatGPT,它说&…

[Qt 教程之Widgets模块] —— QComboBox 组合框

Qt系列教程总目录 文章目录 一、创建QComboBox二、数据结构三、成员函数1. 添加选项2. 插入选项3. 删除选项4. 选项属性5. 当前选择的item6. 用户是否可编辑组合框7. 设置显示item的个数8. 组合框的item个数9. 添加重复的item10. 是否绘制边框11. 查找item12. 插入策略13. 大小…

NodeJS LocalDomainName⑩④

文章目录 ✨文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持😘前言 L o c a l D o m a i n N a m e LocalDomainName LocalDomainName 本地域名 O p e r a t i o n P r o c e d u r e OperationProcedure OperationProced…

【MySQL】一文带你了解过滤数据

🎬 博客主页:博主链接 🎥 本文由 M malloc 原创,首发于 CSDN🙉 🎄 学习专栏推荐:LeetCode刷题集! 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指…

LAMP架构搭建

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Apache服务1.apache概述2.apache的特点3.apache的软件版本4.编译安装优点5.安装步骤 二、LAMP简介与概述1.LAMP平台概述2.构建LAMP平台顺序3.各组件的主要作用 …

KafKa 3.x(一、入门)

前置:熟悉javase,熟悉linux,熟悉idea,熟悉hadoop 1. KafKa 1.1 KafKa定义 前端埋点记录用户(浏览,点赞,收藏,评论)到日志服务器,然后通过Flume&#xff08…

k8s Service服务详解

1. Service 的概念: k8s中Service定义了这样一种抽象:一个pod的逻辑分组,一种可以访问他们的策略—通常称为微服务。这一组pod能够被Service访问到,通常是通过Label Selector Service能够提供负载均衡的能力,但是在使…

第二章 逻辑代数基础--数电(期末复习笔记)

第二章 逻辑代数基础 2.1 概述 逻辑:事物间的因果关系。 逻辑运算:逻辑状态按照指定的某种因果关系进行推理的过程。 逻辑变量:用字母表示,取值只有0和1。此时,0和1不再表示数量的大小,只代表两种不同的状…

人工智能期末复习(简答)

第1章 人工智能概述 1.什么是人工智能? 人工智能(Artificial Intelligence, AI),又称机器智能(MI,Machine Intelligence), 主要研究用人工的方法和技术开发智能机器或智能系统,以模…

交换机配置DHCP服务(华为交换机)

#三层交换机互联互通 配置 #进入系统视图 <Huawei>system-view #关闭系统提示信息 [Huawei]undo info-center enable #启动DHCP功能 [Huawei]dhcp enable #创建vlan 10 并配置 vlanif 地址 [Huawei]vlan 10 [Huawei-vlan10]int vlanif 10 [Huawei-Vlanif10]ip addr…