LINUX设备驱动之设备模型

LINUX 设备驱动驱动程序模型的核心数据结构是 kobject , kobject 数据结构在 /linux/kobject.h 中定义:

struct kobject {

       const char             *name;

       struct list_head       entry;

       struct kobject         *parent;

       struct kset             *kset;

       struct kobj_type     *ktype;

       struct sysfs_dirent  *sd;

       struct kref             kref;

       unsigned int state_initialized:1;

       unsigned int state_in_sysfs:1;

       unsigned int state_add_uevent_sent:1;

       unsigned int state_remove_uevent_sent:1;

       unsigned int uevent_suppress:1;

};

每个 kobject 都有它的父节点 parent 、 kset 、 kobj_type 指针,这三者是驱动模型的基本结构, kset 是 kobject 的集合,在 /linux/kobject.h 中定义:

struct kset {

       struct list_head list;

       spinlock_t list_lock;

       struct kobject kobj;

       struct kset_uevent_ops *uevent_ops;

};

可以看到每个 kset 内嵌了一个 kobject ( kobj 字段),用来表示其自身节点,其 list 字段指向了所包含的 kobject的链表头。我们在后面的分析中将看到 kobject 如果没有指定父节点, parent 将指向其 kset 内嵌的 kobject 。

每个 kobject 都有它的 kobj_type 字段指针,用来表示 kobject 在文件系统中的操作方法, kobj_type 结构也在/linux/kobject.h 中定义:

struct kobj_type {

       void (*release)(struct kobject *kobj);

       struct sysfs_ops *sysfs_ops;

       struct attribute ** default_attrs;

};

release 方法是在 kobject 释放是调用, sysfs_ops 指向 kobject 对应的文件操作, default_attrskobject 的默认属性,sysfs_ops 的将使用 default_attrs 属性(在后面的分析中我们将会看到)。

从上面的分析我们可以想象到 kobject 、 kset 、 kobj_type 的层次结构:

我们可以把一个 kobject 添加到文件系统中去(实际上是添加到其父节点所代表的 kset 中去),内核提供kobject_create_and_add() 接口函数:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

{

       struct kobject *kobj;

       int retval;

 

       kobj = kobject_create();

       if (!kobj)

              return NULL;

 

       retval = kobject_add(kobj, parent, "%s", name);

       if (retval) {

              printk(KERN_WARNING "%s: kobject_add error: %d/n",

                     __func__, retval);

              kobject_put(kobj);

              kobj = NULL;

       }

       return kobj;

}

kobject _create() 为要创建的 kobject 分配内存空间并对其初始化。

struct kobject *kobject_create(void)

{

       struct kobject *kobj;

 

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

       if (!kobj)

              return NULL;

 

       kobject_init(kobj, &dynamic_kobj_ktype);

       return kobj;

}

kobject_init() 对 kobject 基本字段进行初始化,用输入参数设置 kobj_type 属性。

这里粘出代码以供参考:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

       char *err_str;

 

       if (!kobj) {

              err_str = "invalid kobject pointer!";

              goto error;

       }

       if (!ktype) {

              err_str = "must have a ktype to be initialized properly!/n";

              goto error;

       }

       if (kobj->state_initialized) {

              /* do not error out as sometimes we can recover */

              printk(KERN_ERR "kobject (%p): tried to init an initialized "

                     "object, something is seriously wrong./n", kobj);

              dump_stack();

       }

 

       kobject_init_internal(kobj);

       kobj->ktype = ktype;

       return;

 

error:

       printk(KERN_ERR "kobject (%p): %s/n", kobj, err_str);

       dump_stack();

}

static void kobject_init_internal(struct kobject *kobj)

{

       if (!kobj)

              return;

       kref_init(&kobj->kref);

       INIT_LIST_HEAD(&kobj->entry);

       kobj->state_in_sysfs = 0;

       kobj->state_add_uevent_sent = 0;

       kobj->state_remove_uevent_sent = 0;

       kobj->state_initialized = 1;

}

接着看 kobject_add() 函数:

int kobject_add(struct kobject *kobj, struct kobject *parent,

              const char *fmt, ...)

{

       va_list args;

       int retval;

 

       if (!kobj)

              return -EINVAL;

 

       if (!kobj->state_initialized) {

              printk(KERN_ERR "kobject '%s' (%p): tried to add an "

                     "uninitialized object, something is seriously wrong./n",

                     kobject_name(kobj), kobj);

              dump_stack();

              return -EINVAL;

       }

       va_start(args, fmt);

       retval = kobject_add_varg(kobj, parent, fmt, args);

       va_end(args);

 

       return retval;

}

在上面的初始化中已把位变量设位 1

va_start(args, fmt) 和 va_end(args) 使用可变参数(可见参数用法不在这里分析),在 kobject_add_varg 中将把 fmt指向的内容赋给 kobject 的 name 字段。下面我们详细看看 kobject_add_varg 函数:

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,

                         const char *fmt, va_list vargs)

{

       int retval;

 

       retval = kobject_set_name_vargs(kobj, fmt, vargs);

       if (retval) {

              printk(KERN_ERR "kobject: can not set name properly!/n");

              return retval;

       }

       kobj->parent = parent;

       return kobject_add_internal(kobj);

}

kobject_set_name_vargs(kobj, fmt, vargs) ,如果 kobj 的 name 字段指向的内容为空,则为分配一个内存空间并用fmt 指向的内容初始化,把地址赋给 kobj 的 name 字段。

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

                              va_list vargs)

{

       const char *old_name = kobj->name;

       char *s;

 

       if (kobj->name && !fmt)

              return 0;

 

       kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

       if (!kobj->name)

              return -ENOMEM;

 

       /* ewww... some of these buggers have '/' in the name ... */

       while ((s = strchr(kobj->name, '/')))

              s[0] = '!';

 

       kfree(old_name);

       return 0;

}

char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

{

       unsigned int len;

       char *p;

       va_list aq;

 

       va_copy(aq, ap);

       len = vsnprintf(NULL, 0, fmt, aq);

       va_end(aq);

 

       p = kmalloc(len+1, gfp);

       if (!p)

              return NULL;

 

       vsnprintf(p, len+1, fmt, ap);

 

       return p;

}

继续 kobject_add_varg ()返回 kobject_add_internal(kobj) ,就是在这个函数理为 kobj 创建文件系统结构:

static int kobject_add_internal(struct kobject *kobj)

{

       int error = 0;

       struct kobject *parent;

 

       if (!kobj)

              return -ENOENT;

       if (!kobj->name || !kobj->name[0]) {

              WARN(1, "kobject: (%p): attempted to be registered with empty "

                       "name!/n", kobj);

              return -EINVAL;

       }

检查 kobj 和它的 name 字段,不存在则返回错误信息。

 

       parent = kobject_get(kobj->parent);

获得其父节点,并增加父节点的计数器, kobject 结构中的 kref 字段用于容器的计数, kobject_get 和 kobject_put分别增加和减少计数器,如果计数器为 0 ,则释放该 kobject , kobject_get 返回该 kobject 。

       /* join kset if set, use it as parent if we do not already have one */

       if (kobj->kset) {

              if (!parent)

                     parent = kobject_get(&kobj->kset->kobj);

              kobj_kset_join(kobj);

              kobj->parent = parent;

       }

在这里我们可以看到,如果调用 kobject_create_and_add ()时参数 parent 设为 NULL ,则会去检查 kobj 的 kset是否存在,如果存在就会把 kset 所嵌套的 kobj 作为其父节点,并把 kobj 添加到 kset 中去。

              pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",

                kobject_name(kobj), kobj, __func__,

                parent ? kobject_name(parent) : "<NULL>",

                kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

打印一些调试信息,接着为 kobj 创建目录:

       error = create_dir(kobj);

       if (error) {

              kobj_kset_leave(kobj);

              kobject_put(parent);

              kobj->parent = NULL;

 

              /* be noisy on error issues */

              if (error == -EEXIST)

                     printk(KERN_ERR "%s failed for %s with "

                            "-EEXIST, don't try to register things with "

                            "the same name in the same directory./n",

                            __func__, kobject_name(kobj));

              else

                     printk(KERN_ERR "%s failed for %s (%d)/n",

                            __func__, kobject_name(kobj), error);

              dump_stack();

       } else

              kobj->state_in_sysfs = 1;

 

       return error;

}

如果创建不成功,则回滚上面的操作,成功的话则设置 kobj 的 state_in_sysfs 标志。

在看看 create_dir ()函数中具体创建了那些内容:

static int create_dir(struct kobject *kobj)

{

       int error = 0;

       if (kobject_name(kobj)) {

              error = sysfs_create_dir(kobj);

              if (!error) {

                     error = populate_dir(kobj);

                     if (error)

                            sysfs_remove_dir(kobj);

              }

       }

       return error;

}

sysfs_create_dir ()先为 kobj 创建了一个目录文件

int sysfs_create_dir(struct kobject * kobj)

{

       struct sysfs_dirent *parent_sd, *sd;

       int error = 0;

 

       BUG_ON(!kobj);

 

       if (kobj->parent)

              parent_sd = kobj->parent->sd;

       else

              parent_sd = &sysfs_root;

 

       error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

       if (!error)

              kobj->sd = sd;

       return error;

}

如果 kobj->parent 为 NULL ,就把 &sysfs_root 作为父节点 sd ,即 在/sys 下面创建结点。

然后调用 populate_dir :

static int populate_dir(struct kobject *kobj)

{

       struct kobj_type *t = get_ktype(kobj);

       struct attribute *attr;

       int error = 0;

       int i;

 

       if (t && t->default_attrs) {

              for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

                     error = sysfs_create_file(kobj, attr);

                     if (error)

                            break;

              }

       }

       return error;

}

得到 kobj 的 kobj_type ,历遍 kobj_type 的 default_attrs 并创建属性文件,文件的操作会回溯到sysfs_ops的show 和store 会调用封装了attribute 的kobj_attribute 结构的store 和show 方法(在后面的代码中将会分析)。

由于上面kobject_init(kobj, &dynamic_kobj_ktype) 用默认dynamic_kobj_ktype 作为 kobj_type 参数,而dynamic_kobj_ktype 的 default_attrs 为 NULL ,所以这里没有创建属性文件。

至此,我们已经知道了 kobject_create_and_add() 函数创建 kobject ,挂到父 kobject ,并设置其 kobj_type ,在文件系统中为其创建目录和属性文件等。

另外,如果我们已静态定义了要创建的 kobject ,则可以调用 kobject_init_and_add() 来注册 kobject ,其函数如下:

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

                       struct kobject *parent, const char *fmt, ...)

{

       va_list args;

       int retval;

 

       kobject_init(kobj, ktype);

 

       va_start(args, fmt);

       retval = kobject_add_varg(kobj, parent, fmt, args);

       va_end(args);

 

       return retval;

}

通过上面的分析我们很轻松就能理解这个函数。

 

内核提供注销 kobject 的函数是 kobject_del()

void kobject_del(struct kobject *kobj)

{

       if (!kobj)

              return;

 

       sysfs_remove_dir(kobj);

       kobj->state_in_sysfs = 0;

       kobj_kset_leave(kobj);

       kobject_put(kobj->parent);

       kobj->parent = NULL;

}

删除 kobj 目录及其目录下的属性文件,清 kobj 的 state_in_sysfs 标志,把 kobj 从 kset 中删除,减少 kobj->parent 的计数并设其指针为空。


我们已经知道了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 。

在清楚了 kobject 之后,就可以继续分析 device 、 driver 、 bus 了,这三者是设备驱动程序的基本数据结构。

 

我们可以这样理解,内核用 device 来表示各种设备,然后用 driver 来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线 bus_type ,总线上有两个集合(也可以理解为两条链,如上图中的 bus ),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作 2 ),同时可能会到驱动集合去匹配适合它的驱动(图中操作 3 ,在此之前 device 和 driver 没有挂钩),如何找到了就会将它们联系起来(图中操作 6 )。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作 1 ),同时去设备集合中找出它锁驱动的设备(图中操作 4 ,在此之前 device 和 driver 没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作 6 )。

下面我们进入到代码中去:

struct bus_type 结构定义如下:

struct bus_type {

       const char             *name;

       struct bus_attribute *bus_attrs;

       struct device_attribute    *dev_attrs;

       struct driver_attribute    *drv_attrs;

 

       int (*match)(struct device *dev, struct device_driver *drv);

       int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

       int (*probe)(struct device *dev);

       int (*remove)(struct device *dev);

       void (*shutdown)(struct device *dev);

 

       int (*suspend)(struct device *dev, pm_message_t state);

       int (*resume)(struct device *dev);

 

       const struct dev_pm_ops *pm;

 

       struct bus_type_private *p;

};

较之先前一些内核版本, bus_type 把部分私有字段封装到 bus_type_private 类型结构里:

struct bus_type_private {

       struct kset subsys;

       struct kset *drivers_kset;

       struct kset *devices_kset;

       struct klist klist_devices;

       struct klist klist_drivers;

       struct blocking_notifier_head bus_notifier;

       unsigned int drivers_autoprobe:1;

       struct bus_type *bus;

};

字段 klist_devices 和 klist_drivers 分别表示挂在 bus_type 上的驱动和设备链表, bus_type 的其他字段和函数指针将在分析过程中说明。

首先我们要为设备和驱动注册一个总线类型:

int bus_register(struct bus_type *bus)

{

       int retval;

       struct bus_type_private *priv;

 

       priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

       if (!priv)

              return -ENOMEM;

 

       priv->bus = bus;

       bus->p = priv;

分配私有区域的内存空间,并将其关联

       BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

初始化回调函数

       retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

       if (retval)

              goto out;

 

       priv->subsys.kobj.kset = bus_kset;

       priv->subsys.kobj.ktype = &bus_ktype;

       priv->drivers_autoprobe = 1;

 

       retval = kset_register(&priv->subsys);

       if (retval)

              goto out;

这里我们看到了 subsys 用来表示它的文件系统,可以回想上一节 kset 的注册。

这个 bus_kset 是系统启动是创建的,系统 init 进程 kernel_init() 中调用 do_basic_setup() ,其中调用 driver_init(), 其中调用的 buses_init() ,如下

int __init buses_init(void)

{

       bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

       if (!bus_kset)

              return -ENOMEM;

       return 0;

}

从而知道创建的文件系统目录在 /sys/bus 下。

static struct kset_uevent_ops bus_uevent_ops = {

       .filter = bus_uevent_filter,

};

static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)

{

       struct kobj_type *ktype = get_ktype(kobj);

 

       if (ktype == &bus_ktype)

              return 1;

       return 0;

}

继续 bus_register ()中的代码:

       retval = bus_create_file(bus, &bus_attr_uevent);

       if (retval)

              goto bus_uevent_fail;

bus_create_file ()如下 :

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)

{

       int error;

       if (bus_get(bus)) {

              error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);

              bus_put(bus);

       } else

              error = -EINVAL;

       return error;

}

 bus_attr_uevent 创建了 bus->p->subsys.kobj 的属性文件,由上面的赋值知道其读写操作在 bus_ktype 的sysfs_ops ,其定义如下:

static struct kobj_type bus_ktype = {

       .sysfs_ops      = &bus_sysfs_ops,

};

static struct sysfs_ops bus_sysfs_ops = {

       .show     = bus_attr_show,

       .store      = bus_attr_store,

};

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,

                          char *buf)

{

       struct bus_attribute *bus_attr = to_bus_attr(attr);

       struct bus_type_private *bus_priv = to_bus(kobj);

       ssize_t ret = 0;

 

       if (bus_attr->show)

              ret = bus_attr->show(bus_priv->bus, buf);

       return ret;

}

 

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,

                           const char *buf, size_t count)

{

       struct bus_attribute *bus_attr = to_bus_attr(attr);

       struct bus_type_private *bus_priv = to_bus(kobj);

       ssize_t ret = 0;

 

       if (bus_attr->store)

              ret = bus_attr->store(bus_priv->bus, buf, count);

       return ret;

}

由上面的程序可以看出文件的读写操作最终会回到 struct bus_attribute &bus_attr_uevent 的 show 和 store 方法。

bus_attr_uevent 是通过宏定义的:

#define __ATTR(_name,_mode,_show,_store) { /

       .attr = {.name = __stringify(_name), .mode = _mode },     /

       .show     = _show,                             /

       .store      = _store,                             /

}

 

#define BUS_ATTR(_name, _mode, _show, _store)    /

struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

 show 方法为 NULL ,说明不可读。

static ssize_t bus_uevent_store(struct bus_type *bus,

                            const char *buf, size_t count)

{

       enum kobject_action action;

 

       if (kobject_action_type(buf, count, &action) == 0)

              kobject_uevent(&bus->p->subsys.kobj, action);

       return count;

}

在用户空间可以控制事件的发生 , 如 echo add > event 将产生一个 add 的事件。

接着继续 bus_register ()中的代码:

       priv->devices_kset = kset_create_and_add("devices", NULL,

                                            &priv->subsys.kobj);

       if (!priv->devices_kset) {

              retval = -ENOMEM;

              goto bus_devices_fail;

       }

 

       priv->drivers_kset = kset_create_and_add("drivers", NULL,

                                            &priv->subsys.kobj);

       if (!priv->drivers_kset) {

              retval = -ENOMEM;

              goto bus_drivers_fail;

       }

创建两个 kset ,其内嵌 object 的 parent 都指向 priv->subsys.kobj ,说明其文件系统在 bus 所在目录下。由上回分析中知道 kset_create_and_add() 时其内嵌 kobj 的 ktype 指向 kset_ktype ,而这里没有输入参数的 uevent_ops 为NULL ,则会以 priv->subsys.kobj->kset->uevent_ops 来产生事件,我们上面分析中知道这个 uevent_ops 为bus_uevent_ops ,其 filter 会比较 kobj 的 ktype 是不是 &bus_ktype ,而这里是 &kset_ktype ,所以这里是忽略了事件。

       klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

       klist_init(&priv->klist_drivers, NULL, NULL);

初始化总线上设备和驱动的链表。

       retval = add_probe_files(bus);

       if (retval)

              goto bus_probe_files_fail;

add_probe_files() 函数如下:

static int add_probe_files(struct bus_type *bus)

{

       int retval;

 

       retval = bus_create_file(bus, &bus_attr_drivers_probe);

       if (retval)

              goto out;

 

       retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);

       if (retval)

              bus_remove_file(bus, &bus_attr_drivers_probe);

out:

       return retval;

}

同上面创建 bus_attr_uevent 属性一样创建 bus_attr_drivers_probe 和 bus_attr_drivers_autoprobe 属性文件。粘出属性的代码:

static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);

static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,

              show_drivers_autoprobe, store_drivers_autoprobe);

bus_attr_drivers_autoprobe 的 show 指向 NULL ,说明其改文件不可写。

static ssize_t store_drivers_probe(struct bus_type *bus,

                               const char *buf, size_t count)

{

       struct device *dev;

 

       dev = bus_find_device_by_name(bus, NULL, buf);

       if (!dev)

              return -ENODEV;

       if (bus_rescan_devices_helper(dev, NULL) != 0)

              return -EINVAL;

       return count;

}

将用户输 ( 在用户空间 ) 和的设备名称对应的设备与驱动匹配一次。

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)

{

       return sprintf(buf, "%d/n", bus->p->drivers_autoprobe);

}

在用户空间可以打印 drivers_autoprobe 的值 , 如 cat drivers_autoprobe

static ssize_t store_drivers_autoprobe(struct bus_type *bus,

                                   const char *buf, size_t count)

{

       if (buf[0] == '0')

              bus->p->drivers_autoprobe = 0;

       else

              bus->p->drivers_autoprobe = 1;

       return count;

}

在用户空间可以改变 drivers_autoprobe 的值 , 如 echo 1 > drivers_autoprobe

继续分析 bus_register ()中的代码:

       retval = bus_add_attrs(bus);

       if (retval)

              goto bus_attrs_fail;

bus_add_attrs() 如下:

static int bus_add_attrs(struct bus_type *bus)

{

       int error = 0;

       int i;

 

       if (bus->bus_attrs) {

              for (i = 0; attr_name(bus->bus_attrs[i]); i++) {

                     error = bus_create_file(bus, &bus->bus_attrs[i]);

                     if (error)

                            goto err;

              }

       }

done:

       return error;

err:

       while (--i >= 0)

              bus_remove_file(bus, &bus->bus_attrs[i]);

       goto done;

}

如果 bus->bus_attrs 存在,则同样为其创建属性文件。

       pr_debug("bus: '%s': registered/n", bus->name);

       return 0;

 

bus_attrs_fail:

       remove_probe_files(bus);

bus_probe_files_fail:

       kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

       kset_unregister(bus->p->devices_kset);

bus_devices_fail:

       bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

       kset_unregister(&bus->p->subsys);

       kfree(bus->p);

out:

       bus->p = NULL;

       return retval;

bus_register() 分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。

接上一篇文章,在往总线注册注册设备前要先创建device,我们可以静态的定义device结构变量,然后调用device_register()将其注册,或者通过内核提供的device_create()接口函数创建和注册device。先看看device的数据结构定义:

struct device {

       struct device          *parent;

 

       struct device_private      *p;

 

       struct kobject kobj;

       const char             *init_name; /* initial name of the device */

       struct device_type  *type;

 

       struct semaphore    sem;       /* semaphore to synchronize calls to

                                    * its driver.

                                    */

 

       struct bus_type      *bus;             /* type of bus device is on */

       struct device_driver *driver;  /* which driver has allocated this

                                      device */

       void        *platform_data;      /* Platform specific data, device

                                      core doesn't touch it */

       struct dev_pm_info       power;

 

#ifdef CONFIG_NUMA

       int           numa_node;    /* NUMA node this device is close to */

#endif

       u64         *dma_mask;   /* dma mask (if dma'able device) */

       u64         coherent_dma_mask;/* Like dma_mask, but for

                                        alloc_coherent mappings as

                                        not all hardware supports

                                        64 bit addresses for consistent

                                        allocations such descriptors. */

 

       struct device_dma_parameters *dma_parms;

 

       struct list_head       dma_pools;     /* dma pools (if dma'ble) */

 

       struct dma_coherent_mem    *dma_mem; /* internal for coherent mem

                                        override */

       /* arch specific additions */

       struct dev_archdata       archdata;

 

       dev_t                    devt;       /* dev_t, creates the sysfs "dev" */

 

       spinlock_t              devres_lock;

       struct list_head       devres_head;

 

       struct klist_node     knode_class;

       struct class            *class;

       const struct attribute_group **groups; /* optional groups */

 

       void (*release)(struct device *dev);

};

和总线类型一样,device也把部分私有数据封装到device_private结构中:

struct device_private {

       struct klist klist_children;

       struct klist_node knode_parent;

       struct klist_node knode_driver;

       struct klist_node knode_bus;

       void *driver_data;

       struct device *device;

};

创建device的函数device_create()如下:

struct device *device_create(struct class *class, struct device *parent,

                          dev_t devt, void *drvdata, const char *fmt, ...)

{

       va_list vargs;

       struct device *dev;

 

       va_start(vargs, fmt);

       dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);

       va_end(vargs);

       return dev;

}

调用device_create_vargs():

struct device *device_create_vargs(struct class *class, struct device *parent,

                               dev_t devt, void *drvdata, const char *fmt,

                               va_list args)

{

       struct device *dev = NULL;

       int retval = -ENODEV;

 

       if (class == NULL || IS_ERR(class))

              goto error;

 

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

       if (!dev) {

              retval = -ENOMEM;

              goto error;

       }

 

       dev->devt = devt;

       dev->class = class;

       dev->parent = parent;

       dev->release = device_create_release;

       dev_set_drvdata(dev, drvdata);

 

       retval = kobject_set_name_vargs(&dev->kobj, fmt, args);

       if (retval)

              goto error;

 

       retval = device_register(dev);

       if (retval)

              goto error;

 

       return dev;

 

error:

       put_device(dev);

       return ERR_PTR(retval);

}

该函数为创建的device分配内存空间,根据输入参数和一些默认值对其初始化,然后调用device_register将其注册到相应的总线上。

int device_register(struct device *dev)

{

       device_initialize(dev);

       return device_add(dev);

}                       

device_initialize()先对dev初始化:

void device_initialize(struct device *dev)

{

       dev-> kobj.kset = devices_kset;

为其内嵌kobj的kset赋值,类似bus_kset,devices_kset也是系统启动是创建的:

int __init devices_init(void)

{

       devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

       if (!devices_kset)

              return -ENOMEM;

       dev_kobj = kobject_create_and_add("dev", NULL);

       if (!dev_kobj)

              goto dev_kobj_err;

       sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

       if (!sysfs_dev_block_kobj)

              goto block_kobj_err;

       sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

       if (!sysfs_dev_char_kobj)

              goto char_kobj_err;

 

       return 0;

 

 char_kobj_err:

       kobject_put(sysfs_dev_block_kobj);

 block_kobj_err:

       kobject_put(dev_kobj);

 dev_kobj_err:

       kset_unregister(devices_kset);

       return -ENOMEM;

}

对应sysfs中的/sys/devices、/sys/devices/block、/sys/devices/char。

接着device_initialize()中的代码:

       kobject_init(&dev-> kobj, &device_ktype);

初始化dev内嵌的kobj

       INIT_LIST_HEAD(&dev->dma_pools);

       init_MUTEX(&dev->sem);

       spin_lock_init(&dev->devres_lock);

       INIT_LIST_HEAD(&dev->devres_head);

初始化一些其它的字段。

       device_init_wakeup(dev, 0);

       device_pm_init(dev);

电源管理的一些初始化。

       set_dev_node(dev, -1);

设置numa。

}   

初始化dev后调用device_add() 往相应总线上添加设备。分段分析这个函数:

int device_add(struct device *dev)

{

       struct device *parent = NULL;

       struct class_interface *class_intf;

       int error = -EINVAL;

 

       dev = get_device(dev);

       if (!dev)

              goto done;

 

       if (!dev->p) {

              error = device_private_init(dev);

              if (error)

                     goto done;

       }

增加dev的计数,初始化其私有结构,实际上面初始化调用的dev_set_drvdata(dev, drvdata)里边已经包含了device_private_init(),但如果是直接调用device_register()而不是device_create()时,必须在这里再初始化一次,显然第一个调用device_private_init()是不必要id。所以在这里才分析device_private_init(),函数如下:

int device_private_init(struct device *dev)

{

       dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);

       if (!dev->p)

              return -ENOMEM;

为其分配内存。

       dev->p->device = dev;

关联到dev。

       klist_init(&dev->p-> klist_children, klist_children_get,

                 klist_children_put);

初始化klist_children

       return 0;

}

接着device_add()函数。

       /*

        * for statically allocated devices, which should all be converted

        * some day, we need to initialize the name. We prevent reading back

        * the name, and force the use of dev_name()

        */

       if (dev->init_name) {

              dev_set_name(dev, "%s", dev->init_name);

              dev->init_name = NULL;

       }

 

       if (!dev_name(dev))

              goto name_error;

 

       pr_debug("device: '%s': %s/n", dev_name(dev), __func__);

dev->init_name设置dev->kobj.name,而后dev->init_name指向NULL,如果dev->kobj.name则跳到name_error。

       parent = get_device(dev->parent);

增加dev->parent-> kobj的计数。

       setup_parent(dev, parent);

看下这个函数:

static void setup_parent(struct device *dev, struct device *parent)

{

       struct kobject *kobj;

       kobj = get_device_parent(dev, parent);

       if (kobj)

              dev->kobj.parent = kobj;

}

static struct kobject *get_device_parent(struct device *dev,

                                    struct device *parent)

{

       int retval;

 

       if (dev->class) {

              struct kobject *kobj = NULL;

              struct kobject *parent_kobj;

              struct kobject *k;

 

              /*

               * If we have no parent, we live in "virtual".

               * Class-devices with a non class-device as parent, live

               * in a "glue" directory to prevent namespace collisions.

               */

              if (parent == NULL)

                     parent_kobj = virtual_device_parent(dev);

              else if (parent->class)

                     return &parent->kobj;

              else

                     parent_kobj = &parent->kobj;

 

              /* find our class-directory at the parent and reference it */

              spin_lock(&dev->class->p->class_dirs.list_lock);

              list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)

                     if (k->parent == parent_kobj) {

                            kobj = kobject_get(k);

                            break;

                     }

              spin_unlock(&dev->class->p->class_dirs.list_lock);

              if (kobj)

                     return kobj;

 

              /* or create a new class-directory at the parent device */

              k = kobject_create();

              if (!k)

                     return NULL;

              k->kset = &dev->class->p->class_dirs;

              retval = kobject_add(k, parent_kobj, "%s", dev->class->name);

              if (retval < 0) {

                     kobject_put(k);

                     return NULL;

              }

              /* do not emit an uevent for this simple "glue" directory */

              return k;

       }

 

       if (parent)

              return &parent->kobj;

       return NULL;

}

dev-> kobj.parent的设置如下:如果dev->parent,则将dev->parent->kobj赋给它,否则如果device_create() 的class参数不为为NULL时,则通过virtual_device_parent(dev)获得其parent,否则指向NULL,在调用kobject_add()时使其kset字段的kset内嵌的object。

继续device_add()函数

       /* use parent numa_node */

       if (parent)

              set_dev_node(dev, dev_to_node(parent));

 

       /* first, register with generic layer. */

       /* we require the name to be set before, and pass NULL */

       error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

       if (error)

              goto Error;

dev->kobj添加到系统中。

       /* notify platform of device entry */

       if (platform_notify)

              platform_notify(dev);

如果定义了platform_notify()函数,则调用它,在drivers/base/core.c中有:

int (*platform_notify)(struct device *dev) = NULL;

说明默认将不会调用它。

       error = device_create_file(dev, &uevent_attr);

       if (error)

              goto attrError;

建立dev的uevent_attr属性文件,这里的uevent_attr定义如下:

static struct device_attribute uevent_attr =

       __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);

show_uevent()函数如下:

static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,

                        char *buf)

{

       struct kobject *top_kobj;

       struct kset *kset;

       struct kobj_uevent_env *env = NULL;

       int i;

       size_t count = 0;

       int retval;

 

       /* search the kset, the device belongs to */

       top_kobj = &dev->kobj;

       while (!top_kobj->kset && top_kobj->parent)

              top_kobj = top_kobj->parent;

       if (!top_kobj->kset)

              goto out;

 

       kset = top_kobj->kset;

       if (!kset->uevent_ops || !kset->uevent_ops->uevent)

              goto out;

 

       /* respect filter */

       if (kset->uevent_ops && kset->uevent_ops->filter)

              if (!kset->uevent_ops->filter(kset, &dev->kobj))

                     goto out;

 

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

       if (!env)

              return -ENOMEM;

 

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

       retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);

       if (retval)

              goto out;

 

       /* copy keys to file */

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

              count += sprintf(&buf[count], "%s/n", env->envp[i]);

out:

       kfree(env);

       return count;

}

改函数表明如果读dev的uevent则会显示dev-> kobj所属kset产生的环境变量。

store_uevent定义如下:

static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,

                         const char *buf, size_t count)

{

       enum kobject_action action;

 

       if (kobject_action_type(buf, count, &action) == 0) {

              kobject_uevent(&dev->kobj, action);

              goto out;

       }

 

       dev_err(dev, "uevent: unsupported action-string; this will "

                   "be ignored in a future kernel version/n");

       kobject_uevent(&dev->kobj, KOBJ_ADD);

out:

       return count;

}

从程序可以看出对这个文件的作用写则会产生相应的事件。如果输入的字符串不合法,则就会产生一个add事件。

接着device_add()函数中的代码:

       if (MAJOR(dev->devt)) {

              error = device_create_file(dev, & devt_attr);

              if (error)

                     goto ueventattrError;

 

              error = device_create_sys_dev_entry(dev);

              if (error)

                     goto devtattrError;

 

              devtmpfs_create_node(dev);

       }

如果dev->devt的煮设备号不为空,则创建devt_attr属性文件和连接文件,devt_attr定义如下

static struct device_attribute devt_attr =

       __ATTR(dev, S_IRUGO, show_dev, NULL);

该属性的store函数为NULL,说明为只读。读操作函数show_dev():

static ssize_t show_dev(struct device *dev, struct device_attribute *attr,

                     char *buf)

{

       return print_dev_t(buf, dev->devt);

}

#define print_dev_t(buffer, dev)                                /

       sprintf((buffer), "%u:%u/n", MAJOR(dev), MINOR(dev))

读操作为打印dev的设备号。

static int device_create_sys_dev_entry(struct device *dev)

{

       struct kobject *kobj = device_to_dev_kobj(dev);

       int error = 0;

       char devt_str[15];

 

       if (kobj) {

              format_dev_t(devt_str, dev->devt);

              error = sysfs_create_link(kobj, &dev->kobj, devt_str);

       }

 

       return error;

}

static struct kobject *device_to_dev_kobj(struct device *dev)

{

       struct kobject *kobj;

 

       if (dev->class)

              kobj = dev->class->dev_kobj;

       else

              kobj = sysfs_dev_char_kobj;

 

       return kobj;

}

如果定义了dev->class,则在相应的目录下建立链接文件,否则默认在/sys/devices/char下建立链接文件。

接着device_add ()函数:

       error = device_add_class_symlinks(dev);

       if (error)

              goto SymlinkError;

       error = device_add_attrs(dev);

       if (error)

              goto AttrsError;

同样在class子系统下建立链接文件、class->dev_attrs属性文件、group等。

继续device_add()中的代码:

      error = bus_add_device(dev);

       if (error)

              goto BusError;

在对应总线目录下的 device 目录下创建几个到 device 的链接文件。

       error = dpm_sysfs_add(dev);

       if (error)

              goto DPMError;

       device_pm_add(dev);

添加 power 文件。

       /* Notify clients of device addition.  This call must come

         * after dpm_sysf_add() and before kobject_uevent().

         */

       if (dev->bus)

              blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

                                        BUS_NOTIFY_ADD_DEVICE, dev);

调用 bus 的回调函数。

       kobject_uevent(&dev->kobj, KOBJ_ADD);

产生一个 add 事件。

       bus_probe_device(dev);

这个函数将匹配已经注册到总线的驱动程序,如下:

void bus_probe_device(struct device *dev)

{

       struct bus_type *bus = dev->bus;

       int ret;

 

       if (bus && bus->p->drivers_autoprobe) {

              ret = device_attach(dev);

              WARN_ON(ret < 0);

       }

}

只有 bus->p->drivers_autoprobe 设置为 1 是才会去匹配, device_attach() 如下:

int device_attach(struct device *dev)

{

       int ret = 0;

 

       down(&dev->sem);

       if (dev->driver) {

              ret = device_bind_driver(dev);

              if (ret == 0)

                     ret = 1;

              else {

                     dev->driver = NULL;

                     ret = 0;

              }

       } else {

              pm_runtime_get_noresume(dev);

              ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

              pm_runtime_put_sync(dev);

       }

       up(&dev->sem);

       return ret;

}

如果已指定了 dev->driver ,则直接在相应的 driver 目录下建立链接文件,将驱动和设备绑定。

int device_bind_driver(struct device *dev)

{

       int ret;

 

       ret = driver_sysfs_add(dev);

       if (!ret)

              driver_bound(dev);

       return ret;

}

driver_bound() 函数如下:

static void driver_bound(struct device *dev)

{

       if (klist_node_attached(&dev->p->knode_driver)) {

              printk(KERN_WARNING "%s: device %s already bound/n",

                     __func__, kobject_name(&dev->kobj));

              return;

       }

 

       pr_debug("driver: '%s': %s: bound to device '%s'/n", dev_name(dev),

                __func__, dev->driver->name);

 

       if (dev->bus)

              blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

                                        BUS_NOTIFY_BOUND_DRIVER, dev);

 

       klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

}

通知总线绑定驱动,将设备添加到驱动的设备链表。

否则调用 bus_for_each_drv() 匹配总线上的驱动:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,

                   void *data, int (*fn)(struct device_driver *, void *))

{

       struct klist_iter i;

       struct device_driver *drv;

       int error = 0;

 

       if (!bus)

              return -EINVAL;

 

       klist_iter_init_node(&bus->p->klist_drivers, &i,

                          start ? &start->p->knode_bus : NULL);

       while ((drv = next_driver(&i)) && !error)

              error = fn(drv, data);

       klist_iter_exit(&i);

       return error;

}

历遍总线上的驱动,每次都调用回调函数 fn() (这里是 __device_attach ),如果 fn() 返回 1 则匹配成功, __device_attach() 如下:

static int __device_attach(struct device_driver *drv, void *data)

{

       struct device *dev = data;

 

       if (!driver_match_device(drv, dev))

              return 0;

 

       return driver_probe_device(drv, dev);

}

driver_match_device() 如下:

static inline int driver_match_device(struct device_driver *drv,

                                  struct device *dev)

{

       return drv->bus->match ? drv->bus ->match(dev, drv) : 1;

}

 drv->bus 的 match 方法进行匹配,如果成功就会继续调用 driver_probe_device() :

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

       int ret = 0;

 

       if (!device_is_registered(dev))

              return -ENODEV;

 

       pr_debug("bus: '%s': %s: matched device %s with driver %s/n",

                drv->bus->name, __func__, dev_name(dev), drv->name);

 

       pm_runtime_get_noresume(dev);

       pm_runtime_barrier(dev);

       ret = really_probe(dev, drv);

       pm_runtime_put_sync(dev);

 

       return ret;

}

进行一下验证和同步后调用 really_probe() 最终详细的匹配:

static int really_probe(struct device *dev, struct device_driver *drv)

{

       int ret = 0;

 

       atomic_inc(&probe_count);

       pr_debug("bus: '%s': %s: probing driver %s with device %s/n",

                drv->bus->name, __func__, drv->name, dev_name(dev));

       WARN_ON(!list_empty(&dev->devres_head));

 

       dev->driver = drv;

       if (driver_sysfs_add(dev)) {

              printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",

                     __func__, dev_name(dev));

              goto probe_failed;

       }

 

       if (dev->bus->probe) {

              ret = dev->bus->probe(dev);

              if (ret)

                     goto probe_failed;

       } else if (drv->probe) {

              ret = drv->probe(dev);

              if (ret)

                     goto probe_failed;

       }

 

       driver_bound(dev);

       ret = 1;

       pr_debug("bus: '%s': %s: bound device %s to driver %s/n",

                drv->bus->name, __func__, dev_name(dev), drv->name);

       goto done;

 

probe_failed:

       devres_release_all(dev);

       driver_sysfs_remove(dev);

       dev->driver = NULL;

 

       if (ret != -ENODEV && ret != -ENXIO) {

              /* driver matched but the probe failed */

              printk(KERN_WARNING

                     "%s: probe of %s failed with error %d/n",

                     drv->name, dev_name(dev), ret);

       }

       /*

         * Ignore errors returned by ->probe so that the next driver can try

         * its luck.

         */

       ret = 0;

done:

       atomic_dec(&probe_count);

       wake_up(&probe_waitqueue);

       return ret;

}

先假定 dev 的驱动为 drv ,然后如果总线闪的 probe 存在,则调用它,否则调用 drv 的 probe ()函数,返回 0 则匹配成功,调用 driver_bound() 进行设备和驱动的绑定。 driver_bound() 函数在上面已经分析。

 

接着 device_add() 函数中的代码:

       if (dev->class) {

              mutex_lock(&dev->class->p->class_mutex);

              /* tie the class to the device */

              klist_add_tail(&dev->knode_class,

                            &dev->class->p->class_devices);

 

              /* notify any interfaces that the device is here */

              list_for_each_entry(class_intf,

                                &dev->class->p->class_interfaces, node)

                     if (class_intf->add_dev)

                            class_intf->add_dev(dev, class_intf);

              mutex_unlock(&dev->class->p->class_mutex);

       }

Dev 所属 class 的一些操作。

done:

       put_device(dev);

       return error;

  DPMError:

       bus_remove_device(dev);

  BusError:

       device_remove_attrs(dev);

  AttrsError:

       device_remove_class_symlinks(dev);

  SymlinkError:

       if (MAJOR(dev->devt))

              device_remove_sys_dev_entry(dev);

  devtattrError:

       if (MAJOR(dev->devt))

              device_remove_file(dev, &devt_attr);

  ueventattrError:

       device_remove_file(dev, &uevent_attr);

  attrError:

       kobject_uevent(&dev->kobj, KOBJ_REMOVE);

       kobject_del(&dev->kobj);

  Error:

       cleanup_device_parent(dev);

       if (parent)

              put_device(parent);

name_error:

       kfree(dev->p);

       dev->p = NULL;

       goto done;

最后是一下除错撤销处理。

至此,已经详细的分析了创建和注册设备的函数。

内核撤销设备注册的函数为 device_unregister() ,这里不再分析。

  

 

内核提供注册驱动的函数为 driver_register() :

int driver_register(struct device_driver *drv)

{

       int ret;

       struct device_driver *other;

 

       BUG_ON(!drv->bus->p);

 

       if ((drv->bus->probe && drv->probe) ||

           (drv->bus->remove && drv->remove) ||

           (drv->bus->shutdown && drv->shutdown))

              printk(KERN_WARNING "Driver '%s' needs updating - please use "

                     "bus_type methods/n", drv->name);

验证 driver 的包含了需要的方法,并打印信息。

       other = driver_find(drv->name, drv->bus);

       if (other) {

              put_driver(other);

              printk(KERN_ERR "Error: Driver '%s' is already registered, "

                     "aborting.../n", drv->name);

              return -EBUSY;

       }

如果驱动已注册则返回 -EBUSY 。

       ret = bus_add_driver(drv);

       if (ret)

              return ret;

       ret = driver_add_groups(drv, drv->groups);

       if (ret)

              bus_remove_driver(drv);

       return ret;

}

bus_add_driver() 代码如下:

int bus_add_driver(struct device_driver *drv)

{

       struct bus_type *bus;

       struct driver_private *priv;

       int error = 0;

 

       bus = bus_get(drv->bus);

       if (!bus)

              return -EINVAL;

 

       pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);

 

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

       if (!priv) {

              error = -ENOMEM;

              goto out_put_bus;

       }

       klist_init(&priv->klist_devices, NULL, NULL);

       priv->driver = drv;

       drv->p = priv;

       priv->kobj.kset = bus->p->drivers_kset;

       error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

                                 "%s", drv->name);

       if (error)

              goto out_unregister;

 

       if (drv->bus->p->drivers_autoprobe) {

              error = driver_attach(drv);

              if (error)

                     goto out_unregister;

       }

       klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

       module_add_driver(drv->owner, drv);

 

       error = driver_create_file(drv, &driver_attr_uevent);

       if (error) {

              printk(KERN_ERR "%s: uevent attr (%s) failed/n",

                     __func__, drv->name);

       }

       error = driver_add_attrs(bus, drv);

       if (error) {

              /* How the hell do we get out of this pickle? Give up */

              printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",

                     __func__, drv->name);

       }

 

       if (!drv->suppress_bind_attrs) {

              error = add_bind_files(drv);

              if (error) {

                     /* Ditto */

                     printk(KERN_ERR "%s: add_bind_files(%s) failed/n",

                            __func__, drv->name);

              }

       }

 

       kobject_uevent(&priv->kobj, KOBJ_ADD);

       return 0;

 

out_unregister:

       kfree(drv->p);

       drv->p = NULL;

       kobject_put(&priv->kobj);

out_put_bus:

       bus_put(bus);

       return error;

}

简单的分析一下: 为 drv 分配内存并初始化,创建文件系统中相应的目录、属性文件和链接,调用driver_attach() 匹配总线上的设备,将驱动注册到 bus 上,产生一个 kobject_uevent() ADD 事件。详细的过程请参考内核源码。

同样内核提供 driver_unregister() 函数撤销驱动注册,这里不再分析。

 

至此,已经清楚了设备驱动模型的 device 、 driver 和 bus ,在此基础上就可以轻松的去分析各个模块的驱动程序了 ^_^!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值