Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1069783
  • 博文数量: 71
  • 博客积分: 3078
  • 博客等级: 少校
  • 技术积分: 945
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-16 20:10
个人简介

此博客已停用 个人博客: Mangogeek.com

文章分类
文章存档

2016年(1)

2015年(32)

2014年(25)

2011年(13)

分类: LINUX

2014-09-03 08:01:48

原文地址:linux bus总线 作者:steven_miao

linux bus总线

谨以此文纪念过往的岁月

一.前言
在前面学习过如何去注册新的bus,那仅仅是应用,在该文中我们将深入去理解bus是如何注册,如何使用的。在该文中不再讲述kset,kobject等的关系,如何去理解这两者的关系,google或baidu一下。

二.bus的parent
所有的bus类型均会在linux的sysfs中留下痕迹,均会在/sys/bus中留下关于自己总线的信息。
2.1 /sys/bus文件夹得创建
在bus.c文件中buses_init函数会创建/sys/bus文件夹,该函数在系统初始化时被调用,那来看一下这个函数。
int __init buses_init(void)
{
    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
    if (!bus_kset)
        return -ENOMEM;
    return 0;
}
函数很简单,其会调用kset_create_and_add来实现创建文件夹。
参数说明:
name       : 在/sys下创建文件夹的名称
uevent_ops : 这个参数比较难理解,其实就是一些事件处理函数。以上面的bus_uevent_ops为例。
                        static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
                        {
                            struct kobj_type *ktype = get_ktype(kobj);
                        
                            if (ktype == &bus_ktype)
                                return 1;
                            return 0;
                        }
                        
                        static struct kset_uevent_ops bus_uevent_ops = {
                            .filter = bus_uevent_filter,
                        };
                        其就提供了一种事件处理方法,即过滤事件处理。
parent_kobj : 这个参数为kset所指向的父kobj。
struct kset *kset_create_and_add(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)
{
    struct kset *kset;
    int error;

    kset = kset_create(name, uevent_ops, parent_kobj);
    if (!kset)
        return NULL;
    error = kset_register(kset);
    if (error) {
        kfree(kset);
        return NULL;
    }
    return kset;
}
kset只是kobject的容器,而kobject才是真正的实体,而每一个kobject则会对应一个文件夹,以/sys/bus为例。
static struct kset *kset_create(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)
{
    struct kset *kset;

    kset = kzalloc(sizeof(*kset), GFP_KERNEL);
    if (!kset)
        return NULL;
    kobject_set_name(&kset->kobj, name); --设置kobject名称,也可以认为是文件夹的名称
    kset->uevent_ops = uevent_ops;
    kset->kobj.parent = parent_kobj;

    kset->kobj.ktype = &kset_ktype;      --设置实体类型,现在创建的kset为所有bus的父kset,所以该kset的父实体为NULL,而其kset类型为通用kset类型,而非bus_ktype。
    kset->kobj.kset = NULL;

    return kset;   --返回新的kset,该kset为所有bus的父kset。
}
上述函数为create kset,创建完成后需要注册kset。
int kset_register(struct kset *k)
{
    int err;

    if (!k)
        return -EINVAL;

    kset_init(k);           -- kset_init -> kobject_init_internal
    err = kobject_add_internal(&k->kobj);
    if (err)
        return err;
    kobject_uevent(&k->kobj, KOBJ_ADD);
    return 0;
}

void kset_init(struct kset *k)
{
    kobject_init_internal(&k->kobj);   --初始化实体
    INIT_LIST_HEAD(&k->list);          --初始化kset链表
    spin_lock_init(&k->list_lock);     --初始自旋锁
}

static void kobject_init_internal(struct kobject *kobj)
{
    if (!kobj)
        return;
    kref_init(&kobj->kref);            --初始化kobject计数器
    INIT_LIST_HEAD(&kobj->entry);      --初始化链表,用于将该kobj挂载到kset的链表中。
    kobj->state_in_sysfs = 0;          --下面的几个flag用于标示kobject初始化程度。
    kobj->state_add_uevent_sent = 0;
    kobj->state_remove_uevent_sent = 0;
    kobj->state_initialized = 1;
}
在kset init complete后,就是创建/sysfs中了,说白了就是创建一文件夹。仍以/sys/bus为例。
static int kobject_add_internal(struct kobject *kobj)
{
    int error = 0;
    struct kobject *parent;
    parent = kobject_get(kobj->parent);      --记住这里的实体的父实体为空,而对于某一个类型的bus而言,其父实体即为bus kobject。下面在针对某一bus再说。
    if (kobj->kset) {                      --如果kobj->kset存在则将该kobject加入到kset链表中
        if (!parent)                         --如果该实体的parent kobject不存在,则设置该kobject的parnet为其容器的实体。
            parent = kobject_get(&kobj->kset->kobj);
        kobj_kset_join(kobj);
        kobj->parent = parent;
    }
    error = create_dir(kobj);             --创建文件夹,以实体名命名的文件夹
    kobj->state_in_sysfs = 1;
    return error;
}

static void kobj_kset_join(struct kobject *kobj)
{
    if (!kobj->kset)
        return;

    kset_get(kobj->kset);        
    spin_lock(&kobj->kset->list_lock);
    list_add_tail(&kobj->entry, &kobj->kset->list);
    spin_unlock(&kobj->kset->list_lock);
}
到此dir被创建了。其文件夹的创建于kobject和kset息息相关。其实kobject以及kset存在都是为了更好的去管理设备。

2.2 注册bus
在前文中bus注册,对bus_register这个函数稍稍讲了一下,现在再回过头去仔细看这个函数。
以前对这个结构体没有仔细看,现在在看一下。
struct bus_type_private {
    struct kset subsys;           --即该bus的kset,其parent会指向bus_kset
    struct kset *drivers_kset;    --该kset亦如总bus创建一样,为该bus下的driver创建文件夹以及父实体容器。
    struct kset *devices_kset;    --类似上面
    struct klist klist_devices;   
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;        --该bus_type_private指向的bus
};
int bus_register(struct bus_type *bus)
{
    int retval;
    struct bus_type_private *priv;

    priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->bus = bus;
    bus->p = priv;

    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  --设置kobject name,也是在/sys/bus/ 下创建文件夹的名称。
    if (retval)
        goto out;

    priv->subsys.kobj.kset = bus_kset;                               --指向bus_kset
    priv->subsys.kobj.ktype = &bus_ktype;                            --实体类型
    priv->drivers_autoprobe = 1;                                     --自动探测。

    retval = kset_register(&priv->subsys);                           --注册kset,这里的实体的parent是存在的,会将该实体的链表头添加如父容器的链表中。
    if (retval)
        goto out;

    retval = bus_create_file(bus, &bus_attr_uevent);                 --创建属性文件,在该总线文件夹下会有一个名为uevent的文件。
    if (retval)
        goto bus_uevent_fail;

    priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);  --drvier和device的父实体均为该总线的实体。
    if (!priv->devices_kset) {
        retval = -ENOMEM;
        goto bus_devices_fail;
    }

    priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);
    if (!priv->drivers_kset) {
        retval = -ENOMEM;
        goto bus_drivers_fail;
    }

    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
    klist_init(&priv->klist_drivers, NULL, NULL);

    retval = add_probe_files(bus);           --添加probe的属性文件
    if (retval)
        goto bus_probe_files_fail;

    retval = bus_add_attrs(bus);            --添加额外的属性文件
    if (retval)
        goto bus_attrs_fail;

    pr_debug("bus: '%s': registered\n", bus->name);
    return 0;

bus_attrs_fail:
    remove_probe_files(bus);
bus_probe_files_fail:
    kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
    kset_unregister(bus->p->devices_kset);
bus_devices_fail:
    bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
    kset_unregister(&bus->p->subsys);
    kfree(bus->p);
out:
    return retval;
}
为bus创建属性文件。
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
    int error;
    if (bus_get(bus)) {
        error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
        bus_put(bus);
    } else
        error = -EINVAL;
    return error;
}

三.总结
与以前的学习相互印证,好懂很多。

阅读(2119) | 评论(0) | 转发(0) |
0

上一篇:linux cdev详解

下一篇:linux class device

给主人留下些什么吧!~~