内核版本:Linux 2.6.36.4
分析文件:drivers\base\sys.c
include\linux\sysdev.h
注:如果你对设备模型没有概念的话,请学习了设备模型在来看,因为这是基础,标题贴出的链接可以学习下,呵呵
开始
根据sys.c文件介绍该文件是提供了一种假的设备机制模型,主要用在cpus, PICs,timers等。
struct sys_device {
u32 id;
struct sysdev_class * cls;
struct kobject kobj;
};
该设备的定义类似于struct device的定义 都有包含 struct kobject kobj ,kobject在/sys下对应一个目录。
#define to_sysdev(k) container_of(k, struct sys_device, kobj)
该宏根据给定的kobject得到struct sys_device。
struct sysdev_attribute {
struct attribute attr;
ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *);
ssize_t (*store)(struct sys_device *, struct sysdev_attribute *,
const char *, size_t);
};
该结构是对struct attribute的封装。
#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
该宏是根据给定的struct attribute得到对应的struct sysdev_attribute。
static ssize_t
sysdev_show(struct kobject *kobj, struct attribute *attr, char *buffer)
{
struct sys_device *sysdev = to_sysdev(kobj);
struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
if (sysdev_attr->show)
return sysdev_attr->show(sysdev, sysdev_attr, buffer);
return -EIO;
}
该函数实现attribute找到对应的sys_device然后回溯到sys_device的show函数。
static ssize_t
sysdev_store(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t count)
{
struct sys_device *sysdev = to_sysdev(kobj);
struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr);
if (sysdev_attr->store)
return sysdev_attr->store(sysdev, sysdev_attr, buffer, count);
return -EIO;
}
该函数跟sysdev_show函数的原理类似回溯到sysdev_show的store函数。
static const struct sysfs_ops sysfs_ops = {
.show = sysdev_show,
.store = sysdev_store,
};
用sysdev_show和sysdev_store构建sysfs_ops。
static struct kobj_type ktype_sysdev = {
.sysfs_ops = &sysfs_ops,
};
用sysfs_ops构建ktype_sysdev,ktype_sysdev赋值给kobject可以操作对应的属性
int sysdev_create_file(struct sys_device *s, struct sysdev_attribute *a)
{
return sysfs_create_file(&s->kobj, &a->attr);
}
void sysdev_remove_file(struct sys_device *s, struct sysdev_attribute *a)
{
sysfs_remove_file(&s->kobj, &a->attr);
}
这两个函数是对底层函数的封装,分别实现在sys_device的kobject下面创建和删除属性文件。
struct sysdev_class {
const char *name;
struct list_head drivers;
struct sysdev_class_attribute **attrs;
/* Default operations for these types of devices */
int (*shutdown)(struct sys_device *);
int (*suspend)(struct sys_device *, pm_message_t state);
int (*resume)(struct sys_device *);
struct kset kset;
};
封装一个kset,即kobject容器,下面可以放很多同类的kobject对象
#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
该宏根据给定的kset下的kobject来找到对应的sysdev_class,可能有人对这里的kset.kobj感到不解,其实只要把container_of宏展开就可以看的很清楚了,在这里就偷个懒不分析了,嘻嘻
struct sysdev_class_attribute {
struct attribute attr;
ssize_t (*show)(struct sysdev_class *, struct sysdev_class_attribute *,
char *);
ssize_t (*store)(struct sysdev_class *, struct sysdev_class_attribute *,
const char *, size_t);
};
封装一个struct sysdev_class_attribute结构体类似于sysdev_attribute
#define to_sysdev_class_attr(a) container_of(a, \
struct sysdev_class_attribute, attr)
该宏和to_sysdev_attr宏同理,呵呵。
static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
char *buffer)
{
struct sysdev_class *class = to_sysdev_class(kobj);
struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
if (class_attr->show)
return class_attr->show(class, class_attr, buffer);
return -EIO;
}
static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t count)
{
struct sysdev_class *class = to_sysdev_class(kobj);
struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
if (class_attr->store)
return class_attr->store(class, class_attr, buffer, count);
return -EIO;
}
static const struct sysfs_ops sysfs_class_ops = {
.show = sysdev_class_show,
.store = sysdev_class_store,
};
static struct kobj_type ktype_sysdev_class = {
.sysfs_ops = &sysfs_class_ops,
};
同理这两个函数都是为了构建ktype_sysdev_class,跟ktype_sysdev一样的道理啦,内核重复的东西真多,哎 好累
int sysdev_class_create_file(struct sysdev_class *c,
struct sysdev_class_attribute *a)
{
return sysfs_create_file(&c->kset.kobj, &a->attr);
}
EXPORT_SYMBOL_GPL(sysdev_class_create_file);
void sysdev_class_remove_file(struct sysdev_class *c,
struct sysdev_class_attribute *a)
{
sysfs_remove_file(&c->kset.kobj, &a->attr);
}
EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
又重复好无语,不想说了,啦啦啦、、、、、、
高潮来了哦 ,别走开哦、、、、
static struct kset *system_kset;
该变量对应/sys/system目录,以后所有出现的东西都是在这个下面创建的哦, 他会在初始化函数里面创建,先说下,别急最后会分析的哦
下面函数是在/sys/system下注册一个sysdev_class
int sysdev_class_register(struct sysdev_class *cls)
{
int retval;
pr_debug("Registering sysdev class '%s'\n", cls->name);
INIT_LIST_HEAD(&cls->drivers); //这个drivers是用来挂驱动的
memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
cls->kset.kobj.parent = &system_kset->kobj; //这里就可以保证cls是在/sys/system目录下的哦(这里有些多余因为在注册kobject的时候若parent没有的话会这样赋值的)
cls->kset.kobj.ktype = &ktype_sysdev_class; //这里说明cls下的所有属性文件都有调用sysdev_class_show和sysdev_class_store,这个两个函数在上面有定义哦
cls->kset.kobj.kset = system_kset; //说明cls是system_kset的子类
retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name); //赋值kobject的名字
if (retval)
return retval;
retval = kset_register(&cls->kset); //注册kset 这样就在/sys/system目录下就有以cls->name为名字的目录了
if (!retval && cls->attrs)
retval = sysfs_create_files(&cls->kset.kobj, //在刚刚创建的目录下创建它的属性文件
(const struct attribute **)cls->attrs);
return retval;
}
下面注销sysdev_class跟上面的函数是相反的过程,懂了上面就懂下面了或者说不用懂下面了,呵呵
void sysdev_class_unregister(struct sysdev_class *cls)
{
pr_debug("Unregistering sysdev class '%s'\n",
kobject_name(&cls->kset.kobj));
if (cls->attrs)
sysfs_remove_files(&cls->kset.kobj,
(const struct attribute **)cls->attrs);
kset_unregister(&cls->kset);
}
下面的函数是在sysdev_class上挂载driver,还记得在注册sysdev_class的时候初始化的INIT_LIST_HEAD(&cls->drivers);吗, 就是挂在这里啦,不信看下面咯
int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
{
int err = 0;
if (!cls) {//cls无效就返回
WARN(1, KERN_WARNING "sysdev: invalid class passed to "
"sysdev_driver_register!\n");
return -EINVAL;
}
/* Check whether this driver has already been added to a class. */
if (drv->entry.next && !list_empty(&drv->entry)) //判断这个drv是不是已经注册过了,若注册过了就警告下,不返回的哦
WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
" been registered to a class, something is wrong, but "
"will forge on!\n", cls->name, drv);
mutex_lock(&sysdev_drivers_lock);
if (cls && kset_get(&cls->kset)) {
list_add_tail(&drv->entry, &cls->drivers); //这里就是把drv挂在cls的drivers链表上
/* If devices of this class already exist, tell the driver */
if (drv->add) { //挂好后就对cls下面的所有设备sys_device调用add函数
struct sys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry) //cls下的所有设备都挂在cls的kset的list下,待会再解释sys_device注册的时候会讲到,要记住这里哦
drv->add(dev);
}
} else {
err = -EINVAL;
WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
}
mutex_unlock(&sysdev_drivers_lock);
return err;
}
下面函数 跟上面是反的,老规矩,不讲
void sysdev_driver_unregister(struct sysdev_class *cls,
struct sysdev_driver *drv)
{
mutex_lock(&sysdev_drivers_lock);
list_del_init(&drv->entry);
if (cls) {
if (drv->remove) {
struct sys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry) /* 遍历cls->kset.list 上的sys_device 设备*/
drv->remove(dev);
}
kset_put(&cls->kset);
}
mutex_unlock(&sysdev_drivers_lock);
}
下面函数注册sys_device
int sysdev_register(struct sys_device *sysdev)
{
int error;
struct sysdev_class *cls = sysdev->cls;
if (!cls)
return -EINVAL;
pr_debug("Registering sys device of class '%s'\n",
kobject_name(&cls->kset.kobj));
/* initialize the kobject to 0, in case it had previously been used */
memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
/* Make sure the kset is set */
sysdev->kobj.kset = &cls->kset; //这里说明sysdev会在cls的目录下面建立目录
/* Register the object --在kobject_init_and_add 里面会执行list_add_tail(&sysdev->kobj->entry, &cls->kset->list);
所以在sysdev_class的kset的list上挂的是sys_device设备 还记得在sysdev_driver_register里面让你记住的吗,现在懂了吗*/
error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
"%s%d", kobject_name(&cls->kset.kobj),
sysdev->id);
if (!error) {
struct sysdev_driver *drv;
pr_debug("Registering sys device '%s'\n",
kobject_name(&sysdev->kobj));
mutex_lock(&sysdev_drivers_lock);
/* Generic notification is implicit, because it's that
* code that should have called us.
*/
/* Notify class auxillary drivers */
list_for_each_entry(drv, &cls->drivers, entry) { //遍历所有的驱动,对该sysdev调用所有驱动的add函数
if (drv->add) //不知道这么干是为了啥 以后接触到了慢慢体会吧 ,反正原理都在这了懂了其
drv->add(sysdev); //他的就没问题了
}
mutex_unlock(&sysdev_drivers_lock);
kobject_uevent(&sysdev->kobj, KOBJ_ADD); //最后居然会产生一个add消息 ,这是想干嘛呀,呵呵
}
return error;
}
老规矩,不讲
void sysdev_unregister(struct sys_device *sysdev)
{
struct sysdev_driver *drv;
mutex_lock(&sysdev_drivers_lock);
list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
if (drv->remove)
drv->remove(sysdev);
}
mutex_unlock(&sysdev_drivers_lock);
kobject_put(&sysdev->kobj);
}
刚刚的高潮想必你还没过瘾,没关系再来一次吧(还有很多次哦)
还是对上面做个暂时的总结吧,不然你高潮不起来。呵呵:
其实框架已经清楚了,我们所有的sysdev_class都通过sysdev_class_register汗水挂在system_kset下面的,而每个sysdev_class的kset的list(即sysdev_class->kset->list)都放的是设备,每个sysdev_class的drivers有挂着对设备操作的驱动。
下面的函数实现对system_kset下的每个sysdev_class的每个设备分别调用sysdev_class的每个驱动,很晕吧,没事这是高潮的前兆,看下面代码吧!呵
void sysdev_shutdown(void)
{
struct sysdev_class *cls;
pr_debug("Shutting Down System Devices\n");
mutex_lock(&sysdev_drivers_lock);
list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { //遍历system_kset上的每个sysdev_class
struct sys_device *sysdev;
pr_debug("Shutting down type '%s':\n",
kobject_name(&cls->kset.kobj));
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { //遍历sysdev_class上的设备
struct sysdev_driver *drv;
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
/* Call auxillary drivers first */
list_for_each_entry(drv, &cls->drivers, entry) { //遍历到一个设备后遍历sysdev_class上面的驱动,然后让所有驱动去
if (drv->shutdown) //关该设备,很恶心吧!
drv->shutdown(sysdev);
}
/* Now call the generic one */
if (cls->shutdown) //好了sysdev_class上的驱动把该设备关了一遍了,现在可以让sysdev_class关设备了
cls->shutdown(sysdev);
}
}
mutex_unlock(&sysdev_drivers_lock);
}
下面函数用来唤醒sys_device
static void __sysdev_resume(struct sys_device *dev)
{
struct sysdev_class *cls = dev->cls;
struct sysdev_driver *drv;
/* First, call the class-specific one */
if (cls->resume) //先让cls总的函数唤醒该这边
cls->resume(dev);
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled after %pF\n", cls->resume);
/* Call auxillary drivers next. */
list_for_each_entry(drv, &cls->drivers, entry) { //然后再让各个driver来唤醒该设备
if (drv->resume)
drv->resume(dev);
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled after %pF\n", drv->resume);
}
}
下面的函数会调用上面的函数来唤醒,下面的函数是唤醒system_kset下所有的设备
int sysdev_resume(void)
{
struct sysdev_class *cls;
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled while resuming system devices\n");
pr_debug("Resuming System Devices\n");
list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) { //遍历system_kset得到挂在上面的sysdev_class,是不是似曾相识
struct sys_device *sysdev;
pr_debug("Resuming type '%s':\n",
kobject_name(&cls->kset.kobj));
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { //遍历sysdev_class上面的设备
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
__sysdev_resume(sysdev); //然后让sysdev_class上的每个驱动对这个设备进行唤醒
}
}
return 0;
}
下面函数是挂起system_kset下的所有设备,跟上面的函数是反向的,其实大部分都是类似的看懂前面下面就是走马观花了
int sysdev_suspend(pm_message_t state)
{
struct sysdev_class *cls;
struct sys_device *sysdev, *err_dev;
struct sysdev_driver *drv, *err_drv;
int ret;
pr_debug("Checking wake-up interrupts\n");
/* Return error code if there are any wake-up interrupts pending */
ret = check_wakeup_irqs(); //先不用管,也没什么
if (ret)
return ret;
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled while suspending system devices\n");
pr_debug("Suspending System Devices\n");
list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { //遍历system_kset上面的sysdev_class
pr_debug("Suspending type '%s':\n",
kobject_name(&cls->kset.kobj));
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { //获取该sysdev_class上面的sys_device
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
/* Call auxillary drivers first */
list_for_each_entry(drv, &cls->drivers, entry) { //遍历sysdev_class上面的driver,然后对sys_device进行挂起操作
if (drv->suspend) {
ret = drv->suspend(sysdev, state);
if (ret)
goto aux_driver;
}
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled after %pF\n",
drv->suspend);
}
/* Now call the generic one */
if (cls->suspend) {
ret = cls->suspend(sysdev, state); //sysdev_class对sys_device进行挂起
if (ret)
goto cls_driver;
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled after %pF\n",
cls->suspend);
}
}
}
return 0;
/* resume current sysdev */
//出错处理
。。。。
return ret;
}
最后是对system_kset进行初始化,如下
int __init system_bus_init(void)
{
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); //很简单吧 在/sys下面创建system目录
if (!system_kset)
return -ENOMEM;
return 0;
}
终于全完了