现在的位置: 首页 > 技术文章 > 驱动开发 > 正文

Linux驱动之bus_register分析

2016年01月13日 驱动开发 ⁄ 共 6169字 ⁄ 字号 Linux驱动之bus_register分析已关闭评论 ⁄ 阅读 3,040 次

最近在温故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,
};

×