Skip to content

驱动开发基建系统 - 架构设计

目标读者:项目维护者、架构师 | 阅读时间:15分钟 | 难度:🟡-🔴

📋 目录


架构设计原则

🎯 核心设计理念

IMX-Forge驱动开发基建系统遵循以下设计原则:

1. 单一职责原则 (SRP) 🟢

每个脚本和函数只负责一个明确的功能:

  • build_driver.sh - 构建流程控制
  • driver_buildlib.sh - 核心构建逻辑
  • deploy_driver.sh - 部署流程管理
  • review_driver.sh - 产物验证审查

2. 开闭原则 (OCP) 🟡

系统对扩展开放,对修改关闭:

  • 扩展:通过配置文件添加新内核类型、新板卡支持
  • 封闭:核心构建逻辑不需要修改
bash
# 添加新内核类型只需修改配置
KERNEL_CONFIGS[newkernel]="name|output|defconfig|description"

3. 依赖倒置原则 (DIP) 🟡

高层模块不依赖低层模块,都依赖于抽象:

  • 高层脚本(build_driver.sh)依赖抽象接口(driver_buildlib.sh
  • 具体实现(内核编译、设备树编译)在底层库中

4. 接口隔离原则 (ISP) 🟢

客户端不应依赖它不需要的接口:

  • 每个脚本提供独立的命令行接口
  • 用户可以单独使用任何一个脚本

🏗️ 架构目标

目标实现方式收益
可维护性清晰的模块划分、统一的代码风格易于理解和修改
可扩展性配置化设计、插件化架构支持新内核、新板卡
可测试性独立的功能模块、清晰的输入输出易于单元测试
用户友好统一的接口、丰富的提示信息降低学习成本

整体架构

📦 系统分层架构

┌─────────────────────────────────────────────────────────────┐
│                      用户接口层 (CLI)                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │build_driver  │  │deploy_driver │  │review_driver │      │
│  │     .sh      │  │     .sh      │  │     .sh      │      │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘      │
└─────────┼──────────────────┼──────────────────┼─────────────┘
          │                  │                  │
          ▼                  ▼                  ▼
┌─────────────────────────────────────────────────────────────┐
│                    业务逻辑层 (Library)                      │
│  ┌───────────────────────────────────────────────────┐     │
│  │           driver_buildlib.sh                      │     │
│  │  - driver_build()      - build_driver_module()    │     │
│  │  - build_device_tree() - ensure_kernel_config()   │     │
│  │  - check_kernel_built() - generate_build_info()   │     │
│  └───────────────────────────────────────────────────┘     │
└───────────────────────────┬───────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                    配置管理层 (Config)                       │
│  ┌───────────────────────────────────────────────────┐     │
│  │         driver_helper.conf                        │     │
│  │  - DEFAULT_BOARD    - DEFAULT_KERNEL_TYPE         │     │
│  │  - TFTP_DIR         - NFS_DIR                     │     │
│  │  - REMOTE_HOST      - REMOTE_PATH                 │     │
│  └───────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                      系统工具层 (Tools)                      │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐        │
│  │  make   │  │   gcc   │  │   dtc   │  │ modinfo │        │
│  └─────────┘  └─────────┘  └─────────┘  └─────────┘        │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                      底层资源层 (Resources)                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ 内核源码    │  │ 驱动源码    │  │ 设备树文件  │         │
│  │ linux_main  │  │ driver/*/   │  │ *.dts       │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

🔄 数据流架构

┌─────────────────────────────────────────────────────────────┐
│                        构建数据流                            │
└─────────────────────────────────────────────────────────────┘

用户输入


┌─────────────┐
│ 解析参数    │ ├── 驱动名、板卡名、内核类型
└──────┬──────┘


┌─────────────┐
│ 加载配置    │ ├── 读取 driver_helper.conf
└──────┬──────┘


┌─────────────┐
│ 检查内核    │ ├── ensure_kernel_configured()
│             │ ├── check_kernel_built()
└──────┬──────┘


┌─────────────┐
│ 编译驱动    │ ├── build_driver_module()
└──────┬──────┘


┌─────────────┐
│ 编译设备树  │ ├── build_device_tree()
└──────┬──────┘


┌─────────────┐
│ 生成信息    │ ├── generate_build_info()
└──────┬──────┘


┌─────────────┐
│ 输出产物    │ ├── *.ko, *.dtb, build_info.txt
└─────────────┘

脚本分工和职责

📝 顶层脚本 (CLI Layer)

1. build_driver.sh - 构建流程控制器 🟢

职责

  • 解析命令行参数
  • 提供用户交互界面
  • 调用底层构建库
  • 显示构建进度和结果

关键函数

bash
# 主要功能
list_drivers()           # 列出可用驱动
build_specific_driver()  # 构建单个驱动
build_all_drivers()      # 批量构建
clean_specific_driver()  # 清理产物

# 参数解析
--list                   # 列出驱动
--all                    # 构建所有
--board=NAME            # 指定板卡
--kernel=TYPE           # 指定内核

设计特点

  • ✅ 不包含实际构建逻辑
  • ✅ 只负责参数处理和流程控制
  • ✅ 所有构建工作委托给driver_buildlib.sh

2. deploy_driver.sh - 部署管理器 🟢

职责

  • 支持4种部署模式(TFTP/NFS/Local/Remote)
  • 提供交互式和命令行两种接口
  • 处理文件传输和目录创建
  • 提供部署反馈和错误处理

部署模式

bash
deploy_tftp()    # TFTP部署 - 只拷贝设备树
deploy_nfs()     # NFS部署 - 拷贝ko和dtb
deploy_local()   # 本地部署 - 拷贝所有文件
deploy_remote()  # 远程部署 - 通过scp传输

设计特点

  • ✅ 统一的部署接口
  • ✅ 自动备份现有文件(TFTP模式)
  • ✅ 详细的部署日志

3. review_driver.sh - 产物审查器 🟡

职责

  • 验证驱动模块完整性
  • 检查设备树格式
  • 分析符号表和依赖关系
  • 生成审查报告

审查项目

bash
review_driver_module()    # 驱动模块审查
  ├── 文件信息验证
  ├── modinfo信息提取
  ├── ELF头检查
  ├── 代码段分析
  ├── 符号表验证
  └── 依赖关系检查

review_device_tree()      # 设备树审查
  ├── 格式验证
  ├── 魔数检查
  ├── 节点统计
  └── compatible检查

设计特点

  • ✅ 多维度验证
  • ✅ 彩色输出增强可读性
  • ✅ 详细的错误信息

4. show_device_tree.sh - 设备树可视化 🟢

职责

  • 美化显示设备树结构
  • 支持节点搜索
  • 显示完整DTS内容
  • 提供设备树预览

核心功能

bash
print_device_tree()       # 树形显示
print_device_tree_detailed()  # 详细统计
search_node()             # 节点搜索

设计特点

  • ✅ 树形结构可视化
  • ✅ 高亮显示重要属性
  • ✅ 支持多种显示模式

📚 核心库 (Library Layer)

driver_buildlib.sh - 构建逻辑核心 🔴

职责

  • 实现所有核心构建逻辑
  • 管理内核配置和编译状态
  • 处理设备树两阶段编译
  • 生成构建信息

架构设计

bash
# 配置管理
CONFIG_FILE driver_helper.conf
KERNEL_CONFIGS 内核类型映射

# 内核管理
ensure_kernel_configured()  # 自动配置内核
check_kernel_built()        # 检查编译状态

# 构建流程
build_driver_module()       # 驱动模块编译
build_device_tree()         # 设备树编译
generate_build_info()       # 生成构建信息

# 清理流程
clean_driver_artifacts()    # 清理构建产物

# 主入口
driver_build()              # 统一构建接口

关键设计决策

  1. 配置驱动设计
bash
# 内核类型配置
declare -A KERNEL_CONFIGS
KERNEL_CONFIGS[mainline]="linux_mainline|out/mainline/linux|imx_aes_mainline_defconfig|主线内核"
KERNEL_CONFIGS[imx]="linux-imx|out/linux|imx_aes_defconfig|NXP BSP内核"

# 扩展新内核只需添加配置
KERNEL_CONFIGS[newtype]="name|output|defconfig|description"
  1. 错误处理策略
bash
# 不使用 set -e,手动处理错误
check_kernel_built() {
    # ... 检查逻辑
    if [[ ${#missing_files[@]} -gt 0 ]]; then
        log_error "详细错误信息"
        return 1
    fi
    return 0
}
  1. 目录查找策略
bash
# 设备树查找优先级
# 1. driver/device_tree/<board>/<driver>/  (新位置,优先)
# 2. driver/<driver>/<board>/              (旧位置,兼容)

配置系统设计

⚙️ 配置文件结构

driver_helper.conf

bash
# 默认板卡名称
DEFAULT_BOARD="alpha-board"

# 默认内核类型
DEFAULT_KERNEL_TYPE="mainline"

# TFTP 部署目录
TFTP_DIR="${HOME}/tftp"

# NFS 目录(相对于项目根目录)
NFS_DIR="rootfs/nfs"

# 远程部署配置
REMOTE_HOST=""
REMOTE_PATH="/lib/modules"

🔄 配置加载流程

启动脚本


┌─────────────┐
│ 设置默认值  │ ├── 硬编码默认值
└──────┬──────┘


┌─────────────┐
│ 加载配置文件│ ├── source driver_helper.conf
└──────┬──────┘


┌─────────────┐
│ 环境变量覆盖│ ├── ${DEFAULT_BOARD:-"alpha-board"}
└──────┬──────┘


┌─────────────┐
│ 命令行参数  │ ├── --board=NAME
└─────────────┘

📊 配置优先级

命令行参数 > 环境变量 > 配置文件 > 硬编码默认值

示例:DEFAULT_BOARD
1. --board=beta-board      (最高优先级)
2. export DEFAULT_BOARD=beta-board
3. driver_helper.conf: DEFAULT_BOARD="alpha-board"
4. 脚本中的默认值           (最低优先级)

🔧 配置扩展性

添加新配置项

bash
# 1. 在 driver_helper.conf 中添加
MY_NEW_CONFIG="value"

# 2. 在脚本中使用
MY_NEW_CONFIG="${MY_NEW_CONFIG:-default_value}"

添加新内核类型

bash
# 在 driver_buildlib.sh 中添加
KERNEL_CONFIGS[newkernel]="kernel_name|output_dir|defconfig|description"

# 立即可用,无需修改其他代码
./scripts/driver_helper/build_driver.sh driver --kernel=newkernel

设备树编译系统设计

🌳 两阶段编译机制

为什么需要两阶段编译?

单阶段编译的问题

bash
# 直接编译不支持C预处理器宏
dtc -I dts -O dtb input.dts output.dtb

# 无法处理以下情况:
#define GPIO_BASE 0x02000000
reg = <GPIO_BASE 0x4000>;

两阶段编译的优势

  1. ✅ 支持C预处理器宏定义
  2. ✅ 完全兼容内核设备树编译方式
  3. ✅ 支持复杂的include逻辑
  4. ✅ 更好的错误信息

编译流程

┌─────────────────────────────────────────────────────────────┐
│                    设备树编译流程                            │
└─────────────────────────────────────────────────────────────┘

  input.dts


┌─────────────┐
│  阶段1:预处理 │
│  gcc -E      │
└──────┬──────┘


  preprocessed.dts  (纯文本,无宏定义)


┌─────────────┐
│  阶段2:编译  │
│  dtc         │
└──────┬──────┘


  output.dtb  (二进制设备树)

🔧 实现细节

1. 预处理阶段

bash
build_device_tree() {
    # ... 前置处理

    # gcc预处理参数
    local gcc_args=(
        -E -nostdinc -P -x assembler-with-cpp
        -I "${kdir}/arch/arm/boot/dts"
        -I "${kdir}/arch/arm/boot/dts/nxp/imx"
        -I "${kdir}/include"
        -I "$board_dts_dir"
        -undef -D__DTS__
    )

    # 执行预处理
    gcc "${gcc_args[@]}" -o "$dtc_tmp" "$dts_file"
}

参数说明

  • -E:只进行预处理,不编译
  • -nostdinc:不使用标准include路径
  • -P:不生成行号信息
  • -x assembler-with-cpp:使用汇编器模式
  • -I:指定include路径
  • -undef:不预定义任何宏
  • -D__DTS__:定义__DTS__宏

2. 编译阶段

bash
# dtc编译参数
local include_args_array=()
for inc_dir in $dts_include_dirs; do
    include_args_array+=("-i" "$inc_dir")
done

# 执行编译
dtc -I dts -O dtb "${include_args_array[@]}" \
    -o "$dtb_file" "$dtc_tmp"

参数说明

  • -I dts:输入格式为DTS
  • -O dtb:输出格式为DTB
  • -i:指定include路径
  • -o:指定输出文件

3. Include路径策略

bash
# Include路径优先级
1. 内核arch/arm/boot/dts
2. 内核arch/arm/boot/dts/nxp/imx (如果存在)
3. 内核include目录
4. 主板设备树目录 driver/device_tree/<board>/linux/

📂 目录结构设计

新旧位置兼容

# 新位置(推荐)
driver/device_tree/<board>/<driver>/
└── imx6ull-aes-<driver>.dts

# 旧位置(兼容)
driver/<driver>/<board>/
└── imx6ull-aes-<driver>.dts

# 查找逻辑
if [[ -d "driver/device_tree/${board}/${driver}" ]]; then
    # 使用新位置
else
    # 回退到旧位置
fi

错误处理和日志机制

🎨 日志系统设计

日志级别

bash
log_info()  # ✅ 信息 - 绿色
log_error() # ❌ 错误 - 红色
log_warn()  # ⚠️  警告 - 黄色
log_debug() # 🔍 调试 - 蓝色 (需要DEBUG=1)

日志格式

bash
# 标准格式
[LEVEL] message

# 示例
[INFO] 构建驱动: example-driver/alpha-board
[ERROR] 内核源码目录不存在
[WARN] 设备树文件不存在
[DEBUG] 检查内核编译状态

🛡️ 错误处理策略

1. 非致命错误处理

bash
# 不使用 set -e,手动处理错误
build_device_tree() {
    # ... 编译逻辑

    if [[ $dtc_status -ne 0 ]]; then
        log_warn "设备树编译失败(非致命)"
        return 1  # 返回错误但不退出
    fi
}

2. 致命错误处理

bash
check_kernel_built() {
    if [[ ! -f "${kobj}/.config" ]]; then
        log_error "========================================"
        log_error "❌ 内核未正确编译"
        log_error "========================================"
        log_error "缺少以下文件:"
        for file in "${missing_files[@]}"; do
            log_error "  - $file"
        done
        log_error ""
        log_error "💡 解决方案:"
        log_error "   cd $kdir"
        log_error "   make O=$kobj ARCH=$ARCH modules_prepare"
        log_error ""
        log_error "========================================"
        return 1  # 返回致命错误
    fi
}

3. 用户友好的错误信息

bash
# ❌ 不好的错误信息
echo "Error: kernel not built"

# ✅ 好的错误信息
log_error "========================================"
log_error "❌ 内核未正确编译"
log_error "========================================"
log_error "内核类型: $kernel_desc"
log_error "内核目录: $kdir"
log_error ""
log_error "缺少以下文件:"
log_error "  - 内核配置文件: .config"
log_error "  - autoconf.h"
log_error "  - Module.symvers"
log_error ""
log_error "💡 解决方案:"
log_error "   cd $kdir"
log_error "   make O=$kobj modules_prepare"
log_error "========================================"

📊 退出码设计

bash
# 退出码约定
0  # 成功
1  # 一般错误
2  # 参数错误
3  # 内核未配置
4  # 内核未编译
5  # 驱动编译失败
6  # 设备树编译失败

扩展性设计

🔌 插件化架构

1. 内核类型扩展

bash
# 添加新内核类型只需修改配置
KERNEL_CONFIGS[rt-linux]="linux-rt|out/rt/linux|imx_aes_rt_defconfig|实时内核"

# 立即可用
./scripts/driver_helper/build_driver.sh driver --kernel=rt-linux

2. 板卡扩展

bash
# 添加新板卡只需创建目录
mkdir -p driver/my-driver/new-board
cp driver/my-driver/alpha-board/* driver/my-driver/new-board/

# 构建时指定
./scripts/driver_helper/build_driver.sh my-driver new-board

3. 部署方式扩展

bash
# 在 deploy_driver.sh 中添加新函数
deploy_sftp() {
    local src="$1"
    local host="$2"
    local path="$3"

    # SFTP部署逻辑
}

# 在选择菜单中添加选项
select_target() {
    # ...
    echo "5) SFTP服务器"
    # ...
    case "$choice" in
        5) deploy_sftp "$src" "$host" "$path" ;;
    esac
}

🧩 模块化设计

功能模块独立

每个脚本可以独立使用:

bash
# 单独构建
./scripts/driver_helper/build_driver.sh driver

# 单独审查
./scripts/driver_helper/review_driver.sh driver

# 单独部署
./scripts/driver_helper/deploy_driver.sh artifacts_dir

# 单独查看设备树
./scripts/driver_helper/show_device_tree.sh device_tree.dtb

库函数复用

bash
# driver_buildlib.sh 中的函数可被多个脚本调用
source scripts/lib/driver_buildlib.sh

# 调用库函数
driver_build "driver" "board" "build" "kernel_type"

🎯 配置化设计

通过配置控制行为

bash
# 在 driver_helper.conf 中配置
AUTO_CLEAN_BUILD="yes"      # 构建前自动清理
PARALLEL_BUILD="yes"        # 并行构建
VERBOSE_OUTPUT="no"         # 详细输出
KEEP_BACKUP_NUM="5"         # 保留备份数量

# 脚本中读取配置
if [[ "${AUTO_CLEAN_BUILD}" == "yes" ]]; then
    clean_driver_artifacts "$output_dir"
fi

性能优化策略

⚡ 构建性能优化

1. 增量编译

bash
# 只编译修改的文件
make -C kernel M=driver modules

# Make 自动检测修改
# 只重新编译变化的 .c 文件

2. 并行编译

bash
# 使用多核编译
export MAKEFLAGS="-j$(nproc)"

# 在脚本中应用
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm- M=$PWD modules

3. 缓存机制

bash
# 缓存内核配置
[[ -f "${kobj}/.config" ]] && return 0

# 缓存编译状态
[[ -f "${kobj}/Module.symvers" ]] && return 0

💾 存储优化

1. 产物目录结构

out/driver_artifacts/
├── driver1/
│   ├── board1/
│   └── board2/
└── driver2/
    ├── board1/
    └── board2/

优势

  • 按驱动和板卡隔离
  • 便于产物管理
  • 支持并行构建

2. 临时文件清理

bash
# 使用临时文件
local dtc_tmp="/tmp/dtc-$(basename "$dts_file" .dts).tmp"

# 用完立即删除
dtc ... -o "$dtb_file" "$dtc_tmp"
rm -f "$dtc_tmp"

🚀 部署性能优化

1. 增量部署

bash
# 只拷贝修改的文件
for file in "$src"/*.{ko,dtb}; do
    if [[ "$file" -nt "$dst/$(basename "$file")" ]]; then
        cp "$file" "$dst/"
    fi
done

2. 压缩传输

bash
# 远程部署时使用压缩
tar czf - artifacts | ssh user@host "tar xzf - -C /path"

监控和调试

🔍 调试模式

启用详细输出

bash
# 启用调试输出
DEBUG=1 ./scripts/driver_helper/build_driver.sh driver

# 在脚本中
log_debug() {
    [[ "${DEBUG:-0}" == "1" ]] && echo -e "${BLUE}[DEBUG]${NC} $1"
}

日志记录

bash
# 记录构建日志
LOG_FILE="build.log"
exec > >(tee -a "$LOG_FILE")
exec 2>&1

# 所有输出都会记录到文件

📊 性能分析

计时统计

bash
# 记录开始时间
start_time=$(date +%s)

# 执行构建
build_driver_module "$driver_dir" "$output_dir" "$kernel_type"

# 计算耗时
end_time=$(date +%s)
duration=$((end_time - start_time))
log_info "构建耗时: ${duration}秒"

附录

关键设计决策

决策原因影响
不使用 set -e某些命令返回非零但不应退出需要手动错误检查
两阶段设备树编译支持C预处理器宏兼容内核编译方式
配置文件驱动易于扩展无需修改核心代码
分离构建库代码复用便于维护和测试

架构演进历史

版本主要变化原因
v1.0初始版本基本构建功能
v1.5添加设备树编译支持设备树管理
v2.0引入构建库代码复用和模块化
v2.5配置文件支持提升易用性
v3.0多部署方式支持多种场景

相关文档


返回目录README

Built with VitePress