山东大学 2023 级软件工程创新实训项目博客。

项目从哪里开始

我们的项目目标是面向 RISC-V 平台推进 OpenHarmony 6.1 的适配与优化,实际使用的设备是进迭时空 MUSEPaper2。开发板搭载 K1 芯片,系统架构为 64 位 RISC-V,既包含显示、触控、USB、摄像头、Wi-Fi、蓝牙等常见外设,也涉及 OpenHarmony 标准系统的完整构建、镜像打包和启动流程。

刚拿到这个任务时,最明显的问题并不是“某一行代码不会写”,而是不知道应该从哪里开始。OpenHarmony 源码规模很大,板级代码、内核、HDF、系统服务和应用分布在不同仓库中。如果一开始就扎进某个驱动文件,很容易只看到局部,最后连自己的修改为什么生效都说不清楚。

因此,经过组内讨论,我们把项目初期目标定成了三件事:

  1. 搭建一套可以重复使用的源码与编译环境;
  2. 理清 OpenHarmony 从产品配置到镜像生成的基本结构;
  3. 烧录原始系统并建立外设功能基线,再根据测试结果分工。

这一阶段看起来没有“适配出一个新功能”那么显眼,但它决定了后续工作能不能稳定推进。

明确项目边界

最开始需要先区分几个容易混在一起的概念。

RISC-V 是处理器指令集架构

它决定 CPU 能理解什么样的机器指令,也会影响编译工具链、系统 ABI、汇编代码和部分架构相关实现。本项目不是简单地把一个应用编译成 RISC-V 版本,而是要让 OpenHarmony 标准系统及其外设能力在 K1 平台上稳定工作。

K1 是具体的 SoC

除了 RISC-V CPU 核,K1 还提供显示、存储、USB、摄像头、音视频等硬件控制器。因此适配时既要关注通用的 RISC-V 架构问题,也要关注进迭时空提供的 SoC 和板级实现。

MUSEPaper2 是最终运行系统的产品设备

同一颗 SoC 可以被用于不同开发板,而不同板子的 GPIO、设备树、摄像头连接、无线模块和分区配置都可能不同。当前产品名称和设备名称都是 musepaper2,后续构建、设备树和外设配置都必须围绕这个产品展开。

OpenHarmony 6.1 是本次工作的系统基线

我们要处理的不只是 Linux 内核,还包括 OpenHarmony 的构建系统、HDF/HDI、系统服务、ArkUI 应用以及产品镜像打包。一个外设最终“能用”,往往意味着从底层硬件一直到设置或应用界面的整条链路都已经接通。

开发环境准备

项目使用 Ubuntu 22.04 作为主要编译环境。OpenHarmony 标准系统源码和构建产物体积很大,而且完整构建需要较多内存,因此环境准备不能只停留在“把依赖装上”。

我们重点检查了以下内容:

  • Git、Git LFS 和 repo 是否可用;
  • Python、Node.js、Java、GN、Ninja 等构建依赖是否齐全;
  • 磁盘是否能够同时容纳源码、预编译工具和 out 构建目录;
  • 内存与 swap 是否足以支撑长时间并行编译;
  • USB、串口和 HDC 是否能够连接开发板;
  • 原始镜像与关键分区是否有可恢复的备份。

这里有一个很现实的经验:编译 OpenHarmony 时,“机器性能不够”不一定直接表现为一条清晰的编译错误。内存不足可能触发 OOM,磁盘写满可能让桌面环境和构建工具一起异常。因此遇到编译进程突然退出时,除了看最后一行错误,也要检查:

free -h
df -h
dmesg | tail

先从构建系统认识源码

面对庞大的源码,我选择先从 GN 和 Ninja 入手。

可以把二者简单理解为:

产品配置和 BUILD.gn
        ↓
GN 生成构建关系
        ↓
生成 .ninja 文件
        ↓
Ninja 执行实际编译
        ↓
安装模块、生成镜像

GN 负责描述“要编译什么、依赖谁、安装到哪里”,Ninja 负责高效执行这些具体任务。后续摄像头、Wi-Fi、蓝牙适配之所以不仅要改业务代码,还要修改 BUILD.gnohos.build,原因就在这里:如果一个文件没有进入产品依赖关系,即使源码中存在,它也不会自动进入最终镜像。

初期重点阅读了这些位置:

.gn
build/config/BUILDCONFIG.gn
build/core/BUILD.gn
build/ohos.gni
build/ohos/packages/
build/ohos/images/
vendor/spacemit/musepaper2/
device/board/spacemit/musepaper2/

前几个目录帮助理解 OpenHarmony 的全局构建规则,后两个目录则分别承载产品配置和板级实现,是后续外设适配最常接触的地方。

建立 OpenHarmony 分层认识

外设问题通常横跨多层,不能把所有问题都叫作“驱动坏了”。项目初期我们建立了下面这套粗略模型:

应用界面
   ↓
系统服务与 Framework
   ↓
HDI:统一硬件接口
   ↓
HDF Host / Vendor 实现
   ↓
Linux 内核驱动
   ↓
设备树、GPIO、总线与真实硬件

这里几个名词后来频繁出现:

  • HDF:OpenHarmony 的硬件驱动框架,负责驱动加载、服务管理和硬件资源配置等工作;
  • HDI:面向系统服务提供的统一硬件接口,使上层不必直接了解芯片差异;
  • HCS:用来描述设备和驱动配置的数据文件;
  • Vendor/VDI 实现:真正处理板级或芯片差异的实现层;
  • 系统服务:向应用提供摄像头、Wi-Fi、蓝牙等系统能力。

这套分层后来直接影响了我们的排查方法:先找到“最后一个已经成功的层”,再检查它后面的第一处失败,而不是看到功能打不开就立刻修改内核。

烧录原始系统,建立功能基线

环境和代码结构有了基本认识后,我们没有马上开始大规模修改,而是先把原始系统版本烧录到设备,对现有能力逐项测试。

测试范围包括:

  • 系统能否正常启动并进入桌面;
  • HDC 能否连接,重启或静置后是否掉线;
  • USB 设备与 Type-C 功能是否稳定;
  • WLAN 是否能打开、扫描和连接;
  • 摄像头是否能识别前后摄、预览和拍照;
  • 蓝牙是否能开启、扫描和完成初始化;
  • 显示、触控、音频和基础应用是否工作。

建立基线很重要。否则后来看到一个功能异常时,我们无法判断它是原始版本就存在的问题,还是自己修改后引入的回归。

初步测试表明,系统主体可以启动,但若干外设仍需要完善:USB/HDC 存在休眠后掉线风险,Wi-Fi 的启动与供电链路需要补齐,摄像头还没有形成稳定的前后摄预览与拍照闭环,蓝牙也无法稳定完成 HCI 初始化。这些结果成为后续任务拆分的依据。

团队任务如何拆分

根据测试结果和各模块之间的依赖,我们把工作大致拆为:

  1. 源码、构建和产品镜像:保证修改能够进入最终固件,而不是只在当前设备上临时生效;
  2. USB 与电源管理:解决设备休眠、USB PHY 断电和 HDC 稳定性问题;
  3. Wi-Fi:处理 RTL8852BS 的 SDIO、供电、驱动加载和 wpa_supplicant
  4. 摄像头:贯通 V4L2、Camera HDI/VDI、Framework 和 Camera App;
  5. 蓝牙:处理 UART、rfkill、Realtek vendor、固件和 HCI HDI;
  6. 集成测试:进行全量构建、镜像检查、冷启动和外设回归。

虽然任务被拆开了,但 USB、Wi-Fi 和蓝牙之间仍会共享电源或启动时序,摄像头修改也会同时涉及底层和应用。因此分工不等于彼此隔离,修改板级配置前必须确认不会破坏其他模块。

阶段性收获

这一阶段最重要的成果并不是某个补丁,而是建立了后续可以反复使用的工作方法:

阅读文档和源码
    ↓
建立原始功能基线
    ↓
按照系统层次定位
    ↓
先做小范围验证
    ↓
让修改进入源码构建关系
    ↓
全量镜像与冷启动回归

一开始面对 OpenHarmony 源码确实有些无从下手,但当产品配置、GN/Ninja、HDF/HDI 和板级目录之间的关系逐渐清晰后,后续问题就不再是一团日志,而是可以逐层验证的一条链。

下一阶段开始真正执行代码拉取、全量编译、镜像打包、原始系统烧录和设备测试,并根据结果确定第一批需要适配的外设。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐