为了阐明表示总线、设备和设备驱动程序的各个数据结构之间彼此的关联,它们的注册过程是很有必要的。顺序一定是如下:(1)注册总线---bus_register;(2)注册设备device_register;(3)注册设备驱动程序----bus_add_driver。

下文摘自:点击打开链接

   现在我们得费劲心思的捋一遍我们的驱动注册代码,以便找到设备树添加的关键部分。我想我又得强调一下,我的介绍是SDIO驱动,所以请大家看着 linux内核代码drivers/mmc中关于sdio的驱动来理解我下面的笔记中的内容(想不看内核代码就理解设备树,我想太难太难)
    有关 sbi_register 函数(这是在我的wlan驱动代码中的函数,并不需要你太多的关注 )中sdio_register_driver函数(从现在开始就都是内核函数了) 注册驱动的介绍,在 sdio_register_driver 中将会指明驱动的名称(这里是 ”wlan_sdio ” ),此函数的参数为 sdio_driver 结构。 驱动所挂接的总线 sdio_bus_type ,其结构类型为:
  static struct bus_type sdio_bus_type = {
             .name             = "sdio",                 // 总线类型
             .dev_attrs       = sdio_dev_attrs,              // 属性
             .match           = sdio_bus_match,         //ops
             .uevent           = sdio_bus_uevent,
             .probe            = sdio_bus_probe,  
             .remove          = sdio_bus_remove,
};
    这个结构将在 sdio_register_driver 函数中被赋值以产生 device_driver 结构。也就是说 device_driver 被包含在 sdio_driver 中。 随后调用函数 driver_register ,其参数为 device_driver (此结构中定义了 bus_type,也就是驱动挂接的总线类型 )。至此将转入所有驱动(不止是 sdio 卡驱动)的注册代码中。此时的驱动结构已经变为device_drive ( 内核定义的驱动结构 ) 。
    driver_register 将会完成挂接驱动至总线及生成设备树的过程,其完成的任务大致包括:
    1 、设置设备驱动中 kobject 的名字
     2 、将 kobject 添加至 sysfs 中,也就是在 sysfs 树中创建一个目录( kobject 中有一个函数 create_dir 用于创建目录。)
      3 、调用 driver_attach 函数完成 probe 
      4 、 driver_create_file 创建文件属性,会生成一个属性为 driver_attr_uevent 的属性文件。
      5 、 add_bind_files 生成属性为 driver_attr_unbind 和 driver_attr_bind 的属性文件,关于文件的属性,它定义了文件的读写方式。
     如此抽象的描述及流水账般的记述我发现还是很没有说服力,因此我决定给出一个具体的例子,并给出代码的实现。先看例子——让我们看一下 /sys/bus/ 下的目录,然后来个具体的描述:已知我们的总线的目录名字为”sdio” 。也就是说在 /sys/bus 目录下有一个目录叫 sdio ,即 /sys/bus/sdio。它是怎么形成的?内核中有”sdio” 总线的驱动,找到这个函数 bus_register (&sdio_bus_type ) ;就是用来注册总线的。我们在前面不也看到了 driver_register 函数吗,是的,你猜对了,还有一个函数叫 device_register 。在这个函数调用完成后,就会得到 /sys/bus/ 目录下的 sdio 目录了。那么驱动的名字 ”wlan_sdio” 又是如何插入到/sys/bus/drivers/wlan_sdio 目录下的呢。在 driver_register->driver_register->bus_add_driver 函数中有个重要的语句 drv->kobj.kset
= &bus->drivers ; 想象一下我们折腾了那么长时间的 kobject 与 kset 的意义,是的,这里就是将 driver 的 kobj 所属的 kset 挂接上总线的 kset 。我们这里显得很绕对吗?幸运的是我在网上找到了一个非常棒的示意图:
 
                     
 

我该怎么样来形容这个图呢? 它把依赖关系说的已经足够清楚了!!我这里唯一要解释的仅仅是在驱动文件夹被正确后所谓的文件属性有在那里。同样我们顺着目录 /sys/bus/sdio/driver/wlan_sdio/ 下我们发现了 bind 、unbind 和 new_id 三个文件。

好了,别忘了我们在前面提过的问题,kset 是如何扮演容器的角色的呢?图中很清楚吧,看看粉红色的箭头,kset children list用来将将同类型的kobject连接起来以达到容器的效果。

但是我们的驱动还没有结束,关于这个图的建立,我们势必要用代码才能说得明白。

我们还是花费一点时间来看一下内核中的代码,关于 sdio 总线注册的代码部分,其它的部分,大家类举就可以了。懂了这段自然就懂了全部:

int bus_register( struct bus_type
* bus)

{

int retval;

BLOCKING_INIT_NOTIFIER_HEAD (&bus->bus_notifier);

retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);     // 总线的名字 ”sdio”, 我们说过了一个 kobject对应一个目录,这时会为这个目录赋值名字。

if (retval)

goto out;

bus->subsys.kobj.kset = &bus_subsys;                         // 将其 kset 指向 bus_subsys., 如何理解? 看看ldd_bus_type 指向 bus_subsys 的那条蓝线

retval = subsystem_register(&bus->subsys);               //bus->subsys 的注册,实际上是用 kset 指针将其链接在一起。好吧 我得承认实际上这里取消了 subsysem 结构的概念,用 kset 代替了。这里会创建一个目录。它是一个 kset 也是一个 kobject,因为 kset 包含了 kobject 。

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_uevent);             // 创建属性文件

if (retval)

goto bus_uevent_fail;

kobject_set_name(&bus->devices.kobj, "devices");          // 设置 devices
kset 的名字为 devices

bus->devices.kobj.parent = &bus->subsys.kobj;            // 参见 ldd_bus_type->devices 指向 ldd_bus_type->sub_sys的红色箭头(注意这里也不是 subsystem, 而是 kset )

retval = kset_register(&bus->devices);                 // 创建 devices 命名的目录

if (retval)

goto bus_devices_fail;

kobject_set_name(&bus->drivers.kobj, "drivers");       // 设置 devices
kset 的名字为 drivers

bus->drivers.kobj.parent = &bus->subsys.kobj;         // 同样参见 ldd_bus_type->drivers 指向 ldd_bus_type->sub_sys的红色箭头(注意这里也不是 subsystem, 而是 kset )。

bus->drivers.ktype = &driver_ktype;                     // 对 kobject 默认属性的赋值

retval = kset_register(&bus->drivers);                  // 创建 drivers 命名的目录

if (retval)

goto bus_drivers_fail;

klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);     //klist 结构的初始化,关于 klist 链接的作用我们已经说得很清楚了

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

bus->drivers_autoprobe = 1;

retval = add_probe_files(bus);                       // 添加探测属性

if (retval)

goto bus_probe_files_fail;

retval = bus_add_attrs(bus);                        // 添加其他属性

if (retval)

goto bus_attrs_fail;

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

return 0;

bus_attrs_fail :

remove_probe_files(bus);

bus_probe_files_fail :

kset_unregister(&bus->drivers);

bus_drivers_fail :

kset_unregister(&bus->devices);

bus_devices_fail :

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail :

subsystem_unregister(&bus->subsys);

out :

return retval;

}

至此 我们终于理顺了整个过程。

linux设备树的建立过程的更多相关文章

  1. 宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】

    转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...

  2. linux 设备树【转】

    转自:http://blog.csdn.net/chenqianleo/article/details/77779439 [-] linux 设备树 为什么要使用设备树Device Tree 设备树的 ...

  3. linux设备树语法

    设备树语法及绑定 概述 Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF). 就ARM平台来说,设备树文件存放在arch/arm/boot/d ...

  4. 【转载】Linux设备树(Device Tree)机制

    转:Linux设备树(Device Tree)机制   目录 1. 设备树(Device Tree)基本概念及作用2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DT ...

  5. Linux设备树语法详解

    概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写.引入了设备树之后,驱动代 ...

  6. Linux设备树语法详解【转】

    转自:http://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备 ...

  7. Linux设备树学习

    1.概念 设备树用于实现驱动代码与设备信息相分离.驱动代码只负责处理驱动的逻辑而关于设备的具体信息存放到设备树文件中.(dts文件,编译后为dtb文件).一个dts文件对应一个ARM的machine, ...

  8. Linux内核树的建立-基于ubuntu系统

    刚看 O'REILLY 写的<LINUX 设备驱动程序>时.作者一再强调在编写驱动程序时必须 建立内核树.先前的内核只需要有一套内核头文件就够了,但因为2.6的内核模块吆喝内核源码树中的目 ...

  9. linux设备驱动程序注冊过程具体解释

    Linux的驱动程序注冊过程,大致分为两个步骤: 模块初始化 驱动程序注冊 以下以内核提供的演示样例代码pci-skeleton.c,具体说明一个pci设备驱动程序的注冊过程.其它设备的驱动代码注冊过 ...

随机推荐

  1. Comparator中返回0导致数据丢失的大坑

    今天对一列数据进行排序,因为存储的是Map结构,要实现排序,马上就想到了TreeMap,于是查到API,这样新建TreeMap就能实现添加的时候就自动排序. new TreeMap<>(n ...

  2. Cache管理机制(System.Web.Caching)

    一,System .Web.Caching与缓存工作机制简介 System.Web.Caching是用来管理缓存的命名空间,其父级空间是System.Web,由此可见,缓存通常用于Web网站的开发,包 ...

  3. dockerfile运行mysql并初始化数据

    本文目的不仅仅是创建一个MySQL的镜像,而是在其基础上再实现启动过程中自动导入数据及数据库用户的权限设置,并且在新创建出来的容器里自动启动MySQL服务接受外部连接,主要是通过Dockerfile和 ...

  4. function的json对象转换字符串与字符串转换为对象的方法

    // json对象转换成字符串var str = JSON.stringify(json, function(key, val) { if (typeof val === 'function') { ...

  5. [LeetCode] 535. Encode and Decode TinyURL 编码和解码短网址

    Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...

  6. [LeetCode] 555. Split Concatenated Strings 分割串联字符串

    Given a list of strings, you could concatenate these strings together into a loop, where for each st ...

  7. 【视频开发】非常优秀的CxImage各种格式图像开源工具

    下面介绍应用它进行图像类型转换的方式: 1.从一种图像文件类型转换为另一种文件类型(convert from a format to another)  CxImage   image;    //  ...

  8. 学习数据结构Day2

    之前学习过了数组的静态实现方法,同时将数组的所有有可能实现的方法都统一实现了一遍,之后支持了泛型的相关 概念,接下来就是如何对数组进行扩容的操作也就是实现动态数组. private void resi ...

  9. 「模拟赛20191019」C 推式子+贪心+树状数组

    题目描述 给定一棵\(n\)个点的有根树,根节点编号为\(1\),点有点权. 定义\(d(v)\)表示\(v\)到\(1\)的路径上的边数. 定义\(f(v,u)\)在\(v<u\)且\(v\) ...

  10. R统计数据框的行数

    如下三种方法可以实现 使用dim函数 dim(dataframe)[0] 使用nrow函数 nrow(dataframe) 使用length函数统计 length(dataframe[,1])