我认为要了解一个技术框架,最好的切入点是了解这个框架是为了解决什么问题而产生的。
传统驱动加载方式中,依赖关系难以正确管理,因为不同的硬件模块之间可能存在复杂的依赖关系,导致驱动的加载顺序不确定。这可能导致一些驱动在其依赖的硬件还没有初始化的情况下被加载,从而引发系统错误。这里举一个两个驱动之间存在依赖关系并需要按顺序加载的例子:有一个设备,外接了很多I2C控制的smart PA,smart PA的具体配置都在codec驱动里执行。这样就需要smart PA驱动先probe上并初始化后,codec驱动才能进行PA的配置。若这个加载顺序出现了问题,可能会引发系统错误,导致crash并死机。
简单的说component框架可以解决驱动之间的依赖关系和加载问题。component框架引入了component标识符和component关系的概念,使得内核可以更清晰地了解component之间的依赖关系。通过该机制,内核可以按需地加载和卸载component,确保正确的加载和卸载顺序;而不是强制性地依赖预定义的加载顺序。这有助于解决传统加载方式中可能出现的顺序依赖问题。
component框架的使用可以简单的理解为:开车出行(master),需要先点火(slave a),挂挡(slave b),踩油门(所有slave都完成后才有效)然后就能跑了。
核心思想为:先等所有的slave component都probe上后,master component再通过component_bind_all(…)函数回调所有slave component的bind方法。
假设master为平台驱动,slave为i2c驱动; component框架相关的api在#include
头文件
tips: 示例代码省去了错误检查,实际中需要加入错误检查。示例代码参照Linux5.15内核提供的api编写
master {
...
slave_a = <&slave_component_a>;
slave_b = <&slave_component_b>;
};
&i2c {
slave_component_a:a@67 {
...
};
slave_component_b:b@01 {
...
};
};
static int master_compare_of(struct device *dev, void *data)
{
return dev->of_node == data;
}
static void master_release_of(struct device *dev, void *data)
{
of_node_put(data);
}
static void master_unbind(struct device *dev)
{
component_unbind_all(dev, <需要传递给slave component的数据>);
}
static int master_bind(struct device *dev)
{
/*3. 绑定master component的所有slave component*/
component_bind_all(dev, <需要传递给slave component的数据>);
/*4. master驱动一些需要用到slave驱动的操作都可以在这之后调用*/
}
static const struct component_master_ops master_comp_ops = {
.bind = master_bind,
.unbind = master_unbind,
};
static int master_component_probe(struct platform_device *pdev)
{
struct device_node *a_node, *b_node;
struct component_match *match = NULL;
...
/*1. 添加slave component设备*/
a_node = of_parse_phandle(pdev->dev.np, "slave_a", 0);
of_node_get(a_node);
component_match_add_release(&pdev->dev, &match,
master_release_of,
master_compare_of,
a_node
);
b_node = of_parse_phandle(pdev->dev.np, "slave_b", 0);
of_node_get(b_node);
component_match_add_release(&pdev->dev, &match,
master_release_of,
master_compare_of,
b_node
);
...
/*2. 注册component*/
return component_master_add_with_match(&pdev->dev,
&master_comp_ops, match);
}
tips: 步骤2里的添加slave component还有别的方法,可以根据具体实现细节修改
static void slave_unbind(struct device *comp,
struct device *master, void *master_data)
{
;
}
static int slave_bind(struct device *comp,
struct device *master, void *master_data)
{
/*2. slave驱动初始化*/
//comp为slave_component_probe()里component_add()第一个形参传入的struct device *
//master为component_bind_all()里第一个形参传入的struct device *
//master_data为component_bind_all()里第二个形参传入的void *,即master component传递给slave component的数据
}
static const struct component_ops slave_comp_ops = {
.bind = slave_bind,
.unbind = slave_unbind,
};
static int slave_component_probe(struct i2c_client *client)
{
...
/*1. 注册一个slave component*/
return component_add(&client->dev, &slave_comp_ops);
}