LINUX設備驅動之設備模型二--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註冊到文件系統),另外用輸入參數初始化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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章