基于 A17 (ums9230) 平台,Kernel 6.18,Bazel/Kleaf + Kconfig + Makefile 编译框架
目录
- 总编译入口:uck_ddk_modules.bzl
- [两类驱动:unisoc-modules 与 unisoc-ext-modules](#两类驱动:unisoc-modules 与 unisoc-ext-modules)
- 模块注册到编译清单
- [DDK 驱动的文件结构和构建模板](#DDK 驱动的文件结构和构建模板)
- [标准 Kbuild 模块](#标准 Kbuild 模块)
- [Kconfig Fragment 配置机制](#Kconfig Fragment 配置机制)
- [编译产物到 DLKM 分区](#编译产物到 DLKM 分区)
- 添加一个全新驱动的完整步骤
1. 总编译入口:uck_ddk_modules.bzl
uck_ddk_modules.bzl 是整个 6.18 内核驱动编译的总清单。Bazel 只编译这个清单中列出的模块,不在清单里的代码不会编译。
# unisoc-modules/unisoc_bazels/uck_ddk_modules.bzl
UCK_DDK_MODULES = [
{
"name": "//unisoc-modules/drivers/unisoc_platform/power/...:bq2560x_charger",
"soc": "common", # 所有 SoC 都编
"variant": ["user", "userdebug"],
},
{
"name": "//unisoc-ext-modules/touchscreen/focaltech_ft8756_spi:...",
"soc": "common",
"variant": ["user", "userdebug"],
},
{
"name": "//unisoc-ext-modules/gpu/natt/mali:mali_kbase_ums9230",
"soc": "ums9230", # 仅 ums9230 SoC 编
"variant": ["user", "userdebug"],
},
# ... 数百个模块
]
1.1 字段含义
|-----------|----------------------|-------------------------------------|
| 字段 | 含义 | 可选值 |
| name | //bazel包路径:target名 | 指向 BUILD.bazel 中的 target |
| soc | 该模块适用的 SoC | "common"=全SoC, "ums9230"=仅此 SoC |
| variant | 编译类型 | ["user"], ["userdebug"], 或两者 |
1.2 筛选逻辑
get_uck_ddk_modules(soc, variant) 遍历清单,按 soc + variant 筛选:
for ddk in UCK_DDK_MODULES:
if "common" in ddk["soc"] and variant in ddk["variant"]:
加入编译
elif soc in ddk["soc"] and variant in ddk["variant"]:
加入编译
筛选后的列表才是最终编译的模块集合。
1.3 完整的编译清单体系
总共有 3 个独立清单,由 uck_imply.bzl 统一加载:
|-----------------------|-------------------------|-------------------|---------------------------|
| 清单 | 文件 | 内容 | 筛选函数 |
| UCK_DDK_MODULES | uck_ddk_modules.bzl | DDK 驱动(带 .bzl 构建) | get_uck_ddk_modules() |
| UCK_KLEAF_MODULES | uck_kleaf_modules.bzl | 标准 kbuild 模块 | get_uck_kleaf_modules() |
| UCK_DTBS | uck_dtbs.bzl | DTB/DTBO 文件 | get_uck_dtbs() |
2. 两类驱动:unisoc-modules 与 unisoc-ext-modules
根据代码仓库的位置,驱动分为两类。两类驱动共用同一套编译框架 (unisoc_ddk_module()),区别只在于目录组织方式。
2.1 unisoc-modules/drivers/ --- SoC 平台标准驱动
代码位置:
kernel6.18/unisoc-modules/drivers/unisoc_platform/power/sprd_charger/
├── src/
│ ├── bq2560x-charger.c
│ ├── psc2965-charger.c
│ └── sprd_battery_info.c
├── Makefile # kbuild Makefile
├── Kconfig
├── ddk_sprd_charger.bzl # 驱动组入口
├── BUILD.bazel
├── mod_bq2560x_charger.bzl # 单模块定义
└── mod_psc2965_charger.bzl
特点:
- 无驱动级 fragment(依赖 Board 级 fragment 使能
CONFIG_*) - 通常 SoC 必选,长期稳定
2.2 unisoc-ext-modules/ --- 外设/第三方驱动
代码位置:
kernel6.18/unisoc-ext-modules/touchscreen/focaltech_ft8756_spi/
├── BUILD.bazel
├── ddk_focaltech_ft8756_spi.bzl
├── mod_focaltech_ft8756_spi_ts.bzl
├── Kconfig
├── src/
├── include/
└── arch/arm64/configs/ # 可选:驱动级 fragment
├── uck_user.fragment
└── uck_userdebug.fragment
特点:
- 可以自带驱动级 fragment(独立使能,不影响其他 Board)
- 通常外设驱动、第三方驱动、variant 定制驱动
2.3 两类驱动的本质区别
|----------------------|-----------------------------|-------------------------|
| 对比项 | unisoc-modules/drivers/ | unisoc-ext-modules/ |
| 有驱动级 fragment | ❌ 无 | ✅ 可选 |
| 编译框架 | unisoc_ddk_module() | unisoc_ddk_module() |
| 可互依赖 | ✅ 可以 | ✅ 可以 |
| 对应 hardware_info.csv | ❌ 不参与 | ❌ 不参与 |
两类驱动之间可以相互依赖:
# unisoc-modules → unisoc-ext-modules
unisoc_ddk_module(name="sdhci_sprd", ddk_args={
"deps": ["//unisoc-ext-modules/wcn/bsp/unisoc_wcn_bsp:__pkg__"],
})
# unisoc-ext-modules → unisoc-modules
unisoc_ddk_module(name="focaltech_ft8756_spi_ts", ddk_args={
"deps": ["//unisoc-modules/drivers/.../regmap:__pkg__"],
})
3. 模块注册到编译清单
将新驱动加入编译,只需在 uck_ddk_modules.bzl 追加一条记录:
{
"name": "//unisoc-ext-modules/my_driver:my_driver",
# ↑ bazel 包路径 ↑ target 名
# BUILD.bazel 所在位置 在 mod_xx.bzl 中定义的 name
"soc": "common",
"variant": ["user", "userdebug"],
}
name 字段拆解
"//unisoc-ext-modules/my_driver:my_driver"
│ │
└─ kernel6.18/ 下的路径 └─ mod_my_driver.bzl 中的
→ unisoc-ext-modules/ unisoc_ddk_module(name="my_driver", ...)
my_driver/BUILD.bazel
: 后面的 target 名必须与 mod_xxx.bzl 中 unisoc_ddk_module(name=...) 的参数一致,否则 bazel 找不到 target。
4. DDK 驱动的文件结构和构建模板
4.1 标准目录结构
unisoc-ext-modules/my_driver/
├── BUILD.bazel # 入口文件
├── ddk_my_driver.bzl # 封装模块函数
├── mod_my_driver.bzl # 模块定义(源码列表、依赖、配置)
├── Kconfig # Kconfig 菜单
├── Makefile # kbuild Makefile(真正的编译入口)
├── src/
│ └── my_driver.c
├── include/
│ └── public/
│ └── my_driver.h
└── arch/arm64/configs/ # 可选:驱动级 fragment
├── uck_user.fragment
└── uck_userdebug.fragment
4.2 各文件详解
BUILD.bazel --- 入口
load(":ddk_my_driver.bzl", "my_driver_ddk")
my_driver_ddk()
ddk_my_driver.bzl --- 模块封装
load(":mod_my_driver.bzl", "my_driver_module")
load("//unisoc-modules:unisoc_bazels/unisoc_ddk_module.bzl", "unisoc_ddk_module")
def my_driver_ddk():
unisoc_ddk_module(
name = "my_driver",
ddk_args = my_driver_module(),
)
mod_my_driver.bzl --- 核心定义
load("//unisoc-modules:unisoc_bazels/uck_imply.bzl", "get_product_info")
def my_driver_module():
name = "my_driver" # 模块名 = target 名
product_soc, product_variant = get_product_info()
# 驱动级 fragment(可选。不想要就删掉这行)
defconfig = "arch/arm64/configs/uck_{}.fragment".format(product_variant)
kconfig = "Kconfig"
conditional_srcs = {
"CONFIG_MY_DRIVER": { # Kconfig 定义的选项
True: ["src/my_driver.c"], # 使能时编译这些文件
},
}
ddk_args = {
"conditional_srcs": conditional_srcs,
"kconfig": kconfig,
"defconfig": defconfig, # 可选,省略 = 依赖 Board 级 fragment
}
return ddk_args
Makefile --- 真正的编译规则
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MY_DRIVER) += my_driver.o
这是标准 Linux kbuild Makefile,Bazel 最终调用 kbuild 去编译它。
Kconfig --- 配置菜单
config MY_DRIVER
tristate "My New Driver"
depends on I2C
help
My new driver description.
驱动级 fragment(可选)
# arch/arm64/configs/uck_userdebug.fragment
CONFIG_MY_DRIVER=m
4.3 unisoc_ddk_module() 模板做了什么
# unisoc-modules/unisoc_bazels/unisoc_ddk_module.bzl
def unisoc_ddk_module(name, ddk_args):
# 1. 自动确定编译目标(uck_user / uck_userdebug)
target = "uck_" + product_variant
kernel_build = "//unisoc-modules:{}".format(target)
# 2. 调用 Kleaf 的 ddk_module()
ddk_kwargs = dict(ddk_args)
ddk_kwargs["name"] = name
ddk_kwargs["out"] = "{}.ko".format(name)
ddk_kwargs["kernel_build"] = kernel_build
ddk_module(**ddk_kwargs)
# 3. 打包产物
pkg_install(name = name + "_dist", ...)
5. 标准 Kbuild 模块(uck_kleaf_modules.bzl)
DDK 驱动走 uck_ddk_modules.bzl,标准 kbuild 驱动走 uck_kleaf_modules.bzl。
这两种模块的区别在于构建方式:
|----------|-----------------------------|--------------------------------|
| 构建方式 | DDK 驱动 | Kleaf 驱动 |
| 入口 | .bzl 文件 | Makefile |
| 编译命令 | ddk_module() 调用 kbuild | make M=path |
| 示例 | charger / touchscreen / GPU | cfg80211.ko, batman-adv.ko |
# unisoc-modules/unisoc_bazels/uck_kleaf_modules.bzl
UCK_KLEAF_MODULES = [
"drivers/gpu/drm/drm_dma_helper.ko",
"net/wireless/cfg80211.ko",
...
]
UCK_KLEAF_MODULES_618 = [ # 6.18 内核专有
"net/sched/sch_dualpi2.ko",
]
DDK 驱动比 Kleaf 驱动多了一层 :mod_xxx.bzl 负责描述源码列表和依赖,而 Kleaf 模块直接在 kernel 自身的 Makefile 中定义。绝大多数新驱动应该用 DDK 方式。
6. Kconfig Fragment 配置机制
6.1 优先级链(从低到高)
① gki_defconfig ← Google 通用内核基线
↓ merge
② configs/kernel_configs/kernel6.18/sprd_gki.fragment ← SoC 级
↓ merge
③ configs/ddk_configs/sprd_gki.fragment ← Board 级
↓ merge
④ 驱动自带的 defconfig(mod_xxx.bzl 中指定) ← 驱动级
6.2 两种 Fragment 的对比
|--------|------------------------|------------------------------|
| 维度 | Board 级 fragment | 驱动级 fragment |
| 位置 | configs/ddk_configs/ | 驱动目录下的 arch/arm64/configs/ |
| 作用域 | 所有驱动共享 | 仅该驱动自身 |
| 是否必须 | ✅ 必须 | ❌ 可选 |
| 定义者 | SoC 团队 | 驱动开发者 |
| 典型用途 | SoC 标准外设 | 特定 variant 调试、第三方驱动 |
6.3 驱动级 fragment 可以省略吗?
可以。两种情况:
|--------------------------------------|-------------------------------------------|
| 场景 | 做法 |
| Board 级 fragment 已定义了 CONFIG_XXX=m | 驱动级 fragment 不需要 |
| Board 级 fragment 没定义 | 驱动级 fragment 需要(或者改 Board 级 fragment) |
省略 defconfig 参数:
ddk_args = {
"conditional_srcs": conditional_srcs,
"kconfig": kconfig,
# 没有 defconfig → 依赖 Board 级 fragment
}
6.4 fragment 目录结构
unisoc-modules/configs/
├── ddk_configs/
│ └── sprd_gki.fragment # DDK Board 级
└── kernel_configs/kernel6.18/
├── sprd_gki.fragment # SoC 级 GKI
├── sprd_debug.fragment # debug 项
└── sprd_Syzkaller.fragment # 测试专用
7. 编译产物到 DLKM 分区
7.1 全链路
UCK_DDK_MODULES 指定编译哪些模块
│ bazel build
↓
{name}.ko ← 编译产物
│ kernel_modules_install
↓
modules_staging_dir/ ← depmod → modules.dep
│ create_modules_staging() 按 modules.csv 挑选
↓
vendor_dlkm_staging_dir/ ← 分区目录
│ vendor_dlkm_image()
↓
vendor_dlkm.img ← 烧录到 DLKM 分区
7.2 modules.csv --- 哪些 .ko 进哪个分区
kernel6.18/devices/qogirl6/ums9230_arm64/
├── user/modules.csv # user 版
├── userdebug/modules.csv # userdebug 版(含 debug 模块)
├── Skynet/modules.csv # Kasan 测试版
└── gcov/modules.csv # 覆盖率测试版
格式:
modules_name, image_name, mode
bq2560x_charger.ko, vendor_ramdisk vendor_dlkm, recovery second.normal
|----------------|-----------------------------------------------------------------------------------|
| 字段 | 含义 |
| modules_name | .ko 文件名 |
| image_name | 放入哪个分区(vendor_dlkm / vendor_ramdisk / system_dlkm) |
| mode | 什么 boot mode 下加载(recovery / second.normal / second.cali / second.charger) |
7.3 CSV 定义了但 KO 不存在会怎样
在 create_modules_staging() 中:
for module in $(cat modules_list_file); do
if [ -f "$src_dir/$module" ]; then
cp "$module" "$dest_dir/"
else
echo "WARNING: module $module not found, skipping"
fi
done
不会报错,只打印 WARNING 跳过。 该 .ko 不会出现在 dlkm 分区中,开机不会加载。
8. 添加一个全新驱动的完整步骤
需要改动的地方
|-------|----------------------------------------------------|----------------------------|
| # | 文件 | 操作 |
| ① | unisoc-ext-modules/my_driver/ 全目录 | 新建 |
| ② | unisoc-modules/unisoc_bazels/uck_ddk_modules.bzl | 追加一条 UCKD_DDK_MODULES 记录 |
不需要改动的地方
|------------------|------------------------------------------|
| 文件 | 原因 |
| Board 级 fragment | 驱动自带 fragment 或依赖已有配置 |
| modules.csv | 只控制 dlkm 打包,不影响编译 |
| DTS | 新驱动有独立 compatible,DTS 在 board overlay 中加 |
实际操作
# 1. 新建驱动目录
mkdir -p unisoc-ext-modules/my_driver/src
mkdir -p unisoc-ext-modules/my_driver/include/public
mkdir -p unisoc-ext-modules/my_driver/arch/arm64/configs
# 2. 创建文件
touch unisoc-ext-modules/my_driver/{BUILD.bazel,ddk_my_driver.bzl}
touch unisoc-ext-modules/my_driver/{mod_my_driver.bzl,Kconfig,Makefile}
touch unisoc-ext-modules/my_driver/src/my_driver.c
# 3. 可选:驱动级 fragment
echo "CONFIG_MY_DRIVER=m" > unisoc-ext-modules/my_driver/arch/arm64/configs/uck_userdebug.fragment
echo "CONFIG_MY_DRIVER=m" > unisoc-ext-modules/my_driver/arch/arm64/configs/uck_user.fragment
# 4. 注册到编译清单
# 编辑 unisoc-modules/unisoc_bazels/uck_ddk_modules.bzl,追加:
# {
# "name": "//unisoc-ext-modules/my_driver:my_driver",
# "soc": "common",
# "variant": ["user", "userdebug"],
# },
# 5. 可选:如果需要放到 dlkm 分区
# 编辑 devices/qogirl6/ums9230_arm64/userdebug/modules.csv,追加:
# my_driver.ko, vendor_dlkm, second.normal
# 6. 编译
tools/bazel build //unisoc-modules:uck_userdebug_dist