目录
一、无设备树
1、不同点
(1) 使用 ioremap() 将寄存器地址映射成虚拟地址才能被访问:
(2) 用linux驱动提供相应的函数读(readb、readw、readl)写(writeb、writew、writel)映射后的 寄存器:
(3) ioremap之后,退出出口函数的时候,记得要有对应的 iounmap。
gitee代码链接:https://gitee.com/can_mo/gitee_linux_driver/tree/master/03_newchrled
github代码链接:https://github.com/CanvMo/Can/tree/master/03_newchrled
二、有设备树,无其它驱动模型
1、不同点
(1) 编写设备树文件(.dts),在根节点下添加设备树节点:
(2)用 of 函数获取设备树节点或者属性信息:
(3) 可以用 of_iomap() 将寄存器地址映射成虚拟地址,也是使用 iounmap() 取消映射:
(4) 也是用linux驱动提供相应的函数读(readb、readw、readl)写(writeb、writew、writel)映射 后的寄存器;
gitee代码链接:https://gitee.com/can_mo/gitee_linux_driver/tree/master/04_dtsled
github代码链接:Can/04_dtsled at master · CanvMo/Can · GitHub
2、内核匹配设备树文件,函数调用过程
3、内核解析设备树文件,函数调用过程
三、gpio子系统(pintrl子系统),带设备树
1、不同点
(1) 设备树文件需要固定格式
(2) 相关 of 函数获取设备树信息
(3) 用 gpio子系统相关库函数控制GPIO
gitee代码链接:https://gitee.com/can_mo/gitee_linux_driver/tree/master/05_gpioled
github代码链接:Can/05_gpioled at master · CanvMo/Can · GitHub
(4) 普通设备树驱动与gpio子系统的区别:
普通设备树驱动:读取了设备树的一些硬件信息(地址等),得到寄存器的值,可以通过 readl writel等函数操作对应寄存器;
gpio子系统(分层、分离思想):用设备树设置好硬件信息之后,有自己一套更加简单的操作 函数去控制硬件设备,gpio_direction_output(),gpio_set_value()等函数;
难点:设备树是的配置
四、平台设备驱动模型
1、目的:把设备信息和驱动分开;
2、platform驱动模型与设备树驱动模型的区别和联系:
(1) 相当于在设备树驱动模型上加了一层(platform驱动模型),两者设备树相关代码都是一样的。
3、四种匹配方式
第一种匹配方式(常用): OF 类型的匹配,也就是设备树采用的匹配方式;
第二种匹配方式: ACPI 匹配方式;
第三种匹配方式: id_table 匹配;
第四种匹配方式:如果第三种匹配方式的 id_table 不存在的话就直接比较驱动和设备的 name 字段;
4、 设备和驱动匹配成功之后,会执行 probe 函数,自己实现;
5、分为无设备树和有设备树两种驱动模型
(1)无设备树模型
a. 要自己编写一个 device(设备) 文件和一个 driver(驱动) 文件;
b. device文件用于描述硬件信息:
c. driver文件用于驱动设备:
gitee代码链接:https://gitee.com/can_mo/gitee_linux_driver/tree/master/17_platform
github代码链接:https://github.com/CanvMo/Can/tree/master/17_platform
(2)有设备树模型(常用)
a. 设备的描述被放到了设备树中,通过compatible属性与驱动匹配
b. 驱动框架
①通过 platform_driver_register()注册platform驱动结构体
②通过compatible属性设备和驱动匹配,匹配成功之后执行 .probe 函数:
③ led_probe (.probe) 函数主要是字符设备驱动框架基本内容:设置设备号、注册设 备、创建类、创建设备、初始化;
gitee代码链接:https://gitee.com/can_mo/gitee_linux_driver/tree/master/18_dtsplatform
github 代码链接:https://github.com/CanvMo/Can/tree/master/18_dtsplatform
(3)两种设备和驱动匹配方式不一样,其中name 属性用于传统的驱动与设备匹配,也就是检 查驱动和设备的 name 字段是不是相同;of_match_table 属性就是用于设备树下的驱动与 设备检查。
五、MISC(杂项)驱动模型,带设备树
1、优点:MISC 驱动是最简单的字符设备驱动,最大程度缩减了代码;
2、当我们板子上的某些外设无法进行分类的时候就可以使用 MISC(杂项)驱动;MISC(杂项)驱动 通常也是用平台设备驱动模型。
3、所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
4、还是平台设备驱动,主要是简化了注册和反注册
(1)注册
(2)反注册
gitee代码链接:gitee_linux_driver: 个人理解编写的linux驱动 - Gitee.com
github 代码链接:Can/19_miscbeep at master · CanvMo/Can · GitHub
附件:
1、自动分配设备号的原理:mdev机制
(1) mdev机制:使用 busybox 构建根文件系统的时候, busybox 会创建一个 udev 的简化版 本—mdev;Linux 系统中的热插拔事件也由 mdev 管理;
(2) mdev机制的目的:利用信息自动创建设备节点。那么利用哪些信息呢?
mdev扫描/sys/class和/sys/block中所有的类设备目录,如果在目录中含有名为"dev"的 文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev下创建设备节点。
2、设备树常用 OF 操作函数:获取设备树中的节点或者属性信息的函数,这些 OF 函 数原型都定义在 include/linux/of.h 文件中
(1)查找节点的 5个OF 函数:
① of_find_node_by_name 函数:通过节点名字查找指定的节点;
② of_find_node_by_type 函数:通过 device_type 属性查找指定的节点;
③of_find_compatible_node 函数:根据 device_type 和 compatible 这两个属性查找指定的 节点;
④ of_find_matching_node_and_match 函数:通过 of_device_id 匹配表来查找指定的节点;
⑤ of_find_node_by_path 函数:通过路径来查找指定的节点;
(2) 查找父/子节点的 OF 函数
① of_get_parent 函数:用于获取指定节点的父节点(如果有父节点);
② of_get_next_child 函数:用迭代的方式查找子节点;
(3) 提取属性值的 OF 函数
①of_find_property 函数:用于查找指定的属性;
②of_property_count_elems_of_size 函数:用于获取属性中元素的数量;
③of_property_read_u32_index 函数:用于从属性中获取指定标号的 u32 类型数据值(无符号 32位);
④of_property_read_u8_array 函数
of_property_read_u16_array 函数
of_property_read_u32_array 函数
of_property_read_u64_array 函数
这 4 个函数分别是读取属性中 u8、 u16、 u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据;
⑤of_property_read_u8 函数
of_property_read_u16 函数
of_property_read_u32 函数
of_property_read_u64 函数
这四个函数就是用于读取这种只有一个整形值的属性,分别用于读取 u8、
u16、 u32 和 u64 类型属性值;
⑥of_property_read_string 函数:用于读取属性中字符串值;
⑦of_n_addr_cells 函数:用于获取#address-cells 属性值;
⑧of_n_size_cells 函数:用于获取#size-cells 属性值;
(4) 其他常用的 OF 函数
①of_device_is_compatible 函数:用于查看节点的 compatible 属性是否有包含 compat 指定 的字符串,也就是检查设备节点的兼容性;
②of_get_address 函数:用于获取地址相关属性;
③of_translate_address 函数:负责将从设备树读取到的地址转换为物理地址;
④of_address_to_resource 函数:将 reg 属性值转换为 resource 结构体类型;
⑤of_iomap 函数:用于直接内存映射;
(5) 设备树常用操作GPIO的OF函数:这些函数原型都定义在 include/linux/of_gpio.h 文件中;
① of_get_named_gpio 函数:通过设备树节点获取GPIO编号:
函数原型:
np: 设备节点,可通过of_find_node_by_path函数获得;
propname: 包含要获取 GPIO 信息的属性名;
index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
返回值: 正值,获取到的 GPIO 编号;负值,失败。
用法:
3、常用gpio子系统函数,在include/asm-generic/gpio.h文件中:
(1)gpio_direction_output:把某个GPIO设置输出高电平或者低电平:
函数原型:
gpio: GPIO 编号;
value:往GPIO要写的值;
用法:
(2)gpio_set_value:一般只是在这个GPIO口的寄存器上写上某个值,但不确定这个端口是否 设置为输出,为了提高运行效率;
函数原型:
gpio: GPIO 编号;
value:往GPIO要写的值;
用法:
(1)和(2)一般会联合使用:初始化的时候用gpio_direction_output(),后面GPIO再输出操
作的时候,可以使用gpio_set_value(),提高运行效率。