Linux 内核学习(7)---- Linux Component 框架

Linux 内核 component 框架背景

Linux 内核中驱动,某些驱动之间加载是要按照一定的顺序的;kernel 中的 component 框架是为了 subsystem 能够按照一定的初始化顺序初始化设备而提出的架构
subsystem 有较多的设备模块驱动组成,内核在加载这些驱动时间是不确定的,所以需要component 框架来保证加载顺序
component 架构一般用于DRM 显示驱动架构和ALSA音频驱动架构

struct component 框架 关键结构

component_struct.jpg
struct master {
    struct list_head node;
    bool bound;

    const struct component_master_ops *ops;
    struct device *dev;
    struct component_match *match;
    struct dentry *dentry;
};

struct component {
    struct list_head node;
    struct master *master;
    bool bound;

    const struct component_ops *ops;
    int subcomponent;
    struct device *dev;
};

struct component_ops {
// component_bind_all
    int (*bind)(struct device *comp, struct device *master,
            void *master_data);
//component_unbind_all
    void (*unbind)(struct device *comp, struct device *master,
               void *master_data);
};

component 中的 master 被看成是一个统筹的角色,component 表示在这个复杂驱动中不可或缺的组件,每个component 都会和一个 platform_device 结构关联起来

刚开始component 会将自己注册进入系统,并尝试唤醒master 来统筹(component 也不了解自己是不是最后一个注册的,所有有义务唤醒master)
最后master 把自己注册进入系统,根据master 自己定义的对比方法找到所有的 component,调用component 的回调函数

component的注册

驱动都是以probe 的形式加载的,但是probe 函数没有特别明确加载顺序,做为 subsystem 的设备驱动,需要在probe 函数中将自己抽象为 component 设备注册进系统,等待master 来统筹各个component 的加载顺序
使用 component_addcomponent_del 函数注册和注销
注册和注销,是内核组织形式的一种,component 是以链表的形式去组织的,在系统启后就已经注册了两个链表头,用来组织master 和 component

static LIST_HEAD(component_list); //component list head
static LIST_HEAD(masters);//master list head
static int __component_add(struct device *dev, const struct component_ops *ops,
    int subcomponent)
{
    struct component *component;
    int ret;

    // alloc memory for component
    component = kzalloc(sizeof(*component), GFP_KERNEL);
    if (!component)
        return -ENOMEM; 
    // 初始化 component, 并且为 component 绑定一个操作合集, 包含 bind 和 unbind 函数
    component->ops = ops;
    component->dev = dev;
    component->subcomponent = subcomponent;

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

    // 将 component 加入 component_list 链表
    list_add_tail(&component->node, &component_list);

    //尝试唤醒master 来回调各个 component 的 bind 
    ret = try_to_bring_up_masters(component);
    if (ret < 0) {
        if (component->master)
            remove_component(component->master, component);
        list_del(&component->node);

        kfree(component);
    }
    mutex_unlock(&component_mutex);
    return ret < 0 ? ret : 0;
}

主要步骤:

  1. 为component 对象申请内存
  2. 初始化 component,并且为component 添加一个ops 操作合集,包含 bind 和 unbind 函数
  3. 将component 加入 component_list 链表
  4. 尝试唤醒master 去回调各个component 的 bind

struct component match 的创建

struct component_match_array {
    void *data;
    int (*compare)(struct device *, void *);
    int (*compare_typed)(struct device *, int, void *);
    void (*release)(struct device *, void *);
    struct component *component;
    bool duplicate;
};

struct component_match {
    size_t alloc;
    size_t num;
    struct component_match_array *compare;
};

component_match 是一个用于匹配的对象,substem 的所有platform_device,注册成 component 组件,相关的component 都硬件保存在component_match 对象中,最后受到 master 对象的统筹

一个master 对应一个 component_match ,一个component_match 对应一组 component_match_array
使用 component_match_add_release 添加一个带release 函数的 component_match

void component_match_add_release(struct device *master,struct component_match **matchptr,
void (*release)(struct device *, void *), int (*compare)(struct device *, void *), void *compare_data)


static void __component_match_add(struct device *master,
    struct component_match **matchptr,
    void (*release)(struct device *, void *),
    int (*compare)(struct device *, void *),
    int (*compare_typed)(struct device *, int, void *),
    void *compare_data)
{
    struct component_match *match = *matchptr;
    if (IS_ERR(match))
        return;
    // alloc memory for component match 
    if (!match) {
        match = devres_alloc(devm_component_match_release,
                     sizeof(*match), GFP_KERNEL);
        if (!match) {
            *matchptr = ERR_PTR(-ENOMEM);
            return;
        }
        devres_add(master, match);
        *matchptr = match;
    }
    // alloc memory for component match 
    if (match->num == match->alloc) {
        size_t new_size = match->alloc + 16;
        int ret;
        ret = component_match_realloc(master, match, new_size);
        if (ret) {
            *matchptr = ERR_PTR(ret);
            return;
        }
    }
    // 填充一个 component_match array, 包含 compare 函数 和 compare 数据
    match->compare[match->num].compare = compare;
    match->compare[match->num].compare_typed = compare_typed;
    match->compare[match->num].release = release;
    match->compare[match->num].data = compare_data;
    match->compare[match->num].component = NULL;
    match->num++;
}
  1. 如果传进来的component_match对象是NULL,创建一个新的 component_match 对象
  2. 如果一组component_match对象已经满了,或者空的,新增16个空位
  3. 填充一个component_match_array,里面包含了很重要的compare函数和compare 数据
    注意此时的 component 指针设置为NULL,component_match_array 中的元素并没有绑定component

component_match_array 对应一个component,component_match 对象是关联的 component 对象的集合,等待master 对象统一触发
component_match_array 里的 component 对象还没有指定,但是指定了每个 component 的compare 函数,在master 注册的时候,遍历全局的 component 链表,调用 compare 函数,将符合的要求的component 对象加入component_match_array

master 对象的注册

master 作为一个统筹的角色,在所有的 component 被注册进入系统后,就可以将master 对象注册进入系统
通过 component_master_add_with_match 将master 注册进入系统

int component_master_add_with_match(struct device *dev, const struct component_master_ops *ops, struct component_match *match)

master 对象的定义:

struct master {
    struct list_head node;
    bool bound;

    const struct component_master_ops *ops;
    struct device *dev;
    struct component_match *match;
    struct dentry *dentry;
};

master 对应也对应于一个 platform_device,component_master_add_with_match 传入的参数是 master 对应的驱动设备,master 的 操作函数集合和 对应的component_match 对象

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

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

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

    master->dev = dev;
    master->ops = ops;
    master->match = match;

    component_master_debugfs_add(master);
    /* 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)
        free_master(master);

    mutex_unlock(&component_mutex);

    return ret < 0 ? ret : 0;
}
  1. 将master 加入系统之前重新规划了component_match指向的数组,修改成为真实存在的数量
  2. 为master 对象申请空间,绑定master 操作,绑定 component_match
  3. 将master 对象加入全局的链表,最后尝试唤醒 master 对象

master 对象的唤醒

  1. master 注册的时候唤醒:
static int try_to_bring_up_master(struct master *master,
    struct component *component)
{
    int ret;
    dev_dbg(master->dev, "trying to bring up master\n");

    if (find_components(master)) {
        dev_dbg(master->dev, "master has incomplete components\n");
        return 0;
    }
    if (component && component->master != master) {
        dev_dbg(master->dev, "master is not for this component (%s)\n",
            dev_name(component->dev));
        return 0;
    }
    if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
        return -ENOMEM;

    /* 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);
        return ret;
    }
    master->bound = true;
    return 1;
}

其中关键的函数是find_components,find_components会遍历全局 component链表,如果已经和 master 绑定就跳过,如果发现一个孤立的component,就会调用component_match_array 中的 compare 函数来对比此component 是否满足要求,如果满足要求,就将当前的 component 和 component_match_array 的component 指针关联

static int find_components(struct master *master)
{
    struct component_match *match = master->match;
    size_t i;
    int ret = 0;
    /*
     * Scan the array of match functions and attach
     * any components which are found to this master.
     */
    for (i = 0; i < match->num; i++) {
        struct component_match_array *mc = &match->compare[i];
        struct component *c;

        dev_dbg(master->dev, "Looking for component %zu\n", i);
        if (match->compare[i].component)
            continue;
        
        c = find_component(master, mc);
        if (!c) {
            ret = -ENXIO;
            break;
        }
        dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);
        /* Attach this component to the master */
        match->compare[i].duplicate = !!c->master;
        match->compare[i].component = c;
        c->master = master;
    }
    return ret;
}

确保find_components 函数执行完成后,执行 master 对象中的 bind 函数,注意此时 component 对象只是经过compare函数加入了 component macth array,component 对象本身的 bind 函数并没有执行到
一般要在master 对象的bind 函数中,执行 component_bind_all 函数,执行component macth array 所有component 的 bind 函数

component 的 compare 函数

component 的compare 函数是 能否加入component macth array的关键,一般使用 component_match_add_release函数,注意其中最后一个参数 compare_data

void component_match_add_release(struct device *master,
    struct component_match **matchptr,
    void (*release)(struct device *, void *),
    int (*compare)(struct device *, void *), void *compare_data)
{
    __component_match_add(master, matchptr, release, compare, NULL,
                  compare_data);
}

最后 find_component 执行时将 component的 device 参数 和 compare_data作为参数,调用compare 函数

static struct component *find_component(struct master *master,
    struct component_match_array *mc)
{
    struct component *c;

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

        if (mc->compare && mc->compare(c->dev, mc->data))
            return c;

        if (mc->compare_typed &&
            mc->compare_typed(c->dev, c->subcomponent, mc->data))
            return c;
    }

    return NULL;
}

参考博客

https://blog.csdn.net/weixin_44551105/article/details/127315884

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容