关闭

LINUX设备驱动之设备模型二--kset

566人阅读 评论(0) 收藏 举报

我们已经知道了kset内嵌了kobject来表示自身的节点,创建kset就要完成其内嵌kobject,注册kset时会产生一个事件,事件而最终会调用uevent_ops字段指向结构中的函数,这个事件是通过用户空间的hotplug程序处理。下面我们一步一步分析。

内核同样提供了创建和注册kset的函数kset_create_and_add()

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);

    if (!kset)

        return NULL;

    error = kset_register(kset);

    if (error) {

        kfree(kset);

        return NULL;

    }

    return kset;

}

输入参数有一个kset_uevent_ops类型的结构变量,其结构包含三个函数指针,我们在后面的分析到这三个函数在什么时候被调用,kset_uevent_ops结构定义如下:

struct kset_uevent_ops {

    int (*filter)(struct kset *kset, struct kobject *kobj);

    const char *(*name)(struct kset *kset, struct kobject *kobj);

    int (*uevent)(struct kset *kset, struct kobject *kobj,

              struct kobj_uevent_env *env);

};

继续看上面的函数,先调用kset_create ()创建一个kset,接着调用kset_register()注册它。

static struct kset *kset_create(const char *name,

                struct kset_uevent_ops *uevent_ops,

                struct kobject *parent_kobj)

{

    struct kset *kset;

    int retval;

 

    kset = kzalloc(sizeof(*kset), GFP_KERNEL);

    if (!kset)

        return NULL;

    retval = kobject_set_name(&kset->kobj, name);

    if (retval) {

        kfree(kset);

        return NULL;

    }

    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;

 

    return kset;

}

为kset分配内存,如我们上面分析,初始化了kset内嵌的kobject(这里还未将kobject注册到文件系统),另外用输入参数初始化kset的uevent_ops字段。

接着看kset的注册函数kset_register():

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;

}

在这里终于看到调用kobject_add_internal()将kset内嵌的kobject注册到文件系统,这个函数我们在上面已经分析。

我们上面说到注册kset会产生一个事件,就是在这里调用了kobject_uevent(&k->kobj, KOBJ_ADD)

kobject_uevent()在\lib\ kobject_uevent.c中:

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

{

    return kobject_uevent_env(kobj, action, NULL);

}

转入kobject_uevent_env():

这个函数比较长,我们分段分析

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,

               char *envp_ext[])

{

    struct kobj_uevent_env *env;

    const char *action_string = kobject_actions[action];

    const char *devpath = NULL;

    const char *subsystem;

    struct kobject *top_kobj;

    struct kset *kset;

    struct kset_uevent_ops *uevent_ops;

    u64 seq;

    int i = 0;

    int retval = 0;

 

    pr_debug("kobject: '%s' (%p): %s\n",

         kobject_name(kobj), kobj, __func__);

 

    /* search the kset we belong to */

    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;

如果如果kobj的kset和parent字段都不存在,说明找不到所属kset,也就没有uevent_ops,不能产生事件,返回错误信息;相反则找到了存在kset的kobj或父kobject(依次往上找),并赋值给uevent_ops。

 

    /* 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;

    }

如果设置了uevent_suppress字段,说明不希望产生事件,忽略事件正确返回。注意驱动程序将在适当的地方产生改事件。

    /* 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;

        }

如果uevent_ops->filter返回0,同样忽略事件正确返回。

    if (uevent_ops && uevent_ops->name)

        subsystem = uevent_ops->name(kset, kobj);

    else

        subsystem = kobject_name(&kset->kobj);

    if (!subsystem) {

        pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "

             "event to drop!\n", kobject_name(kobj), kobj,

             __func__);

        return 0;

    }

获得子系统的名称,不存在则返回。

    /* environment buffer */

    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

    if (!env)

        return -ENOMEM;

分配一个kobj_uevent_env结构内存,用于存放环境变量的值。

/* complete object path */

    devpath = kobject_get_path(kobj, GFP_KERNEL);

    if (!devpath) {

        retval = -ENOENT;

        goto exit;

    }

获得引发事件的kobject在sysfs中的路径。

    /* default keys */

    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;

        }

    }

调用add_uevent_var()kobj_uevent_env填充action_string,kobject路径,子系统名称以及其他指定环境变量。

 

     /* let the kset specific function add its stuff */

     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,

                    __FUNCTION__, retval);

              goto exit;

         }

     }

调用uevent_ops的uevent函数,编程人员可在此函数中实现自定义的功能。

    /*

     * Mark "add" and "remove" events in the object to ensure proper

     * events to userspace during automatic cleanup. If the object did

     * send an "add" event, "remove" will automatically generated by

     * the core, if not already done by the caller.

     */

    if (action == KOBJ_ADD)

        kobj->state_add_uevent_sent = 1;

    else if (action == KOBJ_REMOVE)

        kobj->state_remove_uevent_sent = 1;

设置KOBJ_ADD和KOBJ_REMOVE的标志。

    /* we will send an event, so request a new sequence number */

    spin_lock(&sequence_lock);

    seq = ++uevent_seqnum;

    spin_unlock(&sequence_lock);

    retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);

    if (retval)

        goto exit;

 

#if defined(CONFIG_NET)

    /* send netlink message */

    if (uevent_sock) {

        struct sk_buff *skb;

        size_t len;

 

        /* allocate message with the maximum possible size */

        len = strlen(action_string) + strlen(devpath) + 2;

        skb = alloc_skb(len + env->buflen, GFP_KERNEL);

        if (skb) {

            char *scratch;

 

            /* add header */

            scratch = skb_put(skb, len);

            sprintf(scratch, "%s@%s", action_string, devpath);

 

            /* copy keys to our continuous event payload buffer */

            for (i = 0; i < env->envp_idx; i++) {

                len = strlen(env->envp[i]) + 1;

                scratch = skb_put(skb, len);

                strcpy(scratch, env->envp[i]);

            }

 

            NETLINK_CB(skb).dst_group = 1;

            retval = netlink_broadcast(uevent_sock, skb, 0, 1,

                           GFP_KERNEL);

            /* ENOBUFS should be handled in userspace */

            if (retval == -ENOBUFS)

                retval = 0;

        } else

            retval = -ENOMEM;

    }

#endif

    /* call uevent_helper, usually only enabled during early boot */

    if (uevent_helper[0]) {

        char *argv [3];

 

        argv [0] = uevent_helper;

        argv [1] = (char *)subsystem;

        argv [2] = NULL;

        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;

添加HOME和PATH环境变量。

        retval = call_usermodehelper(argv[0], argv,

                         env->envp, UMH_WAIT_EXEC);

    }

 

exit:

    kfree(devpath);

    kfree(env);

    return retval;

}

调用hotplug函数。

看一下kset_unregister()

void kset_unregister (struct kset *k)

{

    if (!k)

        return;

    kobject_put(&k-> kobj);

}

减少其内嵌的kobj计数,为0则释放其内存空间。

 

已经分析完kobject和kset,linux的设备模型就是基于这两个数据结构的,在此基础上,后续将分析设备模型中的device、driver、和bus。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:66700次
    • 积分:979
    • 等级:
    • 排名:千里之外
    • 原创:18篇
    • 转载:33篇
    • 译文:0篇
    • 评论:5条
    文章分类
    最新评论