zoukankan      html  css  js  c++  java
  • 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
    ",
    			 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)
    ", 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
    ", 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);
    ...
    }

  • 相关阅读:
    "Automation 服务器不能创建对象" 的解决方法
    让DataGrid拥有单击回传事件并带回指定字段的值
    SQL 和TSQL学习(一)
    数据写入XML
    DATALIST分页存储过程
    The target assembly contains no service types. You may need to adjust the Code Access Security policy of this assembly." 目标程序集不包含服务类型。可能需要调整此程序集的代码访问
    字符串转日期
    C# 日期和时间正则表达式
    js根据输入日期显示该月的最后一天的日期[转]
    JavaScript试题【解析+答案】
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13819381.html
Copyright © 2011-2022 走看看