4、内核配置基础:Kconfig语法、menuconfig使用、defconfig与dts配置
内核配置,说白了就是告诉编译器:哪些功能要编译进内核,哪些编译成模块,哪些干脆不要。我刚开始接触RK3568时,觉得这步就是跑个make menuconfig随便选选。后来发现,配置不对,驱动加载不起来,外设不工作,连系统都起不来。嗯,这里面的门道不少。
这一章,我们就把内核配置的四个核心东西讲透:Kconfig语法、menuconfig操作、defconfig管理、dts配置。它们之间的关系,我画了张图,你先有个整体印象。
4.1 Kconfig语法:菜单项是怎么定义的
Kconfig文件是内核配置菜单的「源代码」。每个内核子目录下都有一个Kconfig,定义了该目录下的配置选项。我最早看Kconfig文件时,觉得它像一门小语言,其实规则很简单。
一个典型的配置项长这样:
config CONFIG_ROCKCHIP_I2S
tristate "Rockchip I2S controller driver"
depends on ARCH_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y here to include support for Rockchip I2S controller.
If unsure, say N.
几个关键字段:
- config:定义配置项名称,最终会变成
CONFIG_ROCKCHIP_I2S。 - tristate:三态选项——Y(编译进内核)、M(编译成模块)、N(不编译)。驱动类常用tristate。
- bool:布尔选项,只有Y/N,没有M。功能开关类常用bool。
- depends on:依赖关系。比如这个驱动依赖
ARCH_ROCKCHIP平台。 - select:反向依赖。选中这个选项时,自动选中另一个选项。我习惯把select理解为「我帮你选上」。
- help:帮助信息,menuconfig里按?能看到。
depends on和select别乱用。depends on多了,菜单会灰一大片;select多了,可能引入循环依赖。我一般只在驱动依赖某个核心框架时才用select。
4.2 menuconfig使用:图形化配置实战
执行make menuconfig,你会看到一个蓝底菜单界面。操作很简单:方向键移动,回车进入子菜单,空格切换Y/M/N,?查看帮助,/搜索。
我重点说几个实用技巧:
- 搜索功能(/):想找某个配置项,直接按/输入关键词。比如搜
I2S,会列出所有包含I2S的选项,还能看到依赖关系和当前状态。这个功能我几乎天天用。 - 快速定位:按/搜到后,按数字键直接跳转到该选项位置,不用一层层翻菜单。
- 保存与加载:退出时选
Save,默认保存为.config。也可以加载已有的配置文件。
4.3 defconfig管理:默认配置的保存与使用
每次手动配一遍太麻烦。所以内核引入了defconfig机制——把常用配置保存成一个文件,下次直接加载。
RK3568的默认配置文件在arch/arm64/configs/下,叫rockchip_linux_defconfig。使用方式:
# 加载默认配置
make ARCH=arm64 rockchip_linux_defconfig
# 基于当前 .config 生成新的 defconfig
make ARCH=arm64 savedefconfig
# 生成的 defconfig 会保存在当前目录,手动复制到 arch/arm64/configs/ 下
savedefconfig这个命令很聪明。它会把.config里非默认值的选项提取出来,生成一个精简的defconfig。我每次调完配置,都会跑一下savedefconfig,把改动保存下来。
| 命令 | 作用 | 常用场景 |
|---|---|---|
make defconfig |
加载架构默认配置 | 首次配置 |
make xxx_defconfig |
加载指定板级配置 | 切换开发板 |
make savedefconfig |
生成精简默认配置 | 保存自定义配置 |
make olddefconfig |
用默认值更新旧配置 | 内核版本升级后 |
避坑指南:我曾经在RK3568上调试一个音频驱动,改了menuconfig后直接编译,没保存defconfig。后来同事拉我的代码,编译出来音频就是坏的。从那以后,我每次改完配置,一定执行make savedefconfig,然后把生成的defconfig提交到git里。
4.4 dts配置:设备树描述硬件
Kconfig和defconfig管的是「软件功能开不开」,而dts(设备树)管的是「硬件长什么样」。RK3568的设备树文件在arch/arm64/boot/dts/rockchip/下。
一个典型的dts节点:
&i2s1 {
status = "okay";
rockchip,clk-trcm = <1>;
pinctrl-names = "default";
pinctrl-0 = <&i2s1_lrck &i2s1_sclk &i2s1_sdi &i2s1_sdo>;
};
关键点:
- &i2s1:引用I2S1节点,在
.dtsi文件里定义。 - status = "okay":使能该外设。默认可能是
disabled。 - pinctrl:引脚复用配置,指定用哪些GPIO引脚。
- clock/regulator:时钟和电源配置,驱动依赖这些资源。
dts和内核配置的关系:dts里的节点,必须有对应的内核驱动配置才能工作。比如你在dts里使能了I2S1,但内核没配CONFIG_ROCKCHIP_I2S,驱动根本不会编译进去,设备树配得再好也没用。
4.5 四者配合:一个完整的配置流程
假设我们要在RK3568上添加一个SPI设备,完整的配置流程是这样的:
- 写Kconfig:在驱动目录下添加配置项,定义依赖关系。
- 写Makefile:根据配置项决定是否编译该驱动。
- 写dts:在设备树里添加SPI控制器和设备的节点。
- 配置内核:执行
make menuconfig,选中SPI驱动。 - 保存defconfig:执行
make savedefconfig,保存配置。 - 编译:执行
make,生成内核镜像。
你看,Kconfig、menuconfig、defconfig、dts,四者缺一不可。Kconfig定义选项,menuconfig提供界面,defconfig保存默认值,dts描述硬件。理解了它们的分工,内核配置就不再是玄学。
一句话总结:Kconfig是菜单源码,menuconfig是点菜界面,defconfig是套餐菜单,dts是食材清单。四者配合,才能做出一盘能跑的内核。