设备树(一)

1、字符设备驱动程序的三种写法
a. 驱动程序编写有3种方法:传统方法、使用总线设备驱动模型、使用设备树
b. 这3种方法也核心都是一样的: 分配、设置、注册 file_operations结构体
   这个结构体中有.open, .read, .write, .ioctl等成员
   驱动程序要实现这些成员,在这些成员函数中操作硬件
c. 这3种方法的差别在于:如何指定硬件资源,比如如何指定LED引脚是哪个      
c.1 传统方法: 在驱动程序代码中写死硬件资源, 代码简单/不易扩展
c.2 总线设备驱动模型: 把驱动程序分为两部分(platform_driver, platform_device)
    在platform_device中指定硬件资源,
    在platform_driver中分配/设置/注册 file_operations结构体, 并从platform_device获得硬件资源
    
    特点:
    易于扩展,但是有很多冗余代码(每种配置都对应一个platform_device结构体), 
    硬件有变动时需要重新编译内核或驱动程序。
    
c.3 使用设备树指定硬件资源: 驱动程序也分为两部分(platform_driver, 设备树*.dts)
    在设备树*.dts中指定硬件资源, dts被编译为dtb文件, 在启动单板时会将dtb文件传给内核,
    内核根据dtb文件分配/设置/注册多个platform_device
    
    platform_driver的编写方法跟"总线设备驱动模型"一样。
    
    特点:
    易于扩展,没有冗余代码
    硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件

2、字符设备驱动的传统写法

a. 分配file_operations结构体
b. 设置file_operations结构体
   该结构体中有.open,.read,.write等成员,
   在这些成员函数中去操作硬件
c. 注册file_operations结构体:
   register_chrdev(major, name, &fops)
d. 入口函数: 调用register_chrdev
e. 出口函数: 调用unregister_chrdev

3、总线设备驱动模型
a. 驱动程序分为platform_device和platform_driver两部分
   platform_device : 指定硬件资源
   platform_driver : 根据与之匹配的platform_device获得硬件资源, 并分配/设置/注册file_operations
b. 如何确定platform_device和platform_driver是否匹配?
b.1 platform_device含有name
b.2 platform_driver.id_table"可能"指向一个数组, 每个数组项都有name, 表示该platform_driver所能支持的platform_device
b.3  platform_driver.driver含有name, 表示该platform_driver所能支持的platform_device
b.4 优先比较b.1, b.2两者的name, 若相同则表示互相匹配
b.5 如果platform_driver.id_table为NULL, 则比较b.1, b.3两者的name, 若相同则表示互相匹配

总线设备驱动模型只是一个编程技巧, 它把驱动程序分为"硬件相关的部分"、"变化不大的驱动程序本身", 
这个技巧并不是驱动程序的核心, 
核心仍然是"分配/设置/注册file_operations"

4、使用设备树时对应的驱动编程
a. 使用"总线设备驱动模型"编写的驱动程序分为platform_device和platform_driver两部分
   platform_device : 指定硬件资源, 来自.c文件
   platform_driver : 根据与之匹配的platform_device获得硬件资源, 并分配/设置/注册file_operations
b. 实际上platform_device也可以来自设备树文件.dts
   dts文件被编译为dtb文件, 
   dtb文件会传给内核, 
   内核会解析dtb文件, 构造出一系列的device_node结构体,
   device_node结构体会转换为platform_device结构体

   所以: 我们可以在dts文件中指定资源, 不再需要在.c文件中设置platform_device结构体
   
c. "来自dts的platform_device结构体" 与 "我们写的platform_driver" 的匹配过程:
    "来自dts的platform_device结构体"里面有成员".dev.of_node", 它里面含有各种属性, 比如 compatible, reg, pin
    "我们写的platform_driver"里面有成员".driver.of_match_table", 它表示能支持哪些来自于dts的platform_device
    
    如果"of_node中的compatible" 跟 "of_match_table中的compatible" 一致, 就表示匹配成功, 则调用 platform_driver中的probe函数;
    在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源

5、设备树的规范(dts和dtb)
第01节_DTS格式
(1) 语法:
Devicetree node格式:
[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

Property格式1:
[label:] property-name = value;

Property格式2(没有值):
[label:] property-name;

Property取值只有3种: 
arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示), 
string(字符串), 
bytestring(1个或多个字节)

示例: 
a. Arrays of cells : cell就是一个32位的数据
interrupts = <17 0xc>;

b. 64bit数据使用2个cell来表示:
clock-frequency = <0x00000001 0x00000000>;

c. A null-terminated string (有结束符的字符串):
compatible = "simple-bus";

d. A bytestring(字节序列) :
local-mac-address = [00 00 12 34 56 78];  // 每个byte使用2个16进制数来表示
local-mac-address = [000012345678];       // 每个byte使用2个16进制数来表示

e. 可以是各种值的组合, 用逗号隔开:
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";

(2) 
DTS文件布局(layout):
/dts-v1/;
[memory reservations]    // 格式为: /memreserve/ <address> <length>;
/ {
    [property definitions]
    [child nodes]
};

(3) 特殊的、默认的属性:
a. 根节点:
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible       // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
                 // 即这个板子兼容哪些平台 
                 // uImage : smdk2410 smdk2440 mini2440     ==> machine_desc         
                 
model            // 咱这个板子是什么
                 // 比如有2款板子配置基本一致, 它们的compatible是一样的
                 // 那么就通过model来分辨这2款板子

b. /memory
device_type = "memory";
reg             // 用来指定内存的地址、大小

c. /chosen
bootargs        // 内核command line参数, 跟u-boot中设置的bootargs作用一样

d. /cpus
/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu
所以 /cpus 中有以下2个属性:
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)

#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
                 // 必须设置为0


e. /cpus/cpu*
device_type = "cpu";
reg             // 表明自己是哪一个cpu

(4) 引用其他节点:
a. phandle : // 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)

pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <1>;   // 使用phandle值为1来引用上述节点
};

b. label:

PIC: pic@10000000 {
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <&PIC>;   // 使用label来引用上述节点, 
                                 // 使用lable时实际上也是使用phandle来引用, 
                                 // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值