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;
}
三.总结
与以前的学习相互印证,好懂很多。
阅读(2609) | 评论(0) | 转发(1) |