linux kernel component框架分析【轉】


轉自:https://blog.csdn.net/shikivs/article/details/103591971

基於4.1.15內核
kernel中的component框架是為了subsystem能夠按照一定的順序初始化設備而提出的架構。
subsystem中由較多設備模塊組成,而內核加載每個模塊時間不定。則需要component框架來保證需最后初始化的設備加載前,所需設備全部加載完畢。

1 component框架描述
1.1 架構描述
在component中,包含兩個基本概念,master和component。master是設備樹中的“超級設備(superdevice)”,負責管理該超級設備下的普通設備。component是由master管理的普通設備,要先初始化。

master在設備樹中一般為XXX-subsystem節點,如display-subsystem。節點有ports屬性,屬性里存有該master應該關聯的普通設備,如ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>;。

display-subsystem {
compatible = "fsl,imx-display-subsystem";
ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>;
};

component是普通的設備節點,其下有與master的prots屬性值一樣的節點,如ipu1_di0的節點。通過屬性值這個字段名,把超級設備與普通設備關聯起來。

ipu1: ipu@02400000 {

...

ipu1_di0: port@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;

ipu1_di0_disp0: disp0-endpoint {
};

ipu1_di0_hdmi: hdmi-endpoint {
remote-endpoint = <&hdmi_mux_0>;
};

ipu1_di0_mipi: mipi-endpoint {
remote-endpoint = <&mipi_mux_0>;
};

ipu1_di0_lvds0: lvds0-endpoint {
remote-endpoint = <&lvds0_mux_0>;
};

ipu1_di0_lvds1: lvds1-endpoint {
remote-endpoint = <&lvds1_mux_0>;
};
};

ipu1_di1: port@3 {
...
};

...
};

關聯之后就能通過框架來進行管理了。

1.2 主要文件
/driver/base/component.c文件包含了主要邏輯。

2 設備的初始化流程
初始化分為兩部分:
master即超級設備,執行probe函數時使用component_master_add_with_match函數注冊自己到component框架中。
component即普通設備,執行probe函數時使用component_add函數注冊自己到component框架中。
兩種流程先后順序並無要求,可隨意順序。每一個設備加入到框架中,框架就嘗試進行匹配,當master匹配上所有component后,會調用master的bind回調,開始按順序進行初始化。保證了當所有子設備全部probe成功后再執行初始化操作。

2.1 master設備的初始化流程
首先probe函數中定義一個component_match結構體的對象指針match。match是一個存儲子節點的數組。遍歷超級設備節點下的"ports"屬性,每解析出1個屬性值。
調用component_match_add函數,在match中添加一個compare數組成員,還會再將每個屬性值關聯的設備添加到match中。
返回probe函數,調用component_master_add_with_match函數,注冊該master,在該函數中調用try_to_bring_up_master函數,嘗試初始化該master。

2.1.1 master設備probe函數。
struct component_match *match = NULL;

for (i = 0; ; i++) {
port = of_parse_phandle(pdev->dev.of_node, "ports", i);
if (!port)
break;

component_match_add(&pdev->dev, &match, compare_of, port);
}

for (i = 0; ; i++) {
port = of_parse_phandle(pdev->dev.of_node, "ports", i);
if (!port)
break;

for_each_child_of_node(port, ep) {
remote = of_graph_get_remote_port_parent(ep);
if (!remote || !of_device_is_available(remote)) {
of_node_put(remote);
continue;
} else if (!of_device_is_available(remote->parent)) {
dev_warn(&pdev->dev, "parent device of %s is not available\n",
remote->full_name);
of_node_put(remote);
continue;
}

component_match_add(&pdev->dev, &match, compare_of,
remote);
of_node_put(remote);
}
of_node_put(port);
}

2.1.2 component_match_add函數
該函數作用為動態擴展match的成員,並申請空間,使macth可以像數組一樣按[]操作。
函數傳入match的地址,一個比較函數compare,設備樹節點compare_data。
首先判斷match是否為空,不為空再判斷match下的alloc值與num值是否相等。alloc值為現在match下已申請的compare數組長度,num為match下compare數組已填入數據的數量。
如果判斷成功,則調用component_match_realloc函數,該函數重新申請空間。申請的compare數組值有如下規則:
如果match為空,則申請15個,加上match本身帶有的match[0],共16個。
如果match不為空,則申請16個。
然后將compare比較函數和設備樹節點compare_data存入compare數組中,num增加1。

void component_match_add(struct device *dev, struct component_match **matchptr,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component_match *match = *matchptr;

if (IS_ERR(match))
return;

if (!match || match->num == match->alloc) {
size_t new_size = match ? match->alloc + 16 : 15;

match = component_match_realloc(dev, match, new_size);

*matchptr = match;

if (IS_ERR(match))
return;
}

match->compare[match->num].fn = compare;
match->compare[match->num].data = compare_data;
match->num++;
}

2.1.3 component_master_add_with_match函數
首先定義了一個component_master_ops 對象,為設備相關操作函數回調,當該master下的所有設備都初始化完成后,調用該回調的bind指針。一般如下

static const struct component_master_ops imx_drm_ops = {
.bind = imx_drm_bind,
.unbind = imx_drm_unbind,
};

該函數傳入imx_drm_ops 指針和已填充完畢的match指針。
如果match指針不為空,則調用component_match_realloc函數,將match重新裁剪內存空間到實際數量所占用的內存值。

if (match) {··
/* Reallocate the match array for its true size */
match = component_match_realloc(dev, match, match->num);
if (IS_ERR(match))
return PTR_ERR(match);
}

申請master對象空間。
填入match和ops回調函數。
初始化一個components列表,保存以后需要掛在這里的component。
然后將master對象掛在masters列表中。

master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;

master->dev = dev;
master->ops = ops;
master->match = match;
INIT_LIST_HEAD(&master->components);

/* Add to the list of available masters. */
mutex_lock(&component_mutex);
list_add(&master->node, &masters);

然后調用try_to_bring_up_master函數,嘗試初始化該master。此函數下面討論。

完整的源碼如下。

int component_master_add_with_match(struct device *dev,
const struct component_master_ops *ops,
struct component_match *match)
{
struct master *master;
int ret;

if (ops->add_components && match)
return -EINVAL;

if (match) {
/* Reallocate the match array for its true size */
match = component_match_realloc(dev, match, match->num);
if (IS_ERR(match))
return PTR_ERR(match);
}

master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;

master->dev = dev;
master->ops = ops;
master->match = match;
INIT_LIST_HEAD(&master->components);

/* Add to the list of available masters. */
mutex_lock(&component_mutex);
list_add(&master->node, &masters);

ret = try_to_bring_up_master(master, NULL);

if (ret < 0) {
/* Delete off the list if we weren't successful */
list_del(&master->node);
kfree(master);
}
mutex_unlock(&component_mutex);

return ret < 0 ? ret : 0;
}

2.2 component設備的初始化流程
初始化特有數據,調用component_add函數注冊component。在該函數中調用try_to_bring_up_masters,函數中調用try_to_bring_up_master函數,嘗試初始化該component。

2.2.1 component的probe函數
static int imx_ldb_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &imx_ldb_ops);
}

2.2.2 component_add函數
先定義了component_ops對象,為設備相關操作函數回調,當初始化該設備時,調用該回調的bind指針。一般如下

static const struct component_ops ipu_crtc_ops = {
.bind = ipu_drm_bind,
.unbind = ipu_drm_unbind,
};

該函數傳入component_ops指針。
先申請component對象空間,將ops指針賦值到該對象中,並將該對象保存至component_list列表中。
最后調用try_to_bring_up_masters函數。

int component_add(struct device *dev, const struct component_ops *ops)
{
struct component *component;
int ret;

component = kzalloc(sizeof(*component), GFP_KERNEL);
if (!component)
return -ENOMEM;

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

dev_dbg(dev, "adding component (ops %ps)\n", ops);

mutex_lock(&component_mutex);
list_add_tail(&component->node, &component_list);

ret = try_to_bring_up_masters(component);
if (ret < 0) {
list_del(&component->node);

kfree(component);
}
mutex_unlock(&component_mutex);

return ret < 0 ? ret : 0;
}

2.2.3 try_to_bring_up_masters函數
該函數遍歷master列表,對每一個master調用try_to_bring_up_master函數,嘗試初始化該master。

static int try_to_bring_up_masters(struct component *component)
{
struct master *m;
int ret = 0;

list_for_each_entry(m, &masters, node) {
ret = try_to_bring_up_master(m, component);
if (ret != 0)
break;
}

return ret;
}

3 節點匹配流程
try_to_bring_up_master函數是關鍵函數,負責匹配節點。
如果master下的match中有一個成員通過component_master_add_child函數查找,遍歷了component_list列表的所有component,卻沒有匹配上,則返回-ENXIO(-2),則將該master下所有component分離,並退出。

如果對於master下的match的每一個成員,都返回0時,表示該master已全部找到所屬設備,則返回try_to_bring_up_master繼續執行。

master找到所有成員,但如果傳入參數component與傳入參數master不匹配,則把master下所有component分離,並退出。

如果匹配,調用master的bind回調。
在bind回調函數中,初始化master自身的資源,最后調用每個component的bind回調。回調成功后,置master的bound為true,至此,master綁定完畢,subsystem初始化完成。

3.1 try_to_bring_up_master函數
首先判斷是否綁定完畢,未綁定則調用find_components。

static int try_to_bring_up_master(struct master *master,
struct component *component)
{
int ret;

if (master->bound)
return 0;

/*
* Search the list of components, looking for components that
* belong to this master, and attach them to the master.
*/
if (find_components(master)) {
/* Failed to find all components */
ret = 0;
goto out;
}

if (component && component->master != master) {
ret = 0;
goto out;
}

if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
ret = -ENOMEM;
goto out;
}

/* Found all components */
ret = master->ops->bind(master->dev);
if (ret < 0) {
devres_release_group(master->dev, NULL);
dev_info(master->dev, "master bind failed: %d\n", ret);
goto out;
}

master->bound = true;
return 1;

out:
master_remove_components(master);

return ret;
}

3.2 find_components函數
遍歷之前master設備注冊的match數組,調用component_master_add_child函數。

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

if (!match) {
/*
* Search the list of components, looking for components that
* belong to this master, and attach them to the master.
*/
return master->ops->add_components(master->dev, master);
}

/*
* Scan the array of match functions and attach
* any components which are found to this master.
*/
for (i = 0; i < match->num; i++) {
ret = component_master_add_child(master,
match->compare[i].fn,
match->compare[i].data);
if (ret)
break;
}
return ret;
}


3.3 component_master_add_child函數
該函數遍歷component_list列表,c為列表中每一個成員。有如下幾種情況:
1 c有master,但c的master不為此時傳來的master,則繼續循環。
2 c的master就是此master,則調用master中match中的compare函數(此函數通過參數傳進來)。而且可以多次調用。然后返回。
3 c沒有master。調用compare函數(為第一次調用),如果compare返回對比成功,則調用component_attach_master函數。
如下,將此master付給c,並將c添加到master的components列表。

int component_master_add_child(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component *c;
int ret = -ENXIO;

list_for_each_entry(c, &component_list, node) {
if (c->master && c->master != master)
continue;

if (compare(c->dev, compare_data)) {
if (!c->master)
component_attach_master(master, c);
ret = 0;
break;
}
}

return ret;
}

3.4 component_attach_master函數
static void component_attach_master(struct master *master, struct component *c)
{
c->master = master;

list_add_tail(&c->master_node, &master->components);
}

4 設備的bind回調分析
匹配成功后,會進行各設備的回調,首先從master的bind回調開始,然后調用component_bind_all函數進行各子設備的bind回調。

4.1master的bind函數
static int imx_drm_bind(struct device *dev)
{
...
/* Now try and bind all our sub-components */
ret = component_bind_all(dev, drm);
if (ret)
goto err_kms;
...
}

4.2 component_bind_all函數
該函數通過傳入的dev,調用下划線master_find函數,遍歷masters列表,找到所屬的master。
遍歷master的components列表,調用component_bind。

int component_bind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
int ret = 0;

WARN_ON(!mutex_is_locked(&component_mutex));

master = __master_find(master_dev, NULL);
if (!master)
return -EINVAL;

list_for_each_entry(c, &master->components, master_node) {
ret = component_bind(c, master, data);
if (ret)
break;
}

if (ret != 0) {
list_for_each_entry_continue_reverse(c, &master->components,
master_node)
component_unbind(c, master, data);
}

return ret;
}

4.2 component_bind函數
關鍵調用為下面代碼,調用到各個設備的bind中。

static int component_bind(struct component *component, struct master *master,
void *data)
{
...
ret = component->ops->bind(component->dev, master->dev, data);
...
————————————————
版權聲明:本文為CSDN博主「shikivs」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/shikivs/java/article/details/103591971


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM