IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linux驱动之bus_register分析

    techbulo发表于 2016-01-12 23:24:53
    love 0

    最近在温故Linux的总线设备驱动模型,下面分析一下bus_register的详细调用过程及行为目的:

    kobject,kset,ktype。这三个结构联合起来一起构成了整个设备模型的基石,请看下面两篇文章:

    《设备模型之kobject,kset及其关系》

    《设备模型之总线,驱动,设备》

    devices_ket和driver_kset

    devices_ket和driver_kset

    上图说明了总线通过两个数据结构:devices_ket和driver_kset来管理注册在此总线上的所有的设备和驱动,为了方便遍历,linux增加了klist_devices和klist_drivers用来实现设备和驱动的遍历。

    我们来跟踪一下代码来看下详细的操作,注册一个总线的接口为bus_register()

    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);

    if (retval)

    goto out;

     

    priv->subsys.kobj.kset = bus_kset; 

    priv->subsys.kobj.ktype = &bus_ktype;

    priv->drivers_autoprobe = 1;

     

    retval = kset_register(&priv->subsys);

    if (retval)

    goto out;

    首先,先为struct bus_type的私有区分配空间,然后将其和struct bus_type关联起来.由于struct bus_type也要在sysfs文件中表示一个节点,因此,它也内嵌也一个kset的结构.这就是priv->subsys.

    首先,它为这个kset的名称赋值为bus的名称,然后将priv->subsys.kobj.kset指向bus_kset. priv->subsys.kobj.ktype指向bus_ktype;然后调用kset_reqister()将priv->subsys注册.这里涉及到的接口都在之前分析过.注册过后,应该会在bus_kset所表示的目录下创建一个总线名称的目录.并且用户空间的hotplug应该会检测到一个add事件.我们来看一下bus_kset到底指向的是什么:

    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

    从此可以看出.这个bus_kset在sysfs中的结点就是/sys/bus.在这里注册的struct bus_types就会在/sys/bus/下面出现;

    bus_create_file()就是在priv->subsys.kobj的这个kobject上建立一个普通属性的文件.这个文件的属性对应在bus_attr_uevent.读写操作对应在priv->subsys.kobj.ktype中.我们到后面才统一分析bus注册时候的文件创建

    priv->devices_kset = kset_create_and_add("devices", NULL,

    &priv->subsys.kobj);

    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);

    这段代码会在bus所在的目录下建立两个目录,分别为devices和drivers.并初始化挂载设备和驱动的链表

    retval = add_probe_files(bus);

    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_attr_drivers_probe, bus_attr_drivers_autoprobe.注册bus_type中的属性建立文件

    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;

    }

    这段代码为出错处理;

    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 = kset_create(name, uevent_ops, parent_kobj);

    if (!kset)

    return NULL;

    //注册kset

    error = kset_register(kset);

    if (error)

    {

    //如果注册失败,释放kset

    kfree(kset);

    return NULL;

    }

    return kset;

    }

    Kset_create()用来创建一个struct kset结构.代码如下:

    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);

    kset->uevent_ops = uevent_ops;

    kset->kobj.parent = parent_kobj;

     

    kset->kobj.ktype = &kset_ktype;

    kset->kobj.kset = NULL;

     

    return kset;

    }

    我们注意,在这里创建kset时.为其内嵌的kobject指定其struct kobj_type ktype结构为kset_ktype.这个结构的定义如下:

    static struct kobj_type kset_ktype = {

    .sysfs_ops    = &kobj_sysfs_ops,

    .release = kset_release,

    };

    属性文件的读写操作全部都包含在sysfs_ops成员里.kobj_sysfs_ops的定义如下:

    struct sysfs_ops kobj_sysfs_ops = {

    .show    = kobj_attr_show,

    .store   = kobj_attr_store,

    };

    创建好了kset之后,会调用kset_register().这个函数就是kset操作的核心代码了.如下:

    int kset_register(struct kset *k)

    {

    int err;

     

    if (!k)

    return -EINVAL;

     

    kset_init(k);

    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);             //只是对kobj中的成员变量做一些赋值的初始化
    INIT_LIST_HEAD(&k->list);
    spin_lock_init(&k->list_lock);
    }

     

     

    static int kobject_add_internal(struct kobject *kobj)

    {

    int error = 0;

    struct kobject *parent;

     

    if (!kobj)

    return -ENOENT;

    //如果kobject的名字为空.退出

    if (!kobj->name || !kobj->name[0]) {

    pr_debug("kobject: (%p): attempted to be registered with empty "

    "name!/n", kobj);

    WARN_ON(1);

    return -EINVAL;

    }

     

    //取kobject的父结点

    parent = kobject_get(kobj->parent);

    //如果kobject的父结点没有指定,就将kset->kobject做为它的父结点

    /* join kset if set, use it as parent if we do not already have one */

    if (kobj->kset) {

    if (!parent)

    parent = kobject_get(&kobj->kset->kobj);

    kobj_kset_join(kobj);

    kobj->parent = parent;

    }

    //调试用

    pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",

    kobject_name(kobj), kobj, __FUNCTION__,

    parent ? kobject_name(parent) : "<NULL>",

    kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

    //在sysfs中创建kobject的相关元素

    error = create_dir(kobj);

    if (error) {

    //v如果创建失败。减少相关的引用计数

    kobj_kset_leave(kobj);

    kobject_put(parent);

    kobj->parent = NULL;

     

    /* be noisy on error issues */

    if (error == -EEXIST)

    printk(KERN_ERR "%s failed for %s with "

    "-EEXIST, don't try to register things with "

    "the same name in the same directory./n",

    __FUNCTION__, kobject_name(kobj));

    else

    printk(KERN_ERR "%s failed for %s (%d)/n",

    __FUNCTION__, kobject_name(kobj), error);

    dump_stack();

    } else

    //如果创建成功。将state_in_sysfs建为1。表示该object已经在sysfs中了

    kobj->state_in_sysfs = 1;

     

    return error;

    }

    这段代码比较简单,它主要完成kobject父结点的判断和选定,然后再调用create_dir()在sysfs创建相关信息。该函数代码如下:

    static int create_dir(struct kobject *kobj)

    {

    int error = 0;

    if (kobject_name(kobj)) {

    //为kobject创建目录

    error = sysfs_create_dir(kobj);

    if (!error) {

    //为kobject->ktype中的属性创建文件

    error = populate_dir(kobj);

    if (error)

    sysfs_remove_dir(kobj);

    }

    }

    return error;

    }

     

    int sysfs_create_dir(struct kobject * kobj)

    {

    struct sysfs_dirent *parent_sd, *sd;

    int error = 0;

     

    BUG_ON(!kobj);

    /*如果kobject的parnet存在。就在目录点的目录下创建这个目录。如果没有父结点不存在,就在/sys下面创建结点。*/

    if (kobj->parent)

    parent_sd = kobj->parent->sd;

    else

    parent_sd = &sysfs_root;

     

    //在sysfs中创建目录

    //create_dir()就是在sysfs中创建目录的接口,在之前已经详细分析过了

    error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

    if (!error)

    kobj->sd = sd;

    return error;

    }

    接着看为kobject->ktype中的属性创建文件。这是在populate_dir()中完成的。代码如下:

    static int populate_dir(struct kobject *kobj)

    {

    struct kobj_type *t = get_ktype(kobj);

    struct attribute *attr;

    int error = 0;

    int i;

     

    if (t && t->default_attrs) {

    for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

    error = sysfs_create_file(kobj, attr);

    if (error)

    break;

    }

    }

    return error;

    }

    这段代码比较简单。它遍历ktype中的属性。然后为其建立文件。请注意:文件的操作最后都会回溯到ktype->sysfs_ops的show和store这两个函数中.

    假如对于上面的bus_register()函数传入的参数为:

    struct bus_type ldd_bus_type = {
    .name = "ldd",
    .match = ldd_match,
    .hotplug  = ldd_hotplug,
    };



沪ICP备19023445号-2号
友情链接