Unisoc(紫光展锐) Kernel 6.18 DDK 编译框架详解

基于 A17 (ums9230) 平台,Kernel 6.18,Bazel/Kleaf + Kconfig + Makefile 编译框架


目录

  1. 总编译入口:uck_ddk_modules.bzl
  2. [两类驱动:unisoc-modules 与 unisoc-ext-modules](#两类驱动:unisoc-modules 与 unisoc-ext-modules)
  3. 模块注册到编译清单
  4. [DDK 驱动的文件结构和构建模板](#DDK 驱动的文件结构和构建模板)
  5. [标准 Kbuild 模块](#标准 Kbuild 模块)
  6. [Kconfig Fragment 配置机制](#Kconfig Fragment 配置机制)
  7. [编译产物到 DLKM 分区](#编译产物到 DLKM 分区)
  8. 添加一个全新驱动的完整步骤

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
相关推荐
无言听风雨丶1 小时前
Nginx 深度实战:从架构原理到生产排错
运维·nginx·架构
曦夜日长2 小时前
主从 Reactor 高并发服务器实战 (三):服务器套接字Socket模块详解与实现
运维·服务器·网络
麻瓜code2 小时前
Spring AI 全模块 API 字典一览(1.1.0 版)
人工智能·windows·spring
z06.2 小时前
Linux系统中MySQL的安装
linux·mysql
wzg20162 小时前
ubuntu通过docker镜像部署安装onlyoffice
linux·ubuntu·docker
Star(up up up)2 小时前
Docker系列---Docker容器基本命令(新手小白保姆级实操教程)
运维·学习·程序人生·青少年编程·docker·容器·dubbo
echome8883 小时前
Linux 定时任务:crontab 写法与常见失效原因
linux·运维·网络·定时任务·crontab
老约家的可汗3 小时前
Linux开发三件套:make、git、gdb 入门指南
linux·运维·服务器
养军博客3 小时前
Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离
java·后端·spring·springsecurity