linux驱动篇之 driver_register 过程分析(二)

个人笔记,欢迎转载,请注明出处,共同分享 共同进步

http://blog.csdn.net/richard_liujh/article/details/48245715

kernel版本3.10.14

1.概述

本篇主要围绕driver_register中的第二步bus_add_driver展开分析。在上一篇博文中主要分析了driver_find的过程,在driver_register中调用driver_find主要是为了检验驱动是否已经注册到kernel中,如果没有注册,那么接下来的几个步骤才是driver_register的核心作用。

driver_register简化过程如下:

[cpp] view plaincopy print?
  1. int driver_register(struct device_driver *drv)
  2. |
  3. |--> driver_find //查找驱动是否已经装载 (上篇博文已经分析)
  4. |--> bus_add_driver//根据总线类型添加驱动
  5. |--> driver_add_groups//将驱动添加到对应组中
  6. |--> kobject_uevent//注册uevent事件

2.bus_add_driver分析

2.1 bus_add_driver源码

bus_add_driver源码在./drivers/base/bus.c文件中

[cpp] view plaincopy print?
  1. /**
  2. * bus_add_driver - Add a driver to the bus.
  3. * @drv: driver.
  4. */
  5. int bus_add_driver(struct device_driver *drv)
  6. {
  7. struct bus_type *bus;
  8. struct driver_private *priv;
  9. int error = 0;
  10. bus = bus_get(drv->bus);
  11. if (!bus)
  12. return -EINVAL;
  13. pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
  14. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  15. if (!priv) {
  16. error = -ENOMEM;
  17. goto out_put_bus;
  18. }
  19. klist_init(&priv->klist_devices, NULL, NULL);
  20. priv->driver = drv;
  21. drv->p = priv;
  22. priv->kobj.kset = bus->p->drivers_kset;
  23. error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
  24. "%s", drv->name);
  25. if (error)
  26. goto out_unregister;
  27. klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
  28. if (drv->bus->p->drivers_autoprobe) {
  29. error = driver_attach(drv);
  30. if (error)
  31. goto out_unregister;
  32. }
  33. module_add_driver(drv->owner, drv);
  34. error = driver_create_file(drv, &driver_attr_uevent);
  35. if (error) {
  36. printk(KERN_ERR "%s: uevent attr (%s) failed\n",
  37. __func__, drv->name);
  38. }
  39. error = driver_add_attrs(bus, drv);
  40. if (error) {
  41. /* How the hell do we get out of this pickle? Give up */
  42. printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
  43. __func__, drv->name);
  44. }
  45. if (!drv->suppress_bind_attrs) {
  46. error = add_bind_files(drv);
  47. if (error) {
  48. /* Ditto */
  49. printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
  50. __func__, drv->name);
  51. }
  52. }
  53. return 0;
  54. out_unregister:
  55. kobject_put(&priv->kobj);
  56. kfree(drv->p);
  57. drv->p = NULL;
  58. out_put_bus:
  59. bus_put(bus);
  60. return error;
  61. }

代码稍微有点长,但是为了保留kernel源码的美感,所以上面代码没有做任何改动。

2.1  bus_add_driver简化过程

为了使分析bus_add_driver不显得太杂乱,这里我将bus_add_driver分为以下几个部分:

[cpp] view plaincopy print?
  1. int bus_add_driver(struct device_driver *drv)
  2. |
  3. |---> bus_get/*获取总线类型(bus_type)*/
  4. |
  5. |---> klist_init/*          --\                                           */
  6. |---> kobject_init_and_add/*   >  kset, kobject节点初始化,插入链表(尾插)  */
  7. |---> klist_add_tail/*      --/                                           */
  8. |
  9. |---> module_add_driver
  10. |---> driver_create_file
  11. |---> driver_add_attrs

在bus_add_driver函数里,只传过来一个参数就是device_driver *drv。为什么bus_add_driver只需要device_driver的指针这一个参数?device_driver是不是很熟悉?我们在写驱动时,device_driver是一个非常重要的结构体。在文件./include/linux/device.h中有device_driver的定义。

[cpp] view plaincopy print?
  1. struct device_driver {
  2. const char      *name;
  3. struct bus_type     *bus;
  4. struct module       *owner;
  5. const char      *mod_name;  /* used for built-in modules */
  6. bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
  7. const struct of_device_id   *of_match_table;
  8. const struct acpi_device_id *acpi_match_table;
  9. int (*probe) (struct device *dev);
  10. int (*remove) (struct device *dev);
  11. void (*shutdown) (struct device *dev);
  12. int (*suspend) (struct device *dev, pm_message_t state);
  13. int (*resume) (struct device *dev);
  14. const struct attribute_group **groups;
  15. const struct dev_pm_ops *pm;
  16. struct driver_private *p;
  17. };

从上面的device_driver结构内容可以看出,里面包含了driver的名字(name),总线的类型(bus),所属的模块(owner)。和一些非常重要的函数指针,例如嗅探函数指针probe,驱动删除函数指针remove,关机时调用的函数指针shutdown,睡眠时调用的函数指针suspend,睡眠唤醒时恢复驱动的函数指针resume等等....

对于一个初级的驱动,是不是最先了解的是name、bus、module、probe这个成员变量?通过后面的分析,我们可以深入理解这些变量的作用。

2.2 简化过程分析

在kernel中的函数名一般都很通俗易懂,例如我们要分析的bus_add_driver,就有简单bus、add和driver等单词组合。所以凭借男人的第六感,能够大概猜出是在某个bus上添加驱动了。所以在刚才简化bus_add_driver的第一个过程就是bus_get。bus_get的名字言简意赅,获得bus。因为我们要在某个bus上添加driver。

2.2.1 bus_get源码:

bus_get 源码./drivers/base/bus.c
[cpp] view plaincopy print?
  1. bus = bus_get(drv->bus);
  2. /*--------------------------------------------------*/
  3. static struct bus_type *bus_get(struct bus_type *bus)
  4. {
  5. if (bus) {//判断bus是否为空
  6. kset_get(&bus->p->subsys);
  7. return bus;//不为空则return bus
  8. }
  9. return NULL;//bus 为空,那么return NULL;
  10. }

bus_get相对比较简单,上一篇博文是以platform_driver_register开始讲解的,所以bus_type为platform_bus_type。

2.2.2 klist_init, kobject_init_and_add, klist_add_tail分析

klist_init,kobject_init_and_add 和klist_add_tail我把他们归纳在一起,主要完成了Kobject的初始化和将初始化的kobjec尾插到kset链表中。还记得在上篇博文讲解中driver_find的过程,就涉及到一个链表的遍历过程吧,如果链表里面有对应驱动的name说明驱动已经注册了。如果第一次注册,驱动的name当然是在链表中不存在的(除非冲突了),所以这里的操作就是将驱动相关的基类Kobject添加到对应kset的循环链表中。

klist_init

在bus_add_driver调用时如下:

[cpp] view plaincopy print?
  1. klist_init(&priv->klist_devices, NULL, NULL);

所以我们观察一下参数,后面两个是NULL,前面是&priv->klist_devices。注意有一个“&”符号,也就是将priv的成员 klist_devices地址 传送过去。

补:像用C编写的代码,尤其是linux 源码,内核中会经常传送指针。一般传送一级指针要留意,传送二级指针要多留意,传送结构体指针要更加留意。
这个函数很明显是将klist_devices的地址传送过去进行初始化了。那么如何初始化?初始化了哪些内容呢?为了解决这个问题,我们得先知道要被初始化的变量是什么类型的!

首先来了解bus_add_driver中的struct driver_private *priv;这个priv是指向driver_private结构体的指针。其成员如下

[cpp] view plaincopy print?
  1. struct driver_private {
  2. struct kobject kobj;
  3. struct klist klist_devices;
  4. struct klist_node knode_bus;
  5. struct module_kobject *mkobj;
  6. struct device_driver *driver;
  7. };

private是私有的意思,很多面向对象的语言都有private关键字,表示资源是私有的,其他人不能随意使用。这里driver_private的意思是driver所拥有的资源,相当于将driver相关

的资源封装了一个结构体中,使得代码的层次感更强,面向对象的美感更好。对于priv ,bus_add_driver有如下几个操作:

[cpp] view plaincopy print?
  1. struct driver_private *priv;//定义priv指针
  2. priv = kzalloc(sizeof(*priv), GFP_KERNEL);//初始化priv指针,指向申请的driver_private结构体地址

好了,这个清楚了后我们就该继续看klist_init初始化的 &priv->klist_devices ,klist_devices在d river_private定义如下:

[cpp] view plaincopy print?
  1. struct klist klist_devices;

klist结构体在文件 ./include/linux/klist.h 中定义

[cpp] view plaincopy print?
  1. struct klist {
  2. spinlock_t      k_lock;
  3. struct list_head    k_list;
  4. void            (*get)(struct klist_node *);
  5. void            (*put)(struct klist_node *);
  6. }

这里就很简单了,既然是要对klist_devices初始化,通过上面的定义可以看到有4个成员变量:自旋锁k_lock,链表节点k_list和两个函数指针get,put。

klist_ini的t源码在文件./lib/klist.c

[cpp] view plaincopy print?
  1. void klist_init(struct klist *k, void (*get)(struct klist_node *),
  2. void (*put)(struct klist_node *))
  3. {
  4. INIT_LIST_HEAD(&k->k_list);
  5. spin_lock_init(&k->k_lock);
  6. k->get = get;
  7. k->put = put;
  8. }

知道了klist_devices的成员,上面的代码就很简单了,就是对klist_devices的四个成员变量进行初始化。代码比较简单,就不细说了。只简单提一下 INIT_LIST_HEAD ,因为在内核中经常可以看到这个函数。

在文件./include/linux/list.h中有INIT_LIST_HEAD定义

[cpp] view plaincopy print?
  1. static inline void INIT_LIST_HEAD(struct list_head *list)
  2. {
  3. list->next = list;
  4. list->prev = list;
  5. }

很明显,这是一个 内联函数(有inline)。实现的功能也很简单,list是链表,我们在实现循环链表时总会定义两个指针 next 和 prev 。next指向下一个节点的地址,prev指向上一个节点的地址。所以INIT_LIST_HEAD其实就是使next和prev都指向自己的地址,我们判断链表是否为空的时候不就是看看next和prev指向的地址是否相同吗,相同表示为空。

下面是INIT_LIST_HEAD的一个简单示意图

kobject_init_and_add

kobject_init_and_add源码在文件./lib/kobject.c

[cpp] view plaincopy print?
  1. /**
  2. * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
  3. * @kobj: pointer to the kobject to initialize
  4. * @ktype: pointer to the ktype for this kobject.
  5. * @parent: pointer to the parent of this kobject.
  6. * @fmt: the name of the kobject.
  7. *
  8. * This function combines the call to kobject_init() and
  9. * kobject_add().  The same type of error handling after a call to
  10. * kobject_add() and kobject lifetime rules are the same here.
  11. */
  12. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
  13. struct kobject *parent, const char *fmt, ...)
  14. {
  15. va_list args;
  16. int retval;
  17. kobject_init(kobj, ktype);
  18. va_start(args, fmt);
  19. retval = kobject_add_varg(kobj, parent, fmt, args);
  20. va_end(args);
  21. return retval;
  22. }
  23. EXPORT_SYMBOL_GPL(kobject_init_and_add);

过注释可以很清楚的知道 kobject_init_and_add 的作用,初始化 kobject 结构体并添加到 kobject 的层次中。

kobject_init_and_add通过kobject_init初始化kobject,通过kobject_add_varg完成添加操作。

kobject_init_and_add

|------ kobject_init

kobject_init源码也在文件./lib/kobject.c  中

[cpp] view plaincopy print?
  1. /**
  2. * kobject_init - initialize a kobject structure
  3. * @kobj: pointer to the kobject to initialize
  4. * @ktype: pointer to the ktype for this kobject.
  5. *
  6. * This function will properly initialize a kobject such that it can then
  7. * be passed to the kobject_add() call.
  8. *
  9. * After this function is called, the kobject MUST be cleaned up by a call
  10. * to kobject_put(), not by a call to kfree directly to ensure that all of
  11. * the memory is cleaned up properly.
  12. */
  13. void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
  14. {
  15. char *err_str;
  16. if (!kobj) {//检查kobj指针是否指向有效的kobject
  17. err_str = "invalid kobject pointer!";
  18. goto error;
  19. }
  20. if (!ktype) {//检查ktype是否指向有效的kobj_type
  21. err_str = "must have a ktype to be initialized properly!\n";
  22. goto error;
  23. }
  24. if (kobj->state_initialized) {//检查kobj的初始化状态是否已经被初始化
  25. /* do not error out as sometimes we can recover */
  26. printk(KERN_ERR "kobject (%p): tried to init an initialized "
  27. "object, something is seriously wrong.\n", kobj);
  28. dump_stack();
  29. }
  30. kobject_init_internal(kobj);//如果上面检查都通过,那么开始初始化
  31. kobj->ktype = ktype;
  32. return;
  33. error:
  34. printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
  35. dump_stack();
  36. }
  37. EXPORT_SYMBOL(kobject_init);

这个函数注释也写的很清楚了This function will properly initialize a kobject such that it can then be passed to the kobject_add() call. 功能虽简单,但是内核做事还是比较严谨,从代码中对kobj、ktype和kobj->state_initialized依次进行了检查。检查无误开始调用kobject_init_internal

kobject_init_internal源码如下:[ ./lib/kobject.c ]

[cpp] view plaincopy print?
  1. static void kobject_init_internal(struct kobject *kobj)
  2. {
  3. if (!kobj)
  4. return;
  5. kref_init(&kobj->kref);
  6. INIT_LIST_HEAD(&kobj->entry);
  7. kobj->state_in_sysfs = 0;
  8. kobj->state_add_uevent_sent = 0;
  9. kobj->state_remove_uevent_sent = 0;
  10. kobj->state_initialized = 1;
  11. }

上面是对kobject真正的初始化。因为kobject是linux设备驱动的核心结构体之一,所涉及到的内容比较多也比较复杂,所以这里就不再深究具体初始化的含义。希望在以后的设备驱动相关博文中详解。

kobject_init_and_add

|------ kobject_add_varg

上面初始化好kobject后,开始通过kobject_add_varg添加kobject

kobject_init_and_add源码如下,在文件./lib/kobject.c中

[cpp] view plaincopy print?
  1. static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
  2. const char *fmt, va_list vargs)
  3. {
  4. int retval;
  5. retval = kobject_set_name_vargs(kobj, fmt, vargs);
  6. if (retval) {
  7. printk(KERN_ERR "kobject: can not set name properly!\n");
  8. return retval;
  9. }
  10. kobj->parent = parent;
  11. return kobject_add_internal(kobj);
  12. }

在分析这个函数时,有必要看看kobject_init_and_add的函数接口定义:

[cpp] view plaincopy print?
  1. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)

最后的参数 const char *fmt, ... 是可变参数,那么fmt的值是什么呢?再来看看调用kobject_init_and_add时的传参:

[cpp] view plaincopy print?
  1. error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);

所以可变参数 const char *fmt, ... 的内容是 "%s", drv->name 也就是驱动的名字。在kobject_init_and_add中的kobject_set_name_vargs函数通过处理可变参数,最终将drv->name的内容给kobj->name。

最后的重点就是

[cpp] view plaincopy print?
  1. return kobject_add_internal(kobj);

kobject_add_internal的作用还是非常多的。通过kobject_add_internal将准备好的kobject添加到kset的循环列表中,并且在sys/目录下创建kobject的目录。

kobject_init_and_add

|------ kobject_add_varg

|-------kobject_add_internal

kobject_add_internal源码在在文件./lib/kobject.c中

[cpp] view plaincopy print?
  1. static int kobject_add_internal(struct kobject *kobj)
  2. {
  3. int error = 0;
  4. struct kobject *parent;
  5. if (!kobj)
  6. return -ENOENT;
  7. if (!kobj->name || !kobj->name[0]) {
  8. WARN(1, "kobject: (%p): attempted to be registered with empty "
  9. "name!\n", kobj);
  10. return -EINVAL;
  11. }
  12. parent = kobject_get(kobj->parent);
  13. /* join kset if set, use it as parent if we do not already have one */
  14. if (kobj->kset) {
  15. if (!parent)
  16. parent = kobject_get(&kobj->kset->kobj);
  17. kobj_kset_join(kobj);
  18. kobj->parent = parent;
  19. }
  20. pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
  21. kobject_name(kobj), kobj, __func__,
  22. parent ? kobject_name(parent) : "<NULL>",
  23. kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
  24. error = create_dir(kobj);
  25. if (error) {
  26. kobj_kset_leave(kobj);
  27. kobject_put(parent);
  28. kobj->parent = NULL;
  29. /* be noisy on error issues */
  30. if (error == -EEXIST)
  31. WARN(1, "%s failed for %s with "
  32. "-EEXIST, don't try to register things with "
  33. "the same name in the same directory.\n",
  34. __func__, kobject_name(kobj));
  35. else
  36. WARN(1, "%s failed for %s (error: %d parent: %s)\n",
  37. __func__, kobject_name(kobj), error,
  38. parent ? kobject_name(parent) : "'none'");
  39. } else
  40. kobj->state_in_sysfs = 1;
  41. return error;
  42. }

上面的的代码说多不多,说少也不简单....
还是为了简化,我把kobject_add_internal的功能简化如下(一些简单的if 判断就不细讲了):

[cpp] view plaincopy print?
  1. static int kobject_add_internal(struct kobject *kobj)
  2. {
  3. parent = kobject_get(kobj->parent);//读取kobject的parent指针
  4. <span style="white-space:pre">    </span>……
  5. /* join kset if set, use it as parent if we do not already have one */
  6. if (kobj->kset) {//判断kobject的kset是否为空
  7. if (!parent)
  8. parent = kobject_get(&kobj->kset->kobj);
  9. kobj_kset_join(kobj);
  10. kobj->parent = parent;
  11. }
  12. <span style="white-space:pre">    </span>……
  13. error = create_dir(kobj);//穿件kobject的相关目录
  14. <span style="white-space:pre">    </span>……
  15. kobj->state_in_sysfs = 1;//更改kobject的sys文件系统状态标志
  16. return error;
  17. }

代码里面if (kobj->kset)对kobject的kset进行的检查,那么我们这里的kset什么值呢?这就要回到bus_add_driver函数,其中有这么一句话priv->kobj.kset = bus->p->drivers_kset;所以此时的kobject->kset不为空。

if (!parent)的作用是判断kobject是否有父类,对于kset、kobject、parent和list,在权威书籍LDD3-chapter14(linux设备驱动)中有一个很经典的图,所以我就借花献佛了。

上图~

解释:

  • 在链表中每一个kobject都有一个指向kset的指针。
  • 在链表中每一个kobject都有指向父类kobject(内嵌在kset中)的指针
  • 在链表中每一个kobject 都有链表指针(next、prev)指向相邻的节点

这张图很简洁的解释了kset和kobject的基本关系。感兴趣的请直接阅读葵花宝典《LDD3》。点击下载LDD3

他们之间的关系清楚了后,我们开始分析kobject是如何添加到kset的链表中的。这个功能是由kobj_kset_join完成的

kobj_kset_join源码在./lib/kobject.c中

[cpp] view plaincopy print?
  1. /* add the kobject to its kset's list */
  2. static void kobj_kset_join(struct kobject *kobj)
  3. {
  4. if (!kobj->kset)
  5. return;
  6. kset_get(kobj->kset);//获取kset
  7. spin_lock(&kobj->kset->list_lock);<span style="font-family: Arial, Helvetica, sans-serif;">//上锁</span>
  8. list_add_tail(&kobj->entry, &kobj->kset->list);
  9. spin_unlock(&kobj->kset->list_lock);解锁
  10. }

其中spin_lock和spin_unlock都是对自旋锁的操作,这里不多讲了。比较重要的就是 list_add_tail。
list_add_tail 通过名字,我们能猜到这个函数就是在链表中添加节点,其中tail应该就是从尾部添加也就是尾插法了。

kobject_init_and_add

|------ kobject_add_varg

|-------kobject_add_internal

|------list_add_tail

list_add_tail源码在头文件./include/linux/list.h 中

[cpp] view plaincopy print?
  1. /**
  2. * list_add_tail - add a new entry
  3. * @new: new entry to be added
  4. * @head: list head to add it before
  5. *
  6. * Insert a new entry before the specified head.
  7. * This is useful for implementing queues.
  8. */
  9. static inline void list_add_tail(struct list_head *new, struct list_head *head)
  10. {
  11. __list_add(new, head->prev, head);
  12. }

而__list_add的源码如下:

[cpp] view plaincopy print?
  1. static inline void __list_add(struct list_head *new,
  2. struct list_head *prev,
  3. struct list_head *next)
  4. {
  5. next->prev = new;
  6. new->next = next;
  7. new->prev = prev;
  8. prev->next = new;
  9. }

很明显,上面的代码就是链表插入节点的操作。只是稍微注意一下传参过程,因为参数的名称并不一致,但是都是指针,细心一点就没有问题的。
总结上述过程,通下图表示:

上图~

当kobject成功添加到kset的链表中后,开始在sysfs中创建kobject的相关目录,这个过程由error = create_dir(kobj);完成。

一下是sysfs穿件目录的核心代码:在文件 ./fs/sysfs/dir.c中

[cpp] view plaincopy print?
  1. /**
  2. *  sysfs_create_dir - create a directory for an object.
  3. *  @kobj:      object we're creating directory for.
  4. */
  5. int sysfs_create_dir(struct kobject * kobj)
  6. {
  7. enum kobj_ns_type type;
  8. struct sysfs_dirent *parent_sd, *sd;
  9. const void *ns = NULL;
  10. int error = 0;
  11. BUG_ON(!kobj);
  12. if (kobj->parent)
  13. parent_sd = kobj->parent->sd;
  14. else
  15. parent_sd = &sysfs_root;
  16. if (!parent_sd)
  17. return -ENOENT;
  18. if (sysfs_ns_type(parent_sd))
  19. ns = kobj->ktype->namespace(kobj);
  20. type = sysfs_read_ns_type(kobj);
  21. error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
  22. if (!error)
  23. kobj->sd = sd;
  24. return error;
  25. }

sysfs穿件目录的代码就不详细说明了,记得在前面分析 kobject_init_and_add 时,有这样的一句注释 initialize a kobject structure and add it to the kobject hierarchy

单词hierarchy是 层级,等级的意思。注释的大致意思是将kobject添加到kobject等级中。这里的“等级”体现最明显的就是目录结构。

说到目录,我们会很快联想到子目录或者上一级目录。要在sysfs里面创建kobject相关的目录,也需要遵守目录的等级制度啦。按照kobject的parent(也是kobject类)就是上一级目录的规则去创建,目录名是kobject->name。为了能让读者更加清楚创建的规则,我就以目前手中的平台为例:

文章开头,我们是以platform_driver_register为例子讲解,目前我手上刚好有一个国产君正M200平台的开发板。处理器是mips架构。

假设,我们要注册的驱动是framebuffer。在君正平台代码中,有如下定义:

[cpp] view plaincopy print?
  1. static struct platform_driver jzfb_driver = {
  2. .probe = jzfb_probe,
  3. .remove = jzfb_remove,
  4. .shutdown = jzfb_shutdown,
  5. .driver = {
  6. .name = "jz-fb",
  7. #ifdef CONFIG_PM
  8. .pm = &jzfb_pm_ops,
  9. #endif
  10. },
  11. };

可以看到driver->name 是“jz-fb”。通过platform_driver_register(&jzfb_driver);注册platform架构驱动。当代码执行到上述过程,肯定会在sysfs下创建相关的目录,并且以 kobject->name 命名。

我通过终端,访问sys目录,结果如下:

上图~

所以可以观察到/sys/bus/platform/drivers/jz-fb 的目录结构刚好符合了我们分析代码的顺序。

总结

上面主要分析了driver在注册过程中,初始化driver的kobject和将kobject添加到对应的层级结构中。

linux驱动篇之 driver_register 过程分析(二)bus_add_driver相关推荐

  1. linux驱动篇之 driver_register 过程分析(一)

    linux驱动注册过程分析--driver_register(一) 个人笔记,欢迎转载,请注明出处,共同分享 共同进步 http://blog.csdn.net/richard_liujh/artic ...

  2. 嵌入式学习之Linux驱动篇-迅为视频更新了

    想学习Linux驱动但是无从下手的同学,学习Linux驱动但是一直不能入门的同学,学习了很多视频和资料还是很懵的同学快来学习拉 https://www.bilibili.com/video/BV1Vy ...

  3. Linux驱动篇之内核模块

    Linux驱动篇之内核模块 1.基本概念 模块与驱动: Linux中,将设备分为三种基本的类型. 字符设备 块设备 网络接口 Linux中还有一个很重要的概念,模块.可在运行时添加到内核中的代码被称为 ...

  4. linux驱动篇-button-int-poll

    本篇是linux下按键设备驱动,采用的中断法和poll机制,也是属于字符设备类的驱动,一起来动手吧.下面的话,老朋友可以跳过了直接从<需求描述>章节看起,新朋友可以试着看看. 前言 前言主 ...

  5. linux驱动篇-Button-中断法

    本篇是linux下按键设备驱动,采用的中断法,也是属于字符设备类的驱动,一起来动手吧.下面的话,老朋友可以跳过了直接从<需求描述>章节看起,新朋友可以试着看看. 前言 在嵌入式行业,有很多 ...

  6. linux驱动篇-touchscreen-完整版

    Touchscreen 本篇文章为触摸屏驱动完整版本,为的是给时间充裕的同学详细讲解.如要时间有限可以看精简版,传送门在下面. https://blog.csdn.net/chichi123137/a ...

  7. linux驱动篇-LCD

    前言 在嵌入式行业,有很多从业者.我们工作的主旋律是拿开源代码,拿厂家代码,完成产品的功能,提升产品的性能,进而解决各种各样的问题.或者是维护一个模块或方向,一搞就是好几年. 时间长了,中年润发现我们 ...

  8. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十九:LCD模块

    实验二十九:LCD模块 据说Alinx 301支持 7"TFT,好奇的朋友一定疑惑道,它们3.2"TFT以及7"TFT等两者之间究竟有何区别呢?答案很简单,前者自带控制器 ...

  9. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十七:TFT模块 - 显示

    实验二十七:TFT模块 - 显示 所谓TFT(Thin Film Transistor)就是众多LCD当中,其中一种支持颜色的LCD,相较古老的点阵LCD(12864笑),它可谓高级了.黑金的TFT ...

最新文章

  1. HTML基础部分(3)iframe,提交信息的一些知识
  2. Pytho学习笔记:电子邮件1
  3. [欧拉函数] Bzoj P2186 沙拉公主的困惑
  4. 文本相似度、文本匹配、文本聚类
  5. agx 安装ros opencv_(五)树莓派3开发环境搭建——5.Android手机端与robot端ROS网络通信...
  6. windows安装mysql-8.0.12-winx64和Navicat客户端连接(亲测有效)
  7. 2021年宇华实验中学高考成绩查询,2021年河南高考状元多少分,今年河南高考状元资料名单...
  8. Python备份文件实现以及备份大文件出错解决方案
  9. Python第三课小节
  10. 电气阀门定位器调试方法
  11. 如何在html页面上画一条渐变线
  12. PHP开发Android应用程序
  13. Paho Java Client 连接阿里云物联网平台
  14. 如果时光可以倒流,会不会有不一样的故事
  15. adobe air linux centos,Adobe AIR(跨平台应用)
  16. Socket套接字(Java)
  17. TOPSIS(优劣解距离法)【附Python实现代码及可视化代码】
  18. 用Python实现斯皮尔曼等级相关性检验
  19. Xcode的调试技巧
  20. Grafana 仪表盘

热门文章

  1. 对一些架构设计原则的反思
  2. Go语言命令行库urfave/cli简介
  3. 网络:http请求之几种常见技术
  4. 【Python】青少年蓝桥杯_每日一题_3.05_排列组合
  5. 用hql查询的数据排序每次都不一样
  6. 对学校的希望和寄语_家长对孩子的期望寄语精选
  7. Android访问瓦片地图 费流量,瓦片地图注意事项
  8. 四种可能导致数据中心瘫痪的网络攻击
  9. 13张图看懂综合类数据中心机房建设标准!
  10. python引用类 魔法方法_Python 学习笔记 -- 类的魔法方法