>> kobject
-
struct kobject {
-
const char *name; // 对象名称
-
struct list_head entry; // kobject的链表入口,该kobject将添加到其所属的kset的list链表中
-
struct kobject *parent; // 父对象
-
struct kset *kset; // 所属的kset
-
struct kobj_type *ktype; // 所属的ktype
-
struct sysfs_dirent *sd; // 在sysfs中的目录结构,在哪里被设置的?
-
struct kref kref; // 引用计数,kobject的引用计数在什么时候会增减?
-
-
// 位域形式定义状态变量
-
unsigned int state_initialized:1; // 标记kobject是否被初始化
-
unsigned int state_in_sysfs:1; // 标记是否已存在于sysfs文件系统中
-
unsigned int state_add_uevent_sent:1; // 标记ADD事件是否被sent过
-
unsigned int state_remove_uevent_sent:1; // 标记REMOVE事件是否被sent过
-
unsigned int uevent_suppress:1; // 这一位为1时,这个kobject不产生任何 uevent
-
};
-
-
struct kobj_type {
-
void (*release)(struct kobject *kobj); // kobject的析构函数
-
const struct sysfs_ops *sysfs_ops; // kobject的属性读写函数
-
/*struct sysfs_ops {
-
ssize_t (*show)(struct kobject *, struct attribute *,char *);
-
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
-
};*/
-
-
struct attribute **default_attrs; // 属性数组(二级指针)
-
/*struct attribute {
-
const char *name;
-
mode_t mode;
-
};*/
-
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); // 新特性,还未研究过
-
const void *(*namespace)(struct kobject *kobj); // 新特性,还未研究过
-
}
kobject_init_and_add 函数用于"初始化"(不是"创建")并添加一个 kobject , 参数fmt一般就是kobject的name, 注意这个函数不会修改kobject的 kset,
应在这个函数调用之前设置好 kobj->kset 。
-
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
-
struct kobject *parent, const char *fmt, ...)
-
{
-
va_list args;
-
int retval;
-
-
kobject_init(kobj, ktype); // 初始化 entry、 ktype和引用计数,并将state_initialized设置为1
-
-
va_start(args, fmt);
-
retval = kobject_add_varg(kobj, parent, fmt, args);
-
// kobject_add_varg内部会先调用kobject_set_name_vargs为kobjcet设置名称,
-
// 再调用kobject_add_internal(kobj)在sysfs中建立kobject的相关信息
-
-
va_end(args);
-
-
return retval;
-
}
kobject_add_internal(kobj)中,几个关键的步骤:
1. 设置kobj的父对象,并将kobj加入到其所属的kset的链表中(通过调用kobj_kset_join(kobj));
关于父对象,如果设置了kobj->kset,但未设置kobj->parent时,就将kobj->kset->kobj作为kobj的父对象。
2. 调用create_dir(kobj)为kobj在sysfs中创建目录,这个函数内部会调用sysfs_create_dir(kobj)
和populate_dir(kobj),前者用于创建目录并设置kobj->sd,后者则用于创建属性文件。
根据kobj的父对象可确定其在sysfs中的上级目录,如果kobj的parent是NULL,则将sysfs的顶级目录作为该
kobj的上级目录。
3. 为kobj创建了目录后,将 kobj->state_in_sysfs 设为 1 。
kobject_del 函数用于释放一个kobject。
-
void kobject_del(struct kobject *kobj)
-
{
-
if (!kobj)
-
return;
-
-
sysfs_remove_dir(kobj); // 移除目录
-
kobj->state_in_sysfs = 0; // 取消state_in_sysfs标记
-
kobj_kset_leave(kobj); // 将kobj从其所属的kset的list列表中移除
-
kobject_put(kobj->parent); // 减少引用计数
-
kobj->parent = NULL; // 父对象设为 NULL
-
}
kobject_del中重要的两步: 1. 移除kobj在sysfs中的目录; 2.取消kobj与其所属的kset的联系。注意这个函数中并不会调用析构函数(ktype->release())
>> kset
-
struct kset {
-
struct list_head list; // the list of all kobjects for this kset
-
spinlock_t list_lock; // 操作 list 的锁
-
struct kobject kobj; // kset本身的kobject对象
-
const struct kset_uevent_ops *uevent_ops; // 用户事件操作
-
};
kset_create_and_add函数用于"创建"并添加一个 kset。这个函数会设置 kset 的parent,但不设置这个kset所属的上一层 kset
-
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);
-
// 创建一个 kset, 并设置其 kobj.name, kobj.ktype=&kset_ktype,(kset专有的ktype,内核提供)
-
// kobj.kset=NULL ,以及 kobj.parent 。
-
if (!kset)
-
return NULL;
-
error = kset_register(kset); // 注册kset。
-
// 内部先调用kset_init(kset)初始化kset
-
// 再调用kobject_add_internal(&kset->kobj)在sysfs中建立kset->kobj的相关信息
-
// 再调用kobject_uevent(&k->kobj, KOBJ_ADD)向用户空间产生热插拔事件(事件类型为ADD)
-
if (error) {
-
kfree(kset);
-
return NULL;
-
}
-
return 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;
-
}
因为添加了kset会产生一个事件,这个事件是通过用户空间的hotplug程序处理的,这就是kset明显不
同于kobject的地方:注册一个kobject不会产生事件,只有注册kset才会。
之后会详细研究一下kobject_uevent函数.这对于我们研究hotplug的深层机理是很有帮助的.
kset的注销函数,只是减一下引用计数
-
void kset_unregister(struct kset *k)
-
{
-
if (!k)
-
return;
-
kobject_put(&k->kobj);
-
}
>> ktype (kobject的属性操作)
kype 决定了一个 kobject的所有属性及属性的操作方法。以 kset_ktype 为例,看看这个结构体该怎么用,以及kobject的属性应如何操作。
前面讲解kset时提到的kset_create函数中会将 kset->kobj.ktype 设置为 &kset_ktype ,相关的定义有:
-
static struct kobj_type kset_ktype = {
-
.sysfs_ops = &kobj_sysfs_ops,
-
.release = kset_release, // 析构函数
-
};
-
static void kset_release(struct kobject *kobj)
-
{
-
struct kset *kset = container_of(kobj, struct kset, kobj);
-
pr_debug("kobject: '%s' (%p): %s\n",
-
kobject_name(kobj), kobj, __func__);
-
kfree(kset); //释放kset的内存
-
}
-
const struct sysfs_ops kobj_sysfs_ops = { // kobject默认的属性操作函数
-
.show = kobj_attr_show,
-
.store = kobj_attr_store,
-
};
-
/* default kobject attribute operations */ // kobject默认的属性读函数
-
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
-
char *buf)
-
{
-
struct kobj_attribute *kattr;
-
ssize_t ret = -EIO;
-
-
kattr = container_of(attr, struct kobj_attribute, attr); // 出现了kobj_attribute结构体
-
if (kattr->show) // 回调kobj_attribute结构体的show函数
-
ret = kattr->show(kobj, kattr, buf);
-
return ret;
-
}
这里看到了另一个结构体,kobj_attribute,利用这个结构体可以为 kobject 的每个属性定制一个独特的读写方法,
它的定义如下:
-
struct kobj_attribute {
-
struct attribute attr; // 属性实体
-
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
-
char *buf); // 属性独特的读函数
-
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
-
const char *buf, size_t count); //属性独特的写函数
-
};
取出kobj_attribute结构体中attr成员的地址,放到ktype结构体的default_attrs数组中,再将ktype结构体的sysfs_ops->show
函数设置为kobj_attr_show,属性的读操作就会自动回调到为该属性定制的独特读函数上。属性的store函数类似。
内核中 __ATTR 宏可以帮助快速地建立一个kobj_attribute结构体的实例:
-
#define __ATTR(_name,_mode,_show,_store) { \
-
.attr = {.name = __stringify(_name), .mode = _mode }, \
-
.show = _show, \
-
.store = _store, \
-
}
>> struct kset_uevent_ops 和 kobject_uevent 函数
kset结构中有一个uevent_ops成员,kset_register(...)函数的最后也调用了kobject_uevent 函数,下面就看下与 uevent相关的
结构体和函数。
-
struct kset_uevent_ops {
-
int (* const filter)(struct kset *kset, struct kobject *kobj); // 事件过滤,返回0时说明事件被过滤,不再处理该事件
-
const char *(* const name)(struct kset *kset, struct kobject *kobj); // 返回kset和kobj所对应的子系统的名称
-
int (* const uevent)(struct kset *kset, struct kobject *kobj, // 为 用户空间事件 设置kset和kobj特有的额外的环境变量(基础环境变量之外的变量)
-
struct kobj_uevent_env *env);
-
};
-
enum kobject_action { // 事件类型
-
KOBJ_ADD,
-
KOBJ_REMOVE,
-
KOBJ_CHANGE,
-
KOBJ_MOVE,
-
KOBJ_ONLINE,
-
KOBJ_OFFLINE,
-
KOBJ_MAX
-
};
kobject_uevent 函数的作用是在 kobj 这个对象上产生一个 action 事件,其内部主要是对kobject_uevent_env的调用。
-
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
-
{
-
return kobject_uevent_env(kobj, action, NULL);
-
}
-
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
-
char *envp_ext[]) // 参数envp_ext是uevent所需的额外的环境变量
-
{
-
...
-
...
-
-
top_kobj = kobj;
-
while (!top_kobj->kset && top_kobj->parent)
-
top_kobj = top_kobj->parent;
-
if (!top_kobj->kset) {
-
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
-
"without kset!\n", kobject_name(kobj), kobj,
-
__func__);
-
return -EINVAL;
-
}
-
kset = top_kobj->kset;
-
uevent_ops = kset->uevent_ops; // 获得 uevent_ops
-
-
1 . 因为对事件的处理函数包含在kobject->kset-> uevent_ops中.要处理事件,就必须要找到上层的
-
一个不为空的kset.上面的代码就是顺着kobject->parent找不到一个不为空的kset.如果不存在
-
这样的kset.就退出;
-
-
/* skip the event, if uevent_suppress is set*/
-
if (kobj->uevent_suppress) {
-
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
-
"caused the event to drop!\n",
-
kobject_name(kobj), kobj, __func__);
-
return 0;
-
}
-
/* skip the event, if the filter returns zero. */
-
if (uevent_ops && uevent_ops->filter)
-
if (!uevent_ops->filter(kset, kobj)) {
-
pr_debug("kobject: '%s' (%p): %s: filter function "
-
"caused the event to drop!\n",
-
kobject_name(kobj), kobj, __func__);
-
return 0;
-
}
-
-
2. 对事件进行过滤。如果 kobj 不接受 uevent (由kobj->uevent_suppress决定),或者
-
该类型的uevent被 filter() 过滤掉了,就不再处理该事件。
-
-
-
/* originating subsystem */
-
if (uevent_ops && uevent_ops->name)
-
subsystem = uevent_ops->name(kset, kobj);
-
else
-
subsystem = kobject_name(&kset->kobj); // 如果uevent_ops的name函数未设置,则用top_kobj所属的kset的名称作为子系统名
-
if (!subsystem) {
-
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
-
"event to drop!\n", kobject_name(kobj), kobj,
-
__func__);
-
return 0;
-
}
-
3. 获得子系统(subsystem)的名称(之后需要将它设置到环境变量中)。
-
-
/* environment buffer */
-
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
-
if (!env)
-
return -ENOMEM;
-
-
4. 申请环境变量所需的内存,主要是申请一个kobj_uevent_env结构体,这个结构体能容纳最多 32 个变量和最多2048字节的缓存空间。
-
-
-
/* complete object path */
-
devpath = kobject_get_path(kobj, GFP_KERNEL); // 获得 kobj 在sysfs中的目录
-
if (!devpath) {
-
retval = -ENOENT;
-
goto exit;
-
}
-
/* default keys */ 设置3个基本环境变量,分别是 动作ACTION、设备路径devpath、子系统subsystem.
-
retval = add_uevent_var(env, "ACTION=%s", action_string);
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
-
if (retval)
-
goto exit;
-
/* keys passed in from the caller */ 设置调用本函数时传递进来的额外的环境变量
-
if (envp_ext) {
-
for (i = 0; envp_ext[i]; i++) {
-
retval = add_uevent_var(env, "%s", envp_ext[i]);
-
if (retval)
-
goto exit;
-
}
-
}
-
/* let the kset specific function add its stuff */// 让kset指定的uevent_ops结构添加特有的环境变量
-
if (uevent_ops && uevent_ops->uevent) {
-
retval = uevent_ops->uevent(kset, kobj, env);
-
if (retval) {
-
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
-
"%d\n", kobject_name(kobj), kobj,
-
__func__, retval);
-
goto exit;
-
}
-
}
-
if (action == KOBJ_ADD)
-
kobj->state_add_uevent_sent = 1; // 根据事件类型设置 kobj 的state_add_uevent_sent和state_remove_uevent_sent状态
-
else if (action == KOBJ_REMOVE)
-
kobj->state_remove_uevent_sent = 1;
-
/* we will send an event, so request a new sequence number */
-
spin_lock(&sequence_lock);
-
seq = ++uevent_seqnum; // 每发送一个uevent, 全局变量uevent_seqnum就加1,并将它的值添加到环境变量中
-
spin_unlock(&sequence_lock);
-
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
-
if (retval)
-
goto exit;
-
5. 添加各种所需的环境变量到kobj_uevent_env结构体中。
-
-
#if defined(CONFIG_NET)
-
...
-
...
-
#endif
-
/* call uevent_helper, usually only enabled during early boot */
-
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
-
char *argv [3];
-
-
argv [0] = uevent_helper;
-
argv [1] = (char *)subsystem; // 子系统作为参数
-
argv [2] = NULL;
-
// 添加最后两个环境变量 , HOME 和 PATH
-
retval = add_uevent_var(env, "HOME=/");
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env,
-
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
-
if (retval)
-
goto exit;
-
-
retval = call_usermodehelper(argv[0], argv, // 调用用户空间的程序
-
env->envp, UMH_WAIT_EXEC);
-
/*enum umh_wait {
-
UMH_NO_WAIT = -1, // don't wait at all
-
UMH_WAIT_EXEC = 0, // wait for the exec, but not the process
-
UMH_WAIT_PROC = 1, // wait for the process to complete
-
};*/
-
-
}
-
6. 调用用户空间的uevent_helper(hotplug?),以子系统名称为参数,hotplug处理程序中的参数和环境变量就是这么来的
-
-
-
exit:
-
kfree(devpath);
-
kfree(env);
-
return retval;
-
}
>> bus_type
-
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);
-
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
-
int (*probe)(struct device *dev);
-
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 subsys_private *p;
-
};
-
struct subsys_private {
-
struct kset subsys;
-
struct kset *devices_kset;
-
-
struct kset *drivers_kset;
-
struct klist klist_devices;
-
struct klist klist_drivers;
-
struct blocking_notifier_head bus_notifier;
-
unsigned int drivers_autoprobe:1;
-
struct bus_type *bus;
-
-
struct list_head class_interfaces;
-
struct kset glue_dirs;
-
struct mutex class_mutex;
-
struct class *class;
-
};
-
#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
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/下面出现.
-
-
retval = bus_create_file(bus, &bus_attr_uevent); // 添加bus_attr_uevent属性,他对应的属性文件名为 uevent 。
-
if (retval)
-
goto bus_uevent_fail;
-
-
bus_create_file()就是在priv->subsys.kobj的这个kobject上建立一个普通属性的文件.
-
这个文件的属性对应在bus_attr_uevent.读写操作对应在priv->subsys.ktype中.
-
我们到后面才统一分析bus注册时候的文件创建
-
-
// drivers和devices两个kset的 uevent_ops 都是NULL,他们的kset也是NULL,
-
// 但parent是subsys.kobj(即总线 bus_type 对应的kobjct)。而subsys.kobj的
-
// kset是bus_kset。 因此drivers和devices这两个kset的 top_kobj(参见kobject_uevent_env函数)
-
// 也是bus_kset,他们产生的 uevent 也都由bus_kset的uevent_ops,即bus_uevent_ops处理。
-
// 在/sys/bus/<bus>/drivers目录下面有所有挂在该总线上的驱动,而/sys/bus/<bus>/devices目录下
-
// 只是指向/sys/devices/..的链接
-
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); // 这个函数内部只创建 bus_attr_drivers_probe和
-
// bus_attr_drivers_autoprobe两个属性文件,它们对应的文件名
-
// 分别是drivers_probe和drivers_autoprobe。
-
if (retval)
-
goto bus_probe_files_fail;
-
-
retval = bus_add_attrs(bus); // 这个函数内部创建 bus->bus_attrs 属性数组中的所有属性文件,这个数组最后一个属性的名称是NULL。
-
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_type创建的三个重要属性是 uevent, drivers_probe 和 drivers_probe 。
uevent 没有读操作,只有写操作,用于在用户空间手动产生事件,如echo add > event就会产生一个add的事件;
drivers_probe 没有读操作,只有写操作,将用户输入该文件的设备名称对应的设备与驱动匹配一次。
drivers_autoprobe 既有读操作又有写操作。文件中的值是1时,表示使能自动匹配;为0时禁止自动匹配。
>> device
-
struct device {
-
struct device *parent;
-
struct device_private *p;
-
-
struct kobject kobj;
-
const char *init_name; /* initial name of the device */
-
...
-
...
-
-
struct bus_type *bus; /* type of bus device is on */
-
struct device_driver *driver; /* which driver has allocated this
-
device */
-
...
-
...
-
-
dev_t devt; /* dev_t, creates the sysfs "dev" */
-
...
-
...
-
struct klist_node knode_class;
-
struct class *class;
-
...
-
...
-
void (*release)(struct device *dev);
-
};
-
-
struct device_private {
-
struct klist klist_children;
-
struct klist_node knode_parent;
-
struct klist_node knode_driver;
-
struct klist_node knode_bus;
-
void *driver_data;
-
struct device *device;
-
};
-
-
-
-
int device_register(struct device *dev)
-
{
-
device_initialize(dev);
-
return device_add(dev);
-
}
-
-
-
void device_initialize(struct device *dev)
-
{
-
dev->kobj.kset = devices_kset; // 重要的两步:设置kset和ktype
-
kobject_init(&dev->kobj, &device_ktype);
-
...
-
...
-
spin_lock_init(&dev->devres_lock);
-
INIT_LIST_HEAD(&dev->devres_head);
-
...
-
...
-
}
-
-
-
-
-
int device_add(struct device *dev)
-
{
-
...
-
...
-
if (dev->init_name) {
-
dev_set_name(dev, "%s", dev->init_name); // 最终设置的是 dev->kobj的name
-
dev->init_name = NULL;
-
}
-
...
-
...
-
parent = get_device(dev->parent);
-
setup_parent(dev, parent);
-
...
-
...
-
/* we require the name to be set before, and pass NULL */
-
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
-
1. 建立dev->kobj的目录
-
...
-
...
-
error = device_create_file(dev, &uevent_attr); // 创建 uevent 属性文件 。device的 uevent属性文件的show函数(读操作)
-
// 会显示出由设备对应的kset即devices_kset所产生的环境变量,这些环境
-
// 变量就是 devices_kset->uevent_ops->uevent()的返回值;它的store函数
-
// (写操作)则用于手动产生事件
-
if (error)
-
goto attrError;
-
if (MAJOR(dev->devt)) {
-
error = device_create_file(dev, &devt_attr); // 如果device存在设备号,则创建设备号属性文件
-
if (error)
-
goto ueventattrError;
-
error = device_create_sys_dev_entry(dev); // 在 /sys/dev 目录下创建符号链接;如果一个设备有所属的class,
-
// 则根据class中指定的dev_kobj决定是在/sys/dev/char还是/sys/dev/block
-
// 下创建链接。链接的名称是设备号
-
if (error)
-
goto devtattrError;
-
devtmpfs_create_node(dev); // 目测是创建 /dev 目录下的设备节点文件?
-
}
-
error = device_add_class_symlinks(dev); // 创建 /sys/class 目录下到device的链接
-
if (error)
-
goto SymlinkError;
-
error = device_add_attrs(dev); //涉及到了group的部分,暂不讨论
-
if (error)
-
goto AttrsError;
-
error = bus_add_device(dev); /* 作用三点:
-
* - Add device's bus attributes.
-
* - Create links to device's bus. 创建 /sys/bus 目录下到device的链接
-
* - Add the device to its bus's list of devices.
-
*/
-
if (error)
-
goto BusError;
-
2. 建立属性文件以及链接,还有设备节点文件
-
-
-
...
-
...
-
kobject_uevent(&dev->kobj, KOBJ_ADD); // 产生ADD事件
-
bus_probe_device(dev); // 将设备自动与挂在总线上面的驱动进行匹配,后面会详细讲这个函数
-
if (parent)
-
klist_add_tail(&dev->p->knode_parent,
-
&parent->p->klist_children); // 在父设备的子设备链表中添加klist节点
-
if (dev->class) {
-
...
-
...
-
}
-
3. 产生ADD事件,并尝试将device与driver进行匹配
-
-
done:
-
put_device(dev);
-
return error;
-
DPMError:
-
bus_remove_device(dev);
-
BusError:
-
...
-
...
-
goto done;
-
}
>> device 与 driver 的匹配过程
device_add(dev)函数的最后会调用bus_probe_device(dev)将dev与驱动匹配,下面就来分析一下这个过程。
bus_probe_device(dev)是一个很重要的函数。它将设备自动与挂在总线上面的驱动进行匹配。代码如下:
-
void bus_probe_device(struct device *dev)
-
{
-
struct bus_type *bus = dev->bus;
-
int ret;
-
-
if (bus && bus->p->drivers_autoprobe) { // 可见只有当 bus_type 的 autoprobe属性为1时才自动匹配驱动和设备
-
ret = device_attach(dev);
-
WARN_ON(ret < 0);
-
}
-
}
-
int device_attach(struct device *dev) //bus目录下的drivers_probe 文件被写入正确的设备名时就会调用这个函数
-
{
-
int ret = 0;
-
-
device_lock(dev);
-
if (dev->driver) {
-
if (klist_node_attached(&dev->p->knode_driver)) {
-
ret = 1;
-
goto out_unlock;
-
}
-
ret = device_bind_driver(dev); // 对于设备自己已经指定驱动的情况,只需要将其直接和驱动绑定即可
-
/* int device_bind_driver(struct device *dev)
-
{
-
int ret;
-
-
ret = driver_sysfs_add(dev);
-
if (!ret)
-
driver_bound(dev);
-
return ret;
-
}*/ // driver_sysfs_add和driver_bound这两个函数在后面讲really_probe()时会提到
-
if (ret == 0)
-
ret = 1;
-
else {
-
dev->driver = NULL;
-
ret = 0;
-
}
-
} else {
-
pm_runtime_get_noresume(dev);
-
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-
//如果没有指定驱动,就遍历匹配总线之上的驱动。将总线上的每个驱动
-
// 与device尝试匹配一次(通过__device_attach函数进行尝试匹配)
-
pm_runtime_put_sync(dev);
-
}
-
out_unlock:
-
device_unlock(dev);
-
return ret;
-
}
-
-
static int __device_attach(struct device_driver *drv, void *data)
-
{
-
struct device *dev = data;
-
-
if (!driver_match_device(drv, dev)) // 如果不匹配match,直接返回0
-
return 0; /* static inline int driver_match_device(struct device_driver *drv,
-
struct device *dev)
-
{
-
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
-
} */
-
-
return driver_probe_device(drv, dev); // 匹配的情况下(match成功),再尝试 probe
-
}
-
-
int driver_probe_device(struct device_driver *drv, struct device *dev)
-
{
-
int ret = 0;
-
-
if (!device_is_registered(dev)) //如果设备没有注册到sysfs之中, 就直接返回。
-
return -ENODEV; /* static inline int device_is_registered(struct device *dev)
-
{
-
return dev->kobj.state_in_sysfs;
-
} */
-
-
-
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
-
drv->bus->name, __func__, dev_name(dev), drv->name);
-
-
pm_runtime_get_noresume(dev);
-
pm_runtime_barrier(dev);
-
ret = really_probe(dev, drv); // 进一步检查是否匹配
-
pm_runtime_put_sync(dev);
-
-
return ret;
-
}
-
-
-
static int really_probe(struct device *dev, struct device_driver *drv)
-
{
-
int ret = 0;
-
-
dev->driver = drv;
-
if (driver_sysfs_add(dev)) {
-
// driver_sysfs_add()建立几个符号链接,它返回0时代表创建链接成功。这几个链接分别为:
-
// 1:在驱动目录下建立一个到设备的同名链接
-
// 2:在设备目录下建立一个名为driver的到驱动的链接
-
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
-
__func__, dev_name(dev));
-
goto probe_failed;
-
}
-
-
然后,再调用总线的probe函数。如果总线的此函数不存在。就会调用驱动的probe函数。
-
如果匹配成功,返回0.如果不成功,就会跳转到probe_failed
-
这一步可以看出,系统会优先使用总线的 probe 函数。
-
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev);
-
if (ret)
-
goto probe_failed;
-
} else if (drv->probe) {
-
ret = drv->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
-
-
driver_bound(dev); // 到这里。设备和驱动已经匹配成功,调用driver_bound()将其关联起来。
-
// 在这个函数里会将设备加至驱动的设备链表。相关的代码如下:
-
// klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
-
-
ret = 1; //至此,这个匹配过程已经圆满结束了。返回1
-
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
-
drv->bus->name, __func__, dev_name(dev), drv->name);
-
goto done;
-
-
probe_failed:
-
...
-
...
-
}
>> device_driver
-
struct device_driver {
-
const char *name;
-
struct bus_type *bus;
-
-
struct module *owner;
-
const char *mod_name; /* used for built-in modules */
-
-
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ // 是否使能手动绑定/解绑驱动和设备
-
-
const struct of_device_id *of_match_table;
-
-
int (*probe) (struct device *dev);
-
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 attribute_group **groups;
-
-
const struct dev_pm_ops *pm;
-
-
struct driver_private *p;
-
};
-
-
struct driver_private {
-
struct kobject kobj;
-
struct klist klist_devices;
-
struct klist_node knode_bus;
-
struct module_kobject *mkobj;
-
struct device_driver *driver;
-
};
-
-
-
int driver_register(struct device_driver *drv)
-
{
-
int ret;
-
struct device_driver *other;
-
-
BUG_ON(!drv->bus->p);
-
-
if ((drv->bus->probe && drv->probe) ||
-
(drv->bus->remove && drv->remove) ||
-
(drv->bus->shutdown && drv->shutdown))
-
printk(KERN_WARNING "Driver '%s' needs updating - please use "
-
"bus_type methods\n", drv->name);
-
// ↑如果设备与总线定义了相同的成员的函数。内核是优先使用bus中定义的.这一点
-
// 我们在分析device注册的时候已经分析过。所以。这里打印出警告信息,用来提醒代码编写者。
-
-
other = driver_find(drv->name, drv->bus);
-
if (other) {
-
put_driver(other);
-
printk(KERN_ERR "Error: Driver '%s' is already registered, "
-
"aborting...\n", drv->name);
-
return -EBUSY;
-
}
-
-
ret = bus_add_driver(drv); // 我们主要分析这个函数
-
if (ret)
-
return ret;
-
ret = driver_add_groups(drv, drv->groups); //在这里,忽略有关group的东西。
-
if (ret)
-
bus_remove_driver(drv);
-
return ret;
-
}
-
-
-
int bus_add_driver(struct device_driver *drv)
-
{
-
struct bus_type *bus;
-
struct driver_private *priv;
-
int error = 0;
-
-
bus = bus_get(drv->bus);
-
if (!bus)
-
return -EINVAL;
-
-
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
-
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-
if (!priv) {
-
error = -ENOMEM;
-
goto out_put_bus;
-
}
-
klist_init(&priv->klist_devices, NULL, NULL);
-
priv->driver = drv;
-
drv->p = priv;
-
priv->kobj.kset = bus->p->drivers_kset;
-
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
-
"%s", drv->name);
-
// ↑初始化驱动的driver_private域。使其内嵌的kobject的kset指bus中的drivers_kset.
-
// 这样,这个内嵌的kobject所生成的目录就会存在于bus对应目录的driver目录之下。
-
// 这里还要注意的是,为内嵌kobject指定的ktype是driver_ktype.属性文件的读写操
-
// 作都回回溯到struct driver_attribute中。
-
-
1. ↑初始化kobj,主要是其ktype和kset.
-
-
if (error)
-
goto out_unregister;
-
-
if (drv->bus->p->drivers_autoprobe) {
-
//如果总线允许自动进行匹配。就会调用driver_attach()进行这个自己匹配过程。
-
// driver_attach()与前面讲的device_attach()的机制类似,最后也是通过调用
-
// driver_probe_device(drv,dev)来匹配设备和驱动
-
error = driver_attach(drv);
-
if (error)
-
goto out_unregister;
-
}
-
2.↑匹配驱动和设备
-
-
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //将驱动挂到bus对应的驱动链表
-
module_add_driver(drv->owner, drv);
-
3. 将驱动挂到bus对应的驱动链表
-
-
error = driver_create_file(drv, &driver_attr_uevent); // 创建uevent属性文件,只能写不能读
-
if (error) {
-
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
-
__func__, drv->name);
-
}
-
error = driver_add_attrs(bus, drv); // 为bus中指定的与driver相关的属性生成属性文件,
-
// 所有挂在这个bus上的驱动都应有这些属性。
-
if (error) {
-
/* How the hell do we get out of this pickle? Give up */
-
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
-
__func__, drv->name);
-
}
-
-
if (!drv->suppress_bind_attrs) {
-
error = add_bind_files(drv); // 生成属性为driver_attr_unbind和driver_attr_bind的属性文件,
-
// 两个属性都是只能写不能读的,向该属性文件中写入一个device名,
-
// 就会将该device与driver进行解绑或绑定(通过调用driver_probe_device)
-
if (error) {
-
/* Ditto */
-
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
-
__func__, drv->name);
-
}
-
}
-
4. 添加属性文件
-
-
kobject_uevent(&priv->kobj, KOBJ_ADD); // 产生ADD事件
-
5. 产生ADD事件
-
-
return 0;
-
-
// 错误处理
-
...
-
...
-
}
>> bus\device\driver 的 事件uevent处理
前面提到过,所有的kobject产生的uevent都由其所属的kset的uevent_ops 处理。
bus 的kset是全局的bus_kset: 在buses_init()函数中,全局的bus_kset的uevent_ops被设为了bus_uevent_ops,
-
static const struct kset_uevent_ops bus_uevent_ops = {
-
.filter = bus_uevent_filter, // bus_uevent_ops 只指定了filter函数
-
};
-
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
-
{
-
struct kobj_type *ktype = get_ktype(kobj);
-
if (ktype == &bus_ktype)
-
return 1; // 过滤掉非总线类型的kobject产生的事件
-
return 0;
-
}
device的kset是全局的devices_kset: 在devices_init()函数中,全局的devices_kset的uevent_ops被设为了device_uevent_ops;
-
static const struct kset_uevent_ops device_uevent_ops = {
-
.filter = dev_uevent_filter,
-
.name = dev_uevent_name,
-
.uevent = dev_uevent,
-
};
-
static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
-
{
-
// 过滤掉类型是非device的kobject的事件;如果产生事件的device没有所属的总线或类,其产生的事件也将被过滤
-
struct kobj_type *ktype = get_ktype(kobj);
-
-
if (ktype == &device_ktype) {
-
struct device *dev = to_dev(kobj);
-
if (dev->bus)
-
return 1;
-
if (dev->class)
-
return 1;
-
}
-
return 0;
-
}
-
static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
-
{
-
struct device *dev = to_dev(kobj);
-
-
if (dev->bus)
-
return dev->bus->name; // 如果设备有自己的总线,则以总线名称作为子系统名称返回
-
if (dev->class)
-
return dev->class->name; // 如果设备没有自己的总线但有所属的类,则将类名作为子系统名称返回
-
return NULL; // 类和总线都可以作为子系统
-
}
-
static int dev_uevent(struct kset *kset, struct kobject *kobj,
-
struct kobj_uevent_env *env)
-
{
-
struct device *dev = to_dev(kobj);
-
int retval = 0;
-
-
// 如果设备有设备号,则导出与设备节点文件相关的环境变量,如主次设备号、设备节点文件名等
-
/* add device node properties if present */
-
if (MAJOR(dev->devt)) {
-
const char *tmp;
-
const char *name;
-
mode_t mode = 0;
-
-
add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
-
add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
-
name = device_get_devnode(dev, &mode, &tmp);
-
if (name) {
-
add_uevent_var(env, "DEVNAME=%s", name);
-
kfree(tmp);
-
if (mode)
-
add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
-
}
-
}
-
//添加设备type和driver相关的环境变量
-
if (dev->type && dev->type->name)
-
add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
-
-
if (dev->driver)
-
add_uevent_var(env, "DRIVER=%s", dev->driver->name);
-
// 导出设备所属的bus特有的环境变量
-
/* have the bus specific function add its stuff */
-
if (dev->bus && dev->bus->uevent) {
-
retval = dev->bus->uevent(dev, env);
-
if (retval)
-
pr_debug("device: '%s': %s: bus uevent() returned %d\n",
-
dev_name(dev), __func__, retval);
-
}
-
// 导出设备所属的class特有的环境变量
-
/* have the class specific function add its stuff */
-
if (dev->class && dev->class->dev_uevent) {
-
retval = dev->class->dev_uevent(dev, env);
-
if (retval)
-
pr_debug("device: '%s': %s: class uevent() "
-
"returned %d\n", dev_name(dev),
-
__func__, retval);
-
}
-
// 导出设备所属的type特有的环境变量
-
/* have the device type specific function add its stuff */
-
if (dev->type && dev->type->uevent) {
-
retval = dev->type->uevent(dev, env);
-
if (retval)
-
pr_debug("device: '%s': %s: dev_type uevent() "
-
"returned %d\n", dev_name(dev),
-
__func__, retval);
-
}
-
-
return retval;
-
}
driver的kset是其所属的bus的drivers_kset成员(非全局,不同总线上的驱动有不同的kset): 在bus_register()函数中,bus的drivers_kset成员的uevent_ops被设为了NULL;
>> class
-
struct class {
-
const char *name;
-
struct module *owner;
-
-
struct class_attribute *class_attrs;
-
struct device_attribute *dev_attrs;
-
struct bin_attribute *dev_bin_attrs;
-
struct kobject *dev_kobj;
-
-
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
-
char *(*devnode)(struct device *dev, mode_t *mode);
-
-
void (*class_release)(struct class *class);
-
void (*dev_release)(struct device *dev);
-
-
int (*suspend)(struct device *dev, pm_message_t state);
-
int (*resume)(struct device *dev);
-
-
const struct kobj_ns_type_operations *ns_type;
-
const void *(*namespace)(struct device *dev);
-
-
const struct dev_pm_ops *pm;
-
-
struct subsys_private *p;
-
};
对比一下,class和bus_type结构体有很多相似之处,而且他们都包含一个subsys_private类型的私有数据,
总线和类都是子系统级别。一个class就是一个子系统
类的注册
-
int __class_register(struct class *cls, struct lock_class_key *key)
-
{
-
struct subsys_private *cp;
-
int error;
-
-
pr_debug("device class '%s': registering\n", cls->name);
-
-
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
-
if (!cp)
-
return -ENOMEM;
-
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
-
INIT_LIST_HEAD(&cp->class_interfaces);
-
kset_init(&cp->glue_dirs);
-
__mutex_init(&cp->class_mutex, "struct class mutex", key);
-
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
-
if (error) {
-
kfree(cp);
-
return error;
-
}
-
1. 对子系统cp做必要的初始化,将子系统名称设置为类名
-
-
-
/* set the default /sys/dev directory for devices of this class */
-
if (!cls->dev_kobj)
-
cls->dev_kobj = sysfs_dev_char_kobj;
-
2. 如果cls->dev_kobj没有指定,则设置本类中的设备在/sys/dev下的默认目录为char。
-
/sys/dev目录下通常只有两个子目录,分别是char和block,他们分别对应sysfs_dev_char_kobj和
-
sysfs_dev_block_kobj这两个kobject对象,在devices_init函数中,有如下几句:
-
* dev_kobj = kobject_create_and_add("dev", NULL); // 创建了/sys/dev目录
-
* sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
-
* // 创建了/sys/dev/block目录
-
* sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
-
* // 创建了/sys/dev/char目录
-
-
-
#if defined(CONFIG_BLOCK)
-
/* let the block class directory show up in the root of sysfs */
-
if (!sysfs_deprecated || cls != &block_class)
-
cp->subsys.kobj.kset = class_kset;
-
#else
-
cp->subsys.kobj.kset = class_kset;
-
#endif
-
3. 设置子系统的kset为class_kset。在classes_init函数中,class_kset被设置:
-
* class_kset = kset_create_and_add("class", NULL, NULL); // 创建了/sys/class目录
-
-
-
cp->subsys.kobj.ktype = &class_ktype;
-
4. 设置子系统的ktype为class_ktype,class_ktype中的release函数就是回调类class的class_release函数,
-
属性读写函数就是回调class_attribute结构的属性读写函数;
-
-
cp->class = cls;
-
cls->p = cp;
-
5.绑定子系统与类
-
-
error = kset_register(&cp->subsys);
-
if (error) {
-
kfree(cp);
-
return error;
-
}
-
6. 注册子系统
-
-
error = add_class_attrs(class_get(cls));
-
class_put(cls);
-
7. 创建属于这个类的属性文件
-
-
return error;
-
}
创建一个class只需要提供该class的名称和所属模块
-
struct class *__class_create(struct module *owner, const char *name,
-
struct lock_class_key *key)
-
{
-
struct class *cls;
-
int retval;
-
-
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
-
if (!cls) {
-
retval = -ENOMEM;
-
goto error;
-
}
-
-
// 设置类的名称和模块,以及析构函数。
-
cls->name = name;
-
cls->owner = owner;
-
cls->class_release = class_create_release;
-
/*static void class_create_release(struct class *cls)
-
{
-
pr_debug("%s called for %s\n", __func__, cls->name);
-
kfree(cls);
-
}*/
-
-
// 注册类
-
retval = __class_register(cls, key);
-
if (retval)
-
goto error;
-
-
return cls;
-
-
error:
-
kfree(cls);
-
return ERR_PTR(retval);
-
}
内核中有一个函数 device_create()可以快速创建一个device,这个函数需要制定device所属的类。
为了更清楚device与class的关系,我们来看一下这个函数。用这个函数创建一个device时,需要
提供device所属的类、它的父设备(可有可无)、设备号、设备名等
-
/**
-
* device_create - creates a device and registers it with sysfs
-
* @class: pointer to the struct class that this device should be registered to
-
* @parent: pointer to the parent struct device of this new device, if any
-
* @devt: the dev_t for the char device to be added
-
* @drvdata: the data to be added to the device for callbacks 要添加到device中供回调用的数据,设备的驱动所需的特有数据
-
* @fmt: string for the device's name 指定设备名称的字符串
-
*
-
* This function can be used by char device classes. A struct device
-
* will be created in sysfs, registered to the specified class.这个函数被字符类的设备使用
-
*
-
* A "dev" file will be created, showing the dev_t for the device, if
-
* the dev_t is not 0,0.
-
* If a pointer to a parent struct device is passed in, the newly created
-
* struct device will be a child of that device in sysfs.
-
* The pointer to the struct device will be returned from the call.
-
* Any further sysfs files that might be required can be created using this
-
* pointer.
-
*
-
* Returns &struct device pointer on success, or ERR_PTR() on error.
-
*
-
* Note: the struct class passed to this function must have previously
-
* been created with a call to class_create().传递到这个函数中的class参数必须是用class_create()函数创建的
-
*/
-
struct device *device_create(struct class *class, struct device *parent,
-
dev_t devt, void *drvdata, const char *fmt, ...)
-
{
-
va_list vargs;
-
struct device *dev;
-
-
va_start(vargs, fmt);
-
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
-
va_end(vargs);
-
return dev;
-
}
-
-
struct device *device_create_vargs(struct class *class, struct device *parent,
-
dev_t devt, void *drvdata, const char *fmt,
-
va_list args)
-
{
-
struct device *dev = NULL;
-
int retval = -ENODEV;
-
-
if (class == NULL || IS_ERR(class))
-
goto error;
-
-
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-
if (!dev) {
-
retval = -ENOMEM;
-
goto error;
-
}
-
-
// 根据穿进来的参数设置device的设备号,设备名,类等信息
-
dev->devt = devt;
-
dev->class = class;
-
dev->parent = parent;
-
dev->release = device_create_release;
-
/*static void device_create_release(struct device *dev)
-
{
-
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-
kfree(dev);
-
}*/
-
dev_set_drvdata(dev, drvdata);
-
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
-
if (retval)
-
goto error;
-
-
// 注册设备
-
retval = device_register(dev);
-
if (retval)
-
goto error;
-
-
return dev;
-
-
error:
-
put_device(dev);
-
return ERR_PTR(retval);
-
}
>> tips:
1. kobject 中虽然记录了 父对象 的指针,但并未记录 子对象 列表,所以通过一个 kobject 可以
获得其父对象,却无法知道它的子对象有哪些;
2. 设置了 kobj->kset 后,再调用kobj_kset_join(kobj)就可以将 kobj 加入到其所属的kset的list链
表中了,二者的关系即建立;kobj_kset_leave(kobj)将kobj从其所属的kset的list列表中移除
3. bus\device\driver 的 uevent 属性的读写权限和作用比较:
bus\driver 的uevent属性都是只写的,用于手动产生事件
device 的uevent属性是可写可读的,写入操作用于手动产生事件,读取操作用于
显示产生事件的设备特有的环境变量(devices_kset->uevent_ops->uevent()所产生的环境变量)
4. bus\device\driver 中出现的klist,以及klist链表与kobject层次的联系
klist结构代表一个链表,klist_node代表链表中的一个节点。
klist链表与kobject组成的层次结构是一致的,kobject系统主要用于建立sysfs目录结构,klist系统
则更详细地记录了设备、驱动和总线之间的层次关系,方便三者之间的交叉访问。
bus_type 有两个链表 :
klist_devices 挂在该总线上的设备组成的链表
klist_drivers 挂在该总线上的驱动组成的链表
device_driver有一个链表和一个节点:
klist_devices 与该驱动绑定的设备组成的链表
knode_bus 该驱动在其所属总线的驱动链表中的节点(在bus_add_driver()函数中设置此节点)
device 有一个链表和4个节点:
klist_children 该设备的子设备组成的链表
knode_parent 该设备在其父设备的子设备链表中的节点(在device_add()中设置此节点)
knode_driver 该设备在其绑定的驱动的设备链表中的节点(在device_bind_driver()或really_probe()中通过调用driver_bound()设置此节点)
knode_bus 该设备再起所属总线的设备链表中的节点(在device_add()中通过调用bus_add_device()设置此节点)
knode_class 该设备在其所属类中的节点(暂不研究这个)
5. 关于之前提到的 group(属性组attribute_group),就是同时创建一组属性,这一组属性会放在其所属的kobject的目录下的一个子目录中。每一个属性组对应一个子目录