我们在分析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 &s