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);
    ...
    ————————————————
    版权声明:本文为CSDN博主「shikivs」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/shikivs/java/article/details/103591971

  • 相关阅读:
    Android 开发 深入理解Handler、Looper、Messagequeue 转载
    Android 开发 Handler的基本使用
    Java 学习 注解
    Android 开发 AlarmManager 定时器
    Android 开发 框架系列 百度语音合成
    Android 开发 框架系列 Google的ORM框架 Room
    Android 开发 VectorDrawable 矢量图 (三)矢量图动画
    Android 开发 VectorDrawable 矢量图 (二)了解矢量图属性与绘制
    Android 开发 VectorDrawable 矢量图 (一)了解Android矢量图与获取矢量图
    Android 开发 知晓各种id信息 获取线程ID、activityID、内核ID
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/12718217.html
Copyright © 2011-2022 走看看