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

    技术2025-10-02  16

    Eric Fang  2010-01-11

    --------------------------------------------------------------

    本站分析linux内核源码,版本号为2.6.32.3

    转载请注明出处:http://ericfang.cublog.cn/

    --------------------------------------------------------------

     

    接着上一篇文章,分析kset

    我们已经知道了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注册到文件系统),另外用输入参数初始化ksetuevent_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;

    如果如果kobjksetparent字段都不存在,说明找不到所属kset,也就没有uevent_ops,不能产生事件,返回错误信息;相反则找到了存在ksetkobj或父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;

        }

    获得引发事件的kobjectsysfs中的路径。

        /* 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_opsuevent函数,编程人员可在此函数中实现自定义的功能。

        /*

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

    添加HOMEPATH环境变量。

            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则释放其内存空间。

     

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

    最新回复(0)