转载:https://blog.csdn.net/u010550992/article/details/50710567
http://www.techbulo.com/1903.html
上图说明了总线通过两个数据结构:devices_ket和driver_kset来管理注册在此总线上的所有的设备和驱动,为了方便遍历,linux增加了klist_devices和klist_drivers用来实现设备和驱动的遍历
/driver/base/bus.c
struct bus_type mini_bus_type ={
.name = "mini",
.match = mini_match
};
static int __init test_init(void)
{
return bus_register(&mini_bus_type);
}
bus_register:工作就是完成bus_type_private的初始化.创建 注册的这条总线需要的目录文件.
在这条总线目录下创建/device /driver 目录
初始化这条总线上的设备链表:struct klist klist_devices;
初始化这条总线上的驱动链表:struct klist klist_drivers;
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv; //是上面mini_bus_type的成员.
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus; //struct bus_type_private *p;所指向的内容动态分配.
bus->p = priv; //p关联到mini_bus_type
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //kobject对应一个目录,这个目录就是我们看到的总线名字/bus/mini
if (retval)
goto out;
//bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
priv->subsys.kobj.kset = bus_kset; //mini目录在bus下,即/bus/mini:初始化kset的成员kobject,为kset_register()做准备.
priv->subsys.kobj.ktype = &bus_ktype; //static struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, };
priv->drivers_autoprobe = 1; //设置该标志, 当有driver注册时,会自动匹配devices上的设备并用probe初始化,
// 当有device注册时,也同样找到driver并会初始化
//int bus_add_driver(struct device_driver *drv)
// if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); }
retval = kset_register(&priv->subsys); //注册kset,创建目录结构,以及层次关系 生成/bus/mini
if (retval)
goto out;
//kset_register(&priv->subsys);
//kobject_add_internal(&k->kobj);
//error = create_dir(kobj);
//error = sysfs_create_dir(kobj);
//&priv->subsys---->mini_bus_type->p->subsys
//priv->subsys.kobj.kset = bus_kset;
retval = bus_create_file(bus, &bus_attr_uevent); //mini目录下生成bus_attr_uevent属性文件
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL, //在mini下面创建一个mini/device,是mini这条总线的device的根目录.
&priv->subsys.kobj); //为什么在mini目录下? (device这个目录:kset->kobj) kset->kobj.parent = &priv->subsys.kobj;
//在某个目录下:kset->kob.kset = x 这个目录在x目录下,这个目录下面“还可有”目录
// kobj.parent = y 这个目录在y目录下,这个目录下面“没有”有目录
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL, //在mini下面创建一个mini/driver,是mini这条总线的driver的根目录.
&priv->subsys.kobj); //struct kset *drivers_kset 指针.
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化 mini_bus_type->p->klist_devices 就是初始化device的list_head
klist_init(&priv->klist_drivers, NULL, NULL); // 就是初始化driver的list_head
//device,driver注册都挂在对应的链表上.
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_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);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
struct bus_type {
const char *name; //总线名称
struct bus_attribute *bus_attrs; //总线属性(设备节点和这个有关系么?)
struct device_attribute *dev_attrs; //设备属性
struct driver_attribute *drv_attrs; //驱动属性
int (*match)(struct device *dev, struct device_driver *drv); //用于匹配总线下的dev和driver
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //用于总线环境变量的添加
int (*probe)(struct device *dev); //总线和驱动匹配成功调用,platform不是设备和驱动匹配才调用么?没搞清楚.
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev); //关机
int (*suspend)(struct device *dev, pm_message_t state); //挂起
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm; //电源管理.
struct bus_type_private *p; //将bus device sysfs关联起来,怎么关联的不明白呀
};
struct bus_type_private {
struct kset subsys; //sysfs
struct kset *drivers_kset; //bus目录下的 drvier子目录
struct kset *devices_kset; //bus目录下的 device子目录
struct klist klist_devices; //bus目录下的设备列表
struct klist klist_drivers; //bus目录下的驱动列表
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
} __attribute__ ((aligned (sizeof(void *))));
------------------------------------------------------------------------------------
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,
};