程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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函数中被初始化:

static int __init component_debug_init(void)
{
        component_debugfs_dir = debugfs_create_dir("device_component", NULL);

        return 0;
}

component_devices_fops如下:

DEFINE_SHOW_ATTRIBUTE(component_devices);

DEFINE_SHOW_ATTRIBUTE定义在include/linux/seq_file.h

#define DEFINE_SHOW_ATTRIBUTE(__name)                                   \
static int __name ## _open(struct inode *inode, struct file *file)      \
{                                                                       \
        return single_open(file, __name ## _show, inode->i_private);    \
}                                                                       \
                                                                        \
static const struct file_operations __name ## _fops = {                 \
        .owner          = THIS_MODULE,                                  \
        .open           = __name ## _open,                              \
        .read           = seq_read,                                     \
        .llseek         = seq_lseek,                                    \
        .release        = single_release,                               \
}

展开后可以得到:

static int component_devices_open(struct inode *inode, struct file *file)  
{                                                                       
        return single_open(file, component_devices_show, inode->i_private);    
}                                                                  
                                                                  
static const struct file_operations component_devices_fops = {              
        .owner          = THIS_MODULE,                                
        .open           = component_devices_open,                              
        .read           = seq_read,                                     
        .llseek         = seq_lseek,                                    
        .release        = single_release,                               
}

其中component_devices_show方法定义在drivers/base/component.c

static int component_devices_show(struct seq_file *s, void *data)
{
        struct aggregate_device *m = s->private;
        struct component_match *match = m->match;
        size_t i;

        mutex_lock(&component_mutex);
        seq_printf(s, "%-40s %20s\n", "aggregate_device name", "status");
        seq_puts(s, "-------------------------------------------------------------\n");
        seq_printf(s, "%-40s %20s\n\n",
                   dev_name(m->parent), m->bound ? "bound" : "not bound");

        seq_printf(s, "%-40s %20s\n", "device name", "status");
        seq_puts(s, "-------------------------------------------------------------\n");
        for (i = 0; i < match->num; i++) {
                struct component *component = match->compare[i].component;

                seq_printf(s, "%-40s %20s\n",
                           component ? dev_name(component->dev) : "(unknown)",
                           component ? (component->bound ? "bound" : "not bound") : "not registered");
        }
        mutex_unlock(&component_mutex);

        return 0;
}

比如,在NanoPC-T4开发板运行如下命令:

root@rk3399:~# ll /sys/kernel/debug/device_component/
-r--r--r--  1 root root 0 Jan  1  1970 display-subsystem
root@rk3399:~# cat /sys/kernel/debug/device_component/display-subsystem
aggregate_device name                                  status
-------------------------------------------------------------
display-subsystem                                       bound

device name                                            status
-------------------------------------------------------------
ff8f0000.vop                                            bound
ff900000.vop                                            bound
ff940000.hdmi                                           bound

以显示子系统子系统为例,这里先输出了显示子系统(即aggregate_device)设备的名称,然后又按顺序输出显示子系统各个组件(即component)设备的名称。

3.4 启动aggregate_device

经过对component以及aggregate_device注册函数的分析,我们知道无论是component还是aggregate_device注册都会调用try_to_bring_up_aggregate_device函数。

try_to_bring_up_aggregate_device函数用于尝试启动aggregate_device,这个是整个component架构的重点,正是这个流程使得各个组件的bind回调能按顺序执行。

  • 如果componentNULL,表示对aggregate_device进行操作;
  • 如果component不为NULL,表示需要检查并确保componentaggregate_device匹配,才能启动aggregate_device设备;

函数定义如下:

/*
 * Try to bring up an aggregate device.  If component is NULL, we're interested
 * in this aggregate device, otherwise it's a component which must be present
 * to try and bring up the aggregate device.
 *
 * Returns 1 for successful bringup, 0 if not ready, or -ve errno.
 */
static int try_to_bring_up_aggregate_device(struct aggregate_device *adev,
        struct component *component)
{
        int ret;

        dev_dbg(adev->parent, "trying to bring up adev\n");

    	// 遍历adev->match->compare数组,校验每一个component_match_array是否指定了与adev匹配的component,如果都指定了返回0
        if (find_components(adev)) {
                dev_dbg(adev->parent, "master has incomplete components\n");
                return 0;
        }

    	// 如果指定了component,但是component所属的adev不是adev,表示该component与adev不匹配,直接返回
        if (component && component->adev != adev) {
                dev_dbg(adev->parent, "master is not for this component (%s)\n",
                        dev_name(component->dev));
                return 0;
        }

    	// 打开一个资源组
        if (!devres_open_group(adev->parent, adev, GFP_KERNEL))
                return -ENOMEM;

        /* Found all components,执行aggregate_device的绑定操作 */
        ret = adev->ops->bind(adev->parent);
        if (ret < 0) {
                devres_release_group(adev->parent, NULL);
                if (ret != -EPROBE_DEFER)
                        dev_info(adev->parent, "adev bind failed: %d\n", ret);
                return ret;
        }

    	// 关闭资源组
        devres_close_group(adev->parent, NULL);
        // 更新标志位
        adev->bound = true;
        return 1;
}

函数主要步骤如下:

  • 调用find_components遍历adev->match->compare数组,校验每一个component_match_array是否指定了与adev匹配的component
  • 如果指定了component,但是component所属的adev不是adev,表示该componentadev不匹配,直接返回;
  • 调用aggregate_device操作集的bind函数,最后在aggregate_devicebind函数里面调用component_bind_all完成对各个componentbind顺序执行;
3.4.1 find_components

这里我们看一下find_components函数,函数:

  • 首先遍历match->compare数组,即component_match指向的component_match_array数组;
    • 然后遍历component_list链表,元素类型为struct component
      • 查找每一个component_match_array是否指定了与adev匹配的component

如果每一个component_match_array都指定了与adev匹配的component,将componentadev关联,将componentcomponent_match_array关联,并返回0,否则返回-ENXIO.
因此通过find_components函数可以找到一个aggregate_device所有的component

static int find_components(struct aggregate_device *adev)
{
        struct component_match *match = adev->match;
        size_t i;
        int ret = 0;

        /*
         * Scan the array of match functions and attach
         * any components which are found to this adev.
         */
        for (i = 0; i < match->num; i++) {
            	// 获取component匹配规则
                struct component_match_array *mc = &match->compare[i];
                struct component *c;

                dev_dbg(adev->parent, "Looking for component %zu\n", i);
				
            	// 如果指定了匹配的component,直接下一个component匹配规则
                if (match->compare[i].component)
                        continue;
	
            	// 遍历component_list链表找到与adev匹配的component,参数mc指定了匹配规则
                c = find_component(adev, mc);
                // 如果没找到与adev匹配的component、则c=NULL
                if (!c) {
                        ret = -ENXIO;
                        break;
                }

                dev_dbg(adev->parent, "found component %s, duplicate %u\n",
                        dev_name(c->dev), !!c->adev);

                /* Attach this component to the adev, 如果没有初始化c->adev,则将duplicate设置为false */
                match->compare[i].duplicate = !!c->adev;
            	// 保存匹配的component
                match->compare[i].component = c;
                // 更新compponent所属的adev
                c->adev = adev;
        }
        return ret;
}
3.4.2 find_component

find_component会遍历component_list链表,找到第一个与adev匹配的component并返回,如果找不到返回NULL

static struct component *find_component(struct aggregate_device *adev,
        struct component_match_array *mc)
{
        struct component *c;
		// 遍历component_list链表中的component
        list_for_each_entry(c, &component_list, node) {
	            // 如果指定了c->adev,但是c->adev不是adev,表示该component与adev不匹配,继续
                if (c->adev && c->adev != adev)
                        continue;
				// 指定了compare函数,匹配成功直接返回当前component
                if (mc->compare && mc->compare(c->dev, mc->data))
                        return c;

            	// 如果指定了compare_typed函数,匹配成功直接返回当前component
                if (mc->compare_typed &&
                    mc->compare_typed(c->dev, c->subcomponent, mc->data))
                        return c;
        }

        return NULL;
}

3.5 component_bind_all

aggregate_devicebind函数如何按顺序执行各个componentbind函数?通常在aggregate_devicebind函数里面调用component_bind_all函数;

/**
 * component_bind_all - bind all components of an aggregate driver
 * @parent: parent device of the aggregate driver
 * @data: opaque pointer, passed to all components
 *
 * Binds all components of the aggregate @dev by passing @data to their
 * &component_ops.bind functions. Should be called from
 * &component_master_ops.bind.
 */
int component_bind_all(struct device *parent, void *data)
{
        struct aggregate_device *adev;
        struct component *c;
        size_t i;
        int ret = 0;

        WARN_ON(!mutex_is_locked(&component_mutex));

        adev = __aggregate_find(parent, NULL);
        if (!adev)
                return -EINVAL;

        /* Bind components in match order */
        for (i = 0; i < adev->match->num; i++)
            	// duplicate为false时进入
                if (!adev->match->compare[i].duplicate) {
                        c = adev->match->compare[i].component;
						// 执行component的bind函数
                        ret = component_bind(c, adev, data);
                        if (ret)
                                break;
                }

        if (ret != 0) { // 正常不会走这里
                for (; i > 0; i--)
                        if (!adev->match->compare[i - 1].duplicate) {
                                c = adev->match->compare[i - 1].component;
                                component_unbind(c, adev, data);
                        }
        }

        return ret;
}

首先通过设备parent找到与之关联的的aggregate_device,再按照aggregate_devicecomponent_match_arraycomponent的顺序找到component,然后就能调用componentbind函数;

static int component_bind(struct component *component, struct aggregate_device *adev,
        void *data)
{
        int ret;

        /*
         * Each component initialises inside its own devres group.
         * This allows us to roll-back a failed component without
         * affecting anything else.
         */
        if (!devres_open_group(adev->parent, NULL, GFP_KERNEL))
                return -ENOMEM;

        /*
         * Also open a group for the device itself: this allows us
         * to release the resources claimed against the sub-device
         * at the appropriate moment.
         */
        if (!devres_open_group(component->dev, component, GFP_KERNEL)) {
                devres_release_group(adev->parent, NULL);
                return -ENOMEM;
        }

        dev_dbg(adev->parent, "binding %s (ops %ps)\n",
                dev_name(component->dev), component->ops);

    	// 调用component的bind函数
        ret = component->ops->bind(component->dev, adev->parent, data);
        if (!ret) { // 正常走这里
                component->bound = true;  // 设置bind执行标志位

                /*
                 * Close the component device's group so that resources
                 * allocated in the binding are encapsulated for removal
                 * at unbind.  Remove the group on the DRM device as we
                 * can clean those resources up independently.
                 */
                devres_close_group(component->dev, NULL);
                devres_remove_group(adev->parent, NULL);

                dev_info(adev->parent, "bound %s (ops %ps)\n",
                         dev_name(component->dev), component->ops);
        } else {
                devres_release_group(component->dev, NULL);
                devres_release_group(adev->parent, NULL);

                if (ret != -EPROBE_DEFER)
                        dev_err(adev->parent, "failed to bind %s (ops %ps): %d\n",
                                dev_name(component->dev), component->ops, ret);
        }

        return ret;
}

3.6 总结

component_match_add_release对应component_match的构建;
component_add对应component的链表构建;
component_master_add_with_match对应aggregate_device的链表构建;
component_bind_allaggregate_device最后执行各个bind的接口。

参考文章

[1] linux component组件架构分析

[2] kernel - component组件用法

[3] linux内核compoent框架分析和使用

posted @ 2023-09-07 00:14  大奥特曼打小怪兽  阅读(259)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步