使用Digi核心模块或单板机,用户并不一定需要学习本文。但在某些时候,您需要了解本章节的一部分知识才能继续开发。比如,您把更改了或得定义了开发套件上的一些接口,添加了一块自定义屏等,这些就涉及到设备树的更改了。另外对于一些深度开发的场景,比如更改默认的Linux内核选项等,也需要学习本章节的一些知识。
本文将从概念入手,到实务操作一步一步指导您实现DEY内核定制和驱动开发的过程和示例。为了更好的理解本文,您还需要先看一下NXP i.MX系列处理器之引脚复用详解 本文在更新过程中,您可以参考Digi官方的文档:
CC6 https://www.digi.com/resources/documentation/digidocs/90002284/
CC6UL https://www.digi.com/resources/documentation/digidocs/90002285/

需将部分内网资料翻译:https://confluence.digi.com/display/DEY/How+to+develop+with+Kernel+in+DEY

Linux的创始人Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pain in the ass”,引发ARM Linux社区的地震,随后ARM社区进行了一系列的重大修正。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的冗余垃圾代码,相当多数的代码只是在描述板级细节,而这些板级硬件细节代码对于内核来讲,不过是垃圾,。

社区必须改变这种局面,于是PowerPC等其他体系架构下已经使用的Flattened Device Tree(FDT)进入ARM社区的视野。Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)。采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

Linux的设备树
Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):

  • CPU的数量和类别
  • 内存基地址和大小
  • 总线和桥
  • 外设连接
  • 中断控制器和中断使用情况
  • GPIO控制器和GPIO使用情况
  • Clock控制器和Clock使用情况

它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

设备树的组成和结构

Device Tree简称DT,它是一种描述硬件的标准数据结构。在引入设备树概念之前,早期的Linux内核包含了完整的硬件描述,在引入Device Tree后,相关的硬件描述位于独立的二进制文件:Device Tree Blob (dtb)。启动器会加载两个二进制文件:内核镜像和dtb文件。

正因为如此,大多数硬件C代码在Linux内核中消失。而像改变时序等基本的硬件参数也不再需要重新配置和编译Linux内核。同一个内核可以加载不同的设备树文件,来支持不同硬件平台上。

  • DTS (设备树源码)

.dts文件是一种ASCII 文本格式的Device Tree描述,此文本格式非常人性化,适合人类的阅读习惯。基本上,在ARM Linux在一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。其他的machine对应的.dts就include这个.dtsi。当然,和C语言的头文件类似,.dtsi也可以include其他的.dtsi,它们的基本元素即为前文所述的结点和属性。

  • DTC (device tree compiler)

将.dts编译为.dtb的工具。通常我们并不会单独和它打交通,所以可以不用了解细节。

了解更多…

了解更多…

DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下,编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y := dtc”这一hostprogs编译target。 在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,那些.dtb文件会被编译出来。

  • Device Tree Blob (.dtb)

.dtb是.dts被DTC编译后的二进制格式的Device Tree描述,可由Linux内核解析。在Digi的内核分区中,存放有boot.scr脚本和设备树文件,之后bootloader在引导kernel的过程中,会先执行启动脚本,以决定读取哪个dtb到内存,Linux内核会根据该设备树文件展开相应的设备驱动。

DT,FDT都是指设备树,FDT(扁平化设备树)是通用的设备树描述语言。通常我们修改驱动要打交道的是.dts或.dtsi文件,而上传测试驱动是要编译后的二进制.dtb文件。

设备树文件在内核目录arch/arm/boot/dts目录下,你可以找到如<chip-board>.dts之类的文件,比如imx6ul-ccimx6ulsbc.dts,通常ccimx字样表明了这个设备树文件和digi平台有关。稍后会介绍通过yocto的devshell来查询或快速修改设备树文件。在bitbake的编译目录里,设备树文件通常在项目文件夹的内核源码目录tmp/sysroots/ccimx6sbc/usr/src/kernel/中。

平台DTS:arch/arm/boot/dts/imx6q-ccimx6sbc.dts它包含了支持四核/双核的共同特性部分的DTSI文件(arch/arm/boot/dts/imx6q.dtsi),而在这DTSI中又包含了对solo,dual,duallite,quad共同特性支持的DTSI文件(arch/arm/boot/dts/imx6qdl.dts)

请参考:https://www.digi.com/resources/documentation/digidocs/90002284/reference/bsp/r_device_tree_files.htm来获取详细的说明。


一个Digi设备树更改和编译的简单例子:设备树修改和编译实例

如果您需要修改Linux内核和设备树,有以下几种方法:通过Yocto项目的devshell,独立的Linux源码树和自定义Yocto内核recipe。

1、使用用Yocto项目的devshell快速修改测试内核和设备树

更改设备树或驱动源码

Yocto是利用recipe来自动编译定制内核和文件系统镜像,虽说改变内核可以通过recipe的改动来实现。但有时您只需要测试一下较小的,临时的改动,看看效果。这种情况,您可以利用yocto项目的devshell来进行修改。devshell是一个在Host主机上模拟运行在目标dey板子的shell终端,在yocto中,它是以虚拟机的方式在主机上运行。 如果您成功编译过DEY镜像,进入您的Yocto系统开发项目文件夹,运行下面命令将快速调出devshell终端,在这个终端里,你可以快速地修改并编译内核源码和设备树文件。

source dey-setup-environment
bitbake -c devshell virtual/kernel

上述命令中,yocto会自动获取并解包Linux源码,一个新的终端会打开<project_folder>/tmp/work-shared/<platform>/kernel-source这个内核源码路径,你可以根据需要编辑相关的源代码,采用Linux独立源码树的方式快速编译。编译的内核和设备树文件在arch/arm/boot和arch/arm/boot/dts下。您也可以用yocto的命令来编译:

bitbake -C compile virtual/kernel

使用yocto的方式编译,您可以在tmp/deploy/images/<platform>中找到相关的内核和设备树文件。

注意,大写的-C使bitbake执行随后的编译命令,它也强制保留临时目录内的改变。如果使用小写的-c,会丢弃这些改变。小写的-c不会删除之前的修改的设备树,但相关的变更也不会生效,要想用目录中的变更来重新编译设备树,需要使用大写的-C开关。

更改内核配置选项

在配置内核选项时,DEY并没有使用内核源码树下的<platform>_defconfig预配置文件,而是使用DEY的meta-digi/meta-digi-arm/recipes-kernel/linux/linux-dey-<version>/<platform>/defconfig,要对内核选项做快速、临时的修改,可以用下面命令:

bitbake -c menuconfig virtual/kernel

根据需修改在这图形选项开关界面进行内核配置。用默认的文件名(.config)保存更改。如果临时编译,可以直接bitbake virtual/kernel,这样会在内核编译路径生成新的内核和相关模块,在没清空或重新编译时,你可以直接再编译镜像来得到相关的内核或内核模块。 为了编译生成更改后的内核,还需要执行一个保存配置选项命令

bitbake -c savedefconfig virtual/kernel

上面这个命令会在build目录生成defconfig配置文件,这也是bitbake编译内核时所用的文件,此时,用

bitbake -C compile virtual/kernel

编译内核时,就会用上这些新的自定义配置。

注意:DEY2.4之前的版本,你需要将内核配置文件保存下来,并拷贝到DEY源码中其对应的路径下,编译时相关更改才会生效。
通常地,为了使用新内核编译出的固件镜像,您需要在bitbake之前清除一下之前的编译结果,您也可以把内核保存下来,拷到dey源码中其对应的路径下,以便下次新建项目时不需要重新配置内核

bitbake -c cleanall dey-image-qt
bitbake -c dey-image-qt
<code>

**撤销更改**

当您测试并验证更改后的内核和设备树时,如果您想撤销更改,可以用下面命令
<code>
bitbake -c cleanall virtual/kernel
bitbake virtual/kernel

2、通过外部独立的Linux源码树编译

这个方法就是克隆DEY中的Linux内核源码库,并在外部编译它。建议那些专注于内核开发的团队使用,它并不需要Yocto,仅需DEY工具链来为Digi平台编译代码。您可以在使用自定义Yocto recipe之前先用这种方式测试内核开发的相关修改。它和第一种方式并不冲突。

3、通过自定义Yocto内核recipe方式编译

这个方法利用Yocto的编译系统,如果您想定制整个系统,一般采用这个方法。根据您的版本管理模式,它有两种实现方式::

  • 基于Git的实现方式(适用于大多数项目):该方式包含创建一个git库(通常是从DEY中fork),并利用该库开发内核,并配置使得DEY系统项目从该git库中获取取源码来编译。注意,这并不会自动应用官方更新,如果要更新并使用最新官方库,您需要合并Digi的官方更新到您所fork的项目。

要用这种方式,修改内核的recipe(linux-dey_<kernel version>.bb)并添加您的git库。

  
SRC_URI = "git://url/to/my/repo.git" 

您也可以用SRCBRANCH来指定内核版本:

 
SRCBRANCH = "v3.14/master"
SRCREV = "${AUTOREV}"
  • 基于补丁的方式:一般的方式是下载官方源码并应用自定义的补丁来满足您的特定需求。这种方式在仅需用到少量修改的补丁时,比较合适。如果您有大量的修改定制,建议您用前一种方式。

这种方式让您可以和官方源码库保持一致,并应用补丁来匹配您的特定需求。您可以利用独立外部源码或是用Yocto的devshell来生成补丁。

  1. 修改需要变动的源代码
  2. 在本地库中用git提交修改
    git add <changed files>  \\  git commit -s -m 'This is a test patch'
  3. 继续修改和提交,直到完成所需修改
  4. 为这些修改生成patch文件,并放到内核recipe目录
    举例来说,您已经做了五次修改并提交,下面生成patch
    git format-patch -5 -o /path/to/Yocto/sources/meta-digi/meta-digi-arm/recipes-kernel/linux/linux-dey-<kernel version>/<platform>
  5. 编辑内核recipe (linux-dey_<kernel version>.bb),将上面生成的patch添加进去
    SRC_URI_append_<platform> = " \ 
    file://0001-this-is-patch1.patch \ 
    file://0002-now-patch2.patch \ 
    file://0003-another-patch.patch \ 
    file://0004-yet-another-patch.patch \ 
    file://0005-this-is-the-final-patch.patch \ "
  6. 重编译内核
bitbake -c cleanall virtual/kernel
bitbake virtual/kernel
  • 更改配置

要使Yocto使用您的自定义配置,您需要拷贝它的defconfig到内核recipe所在目录。

要从当前配置中创建defconfig,在内核源码树中用make savedefconfig命令,在Yocto项目中用bitbake -c savedefconfig virtual/kernel命令

cp /path/to/my-custom-defconfig /path/to/Yocto/sources/meta-digi/meta-digi-arm/recipes-kernel/linux/linux-dey-<version>/<platform>/defconfig

进一步阅读如何为不同于SBC的自定义底板定制系统:为基于CC6UL的产品定制DEY系统