3. 源码目录结构解析

拿到一份高通Android BSP源码后,第一件事不是急着编译,而是先看清目录结构。因为BSP的构建系统本质上是一套“路径驱动”的机制:编译脚本、BoardConfig、Android.mk/Android.bp、设备树、内核配置,全部通过相对路径互相引用。路径认错,后面所有配置都会连锁出错。本节把源码树拆成三条主线来看:AOSP公共源码树、高通专有目录、以及bootloader与kernel目录。

一、顶层目录:AOSP公共源码树

解压后的源码根目录通常包含以下关键目录,它们由AOSP定义,高通在其上做增量扩展:

  • build/:AOSP构建系统核心,包含make/core、make/target、soong/等。BoardConfig.mk的解析规则、PRODUCT_*变量的展开逻辑都在这里。
  • frameworks/:Java框架层,frameworks/base是系统服务与核心API所在,frameworks/av负责音视频,frameworks/native是Native服务与Binder。
  • system/:底层系统组件,如system/core(init、logd、adb)、system/sepolicy(SELinux策略)。
  • hardware/:HAL接口定义,hardware/interfaces是HIDL/AIDL接口的规范目录,hardware/libhardware是传统HAL。
  • packages/:系统应用与输入法、设置等,packages/apps/Settings是常见定制入口。
  • external/:第三方开源库,如external/skia、external/libcxx。
  • out/:编译输出目录,out/target/product/<device>/下生成镜像与中间产物。

这些目录是“平台无关”的,理解它们的作用有助于判断某个改动应该落在AOSP层还是高通层。例如修改默认语言应改build/target/product下的产品定义,而修改某个SoC的GPIO初始化则应落到高通专有目录。

二、高通专有目录:vendor/qcom 与 device/qcom

高通BSP最核心的增量就在这两个目录,它们的分工非常明确:

device/qcom/ 存放“设备与产品配置”,是编译入口所在。典型结构如下:

device/qcom/
├── common/                 # 所有高通平台共用的BoardConfig片段、init脚本
│   ├── BoardConfigCommon.mk
│   └── init/
├── <target>/               # 具体平台,如 kalama、pineapple
│   ├── BoardConfig.mk      # 板级配置:分区、内核、bootloader参数
│   ├── <target>.mk         # 产品定义:PRODUCT_*变量
│   ├── AndroidBoard.mk     # 板级构建规则
│   └── init.target.rc      # 平台专属init
└── <device>/               # 具体机型,如某品牌旗舰
    ├── BoardConfig.mk
    ├── device.mk
    └── overlay/            # 资源覆盖

其中BoardConfig.mk决定分区布局、内核路径、bootloader路径;<target>.mk决定打包哪些模块、哪些HAL、哪些应用。编译时通过lunch <target>-userdebug选择的就是这里的组合。

vendor/qcom/ 存放“高通私有实现”,通常以闭源或半开源形式提供:

  • vendor/qcom/proprietary/:高通私有库、固件、HAL实现,如display/、audio/、gps/、thermal/。
  • vendor/qcom/opensource/:高通开源部分,如kernel相关脚本、commonsys。
  • vendor/qcom/build/:高通构建辅助脚本。

需要特别注意:vendor/qcom/proprietary中的内容往往与SoC型号强绑定,换平台时不能直接复用。而device/qcom/common中的BoardConfig片段则具有较好的跨平台复用性。

三、bootloader与kernel目录

高通平台的bootloader通常称为ABL(Application Bootloader),源码位于bootable/bootloader/edk2/或高通提供的bootloader/目录下。其结构大致为:

bootable/bootloader/edk2/
├── QcomPkg/                # 高通平台包
│   ├── Library/            # 驱动库
│   ├── Include/            # 头文件
│   └── Settings/           # 平台配置
├── ArmPkg/                 # ARM架构支持
├── MdePkg/                 # EDK2基础包
└── UefiCpuPkg/             # CPU相关

ABL负责初始化DDR、加载并校验boot镜像、实现fastboot协议。修改启动logo、fastboot命令、分区表通常在这里。

kernel目录则位于kernel/msm-<version>/或kernel/<platform>/,典型结构:

kernel/msm-5.15/
├── arch/arm64/boot/dts/vendor/qcom/   # 设备树
├── arch/arm64/configs/                # defconfig
├── drivers/                           # 驱动
├── include/                           # 头文件
└── scripts/                           # 构建脚本

设备树是BSP定制的重点,arch/arm64/boot/dts/vendor/qcom/下按平台和机型组织,如kalama.dtsi、pineapple.dtsi。defconfig决定内核编译选项,通常命名为<platform>_defconfig。

路径定位技巧:当不确定某个配置属于哪一层时,用grep -r "关键字" device/qcom/ vendor/qcom/快速定位。若在device/qcom找到,说明是产品级配置;若在vendor/qcom/proprietary找到,说明是高通私有实现,修改需谨慎。

四、三条主线的协作关系

把上述内容串起来看:AOSP提供通用框架与构建系统,device/qcom提供产品与板级配置,vendor/qcom提供SoC私有实现,bootloader负责启动链前半段,kernel负责启动链后半段与硬件驱动。编译时,lunch选择device/qcom中的产品组合,构建系统据此拉取vendor/qcom的库、编译kernel、打包boot与system镜像,最终生成可烧录的super.img、boot.img等。

高通 Android BSP 源码树三条主线与启动链 AOSP 公共源码树 build/ → 构建系统核心 frameworks/ → Java/Native 框架 system/ → init、sepolicy hardware/ → HAL 接口定义 packages/ → 系统应用 out/ → 编译输出镜像 device/qcom(产品配置) common/ → 跨平台共用配置 BoardConfigCommon.mk <target>/ → 平台级配置 BoardConfig.mk / <target>.mk <device>/ → 机型级配置 overlay/ 资源覆盖 vendor/qcom(私有实现) proprietary/ → 私有库与固件 display/ audio/ gps/ thermal/ camera/ opensource/ → 开源部分 build/ → 高通构建脚本 与 SoC 型号强绑定 启动链:bootloader → kernel → Android ABL / EDK2 Bootloader bootable/bootloader/edk2/ Kernel + DTS kernel/msm-5.15/ Android System system/ + vendor/ 编译入口与路径定位 lunch <target>-userdebug → 选择 device/qcom/<target>/<target>.mk 与 BoardConfig.mk BoardConfig.mk 指定 TARGET_KERNEL_SOURCE、TARGET_BOOTLOADER_BOARD_NAME 等路径变量 grep -r "关键字" device/qcom/ vendor/qcom/ → 快速判断配置属于产品层还是私有层

理解这张图后,再去看具体的BoardConfig.mk,就能明白每一行变量为什么指向那个路径。例如TARGET_KERNEL_SOURCE := kernel/msm-5.15告诉构建系统去哪里找内核,TARGET_BOOTLOADER_BOARD_NAME := kalama告诉它用哪个bootloader配置。路径一旦对齐,后续的编译、烧录、调试才有稳定的基础。