本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的。在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解。其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解。
内核版本:2.6.30
个人理解:sysfs向用户空间展示了驱动设备的层次结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。
我们来看看sysfs的文件结构:
[root@yj423 /sys]#ls
block class devices fs module
bus dev firmware kernel power
block:块设备
bus:系统中的总线
class: 设备类型,比如输入设备
dev:系统中已注册的设备节点的视图,有两个子目录char和block。
devices:系统中所有设备拓扑结构视图
fireware:固件
fs:文件系统
kernel:内核配置选项和状态信息
module:模块
power:系统的电源管理数据
要分析sysfs,首先就要分析kobject和kset,因为驱动设备的层次结构的构成就是由这两个东东来完成的。
kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。
kobject用struct kobject来描述。
-
struct kobject {
-
const
char *name;
/*在sysfs建立目录的名字*/
-
struct list_head entry;
/*用于连接到所属kset的链表中*/
-
struct kobject *parent;
/*父对象*/
-
struct kset *kset;
/*属于哪个kset*/
-
struct kobj_type *ktype;
/*类型*/
-
struct sysfs_dirent *sd;
/*sysfs中与该对象对应的文件节点*/
-
struct kref kref;
/*对象的应用计数*/
-
unsigned
int state_initialized:
1;
-
unsigned
int state_in_sysfs:
1;
-
unsigned
int state_add_uevent_sent:
1;
-
unsigned
int state_remove_uevent_sent:
1;
-
unsigned
int uevent_suppress:
1;
-
};
kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。
kset使用struct kset来描述。
-
/**
-
* struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
-
*
-
* A kset defines a group of kobjects. They can be individually
-
* different "types" but overall these kobjects all want to be grouped
-
* together and operated on in the same manner. ksets are used to
-
* define the attribute callbacks and other common events that happen to
-
* a kobject.
-
*
-
* @list: the list of all kobjects for this kset
-
* @list_lock: a lock for iterating over the kobjects
-
* @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
-
* @uevent_ops: the set of uevent operations for this kset. These are
-
* called whenever a kobject has something happen to it so that the kset
-
* can add new environment variables, or filter out the uevents if so
-
* desired.
-
*/
-
struct kset {
-
struct list_head list;
/*属于该kset的kobject链表*/
-
spinlock_t list_lock;
-
struct kobject kobj;
/*该kset内嵌的kobj*/
-
-
struct kset_uevent_ops *uevent_ops;
-
};
每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。
-
struct kobj_type {
-
void (*release)(struct kobject *kobj);
-
struct sysfs_ops *sysfs_ops;
-
struct attribute **default_attrs;
-
};
-
-
struct sysfs_ops {
-
ssize_t (*show)(struct kobject *, struct attribute *,
char *);
-
ssize_t (*store)(struct kobject *,struct attribute *,
const
char *,
size_t);
-
};
-
-
/* FIXME
-
* The *owner field is no longer used.
-
* x86 tree has been cleaned up. The owner
-
* attribute is still left for other arches.
-
*/
-
struct attribute {
-
const
char *name;
-
struct module *owner;
-
mode_t mode;
-
};
-
-
当kobject的引用计数为0时,通过release方法来释放相关的资源。
attribute为属性,每个属性在sysfs中都有对应的属性文件。
sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。
下面这张图非常经典。最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。
从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。
在上一节中,我们知道sys下有一个bus目录,这一将分析如何通过kobject创建bus目录。
下面代码位于drivers/base/bus.c
-
int __
init buses_init(void)
-
{
-
bus_kset = kset_create_and_add(
"bus", &bus_uevent_ops,
NULL);
-
if (!bus_kset)
-
return -ENOMEM;
-
return
0;
-
}
-
-
static
struct kset_uevent_ops bus_uevent_ops = {
-
.filter = bus_uevent_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;
-
return
0;
-
}
这里直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数表示没有父对象。
下面代码位于drivers/base/kobject.c
-
/**
-
* kset_create_and_add - create a struct kset dynamically and add it to sysfs
-
*
-
* @name: the name for the kset
-
* @uevent_ops: a struct kset_uevent_ops for the kset
-
* @parent_kobj: the parent kobject of this kset, if any.
-
*
-
* This function creates a kset structure dynamically and registers it
-
* with sysfs. When you are finished with this structure, call
-
* kset_unregister() and the structure will be dynamically freed when it
-
* is no longer being used.
-
*
-
* If the kset was not able to be created, NULL will be returned.
-
*/
-
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,设置某些字段*/
-
if (!kset)
-
return
NULL;
-
error = kset_register(kset);
/*添加kset到sysfs*/
-
if (error) {
-
kfree(kset);
-
return
NULL;
-
}
-
return kset;
-
}
这里主要调用了两个函数,接下分别来看下。
下面代码位于drivers/base/kobject.c
-
/**
-
* kset_create - create a struct kset dynamically
-
*
-
* @name: the name for the kset
-
* @uevent_ops: a struct kset_uevent_ops for the kset
-
* @parent_kobj: the parent kobject of this kset, if any.
-
*
-
* This function creates a kset structure dynamically. This structure can
-
* then be registered with the system and show up in sysfs with a call to
-
* kset_register(). When you are finished with this structure, if
-
* kset_register() has been called, call kset_unregister() and the
-
* structure will be dynamically freed when it is no longer being used.
-
*
-
* If the kset was not able to be created, NULL will be returned.
-
*/
-
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);
/*分配kset*/
-
if (!kset)
-
return
NULL;
-
kobject_set_name(&kset->kobj, name);
/*设置kobj->name*/
-
kset->uevent_ops = uevent_ops;
-
kset->kobj.parent = parent_kobj;
/*设置父对象*/
-
-
/*
-
* The kobject of this kset will have a type of kset_ktype and belong to
-
* no kset itself. That way we can properly free it when it is
-
* finished being used.
-
*/
-
kset->kobj.ktype = &kset_ktype;
-
kset->kobj.kset =
NULL;
/*本keset不属于任何kset*/
-
-
return kset;
-
}
这个函数中,动态分配了kset结构,调用kobject_set_name设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL,
也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录创建的,自然没有父对象。
随后简要看下由kobject_set_name函数调用引发的一系列调用。
-
/**
-
* kobject_set_name - Set the name of a kobject
-
* @kobj: struct kobject to set the name of
-
* @fmt: format string used to build the name
-
*
-
* This sets the name of the kobject. If you have already added the
-
* kobject to the system, you must call kobject_rename() in order to
-
* change the name of the kobject.
-
*/
-
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
-
{
-
va_list vargs;
-
int retval;
-
-
va_start(vargs, fmt);
-
retval = kobject_set_name_vargs(kobj, fmt, vargs);
-
va_end(vargs);
-
-
return retval;
-
}
-
-
/**
-
* kobject_set_name_vargs - Set the name of an kobject
-
* @kobj: struct kobject to set the name of
-
* @fmt: format string used to build the name
-
* @vargs: vargs to format the string.
-
*/
-
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
-
va_list vargs)
-
{
-
const
char *old_name = kobj->name;
-
char *s;
-
-
if (kobj->name && !fmt)
-
return
0;
-
-
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
-
if (!kobj->name)
-
return -ENOMEM;
-
-
/* ewww... some of these buggers have '/' in the name ... */
-
while ((s =
strchr(kobj->name,
'/')))
-
s[
0] =
'!';
-
-
kfree(old_name);
-
return
0;
-
}
-
-
/* Simplified asprintf. */
-
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
-
{
-
unsigned
int len;
-
char *p;
-
va_list aq;
-
-
va_copy(aq, ap);
-
len = vsnprintf(
NULL,
0, fmt, aq);
-
va_end(aq);
-
-
p = kmalloc(len+
1, gfp);
-
if (!p)
-
return
NULL;
-
-
vsnprintf(p, len+
1, fmt, ap);
-
-
return p;
-
}
下面代码位于drivers/base/kobject.c。
-
/**
-
* kset_register - initialize and add a kset.
-
* @k: kset.
-
*/
-
int kset_register(struct kset *k)
-
{
-
int err;
-
-
if (!k)
-
return -EINVAL;
-
-
kset_init(k);
/*初始化kset*/
-
err = kobject_add_internal(&k->kobj);
/*在sysfs中建立目录*/
-
if (err)
-
return err;
-
kobject_uevent(&k->kobj, KOBJ_ADD);
-
return
0;
-
}
这里面调用了3个函数。这里先介绍前两个函数。
该函数用于初始化kset。
下面代码位于drivers/base/kobject.c。
-
/**
-
* kset_init - initialize a kset for use
-
* @k: kset
-
*/
-
void kset_init(struct kset *k)
-
{
-
kobject_init_internal(&k->kobj);
/*初始化kobject的某些字段*/
-
INIT_LIST_HEAD(&k->
list);
/*初始化链表头*/
-
spin_lock_init(&k->list_lock);
/*初始化自旋锁*/
-
}
-
-
static void kobject_init_internal(struct kobject *kobj)
-
{
-
if (!kobj)
-
return;
-
kref_init(&kobj->kref);
/*初始化引用基计数*/
-
INIT_LIST_HEAD(&kobj->entry);
/*初始化链表头*/
-
kobj->state_in_sysfs =
0;
-
kobj->state_add_uevent_sent =
0;
-
kobj->state_remove_uevent_sent =
0;
-
kobj->state_initialized =
1;
-
}
该函数将在sysfs中建立目录。
下面代码位于drivers/base/kobject.c。
-
static int kobject_add_internal(struct kobject *kobj)
-
{
-
int error =
0;
-
struct kobject *parent;
-
-
if (!kobj)
-
return -ENOENT;
-
/*检查name字段是否存在*/
-
if (!kobj->name || !kobj->name[
0]) {
-
WARN(
1,
"kobject: (%p): attempted to be registered with empty "
-
"name!\n", kobj);
-
return -EINVAL;
-
}
-
-
parent = kobject_get(kobj->parent);
/*有父对象则增加父对象引用计数*/
-
-
/* join kset if set, use it as parent if we do not already have one */
-
if (kobj->kset) {
-
if (!parent)
-
/*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/
-
parent = kobject_get(&kobj->kset->kobj);
-
kobj_kset_join(kobj);
/*将kojbect添加到kset结构中的链表当中*/
-
kobj->parent = parent;
-
}
-
-
pr_debug(
"kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
-
kobject_name(kobj), kobj, __func__,
-
parent ? kobject_name(parent) :
"
" ,
-
kobj->kset ? kobject_name(&kobj->kset->kobj) :
"
" );
-
-
error = create_dir(kobj);
/*根据kobj->name在sys中建立目录*/
-
if (error) {
-
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",
-
__func__, kobject_name(kobj));
-
else
-
printk(KERN_ERR
"%s failed for %s (%d)\n",
-
__func__, kobject_name(kobj), error);
-
dump_stack();
-
}
else
-
kobj->state_in_sysfs =
1;
-
-
return error;
-
}
在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)条件不满足。因此在这个函数中,对name进行了必要的检查之后,调用了create_dir在sysfs中创建目录。
在create_dir执行完成以后会在sysfs的根目录(/sys/)建立文件夹bus。该函数的详细分析将在后面给出。
至此,对bus目录的建立有了简单而直观的了解。我们可以看出kset其实就是表示一个文件夹,而kset本身也含有一个kobject,而该kobject的name字段即为该目录的名字,本例中为bus。
第2节所介绍的是最底层,最核心的内容。下面开始将描述较为高层的内容。
Linux设备模型使用了三个数据结构分别来描述总线、设备和驱动。所有的设备和对应的驱动都必须挂载在某一个总线上,通过总线,可以绑定设备和驱动。
这个属于分离的思想,将设备和驱动分开管理。
同时驱动程序可以了解到所有它所支持的设备,同样的,设备也能知道它对应驱动程序。
总线是处理器与一个设备或者多个设备之间的通道。在设备模型中,所有的设备都挂载在某一个总线上。总线使用struct bus_type来表述。
下列代码位于include/linux/device.h。
-
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 (*suspend_late)(struct device *dev, pm_message_t state);
-
int (*resume_early)(struct device *dev);
-
int (*resume)(struct device *dev);
-
-
struct dev_pm_ops *pm;
-
-
struct bus_type_private *p;
-
};
-
-
/**
-
* struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
-
*
-
* @subsys - the struct kset that defines this bus. This is the main kobject
-
* @drivers_kset - the list of drivers associated with this bus
-
* @devices_kset - the list of devices associated with this bus
-
* @klist_devices - the klist to iterate over the @devices_kset
-
* @klist_drivers - the klist to iterate over the @drivers_kset
-
* @bus_notifier - the bus notifier list for anything that cares about things
-
* on this bus.
-
* @bus - pointer back to the struct bus_type that this structure is associated
-
* with.
-
*
-
* This structure is the one that is the actual kobject allowing struct
-
* bus_type to be statically allocated safely. Nothing outside of the driver
-
* core should ever touch these fields.
-
*/
-
struct bus_type_private {
-
struct kset subsys;
-
struct kset *drivers_kset;
-
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都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。后面我们将看到platform总线的建立。
drivers_kset和devices_kset对应着两个目录,该两个目录下将包含该总线上的设备和相应的驱动程序。
同时总线上的设备和驱动将分别保存在两个链表中:klist_devices和klist_drivers。
设备对象在driver-model中使用struct device来表示。
下列代码位于include/linux/device.h。
-
struct device {
-
struct device *parent;
-
-
struct device_private *p;
-
-
struct kobject kobj;
-
const
char *init_name;
/* initial name of the device */
-
struct device_type *type;
-
-
struct semaphore sem;
/* semaphore to synchronize calls to
-
* its driver.
-
*/
-
-
struct bus_type *bus;
/* type of bus device is on */
-
struct device_driver *driver;
/* which driver has allocated this
-
device */
-
void *driver_data;
/* data private to the driver */
-
void *platform_data;
/* Platform specific data, device
-
core doesn't touch it */
-
struct dev_pm_info power;
-
-
#ifdef CONFIG_NUMA
-
int numa_node;
/* NUMA node this device is close to */
-
#endif
-
u64 *dma_mask;
/* dma mask (if dma'able device) */
-
u64 coherent_dma_mask;
/* Like dma_mask, but for
-
alloc_coherent mappings as
-
not all hardware supports
-
64 bit addresses for consistent
-
allocations such descriptors. */
-
-
struct device_dma_parameters *dma_parms;
-
-
struct list_head dma_pools;
/* dma pools (if dma'ble) */
-
-
struct dma_coherent_mem *dma_mem;
/* internal for coherent mem
-
override */
-
/* arch specific additions */
-
struct dev_archdata archdata;
-
-
dev_t devt;
/* dev_t, creates the sysfs "dev" */
-
-
spinlock_t devres_lock;
-
struct list_head devres_head;
-
-
struct klist_node knode_class;
-
struct class *class;
-
struct attribute_group **groups;
/* optional groups */
-
-
void (*release)(struct device *dev);
-
};
-
-
/**
-
* struct device_private - structure to hold the private to the driver core portions of the device structure.
-
*
-
* @klist_children - klist containing all children of this device
-
* @knode_parent - node in sibling list
-
* @knode_driver - node in driver list
-
* @knode_bus - node in bus list
-
* @device - pointer back to the struct class that this structure is
-
* associated with.
-
*
-
* Nothing outside of the driver core should ever touch these fields.
-
*/
-
struct device_private {
-
struct klist klist_children;
-
struct klist_node knode_parent;
-
struct klist_node knode_driver;
-
struct klist_node knode_bus;
-
struct device *device;
-
};
device本身包含一个kobject,也就是说这个device在sysfs的某个地方有着一个对应的目录。
该device所挂载的bus由knode_bus指定。
该device所对应的设备驱动由knode_driver指定。
设备设备对象在driver-model中使用struct device_driver来表示。
下列代码位于include/linux/device.h。
-
struct device_driver {
-
const
char *name;
-
struct bus_type *bus;
-
-
struct module *owner;
-
const
char *mod_name;
/* used for built-in modules */
-
-
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);
-
struct attribute_group **groups;
-
-
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;
-
};
device_driver本身包含一个kobject,也就是说这个device_driver在sysfs的某个地方有着一个对应的目录。
该设备驱动所支持的设备由klist_devices指定。
该设备驱动所挂载的总线由knode_bus制定。
本节我们将以platform总线为例,来看看,/sys/bus/platform是如何建立的。
platform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。
注:kernel_init()是在rest_init函数中创建内核线程来执行的。
-
int __
init platform_bus_init(void)
-
{
-
int error;
-
-
early_platform_cleanup();
-
-
error = device_register(&platform_bus);
-
if (error)
-
return error;
-
error = bus_register(&platform_bus_type);
-
if (error)
-
device_unregister(&platform_bus);
-
return error;
-
}
-
struct bus_type platform_bus_type = {
-
.name =
"platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.pm = PLATFORM_PM_OPS_PTR,
-
};
-
EXPORT_SYMBOL_GPL(platform_bus_type);
从bus_type,我们看到该总线的名字为platform。
调用了两个函数,我们只关注bus_register函数。
-
/**
-
* bus_register - register a bus with the system.
-
* @bus: bus.
-
*
-
* Once we have that, we registered the bus with the kobject
-
* infrastructure, then register the children subsystems it has:
-
* the devices and drivers that belong to the 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);
-
/*设定kobject->name*/
-
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;
-
-
/*注册kset,在bus/建立目录XXX,XXX为bus->name*/
-
retval = kset_register(&priv->subsys);
-
if (retval)
-
goto out;
-
-
/*创建属性,在bus/XXX/建立文件uevent*/
-
retval = bus_create_file(bus, &bus_attr_uevent);
-
if (retval)
-
goto bus_uevent_fail;
-
-
/*创建kset,在bus/XXX/建立目录devices*/
-
priv->devices_kset = kset_create_and_add(
"devices",
NULL,
-
&priv->subsys.kobj);
-
if (!priv->devices_kset) {
-
retval = -ENOMEM;
-
goto bus_devices_fail;
-
}
-
-
/*创建kset,在bus/XXX/建立目录drivers*/
-
priv->drivers_kset = kset_create_and_add(
"drivers",
NULL,
-
&priv->subsys.kobj);
-
if (!priv->drivers_kset) {
-
retval = -ENOMEM;
-
goto bus_drivers_fail;
-
}
-
/*初始化2个内核链表,*/
-
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
-
klist_init(&priv->klist_drivers,
NULL,
NULL);
-
-
/*创建属性,在bus/XXX/建立文件drivers_autoprobe和drivers_probe*/
-
retval = add_probe_files(bus);
-
if (retval)
-
goto bus_probe_files_fail;
-
/*根据bus->bus_attribute创建属性,在bus/XXX/下建立相应的文件d*/
-
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:
-
bus->p =
NULL;
-
return retval;
-
}
-
EXPORT_SYMBOL_GPL(bus_register);
在这里还用到了bus_kset这个变量,这个变量就是在第3节buses_init函数中建立bus目录所对应的kset对象。
接着,priv->subsys.kobj.kset = bus_kset,设置subsys的kobj在bus_kset对象包含的集合中,也就是说bus目录下将包含subsys对象所对应的目录,即platform。
紧接着调用了kset_register,参数为&priv->subsys。该函数在3.2节中以给出。在该函数的调用过程中,将调用kobj_kset_join函数,该函数将kobject添加到kobject->kset的链表中。
-
/* add the kobject to its kset's list */
-
static void kobj_kset_join(struct kobject *kobj)
-
{
-
if (!kobj->kset)
-
return;
-
-
kset_get(kobj->kset);
/*增加kset引用计数*/
-
spin_lock(&kobj->kset->list_lock);
-
list_add_tail(&kobj->entry, &kobj->kset->
list);
/*将kojbect添加到kset结构中的链表当中*/
-
spin_unlock(&kobj->kset->list_lock);
-
}
kset_register函数执行完成后,将在/sys/bus/下建立目录platform。此刻,我们先来看下kset和kobject之间的关系。
然后,调用了bus_create_file函数在/sys/bus/platform/下建立文件uevent。
-
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;
-
}
-
EXPORT_SYMBOL_GPL(bus_create_file);
有关底层的sysfs将在后面叙述,这里只要关注参数&bus->p->subsys.kobj,表示在该kset下建立文件,也就是platform下建立。
接着调用了2次kset_create_and_add,分别在/sys/bus/platform/下建立了文件夹devices和drivers。该函数位于第3节开始处。
这里和第3节调用kset_create_and_add时的最主要一个区别就是:此时的parent参数不为NULL,而是&priv->subsys.kobj。
也就是说,将要创建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夹对应的kset中。
我们来看下关系图:
随后,调用了add_probe_files创建了属性文件drivers_autoprobe和drivers_probe。
-
static int add_probe_files(struct bus_type *bus)
-
{
-
int retval;
-
-
retval = bus_create_file(bus, &bus_attr_drivers_probe);
-
if (retval)
-
goto out;
-
-
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
-
if (retval)
-
bus_remove_file(bus, &bus_attr_drivers_probe);
-
out:
-
return retval;
-
}
该函数只是简单的调用了两次bus_create_file,该函数已在前面叙述过。
最后调用bus_add_attrs创建总线相关的属性文件。
-
/**
-
* bus_add_attrs - Add default attributes for this bus.
-
* @bus: Bus that has just been registered.
-
*/
-
-
static int bus_add_attrs(struct bus_type *bus)
-
{
-
int error =
0;
-
int i;
-
-
if (bus->bus_attrs) {
-
for (i =
0; attr_name(bus->bus_attrs[i]); i++) {
-
error = bus_create_file(bus, &bus->bus_attrs[i]);
-
if (error)
-
goto err;
-
}
-
}
-
done:
-
return error;
-
err:
-
while (--i >=
0)
-
bus_remove_file(bus, &bus->bus_attrs[i]);
-
goto done;
-
}
我们可以看到这个函数将根据bus_type->bus_arrts来创建属性文件。不过,在本例中,bus_arrts从未给出定义,因此次函数不做任何工作。
好了,整个bus_register调用完成了,我们来看下sysfs中实际的情况。
[root@yj423 platform]#pwd
/sys/bus/platform
[root@yj423 platform]#ls
devices drivers drivers_autoprobe drivers_probe uevent
最后,我们对整个bus_register的过程进行一个小结。
本节将首先讲述如何在/sys/devices下建立虚拟的platform设备,然后再讲述如何在/sys/devices/platform/下建立子设备。
之所以叫虚拟是因为这个platform并不代表任何实际存在的设备,但是platform将是所有具体设备的父设备。
在第5节,platform_bus_init函数中还调用了device_register,现在对其做出分析。
-
int __
init platform_bus_init(void)
-
{
-
int error;
-
-
early_platform_cleanup();
-
-
error = device_register(&platform_bus);
-
if (error)
-
return error;
-
error = bus_register(&platform_bus_type);
-
if (error)
-
device_unregister(&platform_bus);
-
return error;
-
}
-
-
struct device platform_bus = {
-
.init_name =
"platform",
-
};
-
EXPORT_SYMBOL_GPL(platform_bus)
下列函数位于drivers/base/core.c。
-
/**
-
* device_register - register a device with the system.
-
* @dev: pointer to the device structure
-
*
-
* This happens in two clean steps - initialize the device
-
* and add it to the system. The two steps can be called
-
* separately, but this is the easiest and most common.
-
* I.e. you should only call the two helpers separately if
-
* have a clearly defined need to use and refcount the device
-
* before it is added to the hierarchy.
-
*
-
* NOTE: _Never_ directly free @dev after calling this function, even
-
* if it returned an error! Always use put_device() to give up the
-
* reference initialized in this function instead.
-
*/
-
int device_register(struct device *dev)
-
{
-
device_initialize(dev);
/*初始化dev的某些字段*/
-
return device_add(dev);
/*将设备添加到系统中*/
-
}
一个设备的注册分成两部,每步通过调用一个函数函数。首先先看第一步:
下列函数位于drivers/base/core.c。
-
/**
-
* device_initialize - init device structure.
-
* @dev: device.
-
*
-
* This prepares the device for use by other layers by initializing
-
* its fields.
-
* It is the first half of device_register(), if called by
-
* that function, though it can also be called separately, so one
-
* may use @dev's fields. In particular, get_device()/put_device()
-
* may be used for reference counting of @dev after calling this
-
* function.
-
*
-
* NOTE: Use put_device() to give up your reference instead of freeing
-
* @dev directly once you have called this function.
-
*/
-
void device_initialize(struct device *dev)
-
{
-
dev->kobj.kset = devices_kset;
/*设置kobj属于哪个kset,/sys/devices/*/
-
kobject_init(&dev->kobj, &device_ktype);
/*初始化dev->kobj*/
-
INIT_LIST_HEAD(&dev->dma_pools);
/*初始化链表头*/
-
init_MUTEX(&dev->sem);
/*初始化互斥体*/
-
spin_lock_init(&dev->devres_lock);
/*初始化自旋锁*/
-
INIT_LIST_HEAD(&dev->devres_head);
/*初始化链表头*/
-
device_init_wakeup(dev,
0);
/*设置该device不能唤醒*/
-
device_pm_init(dev);
/*设置该device可操作*/
-
set_dev_node(dev,
-1);
/*设置NUMA节点*/
-
}
首先其中用到了devices_kset对象,这个对象和第3节当中的bus_kset是同样的性质,也就是说该对象表示一个目录。
该对象的建立是在devices_init函数中完成的。
-
int __
init devices_init(void)
-
{
-
devices_kset = kset_create_and_add(
"devices", &device_uevent_ops,
NULL);
-
if (!devices_kset)
-
return -ENOMEM;
-
dev_kobj = kobject_create_and_add(
"dev",
NULL);
-
if (!dev_kobj)
-
goto dev_kobj_err;
-
sysfs_dev_block_kobj = kobject_create_and_add(
"block", dev_kobj);
-
if (!sysfs_dev_block_kobj)
-
goto block_kobj_err;
-
sysfs_dev_char_kobj = kobject_create_and_add(
"char", dev_kobj);
-
if (!sysfs_dev_char_kobj)
-
goto char_kobj_err;
-
-
return
0;
-
-
char_kobj_err:
-
kobject_put(sysfs_dev_block_kobj);
-
block_kobj_err:
-
kobject_put(dev_kobj);
-
dev_kobj_err:
-
kset_unregister(devices_kset);
-
return -ENOMEM;
-
}
由此可见,devices_kset对象表示的目录为/sys下的devices目录。
下列函数位于lib/kojbect.c。
-
/**
-
* kobject_init - initialize a kobject structure
-
* @kobj: pointer to the kobject to initialize
-
* @ktype: pointer to the ktype for this kobject.
-
*
-
* This function will properly initialize a kobject such that it can then
-
* be passed to the kobject_add() call.
-
*
-
* After this function is called, the kobject MUST be cleaned up by a call
-
* to kobject_put(), not by a call to kfree directly to ensure that all of
-
* the memory is cleaned up properly.
-
*/
-
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
-
{
-
char *err_str;
-
-
if (!kobj) {
-
err_str =
"invalid kobject pointer!";
-
goto error;
-
}
-
if (!ktype) {
-
err_str =
"must have a ktype to be initialized properly!\n";
-
goto error;
-
}
-
if (kobj->state_initialized) {
-
/* do not error out as sometimes we can recover */
-
printk(KERN_ERR
"kobject (%p): tried to init an initialized "
-
"object, something is seriously wrong.\n", kobj);
-
dump_stack();
-
}
-
-
kobject_init_internal(kobj);
-
kobj->ktype = ktype;
-
return;
-
-
error:
-
printk(KERN_ERR
"kobject (%p): %s\n", kobj, err_str);
-
dump_stack();
-
}
-
EXPORT_SYMBOL(kobject_init);
-
-
static void kobject_init_internal(struct kobject *kobj)
-
{
-
if (!kobj)
-
return;
-
kref_init(&kobj->kref);
/*初始化引用基计数*/
-
INIT_LIST_HEAD(&kobj->entry);
/*初始化链表头*/
-
kobj->state_in_sysfs =
0;
-
kobj->state_add_uevent_sent =
0;
-
kobj->state_remove_uevent_sent =
0;
-
kobj->state_initialized =
1;
-
}
该函数在做了一系列的必要检查后,调用kobject_init_internal初始化了kobject的某些字段。
参数val为0,设置该device不能够唤醒。
-
#ifdef CONFIG_PM
-
-
/* changes to device_may_wakeup take effect on the next pm state change.
-
* by default, devices should wakeup if they can.
-
*/
-
static inline void device_init_wakeup(struct device *dev, int val)
-
{
-
dev->power.can_wakeup = dev->power.should_wakeup = !!val;
-
}
-
。。。。。。
-
#else /* !CONFIG_PM */
-
-
/* For some reason the next two routines work even without CONFIG_PM */
-
static inline void device_init_wakeup(struct device *dev, int val)
-
{
-
dev->power.can_wakeup = !!val;
-
}
-
。。。。。。
-
#endif
-
设置电源的状态。
-
static inline void device_pm_init(struct device *dev)
-
{
-
dev->power.status = DPM_ON;
/*该device被认为可操作*/
-
}
如果使用NUMA,则设置NUMA节点。
-
#ifdef CONFIG_NUMA
-
。。。。。。
-
static inline void set_dev_node(struct device *dev, int node)
-
{
-
dev->numa_node = node;
-
}
-
#else
-
。。。。。。
-
static inline void set_dev_node(struct device *dev, int node)
-
{
-
}
-
#endif
接下来是注册的第二步:调用device_add。
-
/**
-
* device_add - add device to device hierarchy.
-
* @dev: device.
-
*
-
* This is part 2 of device_register(), though may be called
-
* separately _iff_ device_initialize() has been called separately.
-
*
-
* This adds @dev to the kobject hierarchy via kobject_add(), adds it
-
* to the global and sibling lists for the device, then
-
* adds it to the other relevant subsystems of the driver model.
-
*
-
* NOTE: _Never_ directly free @dev after calling this function, even
-
* if it returned an error! Always use put_device() to give up your
-
* reference instead.
-
*/
-
int device_add(struct device *dev)
-
{
-
struct device *parent = NULL;
-
struct class_interface *class_intf;
-
int error = -EINVAL;
-
-
dev = get_device(dev);
/*增加引用计数*/
-
if (!dev)
-
goto done;
-
-
dev->p = kzalloc(
sizeof(*dev->p), GFP_KERNEL);
/*分配device_private结构*/
-
if (!dev->p) {
-
error = -ENOMEM;
-
goto done;
-
}
-
dev->p->device = dev;
/*保存dev*/
-
klist_init(&dev->p->klist_children, klist_children_get,
/*初始化内核链表*/
-
klist_children_put);
-
-
/*
-
* for statically allocated devices, which should all be converted
-
* some day, we need to initialize the name. We prevent reading back
-
* the name, and force the use of dev_name()
-
*/
-
if (dev->init_name) {
-
dev_set_name(dev, dev->init_name);
/*dev->kobject->name = dev->init_name*/
-
dev->init_name =
NULL;
-
}
-
-
if (!dev_name(dev))
/*检查dev->kobject->name*/
-
goto name_error;
-
-
pr_debug(
"device: '%s': %s\n", dev_name(dev), __func__);
-
-
parent = get_device(dev->parent);
/*增加父设备引用计数*/
-
setup_parent(dev, parent);
/*设置dev->kobject->parent*/
-
-
/* use parent numa_node */
-
if (parent)
-
set_dev_node(dev, dev_to_node(parent));
-
-
/* first, register with generic layer. */
-
/* we require the name to be set before, and pass NULL */
-
/* 执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/
-
error = kobject_add(&dev->kobj, dev->kobj.parent,
NULL);
-
if (error)
-
goto Error;
-
-
/* notify platform of device entry */
-
if (platform_notify)
-
platform_notify(dev);
-
-
/*在XXX下建立文件uevent*/
-
error = device_create_file(dev, &uevent_attr);
-
if (error)
-
goto attrError;
-
-
if (MAJOR(dev->devt)) {
/*主设备号不为0*/
-
error = device_create_file(dev, &devt_attr);
/*创建属性文件dev*/
-
if (error)
-
goto ueventattrError;
-
-
/* 在sys/dev/char/下建立symlink,名字为主设备号:次设备号,该链接指向XXX */
-
error = device_create_sys_dev_entry(dev);
-
if (error)
-
goto devtattrError;
-
}
-
-
error = device_add_class_symlinks(dev);
-
if (error)
-
goto SymlinkError;
-
error = device_add_attrs(dev);
/*添加类设备属型文件和属性组*/
-
if (error)
-
goto AttrsError;
-
error = bus_add_device(dev);
/*添加3个symlink*/
-
if (error)
-
goto BusError;
-
error = dpm_sysfs_add(dev);
/*创建power子目录,并在其下添加电源管理的属性组文件*/
-
if (error)
-
goto DPMError;
-
device_pm_add(dev);
/*将该device添加到电源管理链表中*/
-
-
/* Notify clients of device addition. This call must come
-
* after dpm_sysf_add() and before kobject_uevent().
-
*/
-
if (dev->bus)
-
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
-
BUS_NOTIFY_ADD_DEVICE, dev);
-
-
kobject_uevent(&dev->kobj, KOBJ_ADD);
/*通知用户层*/
-
bus_attach_device(dev);
/*将设备添加到总线的设备链表中,并尝试获取驱动*/
-
if (parent)
-
klist_add_tail(&dev->p->knode_parent,
/*有父设备,则将该设备添加到父设备的儿子链表中*/
-
&parent->p->klist_children);
-
-
if (dev->class) {
/*该设备属于某个设备类*/
-
mutex_lock(&dev->class->p->class_mutex);
-
/* tie the class to the device */
-
klist_add_tail(&dev->knode_class,
/*将device添加到class的类设备链表中*/
-
&dev->class->p->class_devices);
-
-
/* notify any interfaces that the device is here */
-
list_for_each_entry(class_intf,
-
&dev->class->p->class_interfaces, node)
-
if (class_intf->add_dev)
-
class_intf->add_dev(dev, class_intf);
-
mutex_unlock(&dev->class->p->class_mutex);
-
}
-
done:
-
put_device(dev);
-
return error;
-
DPMError:
-
bus_remove_device(dev);
-
BusError:
-
device_remove_attrs(dev);
-
AttrsError:
-
device_remove_class_symlinks(dev);
-
SymlinkError:
-
if (MAJOR(dev->devt))
-
device_remove_sys_dev_entry(dev);
-
devtattrError:
-
if (MAJOR(dev->devt))
-
device_remove_file(dev, &devt_attr);
-
ueventattrError:
-
device_remove_file(dev, &uevent_attr);
-
attrError:
-
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
-
kobject_del(&dev->kobj);
-
Error:
-
cleanup_device_parent(dev);
-
if (parent)
-
put_device(parent);
-
name_error:
-
kfree(dev->p);
-
dev->p =
NULL;
-
goto done;
-
}
下列代码位于drivers/base/core.c。
-
static void setup_parent(struct device *dev, struct device *parent)
-
{
-
struct kobject *kobj;
-
kobj = get_device_parent(dev, parent);
-
if (kobj)
-
dev->kobj.parent = kobj;
-
}
-
-
static struct kobject *get_device_parent(struct device *dev,
-