sysdev_class,sys_device,sysdev_driver详解 http://blog.chinaunix.net/uid-20543183-id-1930813.html

来源:互联网 发布:知金教育招聘 编辑:程序博客网 时间:2024/06/14 07:31

内核版本: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;
}

终于全完了




0 0
原创粉丝点击