最全Linux驱动开发八股文(三)

你好,我是拉依达。

这是我的Linux驱动开发八股文详细解析系列

本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。

全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料

现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。

【下面是拉依达推荐学习相关专栏:】
一、Linux驱动学习专栏:拉依达的Linux驱动八股文 - 牛客网
二、Linux应用学习专栏:拉依达的Linux应用八股文 - 牛客网
【我的嵌入式学习和校招经验】 拉依达的嵌入式学习和秋招经验-CSDN博客
嵌入式学习规划/就业经验指导,可私信咨询

———————————————————————————————————————————————————

五、设备树

Device Tree是一种描述硬件的数据结构,以便于操作系统的内核可以管理和使用这些硬件,包括CPU或CPU,内存,总线和其他一些外设。

Linux内核从3.x版本之后开始支持使用设备树,可以实现驱动代码与设备的硬件信息相互的隔离,减少了代码中的耦合性

  • 引入设备树之前:一些与硬件设备相关的具体信息都要写在驱动代码中,如果外设发生相应的变化,那么驱动代码就需要改动

  • 引入设备树之后:通过设备树对硬件信息的抽象,驱动代码只要负责处理逻辑,而关于设备的具体信息存放到设备树文件中。如果只是硬件接口信息的变化而没有驱动逻辑的变化,开发者只需要修改设备树文件信息,不需要改写驱动代码

5.1 DTS、DTB和DTC

alt

  • DTS
    • 设备树源码文件,硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts
  • DTSI
    • 对于一些相同的dts配置可以抽象到dtsi文件中,然后可以用include的方式到dts文件
    • 同一芯片可以做一个dtsi,不同的板子不同的dts,然后include同一dtsi
    • 对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置
  • DTC
    • dtc是编译dts的工具
  • DTB
    • dts经过dtc编译之后会得到dtb文件,设备树的二进制执行文件
    • dtb通过Bootloader引导程序加载到内核。

5.2 设备树框架

1.根节点:\

2.设备节点:nodex

        ①节点名称:node

        ②节点地址:node@0, @后面即为地址

3.属性:属性名称(Property   name)和属性值(Property value)

4.标签
  • “/”是根节点,每个设备树文件只有一个根节点。在设备树文件中会发现有的文件下也有“/”根节点,这两个**“/”根节点的内容会合并成一个根节点。**
  • Linux 内核启动的时会解析设备树中各个节点的信息,并且在根文件系统的/proc/devicetree 目录下根据节点名字创建不同文件夹

5.3 DTS语法

dtsi头文件

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

设备树也支持头文件,设备树的头文件扩展名为.dtsi。在.dts 设备树文件中,还可以通过“#include”来引用.h、 .dtsi 和.dts 文件。

设备节点

  • 设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点

  • 每个节点都通过一些属性信息来描述节点信息,属性就是键—值对

    label: node-name@unit-address
    label:节点标签,方便访问节点:通过&label访问节点,追加节点信息
    node-name:节点名字,为字符串,描述节点功能
    unit-address:设备的地址或寄存器首地址,若某个节点没有地址或者寄存器,可以省略
    
  • 设备树源码中常用的几种数据形式

    1.字符串:  compatible = "arm,cortex-a7";设置 compatible 属性的值为字符串“arm,cortex-a7”
    2.32位无符号整数:reg = <0>; 设置reg属性的值为0
    3.字符串列表:字符串和字符串之间采用“,”隔开
    compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
    设置属性 compatible 的值为“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。
    

属性

  • compatible属性(兼容属性) cpp "manufacturer,model" manufacturer:厂商名称 model:模块对应的驱动名字 例: imx6ull-alientekemmc.dts 中 sound 节点是 音频设备节点,采用的欧胜(WOLFSON)出品的 WM8960, sound 节点的 compatible 属性值如下: cpp compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960"; 属性值有两个,分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字。

    sound这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。

    一般驱动程序文件会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动

    在根节点来说Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核。如果不支持的话那么这个设备就没法启动 Linux 内核。

  • model属性 model 属性值是一个字符串,一般 model 属性描述设备模块信息

  • status属性 status 属性和设备状态有关的, status 属性值是字符串,描述设备的状态信息。

    alt

  • #address-cells 和#size-cells 属性

    用于描述子节点的地址信息,reg属性的address 和 length的字长。

    • #address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),
    • #size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。
    • 子节点的地址信息描述来自于父节点的#address-cells 和#size-cells的值,而不是该节点本身的值(当前节点的信息是描述子节点的,自己的信息在父节点里)
    //每个“address length”组合表示一个地址范围,
    //其中 address 是起始地址, length 是地址长度,
    //#address-cells 表明 address 这个数据所占用的字长,
    // #size-cells 表明 length 这个数据所占用的字长.
    reg = <address1 length1 address2 length2 address3 length3……>
    
  • reg属性 reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息, reg 属性的值一般是(address, length)对.

    uart1: serial@02020000 {
        compatible = "fsl,imx6ul-uart",
            "fsl,imx6q-uart", "fsl,imx21-uart";
        reg = <0x02020000 0x4000>;
        interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clks IMX6UL_CLK_UART1_IPG>,
            <&clks IMX6UL_CLK_UART1_SERIAL>;
        clock-names = "ipg", "per";
        status = "disabled";
    };
    

    uart1 的父节点 aips1: aips-bus@02000000 设置了#address-cells = <1>、 #sizecells = <1>,因此 reg 属性中 address=0x02020000, length=0x4000。都是字长为1.

  • ranges属性

    • ranges属性值可以为或者按照( child-bus-address , parent-bus-address , length )格式编写的数字

    • ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成。

    • 如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换

    child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长
    parent-bus-address: 父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长
    length: 子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长
    
  • 特殊节点

    根节点“/”中有两个特殊的子节点: aliases 和 chosen

    1. aliases

      aliases {
          can0 = &flexcan1;
          can1 = &flexcan2;
          ...
          usbphy0 = &usbphy1;
          usbphy1 = &usbphy2;
      };
      

      aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。

      但是,一般会在节点命名的时候会加上 label,然后通过&label来访问节点。

    2. chosen chosen 不是一个真实的设备, chosen 节点主要是为了 uboot 向 Linux 内核传递数据(bootargs 参数)。

#嵌入式##校招##八股文##linux##linux驱动#
拉依达的Linux驱动八股文 文章被收录于专栏

你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。

全部评论

相关推荐

评论
2
5
分享
牛客网
牛客企业服务