Rockchip RK3399 - component框架
我们在分析RK3399 DRM
驱动过程中,涉及到了component
框架内容,因此这里我们穿插一节内容,专门对component
框架进行介绍。
一、component
概述
1.1 背景
linux
内核中的驱动,需要有一定的加载顺序,用来解决驱动之间的依赖问题。虽然说linux
内核有定传统的驱动优先级,用来定义驱动的先后顺序,但是不足以更加细分的加载。
有的驱动可以独立加载而不依赖于其他驱动,但是在一个比较庞大的驱动面前,细分驱动的加载就比较重要了,因为这些庞大的驱动,环环相扣,一环出了问题就影响整个驱动的顺利走完。
所以component
架构构建功能系统就包括两方面的作用:
- 保证系统安装了所有的组件;
- 规定了系统各组件初始化的顺序;
1.2 介绍
component
架构在linux
内核中现在主要的应用是用来构建display-subsystem
,一个显示子系统由显示控制器(vop
)、接口控制器(mipi
,lvds
,hdmi
等)、液晶背光,电源等多个独立的功能单元构成。而把这些功能单元构成一个系统,就需要这些功能单元间有一定的协同配合。
如在扫描设备树时,每一个设备节点依次被注册到系统中(一般将设备节点转换为platform_device
,然后使用platform_device_register
注册),而只有当所有显示子系统相关的设备节点都注册到系统中时,整个显示系统才能正常工作。
如果没有component
架构的参与,在用platform_device_register
函数注册每个设备节点时,其platform_driver
驱动的probe
函数中会执行了一系列的初始化工作,而此时系统相关的其它设备节点可能还没有注册到内核中,这样可能就会导致一些问题出现。
在component
架构的参下,可以保证在显示子系统的所有功能单元都注册后,才按照一定顺序执行初始化操作。
二、component
核心数据结构
component
架构的实现位于drivers/base/component.c
,其主要涉及到的数据结构有struct component
、struct aggregate_device
、struct 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 component
、struct aggregate_device
、struct component_match
之间的关系,我们绘制了如下关系框图:
刚开始component
会将自己注册进入系统,并尝试唤醒aggregate_device
来统筹(component
也不了解自己是不是最后一个注册的,所有有义务唤醒aggregate_device
).
最后aggregate_device
把自己注册进入系统,根据aggregate_device
自己定义的匹配方法找到所有的component
,调用component
的bind
调函数.
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
;ops
:aggregate_device
可执行的初始化操作;parent
:aggregate_device
所属的device
;match
:该aggregate_device
用到的component_match
,aggregate_device
应用该match
在component_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
的绑定操作;当component
或aggregate_device
注册时,回调该函数;unbind
:执行aggregate_device
的解绑操作;当调用component_master_del
或使用component_del
时,回调该函数;
2.4 struct component_match
struct component_match
是一个用于匹配的对象,用于匹配aggregate_device
和component
,因为系统中有很多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
),数组每个有效条目代表了一个需要安装的组件;
alloc
与num
主要用于内存的动态分配与管理,compare
主要用于aggregate_device
和component
的匹配。
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
: 保存了compare
、compare_typed
用到的匹配数据;compare
:用于实现aggregate_device
匹配component
,compare
函数指针指定了匹配的规则;compare_typed
:用于实现aggregate_device
匹配component
,compare_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
; - 初始化组件成员:
ops
、dev
、subcomponent
; - 将当前组件添加到全局链表
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
,里面包含了很重要的compare
、release
函数和compare
数据等;
component_match_array
到底是什么,可以说一项component_match_array
对应一个component
,因此通过component_match_array
可以找到component
。
就这样,通过component_match_add
就可以往component_match
里面增加一个个的component_match_array
。
需要注意的是:在这一步中,component_match_array
里的component
还没有指定,但是指定了每个component
的compare
函数,在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
; - 初始化组件成员:
ops
、match
、parent
; 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_dir
在component_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
回调能按顺序执行。
- 如果
component
为NULL
,表示对aggregate_device
进行操作; - 如果
component
不为NULL
,表示需要检查并确保component
与aggregate_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
,表示该component
与adev
不匹配,直接返回; - 调用
aggregate_device
操作集的bind
函数,最后在aggregate_device
的bind
函数里面调用component_bind_all
完成对各个component
的bind
顺序执行;
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
,将component
和adev
关联,将component
和component_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_device
的bind
函数如何按顺序执行各个component
的bind
函数?通常在aggregate_device
的bind
函数里面调用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_device
→component_match_array
→component
的顺序找到component
,然后就能调用component
的bind
函数;
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_all
是aggregate_device
最后执行各个bind
的接口。
参考文章