基于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);
...
}

linux kernel component框架分析相关推荐

  1. Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)

    <DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...

  2. Linux PCIe驱动框架分析(第二章)

    目录 项目背景 1. 概述 2. 数据结构 3. 流程分析 3.1 设备驱动模型 3.2 初始化 3.2.1 pci_bus_match 3.2.2 pci_device_probe 3.3 枚举 项 ...

  3. 深入分析Linux PCI驱动框架分析(二)

    说明: Kernel版本:4.14 ARM64处理器 使用工具:Source Insight 3.5, Visio 1. 概述 本文将分析Linux PCI子系统的框架,主要围绕Linux PCI子系 ...

  4. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  5. Linux内核学习(六):linux kernel的Kconfig分析

    Linux内核学习(六):linux kernel的Kconfig分析 前面我们知道了makefile文件,makefile文件会结合配置文件.config来进行操作.这里就再来看看生成内核.conf ...

  6. Linux ALSA音频框架分析五:HDA Driver分析

    Linux ALSA音频框架分析五:HDA Driver分析 一 概述 HDA(High Definition Audio)是intel设计的用来取代AC97的音频标准,硬件架构上由hda dodec ...

  7. Linux i2c驱动框架分析 (二)

    Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...

  8. Linux Kernel Oops异常分析

    0.linux内核异常常用分析方法 异常地址是否在0附近,确认是否是空指针解引用问题 异常地址是否在iomem映射区,确认是否是设备访问总线异常问题,如PCI异常导致的地址访问异常 异常地址是否在st ...

  9. OS相关驱动 Linux USB驱动框架分析

    初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发 ...

最新文章

  1. Elasticsearch 6.3.1、Head插件 安装及配置
  2. Spark集群搭建【Spark+Hadoop+Scala+Zookeeper】
  3. 【c语言】蓝桥杯算法训练 1的个数
  4. thinkPHP学习笔记(2)
  5. signature=4bfbf257ebc393e8ee3071d531b76778,(Mannose).
  6. 人脸检测中,如何构建输入图像金字塔
  7. 回调java 简书_web3j函数回调使用详解
  8. DOS网络命令之 tracert
  9. Web---JSP-EL表达式
  10. 两个时间之间是多少小时_那是两个小时我不会回来
  11. HTML标签之间有什么区别 div 和span?/span
  12. mvc jquery ajax分页实例,jQuery Ajax自定义分页组件(jquery.loehpagerv1.0)实例详解,mvcpagerajax分页...
  13. 加/减/乘/除 下的取余
  14. 数列科技开源全链路压测平台Takin-总结
  15. 在vue中使用echarts实现飞机航线 水滴图 词云图
  16. 项目管理工具——PDCA管理循环
  17. 第8章 Stata主成分分析与因子分析
  18. Oracle 错误疑难解决方案和总结
  19. 系统架构师论文-图书馆网络应用体系安全设计
  20. 想运营好拼多多店铺,做好产品规划必不可少!

热门文章

  1. 职称申报时如何选择职称评委会?
  2. 计网实验原理-TCP/UDP套接字编程
  3. Windows Server 2008 R2 标准版升级到企业版
  4. Flash视频和HTML5视频
  5. 从 PDF 中提取矢量图标 (Extract vector icons from PDF)
  6. 对“判断内六角规格项目”一些改进与完善
  7. 动感地带业务发短信开通及退订代码大全
  8. 充电宝越用越耐用吗?什么样的充电宝好用耐用
  9. java EE与java SE的区分
  10. ubuntuv20启动界面美化_手机图标美化amp;通知小红点美化