李迟按:
上一篇内核的文章是2年半前,期间因工作转行而停止研究,最近又重新捡起。这个系列从2013年起间断地更新,本来想从系统角度逐步写的,但工作量十分庞大,现在也想通了,在适合的时间写,不带目的,不强迫自己完成什么任务。不强调什么平台/芯片、内核版本。一篇文章针对一、两个知识点即可。
本文是笔者接触设备树的第一个实例,仅仅是验证设备树简单的写法和几个函数的使用。
一、设备树
Linux内核很早就引入设备树了。引入设备树之前,arm架构代码中有大量的硬编码——即外设特性、配置全部写到代码中,不管是时钟配置、引脚复用还是MTD分区、GPIO高低电平。这样的话稍有不同的板级配置(如同一SOC,有的有1个LED,有的有2个LED,flash大小亦会有不同),会使用不同的文件或在代码中用宏区分。总之,代码比较臃肿,设备树的引入,使得代码比较简洁,引脚复用、分区等等都放到设备树文件,不同设备树文件对应不同配置的板子,而代码无须大量修改(甚至不用修改)。
关于设备树的引入历史及linus大神的风范,网上有资料,此处不展开。
二、知识点
设备树有固定的语法,本文也不展开。只说一下简单的知识点。
arm的设备树位于目录arch/arm/boot/dts
,该目录有许多不同板子的设备树源文件。
设备树源文件后缀为dts(device tree source),经过编译后生成设备树二进制文件,后缀为dtb(device tree blob)。dtb文件需要传递给内核进行解析。
设备树编译器为dtc,一般在内核源码目录输入make dtbs
即可。使用的Makefile
为arch/arm/boot/dts/Makefile
。该Makefile
根据make menuconfig
选定的芯片类型编译多个设备树,如自行新增的,需要在Makefile对应芯片类型添加,否则不会被编译。
一般不会从头编写设备树,而是使用类似的现成的模板修改。
内核启动参数可以在设备树中使用chosen
指定,示例(imx6q)如下:
1 | chosen { |
本文使用的设备树节点如下:
1 | // 专门用于测试dts的示例,没实例用途 |
其中compatible
与驱动使用的名称必须一致(这样才能匹配上)。其它内容比较简单,分别是字符串、布尔类型、不同位数的数值、数组、子节点。
三、驱动实例
驱动实例如下:
1 | /** |
示例的代码是一个简单的模板,除了学习dts外,没什么用处。但是可以以此展开复杂的、有实际用途的驱动。
与以前的platform驱动不同,platform_driver
中指定of_match_table
,foo_of_match
结构体的.compatible
必须与设备树的compatible
一致。
本驱动涉及到的读取设备树节点信息的函数如下,更多函数,参考内核源码的include/linux/of.h
头文件:
1 | of_property_read_string // 读取字符串 |
如果存在多个子节点,用of_get_available_child_count
获取个数(可用于开辟内存),然后调用for_each_available_child_of_node
遍历所有子节点,注意,of_get_property
与of_property_read_string
有相同效果。只是用法不同而已。
四、小结
设备树有许多用法和功能,本文只是初窥门径而已。从实践角度,笔者建议先用实例来了解设备树的基本用法,熟悉后,再看其它与外设有关的(如时钟、引脚复用)的例子。
网上关于设备树的测试驱动资料比较少,笔者写此文,仅抛砖引玉。
本驱动在qemu的imx6q下测试通过。设备树文件名为imx6q-sabrelite.dts
。
五、资源
李迟 2019.6.12 周三 晚