程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Rockchip RK3399 - component框架

我们在分析RK3399 DRM驱动过程中,涉及到了component框架内容,因此这里我们穿插一节内容,专门对component框架进行介绍。

一、component概述

1.1 背景

linux内核中的驱动,需要有一定的加载顺序,用来解决驱动之间的依赖问题。虽然说linux内核有定传统的驱动优先级,用来定义驱动的先后顺序,但是不足以更加细分的加载。

有的驱动可以独立加载而不依赖于其他驱动,但是在一个比较庞大的驱动面前,细分驱动的加载就比较重要了,因为这些庞大的驱动,环环相扣,一环出了问题就影响整个驱动的顺利走完。

所以component架构构建功能系统就包括两方面的作用:

  • 保证系统安装了所有的组件;
  • 规定了系统各组件初始化的顺序;

1.2 介绍

component架构在linux内核中现在主要的应用是用来构建display-subsystem,一个显示子系统由显示控制器(vop)、接口控制器(mipilvdshdmi等)、液晶背光,电源等多个独立的功能单元构成。而把这些功能单元构成一个系统,就需要这些功能单元间有一定的协同配合。

如在扫描设备树时,每一个设备节点依次被注册到系统中(一般将设备节点转换为platform_device,然后使用platform_device_register注册),而只有当所有显示子系统相关的设备节点都注册到系统中时,整个显示系统才能正常工作。

如果没有component架构的参与,在用platform_device_register函数注册每个设备节点时,其platform_driver驱动的probe函数中会执行了一系列的初始化工作,而此时系统相关的其它设备节点可能还没有注册到内核中,这样可能就会导致一些问题出现。

component架构的参下,可以保证在显示子系统的所有功能单元都注册后,才按照一定顺序执行初始化操作。

二、component核心数据结构

component架构的实现位于drivers/base/component.c,其主要涉及到的数据结构有struct componentstruct aggregate_devicestruct component_match

2.1 关系图

component架构上述结构可以用拼乐高积木的过程来理解:

  • 一个一个形态各异的积木就用struct component来表示;
  • struct aggregate_device就是要拼接成的东西(系统)(例如想拼成一辆车或一个房子);
  • struct component_match就是你手里的图纸;

根据图纸,就可以在一个个积木(component)中,找出需要的积木,然后拼成一个想要的作品(aggregate_device)。
如果对应到display-subsystem中的话,:

  • struct component对应的就是显示子系统的各个功能单元,每个component都会和一个platform_device关联起来;
  • struct aggregate_device对应的就是显示子系统;

为了更加形象的表示struct componentstruct aggregate_devicestruct component_match 之间的关系,我们绘制了如下关系框图:

刚开始component会将自己注册进入系统,并尝试唤醒aggregate_device来统筹(component也不了解自己是不是最后一个注册的,所有有义务唤醒aggregate_device).
最后aggregate_device把自己注册进入系统,根据aggregate_device自己定义的匹配方法找到所有的component,调用componentbind调函数.

2.2 struct component

struct component用来表示系统组件,定义在drivers/base/component.c

struct component {
        struct list_head node;
        struct aggregate_device *adev;
        bool bound;

        const struct component_ops *ops;
        int subcomponent;
        struct device *dev;
};

其中:

  • node: 链表节点,用于将当前节点添加到全局链表component_list,链表component_list保存了注册到系统中的所有component
  • adev:用于保存与该组件匹配的aggregate_device
  • bound:表示组件已经执行了bind操作,当操作集ops中的bind函数被执行,该标志位设置为true
  • ops:组件可执行的初始化操作;
  • dev:组件所属的device
2.2.1 全局链表component_list

全局链表component_list定义如下:

static LIST_HEAD(component_list);
2.2.2 struct component_ops

struct component_ops定义在include/linux/component.h,用于表示component可执行的初始化操作;

/**
 * struct component_ops - callbacks for component drivers
 *
 * Components are registered with component_add() and unregistered with
 * component_del().
 */
struct component_ops {
        /**
         * @bind:
         *
         * Called through component_bind_all() when the aggregate driver is
         * ready to bind the overall driver.
         */
        int (*bind)(struct device *comp, struct device *master,
                    void *master_data);
        /**
         * @unbind:
         *
         * Called through component_unbind_all() when the aggregate driver is
         * ready to bind the overall driver, or when component_bind_all() fails
         * part-ways through and needs to unbind some already bound components.
         */
        void (*unbind)(struct device *comp, struct device *master,
                       void *master_data);
};

该结构体中包含两个回调函数:

  • bind:执行组件的绑定操作;当调用component_bind_all函数时,回调该函数;
  • unbind:执行组件的解绑操作;当调用component_unbind_all函数时,回调该函数;

2.3 struct aggregate_device

struct aggregate_device表示需要构建的系统,定义在drivers/base/component.c

struct aggregate_device {
        struct list_head node;
        bool bound;

        const struct component_master_ops *ops;
        struct device *parent;
        struct component_match *match;
};

其中:

  • node:链表节点,用于将当前节点添加到全局链表aggregate_devices,链表aggregate_devices保存了注册到系统中的所有aggregate_device
  • bound:表示系统已经执行了bind操作,当操作集ops中的bind函数被执行,该标志位设置为true
  • opsaggregate_device可执行的初始化操作;
  • parentaggregate_device所属的device
  • match:该aggregate_device用到的component_matchaggregate_device应用该matchcomponent_list链表中找到适配于自己的component组件,并将所有匹配的component保存到该结构体的中;
2.3.1 全局链表aggregate_devices

全局链表aggregate_devices定义如下:

static LIST_HEAD(aggregate_devices);
2.3.2 struct component_master_ops

struct component_master_ops定义在include/linux/component.h,用于表示aggregate_device可执行的初始化操作;

/**
 * struct component_master_ops - callback for the aggregate driver
 *
 * Aggregate drivers are registered with component_master_add_with_match() and
 * unregistered with component_master_del().
 */
struct component_master_ops {
        /**
         * @bind:
         *
         * Called when all components or the aggregate driver, as specified in
         * the match list passed to component_master_add_with_match(), are
         * ready. Usually there are 3 steps to bind an aggregate driver:
         *
         * 1. Allocate a structure for the aggregate driver.
         *
         * 2. Bind all components to the aggregate driver by calling
         *    component_bind_all() with the aggregate driver structure as opaque
         *    pointer data.
         *
         * 3. Register the aggregate driver with the subsystem to publish its
         *    interfaces.
         *
         * Note that the lifetime of the aggregate driver does not align with
         * any of the underlying &struct device instances. Therefore devm cannot
         * be used and all resources acquired or allocated in this callback must
         * be explicitly released in the @unbind callback.
         */
        int (*bind)(struct device *master);
        /**
         * @unbind:
         *
         * Called when either the aggregate driver, using
         * component_master_del(), or one of its components, using
         * component_del(), is unregistered.
         */
        void (*unbind)(struct device *master);
};

该结构体中包含两个回调函数:

  • bind:执行aggregate_device的绑定操作;当componentaggregate_device注册时,回调该函数;
  • unbind:执行aggregate_device的解绑操作;当调用component_master_del或使用component_del时,回调该函数;

2.4 struct component_match

struct component_match是一个用于匹配的对象,用于匹配aggregate_devicecomponent,因为系统中有很多component,一个aggregate_device并不是统筹所有的component,而是统筹有一定特征的component,所以需要一个匹配的工具,那就是component_match
其定义在drivers/base/component.c

struct component_match {
        size_t alloc;
        size_t num;
        struct component_match_array *compare;
};

其中:

  • alloc:用于记录分配的compare数组大小;
  • num:用于记录系统实际需要的组件(compnents)数量,更准确地说是保存compare数组有效component_match_array的个数;
  • compare:指向一个数组,保存一个系统(aggregate_device)中需要的所有组件(compnents),数组每个有效条目代表了一个需要安装的组件;

allocnum主要用于内存的动态分配与管理,compare主要用于aggregate_devicecomponent的匹配。

2.4.1 struct component_match_array

struct component_match_array定义在drivers/base/component.c,描述与aggregate_device匹配的component

struct component_match_array {
        void *data;
        int (*compare)(struct device *, void *);
        int (*compare_typed)(struct device *, int, void *);
        void (*release)(struct device *, void *);
        struct component *component;
        bool duplicate;
};

其中:

  • data: 保存了comparecompare_typed用到的匹配数据;
  • compare:用于实现aggregate_device匹配componentcompare函数指针指定了匹配的规则;
  • compare_typed:用于实现aggregate_device匹配componentcompare_typed函数指针指定了匹配的规则;
  • component:保存与aggregate_devices匹配的component

三、component核心API

3.1 component注册

如开头所说,驱动都是通过probe进入加载的,但是probe并没有特别明确的加载顺序,做为display-subsystem的设备驱动,需要在probe函数中将自己抽象为component设备注册进系统,然后等待aggregate_device来统筹每个conponent的加载顺序,现在就来看看component如何加载。
component_add函数为设备dev向系统注册一个component,函数定义在drivers/base/component.c

static int __component_add(struct device *dev, const struct component_ops *ops,
        int subcomponent)  // 设置为0
{
        struct component *component;
        int ret;

    	// 动态分配一个struct component
        component = kzalloc(sizeof(*component), GFP_KERNEL);
        if (!component)
                return -ENOMEM;

        component->ops = ops;
        component->dev = dev;
        component->subcomponent = subcomponent;

        dev_dbg(dev, "adding component (ops %ps)\n", ops);
	
    	// 获取互斥锁
        mutex_lock(&component_mutex);
    	// 将当前component添加到全局链表component_list
        list_add_tail(&component->node, &component_list);

	    // 尝试启动和component相关的aggregate_device
        ret = try_to_bring_up_masters(component);
        if (ret < 0) {
                if (component->adev)
                        remove_component(component->adev, component);
                list_del(&component->node);

                kfree(component);
        }
    	// 释放互斥锁
        mutex_unlock(&component_mutex);

        return ret < 0 ? ret : 0;
}


/**
 * component_add - register a component
 * @dev: component device
 * @ops: component callbacks
 *
 * Register a new component for @dev. Functions in @ops will be called when the
 * aggregate driver is ready to bind the overall driver by calling
 * component_bind_all(). See also &struct component_ops.
 *
 * The component needs to be unregistered at driver unload/disconnect by
 * calling component_del().
 *
 * See also component_add_typed() for a variant that allows multipled different
 * components on the same device.
 */
int component_add(struct device *dev, const struct component_ops *ops)
{
        return __component_add(dev, ops, 0);
}

函数主要步骤如下:

  • 调用kzalloc动态分配一个struct component
  • 初始化组件成员:opsdevsubcomponent
  • 将当前组件添加到全局链表component_list
  • 调用try_to_bring_up_masters函数尝试启动和component相关的aggregate_device

try_to_bring_up_masters用于启动和component相关的aggregate_device,实现如下;

static int try_to_bring_up_masters(struct component *component)
{
        struct aggregate_device *adev;
        int ret = 0;
		// 遍历aggregate_devices链表
        list_for_each_entry(adev, &aggregate_devices, node) {
            	// 当前adev尚未执行bind操作
                if (!adev->bound) {
	                    // 尝试启动aggregate_device
                        ret = try_to_bring_up_aggregate_device(adev, component);
                        if (ret != 0)
                                break;
                }
        }

        return ret;
}

遍历aggregate_device并调用try_to_bring_up_aggregate_device尝试启动aggregate_device,这个函数后面介绍。

3.2 component_match注册

一个aggregate_device对应一个component_match,一个component_match对应一组component_match_array,使用component_match_add_release来构建一个带release函数的component_match,函数定义在drivers/base/component.c

static void __component_match_add(struct device *parent,
        struct component_match **matchptr,
        void (*release)(struct device *, void *),
        int (*compare)(struct device *, void *),
        int (*compare_typed)(struct device *, int, void *),
        void *compare_data)
{
        struct component_match *match = *matchptr;

        if (IS_ERR(match))
                return;

		// 未指定match,动态分配一个match
        if (!match) {	
                match = devres_alloc(devm_component_match_release,
                                     sizeof(*match), GFP_KERNEL);
                if (!match) {
                        *matchptr = ERR_PTR(-ENOMEM);
                        return;
                }

                devres_add(parent, match);

                *matchptr = match;
        }

		// 如果一组`component_match_array`已经满了,或者空的,那么就新增16个空位
        if (match->num == match->alloc) {
                size_t new_size = match->alloc + 16;
                int ret;
				// 动态扩容
                ret = component_match_realloc(match, new_size);
                if (ret) {
                        *matchptr = ERR_PTR(ret);
                        return;
                }
        }
		// 填充一个`component_match_array`,里面包含了很重要的`compare`函数和`compare`数据等
        match->compare[match->num].compare = compare;
        match->compare[match->num].compare_typed = compare_typed;
        match->compare[match->num].release = release;
        match->compare[match->num].data = compare_data;
        match->compare[match->num].component = NULL;
		
		// 记录compare数组有效`component_match_array`的个数
        match->num++;
}


/**
 * component_match_add_release - add a component match entry with release callback
 * @parent: parent device of the aggregate driver
 * @matchptr: pointer to the list of component matches
 * @release: release function for @compare_data
 * @compare: compare function to match against all components
 * @compare_data: opaque pointer passed to the @compare function
 *
 * Adds a new component match to the list stored in @matchptr, which the
 * aggregate driver needs to function. The list of component matches pointed to
 * by @matchptr must be initialized to NULL before adding the first match. This
 * only matches against components added with component_add().
 *
 * The allocated match list in @matchptr is automatically released using devm
 * actions, where upon @release will be called to free any references held by
 * @compare_data, e.g. when @compare_data is a &device_node that must be
 * released with of_node_put().
 *
 * See also component_match_add() and component_match_add_typed().
 */
void component_match_add_release(struct device *parent,
        struct component_match **matchptr,
        void (*release)(struct device *, void *),
        int (*compare)(struct device *, void *), void *compare_data)
{
        __component_match_add(parent, matchptr, release, compare, NULL,
                              compare_data);
}

具体步骤如下:

  • 如果传进来的component_match对象是NULL,那么意味着需要创建一个新的component_match对象;
  • 如果一组component_match_array已经满了,或者空的,那么就新增16个空位;
  • 填充一个component_match_array,里面包含了很重要的comparerelease函数和compare数据等;
    component_match_array到底是什么,可以说一项component_match_array对应一个component,因此通过component_match_array可以找到component
    就这样,通过component_match_add就可以往component_match里面增加一个个的component_match_array

需要注意的是:在这一步中,component_match_array里的component还没有指定,但是指定了每个componentcompare函数,在aggregate_device注册的时候,遍历全局链表component_list,调用compare函数,将符合的要求的component加入component_match_array

3.3 aggregate_device注册

aggregate_device作为一个统筹的角色,在所有component都被注册进系统后,就可以将aggregate_device注册进系统,component_master_add_with_match函数用于向系统注册一个aggregate_device
函数定义在drivers/base/component.c,可以看到传入的参数是aggregate_device所属的设备,aggregate_device的操作集合,以及刚刚构建好的component_match函数,这正好对应上了:一个aggregate_device对应一个component_match

/**
 * component_master_add_with_match - register an aggregate driver
 * @parent: parent device of the aggregate driver
 * @ops: callbacks for the aggregate driver
 * @match: component match list for the aggregate driver
 *
 * Registers a new aggregate driver consisting of the components added to @match
 * by calling one of the component_match_add() functions. Once all components in
 * @match are available, it will be assembled by calling
 * &component_master_ops.bind from @ops. Must be unregistered by calling
 * component_master_del().
 */
int component_master_add_with_match(struct device *parent,
        const struct component_master_ops *ops,
        struct component_match *match)
{
        struct aggregate_device *adev;
        int ret;

        /* Reallocate the match array for its true size  */
        ret = component_match_realloc(match, match->num);
        if (ret)
                return ret;

	    // 动态分配一个struct aggregate_device
        adev = kzalloc(sizeof(*adev), GFP_KERNEL);
        if (!adev)
                return -ENOMEM;

        adev->parent = parent;
        adev->ops = ops;
        adev->match = match;

    	// debugfs文件系统相关
        component_debugfs_add(adev);
        /* Add to the list of available aggregate devices. */
        mutex_lock(&component_mutex);
	    // 将当前caggregate_device添加到全局链表aggregate_devices
        list_add(&adev->node, &aggregate_devices);

	    // 尝试启动aggregate_device
        ret = try_to_bring_up_aggregate_device(adev, NULL);

        if (ret < 0)
                free_aggregate_device(adev);

        mutex_unlock(&component_mutex);

        return ret < 0 ? ret : 0;
}

函数主要步骤如下:

  • aggregate_device加进系统之前重新规划component_match指向的数组component_match_array,修改成真实存在的数量,因为要知道我们通过component_match_add_typed一下子申请了16个空位,但是加进去的不一定有16个component
  • 调用kzalloc动态分配一个struct aggregate_device
  • 初始化组件成员:opsmatchparent
  • debugfs系统增加内容;
  • 将当前aggregate_device添加到全局链表aggregate_devices
  • 调用try_to_bring_up_masters函数尝试启动aggregate_device
3.3.1 component_match_realloc

component_match_realloc函数用于动态扩容(即为match->compare重新分配内存),其中num为需要分配的数组的长度,函数定义在drivers/base/component.c

static int component_match_realloc(struct component_match *match, size_t num)
{
        struct component_match_array *new;

        //  如果已经分配了长度为num的数组,则不需要扩容
        if (match->alloc == num)
                return 0;
	
    	// 动态分配数组,数组长度为num
        new = kmalloc_array(num, sizeof(*new), GFP_KERNEL);
        if (!new)
                return -ENOMEM;

        // 如果已经存在,将数据拷贝到新分配的数组中
        if (match->compare) {
                memcpy(new, match->compare, sizeof(*new) *
                                            min(match->num, num));
            	// 释放之前的内存
                kfree(match->compare);
        }
    	// 保存分配的数组
        match->compare = new;
    	// 记录分配的compare数组大小
        match->alloc = num;

        return 0;
}
3.3.2 component_debugfs_add

component_debugfs_add函数用于在debugfs文件系统创建以m->parent设备命名的文件:

static void component_debugfs_add(struct aggregate_device *m)
{
        debugfs_create_file(dev_name(m->parent), 0444, component_debugfs_dir, m,
                            &component_devices_fops);
}
core_initcall(component_debug_init);

其中:

  • 文件名为dev_name(m->parent)
  • 0444描述了该文件应该具有的访问权限;
  • component_debugfs_dir表示应该保存该文件的目录,比如/sys/kernel/debug/device_component
  • m将被保存在产生的inode结构的i_private字段中;
  • component_devices_fops是一组实现该文件行为的文件操作函数。

component_debugfs_dircomponent_debug_init函数中被初始化:

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(634)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示