Skip to content

嵌入式 Android 系统开发 - 零基础入门指南

本文档专为零基础新手设计,从最基础的概念开始,一步步带你进入嵌入式 Android 系统开发的世界。


目录

  1. 写在前面:给新手的话
  2. 什么是嵌入式 Android
  3. 学习路线总览
  4. 第零阶段:基础知识准备
  5. 第一阶段:开发环境搭建
  6. 第二阶段:Linux 基础
  7. 第三阶段:C 语言与数据结构
  8. 第四阶段:Android 系统架构认知
  9. 第五阶段:Bootloader 与 U-Boot
  10. 第六阶段:Linux Kernel 驱动开发
  11. 第七阶段:设备树 (Device Tree)
  12. 第八阶段:Android Init 系统
  13. 第九阶段:HAL 硬件抽象层
  14. 第十阶段:Android Framework
  15. 实践项目
  16. 常见问题与解答
  17. 学习资源推荐

1. 写在前面:给新手的话

1.1 这份指南适合谁?

  • 完全没有嵌入式开发经验的新手
  • 想从手机/应用开发转向系统开发的程序员
  • 对智能电视、机顶盒等设备开发感兴趣的人
  • 想深入理解 Android 系统底层的开发者

1.2 学习这个需要多久?

诚实地说:嵌入式 Android 开发是一个庞大的领域,完全掌握需要较长时间。但你可以:

入门阶段(能看懂代码、做简单修改):    3-6 个月
熟练阶段(能独立解决问题):            6-12 个月
精通阶段(能设计架构、移植系统):      2-3 年

不要被吓到! 你不需要学完所有内容才能开始工作。学完前几个阶段,你就能参与实际项目了。

1.3 学习建议

┌───────────────────────────────────────────────────────────────────────────┐
│                            新手学习黄金法则                               │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  1. 【动手第一】 看 10 遍不如自己敲 1 遍                                  │
│                  每个代码示例都要亲手输入运行                             │
│                                                                           │
│  2. 【不求甚解】 第一遍学习时,遇到不懂的可以先跳过                       │
│                  等学完后面内容再回头看,往往豁然开朗                     │
│                                                                           │
│  3. 【善用搜索】 遇到错误先 Google/百度                                   │
│                  90% 的问题别人都遇到过                                   │
│                                                                           │
│  4. 【记录笔记】 建立自己的知识库                                         │
│                  今天解决的问题,明天可能还会遇到                         │
│                                                                           │
│  5. 【循序渐进】 不要跳跃学习                                             │
│                  每个阶段都是下一阶段的基础                               │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

2. 什么是嵌入式 Android

2.1 用大白话解释

普通 Android 开发:你写一个 App(比如微信),用户下载安装后使用。你只需要关心 App 本身。

嵌入式 Android 开发:你要让整个 Android 系统在一块硬件板子上跑起来。从开机第一行代码,到用户看到桌面,都是你的工作范围。

2.2 嵌入式 Android 用在哪里?

┌───────────────────────────────────────────────────────────────────────────┐
│                         嵌入式 Android 应用场景                           │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│   🖥️  智能电视          - 小米电视、创维电视、海信电视...                 │
│                                                                           │
│   📺  机顶盒            - 运营商送的盒子、小米盒子、天猫魔盒...           │
│                                                                           │
│   🚗  车载系统          - 比亚迪、蔚来、小鹏的中控屏幕                    │
│                                                                           │
│   🎮  游戏机            - 一些安卓游戏掌机                                │
│                                                                           │
│   🏭  工业设备          - 工业平板、点餐机、广告机                        │
│                                                                           │
│   🏠  智能家居          - 智能音箱带屏幕的、智能冰箱                      │
│                                                                           │
│   📱  定制手机          - 一些特殊用途的手机(如老人机、儿童手机)        │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

2.3 我们学习的平台

本指南基于 Amlogic S905X5M 芯片平台,运行 Android 14 系统。

为什么选这个平台?

  • Amlogic(晶晨半导体)是国内最大的机顶盒/智能电视芯片厂商之一
  • 市面上大量设备使用 Amlogic 芯片
  • 学会这个平台,很容易迁移到其他类似平台

2.4 嵌入式 Android vs 手机 Android

对比项手机 Android嵌入式 Android (如电视盒子)
触摸屏必须有通常没有,用遥控器
电池有电池,需要省电插电使用,不太考虑省电
摄像头必须有通常没有
通话功能没有
视频输出手机屏幕HDMI 输出到电视
主要交互触摸遥控器、语音(有的项目有)
应用场景移动便携固定位置使用

3. 学习路线总览

3.1 完整系统架构图(先混个眼熟)

┌───────────────────────────────────────────────────────────────────────────┐
│                          Android 系统架构全景图                           │
│                        (从上到下,从软件到硬件)                         │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │ 第10阶段     应用层 (Applications)                                │    │
│  │              你平时用的 App:设置、视频播放器、浏览器等           │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│                                    ▲                                      │
│                                    │ 调用                                 │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │ 第10阶段     Framework 层 (Android 框架)                          │    │
│  │              Java/Kotlin 写的系统服务,管理 App 的生命周期等      │    │ 
│  └───────────────────────────────────────────────────────────────────┘    │
│                                    ▲                                      │
│                                    │ 调用                                 │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │ 第9阶段      HAL 层 (硬件抽象层)                                  │    │
│  │              C/C++ 写的,连接 Android 和硬件驱动的桥梁            │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│                                    ▲                                      │
│                                    │ 调用                                 │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │ 第6-8阶段    Linux Kernel (Linux 内核)                            │    │
│  │              操作系统核心 + 硬件驱动程序                          │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│                                    ▲                                      │
│                                    │ 启动                                 │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │ 第5阶段      Bootloader (引导程序)                                │    │
│  │              开机后第一个运行的程序,负责加载 Linux 内核          │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│                                    ▲                                      │
│                                    │ 上电                                 │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │              Hardware (硬件)                                      │    │
│  │              CPU、内存、存储、HDMI、USB、网口等                   │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

3.2 学习路线图

                              学习路线图
    ════════════════════════════════════════════════════════════════════

    基础准备                    核心技能                    进阶技能
    ────────                   ────────                   ────────

    ┌────────┐
    │ 第0阶段│   基础知识准备
    │ 计算机 │   (1-2周)
    │ 基础   │   了解计算机、操作系统基本概念
    └───┬────┘


    ┌────────┐
    │ 第1阶段│   环境搭建
    │ 环境   │   (1周)
    │ 搭建   │   安装 Linux、配置开发环境
    └───┬────┘


    ┌────────┐
    │ 第2阶段│   Linux 基础
    │ Linux  │   (2-3周)
    │ 基础   │   命令行、文件系统、Shell
    └───┬────┘


    ┌────────┐
    │ 第3阶段│   C 语言
    │ C 语言 │   (3-4周)
    │        │   指针、结构体、内存管理
    └───┬────┘


    ┌────────┐   ┌────────┐   ┌────────┐   ┌────────┐
    │ 第4阶段│   │ 第5阶段│   │ 第6阶段│   │ 第7阶段│
    │ Android│──▶│ Boot-  │──▶│ Kernel │──▶│ 设备树 │
    │ 架构   │   │ loader │   │ 驱动   │   │ DTS    │
    │ (1周)  │   │ (2周)  │   │ (3-4周)│   │ (2周)  │
    └───┬────┘   └────────┘   └────────┘   └───┬────┘
        │                                      │
        │         ┌────────────────────────────┘
        │         │
        ▼         ▼
    ┌────────┐   ┌────────┐   ┌────────┐
    │ 第8阶段│   │ 第9阶段│   │第10阶段│
    │ Android│──▶│ HAL    │──▶│Frame-  │
    │ Init   │   │ 硬件   │   │ work   │
    │ (2周)  │   │ 抽象层 │   │        │
    └────────┘   │ (3周)  │   │ (4周+) │
                 └────────┘   └────────┘

3.3 各阶段详细说明

阶段名称主要内容学习目标难度
0基础知识准备计算机组成、操作系统概念理解基本术语★☆☆☆☆
1环境搭建Ubuntu 安装、工具配置能编译 Android 源码★★☆☆☆
2Linux 基础命令行、Shell、文件系统熟练使用 Linux★★☆☆☆
3C 语言指针、结构体、内存能读写 C 代码★★★☆☆
4Android 架构系统组成、编译系统理解整体架构★★☆☆☆
5BootloaderU-Boot、启动流程理解开机过程★★★☆☆
6Kernel 驱动驱动模型、字符设备能写简单驱动★★★★☆
7设备树DTS 语法、硬件描述能修改设备树★★★☆☆
8Android Initinit.rc、属性系统能定制启动流程★★★☆☆
9HALHIDL/AIDL 接口能读懂 HAL 代码★★★★☆
10Framework系统服务、Binder能进行系统定制★★★★★

4. 第零阶段:基础知识准备

目标:了解计算机和操作系统的基本概念,为后续学习打好基础

4.1 你需要知道的基本概念

4.1.1 计算机是怎么工作的?

┌───────────────────────────────────────────────────────────────────────────┐
│                          计算机工作原理简图                               │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│                        ┌─────────────────┐                                │
│                        │      CPU        │  大脑:执行指令                │
│                        │   (处理器)      │                                │
│                        └────────┬────────┘                                │
│                                 │                                         │
│              ┌──────────────────┼──────────────────┐                      │
│              │                  │                  │                      │
│              ▼                  ▼                  ▼                      │
│      ┌───────────┐      ┌───────────┐      ┌───────────┐                  │
│      │   内存    │      │   存储    │      │  输入输出  │                 │
│      │  (RAM)    │      │(硬盘/闪存)│      │   设备     │                 │
│      │           │      │           │      │           │                  │
│      │ 临时存放  │      │ 永久保存  │      │ 键盘鼠标  │                  │
│      │ 运行数据  │      │ 文件数据  │      │ 显示器等  │                  │
│      └───────────┘      └───────────┘      └───────────┘                  │
│                                                                           │
│  类比:                                                                   │
│  CPU = 厨师        (负责干活)                                           │
│  内存 = 案板       (放正在处理的食材,关机就清空)                       │
│  存储 = 冰箱       (长期保存食材,关机不丢失)                           │
│  输入输出 = 窗口   (和外界交流)                                         │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

4.1.2 什么是操作系统?

操作系统就像一个"大管家",负责:

1. 管理硬件资源
   - 决定谁能用 CPU
   - 分配内存给各个程序
   - 控制硬盘读写

2. 提供统一接口
   - 程序不需要知道硬件细节
   - 通过操作系统提供的接口操作硬件

3. 运行应用程序
   - 加载程序到内存
   - 调度程序运行

常见操作系统:
- Windows(电脑)
- macOS(苹果电脑)
- Linux(服务器、嵌入式设备)
- Android(手机、电视盒子)—— 基于 Linux
- iOS(苹果手机)

4.1.3 什么是 Linux?

┌───────────────────────────────────────────────────────────────────────────┐
│                              Linux 简介                                   │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  Linux 是一个开源的操作系统内核,由 Linus Torvalds 在 1991 年创建         │
│                                                                           │
│  特点:                                                                   │
│  ├── 开源免费:任何人都可以查看、修改、分发源代码                         │
│  ├── 稳定可靠:服务器领域占统治地位                                       │
│  ├── 高度可定制:可以裁剪到很小,适合嵌入式设备                           │
│  └── 生态丰富:大量工具和软件支持                                         │
│                                                                           │
│  常见 Linux 发行版:                                                      │
│  ├── Ubuntu:最流行,适合新手(我们用这个)                               │
│  ├── CentOS/Rocky:服务器常用                                             │
│  ├── Debian:稳定,Ubuntu 基于它                                          │
│  └── Arch:高度可定制,适合高手                                           │
│                                                                           │
│  Android 和 Linux 的关系:                                                │
│  ┌─────────────────────────────────┐                                      │
│  │         Android 系统            │                                      │
│  │  ┌──────────────────────────┐   │                                      │
│  │  │    Android Framework     │   │  ← Google 开发的                     │
│  │  │    (Java/Kotlin 层)      │   │                                      │
│  │  ├──────────────────────────┤   │                                      │
│  │  │      Native 层           │   │  ← 各种 C/C++ 库                     │
│  │  ├──────────────────────────┤   │                                      │
│  │  │     Linux Kernel         │   │  ← Linux 内核(开源)                │
│  │  │     (Linux 内核)         │   │                                      │
│  │  └──────────────────────────┘   │                                      │
│  └─────────────────────────────────┘                                      │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

4.1.4 进制转换(必须掌握)

在嵌入式开发中,你会频繁遇到不同进制的数字:

十进制 (我们日常用的):  0, 1, 2, ... 9, 10, 11 ...
二进制 (计算机用的):    0, 1, 10, 11, 100, 101 ...
十六进制 (程序员常用):  0, 1, 2 ... 9, A, B, C, D, E, F, 10, 11 ...

为什么用十六进制?
- 二进制太长:11111111 = 255
- 十六进制简短:FF = 255
- 每个十六进制位对应 4 个二进制位,转换方便

常见表示方法:
- 十进制:255
- 二进制:0b11111111 或 11111111b
- 十六进制:0xFF 或 0xff 或 FFh

常用对照表:
┌────────┬────────┬────────┐
│ 十进制 │ 二进制 │十六进制│
├────────┼────────┼────────┤
│   0    │  0000  │   0    │
│   1    │  0001  │   1    │
│   2    │  0010  │   2    │
│   3    │  0011  │   3    │
│   4    │  0100  │   4    │
│   5    │  0101  │   5    │
│   6    │  0110  │   6    │
│   7    │  0111  │   7    │
│   8    │  1000  │   8    │
│   9    │  1001  │   9    │
│   10   │  1010  │   A    │
│   11   │  1011  │   B    │
│   12   │  1100  │   C    │
│   13   │  1101  │   D    │
│   14   │  1110  │   E    │
│   15   │  1111  │   F    │
│   255  │11111111│   FF   │
└────────┴────────┴────────┘

4.1.5 常见存储单位

1 Byte (字节) = 8 bits (位)
1 KB = 1024 Bytes
1 MB = 1024 KB
1 GB = 1024 MB

常见大小参考:
- 一个英文字符:1 Byte
- 一个中文字符:2-3 Bytes (UTF-8)
- 一张手机照片:2-5 MB
- 一部高清电影:1-2 GB
- Android 系统源码:约 100 GB
- 编译后的镜像:约 1-4 GB

4.2 第零阶段检验

完成以下问题,确保你理解了基本概念:


5. 第一阶段:开发环境搭建

目标:搭建一个能够编译 Android 源码的开发环境

5.1 硬件要求

┌───────────────────────────────────────────────────────────────────────────┐
│                           开发电脑硬件要求                                │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  最低配置(能用但很慢):                                                 │
│  ├── CPU:4 核                                                            │
│  ├── 内存:16 GB                                                          │
│  ├── 硬盘:256 GB SSD                                                     │
│  └── 网络:能访问 Google(或配置镜像源)                                  │
│                                                                           │
│  推荐配置(流畅开发):                                                   │
│  ├── CPU:8 核以上(如 Intel i7/i9 或 AMD Ryzen 7/9)                     │
│  ├── 内存:32 GB 以上(64 GB 更好)                                       │
│  ├── 硬盘:512 GB SSD 以上(1TB 更好,且建议 NVMe)                       │
│  └── 网络:稳定的网络连接                                                 │
│                                                                           │
│  说明:                                                                   │
│  - 编译 Android 源码非常消耗资源                                          │
│  - 内存不足会导致编译失败或极慢                                           │
│  - SSD 比机械硬盘快很多,强烈推荐                                         │
│  - 编译时 CPU 会满载,注意散热                                            │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

5.2 安装 Ubuntu 操作系统

5.2.1 为什么用 Ubuntu?

  • Android 官方推荐在 Linux 下编译
  • Ubuntu 是最流行的 Linux 发行版,资料最多
  • 大部分问题都能搜到解决方案

5.2.2 安装方式选择

5.2.3 Ubuntu 安装步骤(双系统示例)

5.3 配置开发环境

打开终端(Ctrl + Alt + T),依次执行以下命令:

5.3.1 更新系统

bash
# 更新软件包列表
sudo apt update

# 升级已安装的软件包
sudo apt upgrade -y

5.3.2 安装编译 Android 所需的依赖

bash
# 安装必要的软件包
sudo apt install -y \
    git \
    gnupg \
    flex \
    bison \
    build-essential \
    zip \
    curl \
    zlib1g-dev \
    libc6-dev-i386 \
    libncurses5 \
    lib32ncurses5-dev \
    x11proto-core-dev \
    libx11-dev \
    lib32z1-dev \
    libgl1-mesa-dev \
    libxml2-utils \
    xsltproc \
    unzip \
    fontconfig \
    python3 \
    python-is-python3 \
    libssl-dev \
    bc \
    rsync \
    ccache \
    device-tree-compiler \
    u-boot-tools \
    lzop \
    vim \
    openssh-server

5.3.3 安装 ADB 和 Fastboot

bash
# 安装 Android 平台工具
sudo apt install -y android-tools-adb android-tools-fastboot

# 添加 udev 规则(让普通用户能使用 USB 设备)
sudo usermod -aG plugdev $USER

# 创建 udev 规则文件
sudo tee /etc/udev/rules.d/51-android.rules << 'EOF'
# Amlogic devices
SUBSYSTEM=="usb", ATTR{idVendor}=="1b8e", MODE="0666", GROUP="plugdev"
EOF

# 重新加载 udev 规则
sudo udevadm control --reload-rules

5.3.4 配置 Git

bash
# 设置你的名字和邮箱(改成你自己的)
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

# 设置默认编辑器为 vim(或改成你喜欢的)
git config --global core.editor vim

# 设置凭证缓存,避免每次输入密码
git config --global credential.helper cache

5.3.5 配置 ccache(加速编译)

bash
# 设置 ccache 缓存大小(50GB,根据你的硬盘空间调整)
ccache -M 50G

# 在 ~/.bashrc 中添加环境变量
echo 'export USE_CCACHE=1' >> ~/.bashrc
echo 'export CCACHE_DIR=~/.ccache' >> ~/.bashrc

# 使配置生效
source ~/.bashrc

5.3.6 安装 Java (Android 14 需要)

bash
# 安装 OpenJDK 17
sudo apt install -y openjdk-17-jdk

# 验证安装
java -version

5.4 获取源代码

5.4.1 创建工作目录

bash
# 创建源码目录
mkdir -p ~/android/s905x5
cd ~/android/s905x5

5.4.2 源码获取方式

通常有以下几种方式获取源码:

1. 从芯片厂商获取
   - Amlogic 会提供 SDK 给客户
   - 通过内部 Git 服务器或 FTP 下载

2. 从公司内部服务器获取
   - 公司一般会有内部的代码服务器
   - 使用 git clone 或 repo sync

3. 从 AOSP 获取(纯净 Android,不含芯片厂商代码)
   - 仅用于学习 Android 框架
   - 不能直接在 Amlogic 板子上运行

5.4.3 源码目录结构概览

当你获取源码后,会看到类似这样的目录结构:

android-source/
├── art/                    # Android Runtime(ART 虚拟机)
├── bionic/                 # Android 的 C 库
├── bootable/               # 引导程序相关
│   ├── bootloader/         # Bootloader 源码
│   └── recovery/           # Recovery 模式
├── build/                  # ★ 编译系统
│   └── make/               # Makefile 和编译脚本
├── common/                 # ★ Linux 内核源码
│   └── common14-5.15/      # 5.15 版本内核
├── device/                 # ★ 设备配置
│   └── amlogic/            # Amlogic 设备配置
│       └── ross/           # 你的产品配置
├── external/               # 第三方开源项目
├── frameworks/             # ★ Android 框架
│   ├── av/                 # 音视频框架
│   ├── base/               # 核心框架
│   └── native/             # Native 服务
├── hardware/               # ★ HAL 硬件抽象层
│   └── amlogic/            # Amlogic HAL
├── kernel/                 # 内核相关
├── packages/               # 应用程序
│   └── apps/               # 系统应用
├── prebuilts/              # 预编译工具
├── system/                 # 系统核心组件
├── vendor/                 # ★ 厂商定制
│   └── amlogic/            # Amlogic 定制
├── Makefile                # 顶层 Makefile
└── README.md               # 说明文件

带 ★ 的是你最常打交道的目录

5.5 第一次编译

5.5.1 初始化编译环境

bash
# 进入源码目录
cd ~/android/s905x5

# 初始化编译环境(每次打开新终端都要执行)
source build/envsetup.sh

# 选择编译目标
lunch

# 会显示类似的菜单:
# 1. ross-userdebug
# 2. ross-user
# 选择 ross-userdebug(带调试功能)

5.5.2 开始编译

bash
# 方式一:使用项目提供的编译脚本(推荐)
source build/envsetup.sh && lunch ross-userdebug && make -j16

# 参数说明:
# -a : 编译 Android
# -c : 清理编译
# -k : 编译内核
# -o : 生成 OTA 包
# -j200 : 使用 200 个并行任务(根据 CPU 核心数调整)

# 方式二:使用标准 make 命令
make -j$(nproc)
# $(nproc) 自动获取 CPU 核心数

5.5.3 编译时间参考

首次完整编译:
├── 高配机器(32核/64GB):1-2 小时
├── 中配机器(8核/32GB):3-5 小时
└── 低配机器(4核/16GB):6-10 小时

后续增量编译(只改了少量代码):
├── 几分钟到几十分钟不等
└── 开启 ccache 后更快

5.5.4 编译输出

bash
# 编译成功后,镜像文件位于:
out/target/product/ross/

# 主要文件:
├── boot.img          # 内核镜像
├── system.img        # 系统分区镜像
├── vendor.img        # 厂商分区镜像
├── userdata.img      # 用户数据分区
├── recovery.img      # Recovery 镜像
└── update.zip        # OTA 升级包

5.6 烧录测试

5.6.1 准备工作

bash
# 确保 ADB 工具可用
adb version

# 连接设备(USB 线连接电视盒子和电脑)
# 在盒子上启用"开发者选项" > "USB 调试"

# 检查设备是否连接
adb devices
# 应该显示类似:
# List of devices attached
# 1234567890ABCDEF    device

5.6.2 进入刷机模式

bash
# 方式一:通过 ADB 命令
adb reboot bootloader

# 方式二:按键组合
# 断电 → 按住复位键 → 上电 → 松开复位键

5.6.3 烧录镜像

bash
# 进入编译输出目录
cd out/target/product/ross

# 使用 fastboot 烧录(各分区分别烧录)
fastboot flash boot boot.img
fastboot flash system system.img
fastboot flash vendor vendor.img
fastboot reboot

# 或者使用 Amlogic 提供的烧录工具
# USB_Burning_Tool(Windows)
# aml-flash-tool(Linux)

5.7 第一阶段检验


6. 第二阶段:Linux 基础

目标:熟练使用 Linux 命令行,为后续开发打好基础

6.1 为什么要学 Linux 命令行?

在嵌入式 Android 开发中,你会:

1. 在 Linux 电脑上编译代码(每天都用)
2. 通过串口/ADB 登录设备的 Linux Shell(调试必备)
3. 阅读和编写 Shell 脚本(编译脚本、启动脚本)
4. 查看系统日志、调试问题(定位问题)

可以说,不会 Linux 命令行 = 无法进行嵌入式开发

6.2 文件系统基础

6.2.1 Linux 目录结构

/                           # 根目录(一切的起点)
├── bin/                    # 基本命令(ls, cp, mv 等)
├── boot/                   # 启动文件(内核、bootloader)
├── dev/                    # 设备文件(★ 嵌入式开发常用)
│   ├── null               # 空设备
│   ├── ttyS0              # 串口设备
│   └── sda                # 硬盘设备
├── etc/                    # 配置文件
│   ├── passwd             # 用户信息
│   └── fstab              # 挂载配置
├── home/                   # 用户主目录
│   └── yourname/          # 你的主目录 (~)
├── lib/                    # 系统库文件
├── mnt/                    # 挂载点
├── opt/                    # 可选软件
├── proc/                   # ★ 进程和内核信息(虚拟文件系统)
│   ├── cpuinfo            # CPU 信息
│   └── meminfo            # 内存信息
├── root/                   # root 用户主目录
├── sbin/                   # 系统管理命令
├── sys/                    # ★ 系统和设备信息(虚拟文件系统)
├── tmp/                    # 临时文件
├── usr/                    # 用户程序
│   ├── bin/               # 用户命令
│   ├── lib/               # 用户库
│   └── share/             # 共享数据
└── var/                    # 可变数据(日志等)
    └── log/               # 系统日志

6.2.2 路径表示

绝对路径:从根目录开始的完整路径
例如:/home/yourname/android/source

相对路径:相对于当前目录的路径
例如:./source  或  ../android/source

特殊目录表示:
~     : 当前用户的主目录,等同于 /home/yourname
.     : 当前目录
..    : 上一级目录
-     : 上一次所在的目录

6.3 基础命令学习

6.3.1 文件和目录操作

bash
# ========== 查看 ==========
ls                  # 列出当前目录内容
ls -l               # 详细信息(权限、大小、时间)
ls -la              # 包含隐藏文件(以 . 开头的文件)
ls -lh              # 人性化显示文件大小(KB, MB, GB)

pwd                 # 显示当前目录的绝对路径

cat file.txt        # 显示文件内容(适合小文件)
less file.txt       # 分页查看(按 q 退出,空格翻页)
head -n 20 file.txt # 查看前 20 行
tail -n 20 file.txt # 查看后 20 行
tail -f log.txt     # 实时查看文件更新(看日志常用)

# ========== 切换目录 ==========
cd /path/to/dir     # 切换到指定目录
cd ~                # 切换到主目录
cd ..               # 切换到上级目录
cd -                # 切换到上次的目录

# ========== 创建 ==========
mkdir dirname       # 创建目录
mkdir -p a/b/c      # 递归创建多级目录
touch file.txt      # 创建空文件(或更新时间戳)

# ========== 复制 ==========
cp source dest      # 复制文件
cp -r dir1 dir2     # 递归复制目录(-r = recursive)
cp -a dir1 dir2     # 保留所有属性复制(推荐)

# ========== 移动/重命名 ==========
mv old.txt new.txt  # 重命名
mv file.txt /path/  # 移动到指定目录

# ========== 删除 ==========
rm file.txt         # 删除文件
rm -r dirname       # 递归删除目录
rm -rf dirname      # 强制递归删除(⚠️ 危险!无确认)

# ⚠️ 警告:rm -rf 非常危险!
# 永远不要执行:rm -rf /   或   rm -rf ~

6.3.2 文件查找

bash
# find 命令(功能强大)
find /path -name "*.c"           # 按名字查找
find /path -name "*.c" -type f   # 只找文件
find /path -name "*.c" -type d   # 只找目录
find /path -size +100M           # 找大于 100MB 的文件
find /path -mtime -7             # 找 7 天内修改的文件

# locate 命令(更快,但需要更新数据库)
sudo updatedb                    # 更新数据库
locate filename                  # 快速查找

# which 命令(查找命令位置)
which python                     # 显示 python 命令的路径

# grep 命令(搜索文件内容)★ 非常常用
grep "pattern" file.txt          # 在文件中搜索
grep -r "pattern" /path          # 递归搜索目录
grep -i "pattern" file.txt       # 忽略大小写
grep -n "pattern" file.txt       # 显示行号
grep -l "pattern" *.c            # 只显示包含的文件名

# 组合使用(在代码中搜索)
grep -rn "function_name" --include="*.c" .

6.3.3 文件权限

权限格式解读:
-rwxr-xr-x  1  user  group  1234  Jan 1 12:00  filename
│└┬┘└┬┘└┬┘
│ │  │  └── 其他人权限
│ │  └───── 组权限
│ └──────── 所有者权限
└────────── 文件类型(- 普通文件,d 目录,l 链接)

权限字母含义:
r (read)    : 读权限    = 4
w (write)   : 写权限    = 2
x (execute) : 执行权限  = 1

常见权限组合:
755 = rwxr-xr-x  (所有者可读写执行,其他人可读执行)
644 = rw-r--r--  (所有者可读写,其他人只读)
777 = rwxrwxrwx  (所有人可读写执行,通常不推荐)
bash
# 修改权限
chmod 755 script.sh       # 数字方式
chmod +x script.sh        # 添加执行权限
chmod -w file.txt         # 移除写权限

# 修改所有者
chown user:group file.txt
chown -R user:group dir/  # 递归修改

# 以管理员权限执行
sudo command              # 临时获取 root 权限
sudo -i                   # 切换到 root 用户

6.3.4 进程管理

bash
# 查看进程
ps aux                    # 查看所有进程
ps aux | grep process     # 搜索特定进程
top                       # 实时查看进程(按 q 退出)
htop                      # 更好用的 top(需要安装)

# 杀死进程
kill PID                  # 发送终止信号
kill -9 PID               # 强制杀死
killall process_name      # 按名字杀死

# 后台运行
command &                 # 后台运行命令
nohup command &           # 后台运行,断开终端不停止
jobs                      # 查看后台任务
fg %1                     # 将任务 1 调到前台

6.3.5 管道和重定向

bash
# 管道 | :将前一个命令的输出作为后一个命令的输入
ls -l | grep ".txt"       # 列出文件并筛选 .txt
ps aux | grep java        # 查找 java 进程
cat log.txt | head -100   # 查看日志前 100 行

# 重定向:将输出保存到文件
command > file.txt        # 输出到文件(覆盖)
command >> file.txt       # 输出到文件(追加)
command 2> error.txt      # 错误输出到文件
command > all.txt 2>&1    # 标准输出和错误都到文件

# 实用示例
# 编译并保存日志
make 2>&1 | tee build.log

# 搜索代码并统计行数
grep -r "TODO" --include="*.c" . | wc -l

6.4 文本编辑器

6.4.1 Vim 编辑器(推荐掌握)

为什么学 Vim?
- 几乎所有 Linux 系统都有 vim
- 在远程服务器、嵌入式设备上常用
- 熟练后效率很高

Vim 三种模式:
┌───────────────────────────────────────────────────────────────────────────┐
│                                                                           │
│                         ┌─────────────────┐                               │
│                         │   普通模式      │                               │
│                         │  (Normal Mode)  │                               │
│                         │  浏览、命令操作 │                               │
│                         └───────┬─────────┘                               │
│                                 │                                         │
│              ┌──────────────────┼──────────────────┐                      │
│              │ 按 i             │                  │ 按 :                 │
│              ▼                  │                  ▼                      │
│      ┌───────────────┐          │          ┌───────────────┐              │
│      │  插入模式     │          │          │  命令行模式   │              │
│      │ (Insert Mode) │          │          │(Command Mode) │              │
│      │  输入文字     │          │          │ 保存、退出等  │              │
│      └───────────────┘          │          └───────────────┘              │
│              │                  │                  │                      │
│              │ 按 Esc           │                  │ 按 Esc/Enter         │
│              └──────────────────┴──────────────────┘                      │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘
Vim 基础命令速查表:

启动和退出:
vim file.txt        打开文件
:w                  保存
:q                  退出
:wq                 保存并退出
:q!                 强制退出(不保存)
:wq!                强制保存并退出

移动光标(普通模式):
h j k l             左下上右(或用方向键)
gg                  到文件开头
G                   到文件末尾
0                   到行首
$                   到行尾
w                   下一个单词
b                   上一个单词
Ctrl+f              下一页
Ctrl+b              上一页
:123                跳到第 123 行

编辑(普通模式):
i                   在光标前插入
a                   在光标后插入
o                   在下方新建一行并插入
O                   在上方新建一行并插入
x                   删除光标处字符
dd                  删除整行
yy                  复制整行
p                   粘贴
u                   撤销
Ctrl+r              重做

搜索:
/pattern            向下搜索
?pattern            向上搜索
n                   下一个匹配
N                   上一个匹配
:%s/old/new/g       全局替换

6.4.2 VS Code(推荐日常使用)

bash
# 安装 VS Code
sudo snap install code --classic

# 或者下载 .deb 包安装
# https://code.visualstudio.com/download

# 推荐安装的插件:
# - C/C++
# - Remote - SSH(远程开发)
# - GitLens
# - Android AOSP

6.5 Shell 脚本基础

6.5.1 什么是 Shell 脚本?

Shell 脚本就是把一系列命令写在一个文件里,一次性执行。

用途:
- 自动化编译流程
- 批量处理文件
- 系统管理任务
- Android 启动脚本

6.5.2 第一个 Shell 脚本

bash
#!/bin/bash
# 文件名: hello.sh
# 第一行叫 shebang,指定用什么程序执行这个脚本

# 这是注释
echo "Hello, World!"        # echo 用于输出

# 变量
NAME="Android"
echo "Hello, $NAME!"

# 接收参数
# $0 脚本名, $1 第一个参数, $2 第二个参数...
echo "脚本名: $0"
echo "第一个参数: $1"

# 条件判断
if [ -f "file.txt" ]; then
    echo "file.txt 存在"
else
    echo "file.txt 不存在"
fi

# 循环
for i in 1 2 3 4 5; do
    echo "数字: $i"
done

# 遍历文件
for file in *.txt; do
    echo "处理文件: $file"
done
bash
# 运行脚本
chmod +x hello.sh    # 添加执行权限
./hello.sh           # 执行
./hello.sh arg1 arg2 # 带参数执行

6.5.3 常用 Shell 语法

bash
# ========== 变量 ==========
NAME="value"                # 定义变量(= 两边不能有空格!)
echo $NAME                  # 使用变量
echo ${NAME}                # 推荐写法
echo "${NAME}_suffix"       # 拼接字符串

# 命令结果赋值给变量
FILES=$(ls)
COUNT=`wc -l < file.txt`    # 反引号也可以

# ========== 条件判断 ==========
# 文件测试
[ -f file ]    # 文件存在且是普通文件
[ -d dir ]     # 目录存在
[ -e path ]    # 路径存在
[ -r file ]    # 文件可读
[ -w file ]    # 文件可写
[ -x file ]    # 文件可执行

# 字符串比较
[ "$a" = "$b" ]    # 相等
[ "$a" != "$b" ]   # 不等
[ -z "$a" ]        # 为空
[ -n "$a" ]        # 不为空

# 数字比较
[ $a -eq $b ]      # 等于
[ $a -ne $b ]      # 不等于
[ $a -lt $b ]      # 小于
[ $a -gt $b ]      # 大于
[ $a -le $b ]      # 小于等于
[ $a -ge $b ]      # 大于等于

# if-else 语句
if [ condition ]; then
    commands
elif [ condition2 ]; then
    commands
else
    commands
fi

# ========== 循环 ==========
# for 循环
for i in 1 2 3; do
    echo $i
done

for file in *.c; do
    echo $file
done

for ((i=0; i<10; i++)); do
    echo $i
done

# while 循环
while [ condition ]; do
    commands
done

# ========== 函数 ==========
my_function() {
    echo "参数1: $1"
    echo "参数2: $2"
    return 0
}

# 调用函数
my_function arg1 arg2

6.6 第二阶段检验

完成以下任务,检验你的 Linux 基础:


7. 第三阶段:C 语言与数据结构

目标:掌握 C 语言基础,能够阅读和编写内核/驱动代码

7.1 为什么要学 C 语言?

在嵌入式 Android 开发中:

Linux 内核          → 99% 用 C 语言编写
硬件驱动            → 99% 用 C 语言编写
HAL 层              → C/C++ 混合
Bootloader (U-Boot) → C 语言
Native 库           → C/C++ 编写

结论:不会 C 语言 = 无法进行底层开发

7.2 C 语言基础语法

7.2.1 第一个 C 程序

c
// hello.c
#include <stdio.h>     // 包含标准输入输出头文件

int main(int argc, char *argv[])  // 程序入口
{
    printf("Hello, World!\n");    // 打印输出
    return 0;                     // 返回 0 表示成功
}
bash
# 编译
gcc hello.c -o hello

# 运行
./hello

7.2.2 数据类型

c
// ========== 基本数据类型 ==========

// 整数类型
char c = 'A';           // 1 字节,-128 ~ 127
short s = 100;          // 2 字节
int i = 1000;           // 4 字节(通常)
long l = 100000;        // 4 或 8 字节
long long ll = 10000000000LL;  // 8 字节

// 无符号整数(只能表示正数)
unsigned char uc = 255;      // 0 ~ 255
unsigned int ui = 4294967295;// 0 ~ 4294967295

// 浮点数
float f = 3.14f;        // 4 字节
double d = 3.14159265;  // 8 字节

// ========== 内核开发常用的类型(需要 #include <stdint.h>)==========

uint8_t     // 无符号 8 位  (0 ~ 255)
uint16_t    // 无符号 16 位 (0 ~ 65535)
uint32_t    // 无符号 32 位
uint64_t    // 无符号 64 位

int8_t      // 有符号 8 位
int16_t     // 有符号 16 位
int32_t     // 有符号 32 位
int64_t     // 有符号 64 位

// 为什么用这些?
// 因为 int 在不同平台可能大小不同,而 uint32_t 永远是 32 位

7.2.3 指针(★★★★★ 最重要)

c
// 指针是 C 语言的灵魂,必须掌握!

// ========== 基本概念 ==========
// 指针就是"内存地址"

int a = 10;        // 变量 a,假设地址是 0x1000
int *p = &a;       // 指针 p,存储 a 的地址 0x1000

// & 取地址运算符:获取变量的地址
// * 解引用运算符:获取指针指向的值

printf("a 的值: %d\n", a);      // 输出: 10
printf("a 的地址: %p\n", &a);   // 输出: 0x1000(具体值不同)
printf("p 的值: %p\n", p);      // 输出: 0x1000
printf("p 指向的值: %d\n", *p); // 输出: 10

*p = 20;           // 通过指针修改 a 的值
printf("a 的值: %d\n", a);      // 输出: 20

// ========== 指针图解 ==========
/*
    变量 a:              指针 p:
    ┌─────────┐          ┌─────────┐
    │   10    │ ◄────────│ 0x1000  │
    └─────────┘          └─────────┘
    地址: 0x1000          地址: 0x2000

    p 存储的是 a 的地址
    *p 就是去 0x1000 这个地址取值,得到 10
*/

// ========== 指针和数组 ==========
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;      // 数组名就是首地址

printf("%d\n", arr[0]);   // 输出: 1
printf("%d\n", *p);       // 输出: 1
printf("%d\n", p[0]);     // 输出: 1
printf("%d\n", *(p+1));   // 输出: 2
printf("%d\n", p[1]);     // 输出: 2

// p+1 不是地址+1,而是地址+sizeof(int)
// 如果 int 是 4 字节,p+1 实际是地址+4

// ========== 指针的指针 ==========
int a = 10;
int *p = &a;       // p 指向 a
int **pp = &p;     // pp 指向 p

printf("%d\n", **pp);  // 输出: 10

// ========== 空指针 ==========
int *p = NULL;     // 空指针,不指向任何有效地址
// 使用前一定要判断
if (p != NULL) {
    *p = 10;
}

7.2.4 结构体(★★★★★ 非常重要)

c
// 结构体:把多个不同类型的数据组合在一起

// ========== 定义结构体 ==========
struct Person {
    char name[50];
    int age;
    float height;
};

// 使用
struct Person p1;
strcpy(p1.name, "张三");
p1.age = 25;
p1.height = 175.5;

// ========== typedef 简化 ==========
typedef struct {
    char name[50];
    int age;
    float height;
} Person;

Person p2;  // 不需要 struct 关键字了

// ========== 结构体指针 ==========
Person *pp = &p1;

// 访问成员的两种方式
printf("%s\n", (*pp).name);  // 方式1:先解引用
printf("%s\n", pp->name);    // 方式2:箭头运算符(推荐)

// ========== 内核中的常见模式 ==========
// 内核代码大量使用结构体来描述硬件、设备、驱动等

// 示例:字符设备结构
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    dev_t dev;
    unsigned int count;
};

// 示例:文件操作结构
struct file_operations {
    struct module *owner;
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
    int (*open)(struct inode *, struct file *);
    int (*release)(struct inode *, struct file *);
    // ... 更多操作
};

7.2.5 内存管理

c
#include <stdlib.h>

// ========== 动态内存分配 ==========

// malloc: 分配内存
int *p = (int *)malloc(sizeof(int) * 10);  // 分配 10 个 int 的空间
if (p == NULL) {
    // 分配失败
    printf("内存分配失败!\n");
    return -1;
}

// 使用内存
for (int i = 0; i < 10; i++) {
    p[i] = i * 10;
}

// free: 释放内存(必须!否则内存泄漏)
free(p);
p = NULL;  // 好习惯:释放后置为 NULL

// ========== calloc: 分配并清零 ==========
int *p2 = (int *)calloc(10, sizeof(int));  // 10 个 int,初始化为 0

// ========== realloc: 重新分配 ==========
p = (int *)realloc(p, sizeof(int) * 20);   // 扩展到 20 个 int

// ========== 内存泄漏示例(错误!)==========
void bad_function() {
    int *p = malloc(100);
    // 忘记 free(p)
    return;  // p 丢失,100 字节永远无法释放
}

// ========== 内核中的内存分配 ==========
// 内核不用 malloc/free,而是用:
// kmalloc / kfree      - 小块内存
// vmalloc / vfree      - 大块虚拟连续内存
// devm_*               - 设备管理的内存(自动释放)

7.3 C 语言进阶

7.3.1 函数指针

c
// 函数指针:指向函数的指针

// 定义一个普通函数
int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

// 定义函数指针
int (*operation)(int, int);

// 使用函数指针
operation = add;            // 指向 add 函数
printf("%d\n", operation(3, 2));  // 输出: 5

operation = sub;            // 指向 sub 函数
printf("%d\n", operation(3, 2));  // 输出: 1

// ========== 在内核中的应用 ==========
// 内核大量使用函数指针实现多态

struct file_operations {
    // 这些都是函数指针
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
    int (*open)(struct inode *, struct file *);
    int (*release)(struct inode *, struct file *);
};

// 驱动实现自己的函数
static ssize_t my_read(struct file *file, char __user *buf,
                       size_t count, loff_t *pos) {
    // 实现读取逻辑
    return 0;
}

// 注册到结构体
static struct file_operations my_fops = {
    .read = my_read,
    .write = my_write,
    .open = my_open,
    .release = my_release,
};

7.3.2 预处理器

c
// ========== 宏定义 ==========
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))

// 使用
float area = PI * r * r;
int max = MAX(10, 20);

// ⚠️ 宏是文本替换,注意加括号
#define SQUARE(x) ((x) * (x))
// 如果不加括号:#define SQUARE(x) x * x
// SQUARE(1+2) 变成 1+2 * 1+2 = 5,而不是 9

// ========== 条件编译 ==========
#ifdef DEBUG
    printf("调试信息: x = %d\n", x);
#endif

#ifndef HEADER_H
#define HEADER_H
// 头文件内容,防止重复包含
#endif

#if defined(PLATFORM_ARM)
    // ARM 平台代码
#elif defined(PLATFORM_X86)
    // X86 平台代码
#else
    // 默认代码
#endif

// ========== 内核中常见的宏 ==========
#define BIT(x) (1 << (x))        // BIT(3) = 0b1000 = 8
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define container_of(ptr, type, member) // 通过成员指针获取结构体指针

7.3.3 位操作

c
// 嵌入式开发大量使用位操作来控制寄存器

// ========== 位运算符 ==========
& (与)    : 1 & 1 = 1, 其他都是 0
| (或)    : 0 | 0 = 0, 其他都是 1
^ (异或)  : 相同为 0,不同为 1
~ (取反)  : ~0 = 1, ~1 = 0
<< (左移) : 1 << 3 = 0b1000 = 8
>> (右移) : 8 >> 2 = 0b10 = 2

// ========== 常用位操作 ==========

// 设置某一位为 1
reg |= (1 << bit);

// 清除某一位为 0
reg &= ~(1 << bit);

// 切换某一位
reg ^= (1 << bit);

// 检查某一位是否为 1
if (reg & (1 << bit)) {
    // 该位为 1
}

// ========== 实际例子 ==========
// 假设有一个 8 位寄存器控制 LED
// bit 0: LED1, bit 1: LED2, bit 2: LED3 ...

uint8_t led_reg = 0;

// 打开 LED1 (bit 0)
led_reg |= (1 << 0);    // led_reg = 0b00000001

// 打开 LED3 (bit 2)
led_reg |= (1 << 2);    // led_reg = 0b00000101

// 关闭 LED1
led_reg &= ~(1 << 0);   // led_reg = 0b00000100

// 检查 LED3 是否亮
if (led_reg & (1 << 2)) {
    printf("LED3 is ON\n");
}

// ========== 位域(内核常用)==========
struct {
    uint8_t led1 : 1;    // 占 1 位
    uint8_t led2 : 1;    // 占 1 位
    uint8_t led3 : 1;    // 占 1 位
    uint8_t reserved : 5;// 保留 5 位
} led_status;

led_status.led1 = 1;     // 打开 LED1

7.4 阅读内核代码的技巧

1. 不要试图一次读懂所有代码
   - 先了解整体架构
   - 再深入感兴趣的部分

2. 善用搜索
   - grep -r "关键字" .
   - 找函数定义:grep -rn "^int function_name"

3. 理解常见模式
   - 结构体 + 函数指针 = 面向对象
   - probe/remove = 设备发现和移除
   - init/exit = 模块加载和卸载

4. 善用内核文档
   - Documentation/ 目录下有很多文档
   - 函数注释通常很详细

5. 借助工具
   - VS Code + C/C++ 插件
   - Source Insight(付费)
   - cscope + ctags(命令行)

7.5 第三阶段检验


8. 第四阶段:Android 系统架构认知

目标:理解 Android 系统的整体架构,知道各层的作用

8.1 Android 系统架构详解

┌─────────────────────────────────────────────────────────────────────────┐
│                        Android 系统架构详解                             │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                     应用层 (Applications)                       │    │
│  │                                                                 │    │
│  │   这是用户直接接触的层,包括:                                  │    │
│  │   - 系统应用:设置、电话、短信、相机(电视上是设置、视频播放器)│    │
│  │   - 第三方应用:微信、抖音、爱奇艺等                            │    │
│  │   - 你开发的应用                                                │    │
│  │                                                                 │    │
│  │   使用语言:Java / Kotlin                                       │    │
│  │   对应目录:packages/apps/                                      │    │
│  │                                                                 │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                    │                                    │
│                                    │ 调用 Android API                   │
│                                    ▼                                    │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                   Framework 层 (Android 框架)                   │    │
│  │                                                                 │    │
│  │   Android 的核心,提供 API 给应用使用:                         │    │
│  │   - Activity Manager:管理应用生命周期                          │    │
│  │   - Window Manager:管理窗口显示                                │    │
│  │   - Content Provider:应用间数据共享                            │    │
│  │   - Package Manager:应用安装管理                               │    │
│  │   - Notification Manager:通知管理                              │    │
│  │   - Resource Manager:资源管理                                  │    │
│  │                                                                 │    │
│  │   使用语言:Java                                                │    │
│  │   对应目录:frameworks/base/                                    │    │
│  │                                                                 │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                    │                                    │
│                                    │ JNI 调用                           │
│                                    ▼                                    │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                     Native 层 (C/C++ 库)                        │    │
│  │                                                                 │    │
│  │   高性能的底层库:                                              │    │
│  │   - libc (Bionic):Android 定制的 C 库                          │    │
│  │   - OpenGL ES:图形渲染                                         │    │
│  │   - Media Framework:音视频播放                                 │    │
│  │   - SQLite:数据库                                              │    │
│  │   - WebKit:网页渲染                                            │    │
│  │   - SurfaceFlinger:图形合成                                    │    │
│  │   - AudioFlinger:音频混合                                      │    │
│  │                                                                 │    │
│  │   使用语言:C/C++                                               │    │
│  │   对应目录:frameworks/native/, external/                       │    │
│  │                                                                 │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                    │                                    │
│                                    │ 调用 HAL 接口                      │
│                                    ▼                                    │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                      HAL 层 (硬件抽象层)                        │    │
│  │                                                                 │    │
│  │   连接 Android 和硬件驱动的桥梁:                               │    │
│  │   - 定义统一的硬件接口                                          │    │
│  │   - 不同硬件实现相同接口                                        │    │
│  │   - 隔离 Android 和具体硬件                                     │    │
│  │                                                                 │    │
│  │   常见 HAL:                                                    │    │
│  │   - Audio HAL:音频                                             │    │
│  │   - Camera HAL:摄像头                                          │    │
│  │   - Graphics HAL (Gralloc, HWComposer):显示                    │    │
│  │   - Sensors HAL:传感器                                         │    │
│  │                                                                 │    │
│  │   使用语言:C/C++                                               │    │
│  │   对应目录:hardware/                                           │    │
│  │                                                                 │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                    │                                    │
│                                    │ 系统调用 / ioctl                   │
│                                    ▼                                    │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                     Linux Kernel (Linux 内核)                   │    │
│  │                                                                 │    │
│  │   操作系统核心 + 硬件驱动:                                     │    │
│  │   - 进程管理                                                    │    │
│  │   - 内存管理                                                    │    │
│  │   - 文件系统                                                    │    │
│  │   - 网络协议栈                                                  │    │
│  │   - 设备驱动:显示、音频、USB、WiFi、蓝牙等                     │    │
│  │                                                                 │    │
│  │   使用语言:C(少量汇编)                                       │    │
│  │   对应目录:common/common14-5.15/                               │    │
│  │                                                                 │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

8.2 Android 启动流程概览

┌─────────────────────────────────────────────────────────────────────────┐
│                        Android 完整启动流程                             │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  上电                                                                   │
│    │                                                                    │
│    ▼                                                                    │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 1. BootROM(固化在芯片中的代码)                               │    │
│  │    - 芯片上电后执行的第一段代码                                │    │
│  │    - 初始化最基本的硬件                                        │    │
│  │    - 加载 Bootloader 到内存                                    │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 2. Bootloader (U-Boot)                                         │    │
│  │    - 初始化 CPU、内存、存储等硬件                               │    │
│  │    - 显示开机 Logo                                              │    │
│  │    - 加载 Linux 内核到内存                                      │    │
│  │    - 传递启动参数给内核                                         │    │
│  │    - 跳转到内核执行                                             │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 3. Linux Kernel                                                 │    │
│  │    - 初始化内存管理、进程调度                                   │    │
│  │    - 加载设备驱动                                               │    │
│  │    - 挂载文件系统                                               │    │
│  │    - 启动第一个用户进程:/init                                  │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 4. init 进程(Android 第一个进程,PID=1)                        │    │
│  │    - 解析 init.rc 配置文件                                      │    │
│  │    - 设置属性系统                                               │    │
│  │    - 启动各种系统服务                                           │    │
│  │    - 启动 Zygote 进程                                           │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 5. Zygote 进程(所有 Java 进程的父进程)                          │    │
│  │    - 启动 Android Runtime (ART)                                 │    │
│  │    - 预加载 Java 类和资源                                       │    │
│  │    - 启动 System Server                                         │    │
│  │    - 等待 fork 请求创建新进程                                   │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 6. System Server(系统服务进程)                                 │    │
│  │    - 启动所有核心系统服务:                                     │    │
│  │      Activity Manager Service                                   │    │
│  │      Window Manager Service                                     │    │
│  │      Package Manager Service                                    │    │
│  │      Power Manager Service                                      │    │
│  │      ...                                                        │    │
│  │    - 发送 ACTION_BOOT_COMPLETED 广播                            │    │
│  └─────────────────────────────┬──────────────────────────────────┘    │
│                                │                                        │
│                                ▼                                        │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ 7. Launcher(桌面)                                              │    │
│  │    - 显示应用图标                                               │    │
│  │    - 等待用户操作                                               │    │
│  │    - 启动完成!                                                 │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                         │
│  整个过程大约需要 10-60 秒,取决于硬件和系统复杂度                       │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

8.3 Android 编译系统

8.3.1 编译系统简介

Android 使用两套编译系统:

1. Make(传统,正在逐步淘汰)
   - 使用 Makefile 和 Android.mk
   - 历史悠久,很多老代码还在用

2. Soong(现代,Android 7.0+ 推荐)
   - 使用 Android.bp(Blueprint 语法)
   - 更简洁、更快速
   - 现代 Android 代码应该用这个

8.3.2 编译命令详解

bash
# 进入源码目录
cd ~/android/s905x5

# 1. 初始化编译环境(每次新开终端都要执行)
source build/envsetup.sh

# 2. 选择编译目标
lunch ross-userdebug

# lunch 目标格式:<product>-<variant>
# product: 产品名称(如 ross)
# variant: 编译类型
#   - user: 正式发布版本,没有 root 权限
#   - userdebug: 调试版本,有 root 权限(开发常用)
#   - eng: 工程版本,最多调试信息

# 3. 开始编译
make -j$(nproc)
# -j$(nproc) 表示使用所有 CPU 核心并行编译

# 或者使用项目脚本
make -j16

# 4. 只编译特定模块
make Settings  # 编译设置应用
make framework  # 编译 framework
make bootimage  # 编译内核镜像

8.3.3 Android.bp 示例

python
# Android.bp 基本语法

# 编译一个 C 可执行程序
cc_binary {
    name: "my_tool",
    srcs: ["main.c", "utils.c"],
    cflags: ["-Wall", "-Werror"],
}

# 编译一个共享库
cc_library_shared {
    name: "libmy_utils",
    srcs: ["utils.c"],
    export_include_dirs: ["include"],
}

# 编译一个 Java 库
java_library {
    name: "my_java_lib",
    srcs: ["src/**/*.java"],
    sdk_version: "current",
}

# 编译一个 APK
android_app {
    name: "MyApp",
    srcs: ["src/**/*.java"],
    resource_dirs: ["res"],
    manifest: "AndroidManifest.xml",
    platform_apis: true,
    certificate: "platform",
}

8.4 重要目录说明

android-source/

├── build/                      # 编译系统
│   ├── make/                   # Make 编译规则
│   └── soong/                  # Soong 编译规则

├── device/amlogic/ross/        # ★ 产品配置(你最常改的地方)
│   ├── ross.mk                 # 产品 Makefile
│   ├── BoardConfig.mk          # 板级配置
│   ├── init.amlogic.rc         # init 配置
│   └── ...

├── common/common14-5.15/       # ★ Linux 内核
│   ├── arch/arm64/boot/dts/    # 设备树
│   ├── drivers/amlogic/        # Amlogic 驱动
│   └── ...

├── hardware/amlogic/           # ★ Amlogic HAL
│   ├── audio/                  # 音频 HAL
│   ├── gralloc/               # 图形内存 HAL
│   └── ...

├── frameworks/                 # Android 框架
│   ├── base/                   # 核心框架
│   │   ├── core/java/android/  # Android API
│   │   └── services/           # 系统服务
│   └── native/                 # Native 服务

├── packages/apps/              # 系统应用
│   ├── Settings/               # 设置
│   ├── Launcher3/              # 启动器
│   └── ...

└── out/target/product/ross/    # ★ 编译输出
    ├── system/                 # system 分区内容
    ├── vendor/                 # vendor 分区内容
    ├── boot.img                # 内核镜像
    └── ...

8.5 第四阶段检验


9. 第五阶段:Bootloader 与 U-Boot

📚 详细文档: Bootloader 与 U-Boot 开发指南

目标:理解设备开机的第一步,掌握 U-Boot 基本操作

9.1 什么是 Bootloader?

┌───────────────────────────────────────────────────────────────────────────┐
│                          Bootloader 是什么?                              │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  Bootloader = 引导加载程序                                                │
│                                                                           │
│  类比:                                                                   │
│  想象你的电脑是一辆汽车:                                                 │
│  - Bootloader 就像启动车辆的钥匙和点火系统                                │
│  - 它负责唤醒"沉睡"的系统,做好准备工作                                   │
│  - 然后把控制权交给"驾驶员"(操作系统)                                   │
│                                                                           │
│  Bootloader 的职责:                                                      │
│  1. 初始化硬件(CPU、内存、存储)                                         │
│  2. 显示开机 Logo                                                         │
│  3. 加载操作系统内核                                                      │
│  4. 传递启动参数                                                          │
│  5. 提供刷机模式(fastboot)                                              │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

9.2 Amlogic 启动流程

上电


┌───────────────────────────────────────────────────────────────────────────┐
│  BL1 (BootROM)                                                            │
│  - 固化在芯片内部,无法修改                                               │
│  - 初始化最基本的硬件                                                     │
│  - 从存储设备加载 BL2                                                     │
└───────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│  BL2                                                                      │
│  - 初始化 DDR 内存                                                        │
│  - 安全启动验证                                                           │
│  - 加载 BL2E、BL2X                                                        │
└───────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│  BL31 (ARM Trusted Firmware)                                              │
│  - 安全监控程序                                                           │
│  - 运行在最高权限级别                                                     │
│  - 处理安全相关的功能                                                     │
└───────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│  BL32 (可选, Secure OS)                                                   │
│  - 安全操作系统(如 OP-TEE)                                              │
│  - DRM、安全支付等功能                                                    │
└───────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│  BL33 (U-Boot) ★ 这是我们主要关注的                                       │
│  - 功能丰富的引导程序                                                     │
│  - 加载 Linux 内核                                                        │
│  - 提供命令行交互                                                         │
│  - 支持 fastboot 刷机                                                     │
└───────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────┐
│  Linux Kernel                                                             │
│  - 操作系统内核开始运行                                                   │
└───────────────────────────────────────────────────────────────────────────┘

9.3 U-Boot 基础

9.3.1 进入 U-Boot 命令行

方法1:串口连接
1. 用 USB 转 TTL 线连接板子的调试串口
2. 打开串口终端(如 minicom, screen, putty)
3. 波特率通常是 115200
4. 开机时快速按回车键(在看到 "Hit any key to stop autoboot" 时)

方法2:ADB 命令
adb reboot bootloader
# 然后通过串口查看

串口终端设置示例(Linux):
sudo minicom -D /dev/ttyUSB0 -b 115200
# 或
sudo screen /dev/ttyUSB0 115200

9.3.2 常用 U-Boot 命令

bash
# ========== 信息查看 ==========
help                    # 查看所有命令
help <cmd>             # 查看某个命令的帮助
version                # 查看 U-Boot 版本
bdinfo                 # 查看板级信息
printenv               # 打印所有环境变量
printenv <var>         # 打印某个环境变量

# ========== 环境变量 ==========
setenv var value       # 设置环境变量
saveenv                # 保存环境变量到存储
run <var>              # 执行环境变量中的命令

# ========== 内存操作 ==========
md 0x01000000 0x100    # 显示内存内容
mw 0x01000000 0x12345678  # 写入内存

# ========== 存储操作 ==========
mmc list              # 列出 MMC 设备
mmc dev 0             # 选择设备 0
mmc info              # 显示 MMC 信息
mmc read addr blk cnt # 读取数据到内存

# ========== 启动相关 ==========
boot                  # 执行默认启动命令
booti addr            # 启动 Image 格式内核
reset                 # 重启

9.3.3 重要环境变量

bash
# 查看启动命令
printenv bootcmd
# 通常类似:run storeboot

# 查看/修改启动参数
printenv bootargs
# 包含传递给内核的参数,如:
# root=/dev/mmcblk0p18 rootfstype=ext4 init=/init ...

# 设置启动延迟(秒)
setenv bootdelay 3

# 保存修改
saveenv

9.4 Fastboot 模式

bash
# Fastboot 是 Android 的刷机协议

# 进入 fastboot 模式
# 方法1:U-Boot 命令
fastboot  # 或 run fastboot

# 方法2:ADB 命令
adb reboot fastboot

# 方法3:按键组合(不同设备不同)

# PC 端 fastboot 命令
fastboot devices              # 列出连接的设备
fastboot flash boot boot.img  # 烧录 boot 分区
fastboot flash system system.img
fastboot reboot               # 重启
fastboot oem unlock           # 解锁 bootloader(如果支持)

9.5 第五阶段检验


10. 第六阶段:Linux Kernel 驱动开发

📚 详细文档: Linux Kernel 驱动开发指南

目标:掌握 Linux 驱动开发基础,能编写简单的驱动

10.1 驱动开发基础概念

10.1.1 什么是驱动?

┌───────────────────────────────────────────────────────────────────────────┐
│                             驱动是什么?                                  │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  驱动 = 操作系统和硬件之间的"翻译官"                                      │
│                                                                           │
│  ┌─────────────┐                             ┌─────────────┐              │
│  │  应用程序   │                             │   硬件设备  │              │
│  │             │                             │             │              │
│  │  printf()   │                             │   串口芯片  │              │
│  │  read()     │                             │   GPIO      │              │
│  │  write()    │                             │   I2C 设备  │              │
│  └──────┬──────┘                             └──────▲──────┘              │
│         │                                           │                     │
│         │ 统一的接口                                │ 硬件操作            │
│         │ (系统调用)                                │ (读写寄存器)        │
│         ▼                                           │                     │
│  ┌─────────────────────────────────────────────────────────────────┐     │
│  │                           驱动程序                              │     │
│  │                                                                 │     │
│  │   把"read 文件"翻译成"读取硬件寄存器"                           │     │
│  │   把"write 文件"翻译成"写入硬件寄存器"                          │     │
│  │                                                                 │     │
│  └─────────────────────────────────────────────────────────────────┘     │
│                                                                           │
│  为什么需要驱动?                                                         │
│  - 应用程序只需要 read/write,不需要知道硬件细节                          │
│  - 同样的程序可以在不同硬件上运行(只要有对应驱动)                        │
│  - 硬件变化时只需要改驱动,不需要改应用                                   │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

10.1.2 Linux 驱动的分类

┌───────────────────────────────────────────────────────────────────────────┐
│                           Linux 驱动分类                                  │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  1. 字符设备 (Character Device)                                           │
│     - 按字节流访问,像文件一样读写                                        │
│     - 例如:串口、键盘、LED、GPIO                                         │
│     - 设备文件:/dev/ttyS0, /dev/input/event0                             │
│                                                                           │
│  2. 块设备 (Block Device)                                                 │
│     - 按块(通常512字节或4KB)访问                                        │
│     - 例如:硬盘、U盘、SD卡                                               │
│     - 设备文件:/dev/sda, /dev/mmcblk0                                    │
│                                                                           │
│  3. 网络设备 (Network Device)                                             │
│     - 处理网络数据包                                                      │
│     - 例如:以太网卡、WiFi                                                │
│     - 没有设备文件,通过 socket 访问                                      │
│                                                                           │
│  对于嵌入式开发,最常接触的是字符设备!                                   │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

10.2 第一个内核模块

10.2.1 Hello World 模块

c
/* hello.c - 最简单的内核模块 */

#include <linux/module.h>   /* 所有模块都需要 */
#include <linux/kernel.h>   /* printk() 函数 */
#include <linux/init.h>     /* __init 和 __exit 宏 */

/*
 * 模块加载时执行的函数
 * __init 表示这个函数只在初始化时使用,之后可以释放内存
 */
static int __init hello_init(void)
{
    /*
     * printk 是内核的打印函数(不是 printf!)
     * KERN_INFO 是日志级别
     */
    printk(KERN_INFO "Hello, Kernel World!\n");
    return 0;  /* 返回 0 表示成功 */
}

/*
 * 模块卸载时执行的函数
 * __exit 表示只在卸载时使用
 */
static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye, Kernel World!\n");
}

/* 注册初始化和退出函数 */
module_init(hello_init);
module_exit(hello_exit);

/* 模块信息 */
MODULE_LICENSE("GPL");              /* 许可证,必须有 */
MODULE_AUTHOR("Your Name");         /* 作者 */
MODULE_DESCRIPTION("A simple hello module");  /* 描述 */
MODULE_VERSION("1.0");              /* 版本 */

10.2.2 Makefile

makefile
# 内核模块的 Makefile

# 模块名(hello.c -> hello.ko)
obj-m := hello.o

# 内核源码路径(根据你的实际路径修改)
KERNEL_DIR := /home/user/android/s905x5/common/common14-5.15

# 当前目录
PWD := $(shell pwd)

# 交叉编译器前缀
CROSS_COMPILE := aarch64-linux-gnu-

# 目标架构
ARCH := arm64

all:
	make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules

clean:
	make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) clean

10.2.3 编译和测试

bash
# 编译模块
make

# 生成 hello.ko 文件

# 推送到设备
adb push hello.ko /data/local/tmp/

# 在设备上加载模块
adb shell
su
cd /data/local/tmp
insmod hello.ko          # 加载模块
dmesg | tail             # 查看内核日志,应该看到 "Hello, Kernel World!"

# 查看已加载的模块
lsmod | grep hello

# 卸载模块
rmmod hello
dmesg | tail             # 应该看到 "Goodbye, Kernel World!"

10.3 字符设备驱动

10.3.1 字符设备驱动框架

c
/* mychar.c - 简单的字符设备驱动 */

#include <linux/module.h>
#include <linux/fs.h>           /* file_operations */
#include <linux/cdev.h>         /* cdev 结构 */
#include <linux/uaccess.h>      /* copy_to_user, copy_from_user */

#define DEVICE_NAME "mychar"
#define BUFFER_SIZE 1024

static dev_t dev_num;           /* 设备号 */
static struct cdev my_cdev;     /* 字符设备结构 */
static struct class *my_class;  /* 设备类 */
static char buffer[BUFFER_SIZE];/* 数据缓冲区 */
static int buffer_len = 0;      /* 缓冲区数据长度 */

/*
 * 打开设备时调用
 */
static int my_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "mychar: 设备被打开\n");
    return 0;
}

/*
 * 关闭设备时调用
 */
static int my_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "mychar: 设备被关闭\n");
    return 0;
}

/*
 * 读取设备时调用
 * 用户程序调用 read() 时会执行这个函数
 */
static ssize_t my_read(struct file *file, char __user *user_buf,
                       size_t count, loff_t *offset)
{
    int bytes_to_read;

    /* 计算要读取的字节数 */
    bytes_to_read = min((int)count, buffer_len);

    if (bytes_to_read == 0) {
        return 0;  /* 没有数据可读 */
    }

    /* 把数据从内核空间拷贝到用户空间 */
    if (copy_to_user(user_buf, buffer, bytes_to_read)) {
        return -EFAULT;  /* 拷贝失败 */
    }

    printk(KERN_INFO "mychar: 读取了 %d 字节\n", bytes_to_read);
    return bytes_to_read;
}

/*
 * 写入设备时调用
 * 用户程序调用 write() 时会执行这个函数
 */
static ssize_t my_write(struct file *file, const char __user *user_buf,
                        size_t count, loff_t *offset)
{
    int bytes_to_write;

    /* 计算要写入的字节数 */
    bytes_to_write = min((int)count, BUFFER_SIZE - 1);

    /* 把数据从用户空间拷贝到内核空间 */
    if (copy_from_user(buffer, user_buf, bytes_to_write)) {
        return -EFAULT;  /* 拷贝失败 */
    }

    buffer[bytes_to_write] = '\0';
    buffer_len = bytes_to_write;

    printk(KERN_INFO "mychar: 写入了 %d 字节: %s\n", bytes_to_write, buffer);
    return bytes_to_write;
}

/*
 * 文件操作结构体 - 把我们的函数和系统调用关联起来
 */
static struct file_operations my_fops = {
    .owner   = THIS_MODULE,
    .open    = my_open,
    .release = my_release,
    .read    = my_read,
    .write   = my_write,
};

/*
 * 模块初始化函数
 */
static int __init mychar_init(void)
{
    int ret;

    /* 1. 分配设备号 */
    ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "mychar: 分配设备号失败\n");
        return ret;
    }
    printk(KERN_INFO "mychar: 设备号 %d:%d\n", MAJOR(dev_num), MINOR(dev_num));

    /* 2. 初始化 cdev 结构 */
    cdev_init(&my_cdev, &my_fops);
    my_cdev.owner = THIS_MODULE;

    /* 3. 添加 cdev 到系统 */
    ret = cdev_add(&my_cdev, dev_num, 1);
    if (ret < 0) {
        unregister_chrdev_region(dev_num, 1);
        return ret;
    }

    /* 4. 创建设备类(用于自动创建设备节点)*/
    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(my_class)) {
        cdev_del(&my_cdev);
        unregister_chrdev_region(dev_num, 1);
        return PTR_ERR(my_class);
    }

    /* 5. 创建设备节点 /dev/mychar */
    device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME);

    printk(KERN_INFO "mychar: 驱动加载成功\n");
    return 0;
}

/*
 * 模块退出函数
 */
static void __exit mychar_exit(void)
{
    device_destroy(my_class, dev_num);
    class_destroy(my_class);
    cdev_del(&my_cdev);
    unregister_chrdev_region(dev_num, 1);
    printk(KERN_INFO "mychar: 驱动卸载成功\n");
}

module_init(mychar_init);
module_exit(mychar_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple char device driver");

10.3.2 测试字符设备驱动

bash
# 加载驱动
insmod mychar.ko

# 查看设备节点是否创建
ls -l /dev/mychar

# 写入数据
echo "Hello, Driver!" > /dev/mychar

# 读取数据
cat /dev/mychar

# 使用 C 程序测试
c
/* test_mychar.c - 测试程序 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd;
    char write_buf[] = "Test message from user space";
    char read_buf[100] = {0};

    /* 打开设备 */
    fd = open("/dev/mychar", O_RDWR);
    if (fd < 0) {
        perror("打开设备失败");
        return -1;
    }

    /* 写入数据 */
    write(fd, write_buf, strlen(write_buf));
    printf("写入: %s\n", write_buf);

    /* 读取数据 */
    read(fd, read_buf, sizeof(read_buf));
    printf("读取: %s\n", read_buf);

    /* 关闭设备 */
    close(fd);
    return 0;
}

10.4 平台设备驱动(重点)

10.4.1 平台驱动模型简介

┌───────────────────────────────────────────────────────────────────────────┐
│                           平台驱动模型                                    │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  为什么需要平台驱动?                                                     │
│                                                                           │
│  传统方式:硬件信息硬编码在驱动中                                         │
│  ┌─────────────────────────────────────┐                                  │
│  │ #define REG_BASE  0xFE001000       │  每换一个板子                     │
│  │ #define IRQ_NUM   32               │  都要改代码                       │
│  │ #define CLK_RATE  24000000         │  重新编译                         │
│  └─────────────────────────────────────┘                                  │
│                                                                           │
│  平台驱动:硬件信息从设备树获取                                           │
│  ┌─────────────────────────┐    ┌─────────────────────────┐               │
│  │     设备树 (DTS)        │    │     平台驱动            │               │
│  │                         │    │                         │               │
│  │ my_device {             │    │ static struct           │               │
│  │   compatible =          │◀──▶│ platform_driver = {     │               │
│  │     "vendor,my-dev";    │    │   .probe = my_probe,    │               │
│  │   reg = <...>;          │    │   .driver = {           │               │
│  │   interrupts = <...>;   │    │     .of_match_table =   │               │
│  │ };                      │    │       my_of_match,      │               │
│  └─────────────────────────┘    │   },                    │               │
│                                 │ };                      │               │
│  换板子只需改设备树              └─────────────────────────┘               │
│  驱动代码不用变                                                           │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

10.4.2 平台驱动框架代码

c
/* my_platform.c - 平台驱动示例 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/io.h>

/*
 * probe 函数 - 当设备树中的设备与驱动匹配时调用
 * 这是初始化设备的地方
 */
static int my_probe(struct platform_device *pdev)
{
    struct resource *res;
    void __iomem *base;
    int irq;
    u32 my_property;

    printk(KERN_INFO "my_platform: probe 被调用\n");

    /* 从设备树获取寄存器资源 */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "获取内存资源失败\n");
        return -ENODEV;
    }
    printk(KERN_INFO "寄存器地址: 0x%lx, 大小: 0x%lx\n",
           (unsigned long)res->start, (unsigned long)resource_size(res));

    /* 映射寄存器地址到虚拟地址 */
    base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(base)) {
        return PTR_ERR(base);
    }

    /* 从设备树获取中断号 */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_warn(&pdev->dev, "没有找到中断资源\n");
    } else {
        printk(KERN_INFO "中断号: %d\n", irq);
    }

    /* 从设备树读取自定义属性 */
    if (of_property_read_u32(pdev->dev.of_node, "my-property", &my_property)) {
        dev_warn(&pdev->dev, "没有找到 my-property\n");
        my_property = 0;  /* 默认值 */
    } else {
        printk(KERN_INFO "my-property = %d\n", my_property);
    }

    /* TODO: 初始化硬件... */

    return 0;
}

/*
 * remove 函数 - 设备被移除时调用
 */
static int my_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform: remove 被调用\n");
    /* TODO: 清理资源... */
    return 0;
}

/*
 * 设备树匹配表 - 定义这个驱动支持哪些设备
 * compatible 字符串必须和设备树中的一致
 */
static const struct of_device_id my_of_match[] = {
    { .compatible = "myvendor,my-device", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);

/*
 * 平台驱动结构体
 */
static struct platform_driver my_driver = {
    .probe  = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my-platform-driver",
        .of_match_table = my_of_match,
    },
};

/* 简化的模块注册宏 */
module_platform_driver(my_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple platform driver");

10.4.3 对应的设备树节点

dts
/* 在设备树中添加设备节点 */

my_device: my_device@fe001000 {
    compatible = "myvendor,my-device";  /* 必须和驱动中的一致 */
    reg = <0x0 0xfe001000 0x0 0x100>;   /* 寄存器地址和大小 */
    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;  /* 中断 */
    my-property = <12345>;              /* 自定义属性 */
    status = "okay";                    /* 启用设备 */
};

10.5 常见的内核 API

c
/* ========== 内存操作 ========== */

/* 动态分配内存(小块) */
void *ptr = kmalloc(size, GFP_KERNEL);
kfree(ptr);

/* 分配并清零 */
void *ptr = kzalloc(size, GFP_KERNEL);

/* 设备管理的内存分配(驱动卸载时自动释放) */
void *ptr = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
void *ptr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);

/* ========== IO 操作 ========== */

/* 映射物理地址到虚拟地址 */
void __iomem *base = ioremap(phys_addr, size);
iounmap(base);

/* 设备管理版本(推荐) */
void __iomem *base = devm_ioremap_resource(&pdev->dev, res);

/* 读写映射后的地址 */
u32 val = readl(base + offset);      /* 读 32 位 */
writel(val, base + offset);          /* 写 32 位 */
u16 val = readw(base + offset);      /* 读 16 位 */
writew(val, base + offset);          /* 写 16 位 */
u8 val = readb(base + offset);       /* 读 8 位 */
writeb(val, base + offset);          /* 写 8 位 */

/* ========== 延时 ========== */
mdelay(ms);      /* 毫秒级忙等待延时 */
udelay(us);      /* 微秒级忙等待延时 */
msleep(ms);      /* 毫秒级睡眠延时(可被打断) */
usleep_range(min_us, max_us);  /* 微秒级睡眠(推荐) */

/* ========== 打印日志 ========== */
printk(KERN_ERR "错误信息\n");
printk(KERN_WARNING "警告信息\n");
printk(KERN_INFO "一般信息\n");
printk(KERN_DEBUG "调试信息\n");

/* 推荐使用 dev_* 系列(会自动加上设备名) */
dev_err(&pdev->dev, "错误: %d\n", err);
dev_warn(&pdev->dev, "警告\n");
dev_info(&pdev->dev, "信息\n");
dev_dbg(&pdev->dev, "调试\n");

/* ========== 中断 ========== */
/* 注册中断处理函数 */
int ret = request_irq(irq, my_handler, IRQF_TRIGGER_RISING, "my_irq", data);
free_irq(irq, data);

/* 设备管理版本(推荐) */
int ret = devm_request_irq(&pdev->dev, irq, my_handler,
                           IRQF_TRIGGER_RISING, "my_irq", data);

/* 中断处理函数 */
static irqreturn_t my_handler(int irq, void *data)
{
    /* 处理中断 */
    return IRQ_HANDLED;
}

10.6 第六阶段检验


11. 第七阶段:设备树 (Device Tree)

📚 详细文档: 设备树开发指南

目标:掌握设备树语法,能够修改设备树配置硬件

11.1 设备树基础

11.1.1 什么是设备树?

┌───────────────────────────────────────────────────────────────────────────┐
│                             设备树简介                                    │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  设备树(Device Tree)是一种描述硬件的数据结构                            │
│                                                                           │
│  打个比方:                                                               │
│  - 设备树就像是硬件的"配置文件"或"说明书"                                 │
│  - 告诉 Linux 内核这个板子上有什么硬件,地址是多少,怎么连接的            │
│                                                                           │
│  为什么需要设备树?                                                       │
│  - 以前:硬件信息写死在代码里,换板子就要改代码、重新编译内核             │
│  - 现在:硬件信息写在设备树里,换板子只需要换设备树文件                   │
│                                                                           │
│  设备树文件类型:                                                         │
│  - .dts (Device Tree Source):源文件,人可读                              │
│  - .dtsi (Device Tree Source Include):被包含的源文件                     │
│  - .dtb (Device Tree Blob):编译后的二进制文件,给内核用                  │
│                                                                           │
│  工作流程:                                                               │
│  ┌─────────┐   编译    ┌─────────┐   加载    ┌─────────┐                 │
│  │  .dts   │ ───────▶ │  .dtb   │ ───────▶ │  内核   │                 │
│  │  源文件 │   (dtc)   │  二进制 │          │         │                 │
│  └─────────┘          └─────────┘          └─────────┘                 │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

11.1.2 设备树语法详解

dts
/* 设备树基本语法示例 */

/dts-v1/;                       // 版本声明(必须在第一行)

// 可以包含其他文件
#include "base.dtsi"

/ {                              // 根节点,用 / 表示
    // ========== 根节点属性 ==========
    model = "My Board Name";     // 板子型号(描述性字符串)
    compatible = "vendor,board"; // 兼容性字符串

    // 地址相关配置(影响子节点的 reg 属性解析)
    #address-cells = <2>;        // 地址占用 2 个 32 位单元(64位地址)
    #size-cells = <2>;           // 大小占用 2 个 32 位单元

    // ========== 子节点 ==========
    // 格式:[标签:] 节点名[@地址] { ... }

    // 示例1:简单节点
    chosen {
        // chosen 节点用于传递参数给内核
        bootargs = "console=ttyS0,115200";
    };

    // 示例2:内存节点
    memory@0 {
        device_type = "memory";
        reg = <0x0 0x00000000 0x0 0x80000000>;  // 起始地址 0,大小 2GB
        //     ^高32位^ ^低32位^  ^高32位^ ^低32位^
    };

    // 示例3:带标签的设备节点
    uart0: serial@fe001000 {     // uart0 是标签,serial 是节点名
        compatible = "vendor,uart";   // 用于匹配驱动
        reg = <0x0 0xfe001000 0x0 0x100>;  // 寄存器地址和大小
        interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;  // 中断
        clocks = <&clk_uart>;         // 引用时钟节点
        clock-names = "uart_clk";     // 时钟名称
        status = "okay";              // 启用状态
    };

    // 示例4:GPIO 控制器节点
    gpio: gpio@fe004000 {
        compatible = "vendor,gpio";
        reg = <0x0 0xfe004000 0x0 0x100>;
        gpio-controller;              // 标记这是 GPIO 控制器
        #gpio-cells = <2>;            // 引用时需要 2 个参数
    };

    // 示例5:使用 GPIO 的 LED 节点
    leds {
        compatible = "gpio-leds";

        led1 {
            label = "power";
            gpios = <&gpio 5 GPIO_ACTIVE_HIGH>;  // 引用 gpio 节点的第5个引脚
            default-state = "on";
        };
    };
};

// 在根节点外修改已有节点(Overlay 语法)
&uart0 {                         // & 表示引用已存在的节点
    status = "disabled";         // 禁用这个串口
};

11.2 常用属性详解

┌───────────────────────────────────────────────────────────────────────────┐
│                           常用属性参考表                                  │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  属性名              说明                          示例                   │
│  ───────────────────────────────────────────────────────────────────────  │
│  compatible         兼容性字符串,用于             "amlogic,meson-uart"   │
│                     匹配驱动                                              │
│                                                                           │
│  reg                寄存器地址和大小               <0x0 0xfe001000        │
│                     格式受 #address-cells          0x0 0x100>             │
│                     和 #size-cells 影响                                   │
│                                                                           │
│  interrupts         中断配置                       <GIC_SPI 10            │
│                                                    IRQ_TYPE_LEVEL_HIGH>   │
│                                                                           │
│  clocks             时钟引用                       <&clkc CLKID_UART>     │
│                                                                           │
│  clock-names        时钟名称(对应 clocks)        "core", "bus"          │
│                                                                           │
│  resets             复位信号引用                   <&reset RESET_UART>    │
│                                                                           │
│  pinctrl-0          引脚配置引用                   <&uart_pins>           │
│  pinctrl-names      引脚配置名称                   "default"              │
│                                                                           │
│  status             设备状态                       "okay" 或 "disabled"   │
│                     okay=启用, disabled=禁用                              │
│                                                                           │
│  #address-cells     子节点地址占用的 cell 数       <1> 或 <2>             │
│  #size-cells        子节点大小占用的 cell 数       <1> 或 <2>             │
│                                                                           │
│  phandle            节点句柄(自动生成)           <0x01>                 │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

11.3 S905X5 设备树结构

arch/arm64/boot/dts/amlogic/

├── meson-s7d.dtsi                    # S7D SoC 基础定义(所有 S7D 芯片共用)
│   ├── CPU 配置
│   ├── 中断控制器
│   ├── 时钟配置
│   ├── 各种外设定义(UART, I2C, SPI, USB...)
│   └── ...

├── meson-s7d-bm201.dtsi              # BM201 参考板的公共定义
│   ├── 内存配置
│   ├── 启用的外设
│   ├── 引脚配置
│   └── ...

├── s7d_s905x5m_bm201.dts             # ★ 最终产品配置(2GB DDR)
│   ├── #include "meson-s7d-bm201.dtsi"
│   ├── 产品特有配置
│   └── ...

├── s7d_s905x5m_bm201-1g.dts          # 1GB DDR 版本
└── s7d_s905x5m_bm201-4g.dts          # 4GB DDR 版本

文件包含关系(从下到上):
┌───────────────────────────────────────────────────────────────────────────┐
│  s7d_s905x5m_bm201.dts                                                    │
│    └── #include "meson-s7d-bm201.dtsi"                                    │
│          └── #include "meson-s7d.dtsi"                                    │
│                └── #include "meson-s7-base.dtsi"                          │
│                      └── (ARM64 和 Amlogic 基础定义)                      │
└───────────────────────────────────────────────────────────────────────────┘

11.4 常见修改场景

11.4.1 启用/禁用设备

dts
/* 禁用设备 - 方式1:在 DTS 文件中直接修改 */
&uart_b {
    status = "disabled";
};

/* 启用设备 */
&uart_a {
    status = "okay";
};

11.4.2 添加 GPIO LED

dts
/* 在 DTS 中添加 LED 节点 */
/ {
    leds {
        compatible = "gpio-leds";

        power_led {
            label = "power";
            gpios = <&gpio GPIOH_5 GPIO_ACTIVE_HIGH>;
            default-state = "on";
        };

        status_led {
            label = "status";
            gpios = <&gpio GPIOH_6 GPIO_ACTIVE_LOW>;
            linux,default-trigger = "heartbeat";
        };
    };
};

11.4.3 添加 I2C 设备

dts
/* 启用 I2C 总线并添加设备 */
&i2c0 {
    status = "okay";
    pinctrl-0 = <&i2c0_pins>;
    pinctrl-names = "default";
    clock-frequency = <400000>;   /* 400KHz */

    /* 添加 EEPROM */
    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;             /* I2C 地址 */
    };

    /* 添加 RTC */
    rtc@68 {
        compatible = "dallas,ds1307";
        reg = <0x68>;
    };
};

11.4.4 修改内存配置

dts
/* 修改内存大小 */
/ {
    memory@0 {
        device_type = "memory";
        reg = <0x0 0x00000000 0x0 0x80000000>;  /* 2GB */
        /* 改为 1GB: */
        /* reg = <0x0 0x00000000 0x0 0x40000000>; */
        /* 改为 4GB: */
        /* reg = <0x0 0x00000000 0x1 0x00000000>; */
    };
};

11.5 设备树调试

bash
# 编译设备树
cd common/common14-5.15
make ARCH=arm64 dtbs

# 编译后的文件位置
ls arch/arm64/boot/dts/amlogic/*.dtb

# 反编译 DTB 为 DTS(查看最终合并结果)
dtc -I dtb -O dts -o output.dts arch/arm64/boot/dts/amlogic/s7d_s905x5m_bm201.dtb

# 在设备上查看设备树
adb shell ls /proc/device-tree/
adb shell cat /proc/device-tree/compatible
adb shell cat /proc/device-tree/model

# 查看设备树节点
adb shell ls /sys/firmware/devicetree/base/

# 查看某个节点的属性
adb shell xxd /proc/device-tree/serial@fe001000/reg

11.6 第七阶段检验


12. 第八阶段:Android Init 系统

📚 详细文档: Android Init 系统开发指南 | Android 启动流程分析

目标:理解 Android 启动流程,掌握 init.rc 配置

12.1 Init 进程简介

┌───────────────────────────────────────────────────────────────────────────┐
│                          Android Init 进程                                │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  init 是 Android 的第一个用户空间进程(PID = 1)                          │
│                                                                           │
│  主要职责:                                                               │
│  1. 挂载文件系统(/dev, /proc, /sys 等)                                  │
│  2. 初始化 SELinux 安全策略                                               │
│  3. 解析并执行 init.rc 配置文件                                           │
│  4. 启动属性服务(property service)                                      │
│  5. 启动各种系统服务                                                      │
│  6. 监控服务状态,必要时重启崩溃的服务                                    │
│                                                                           │
│  init.rc 是什么?                                                         │
│  - 类似于"启动脚本"                                                       │
│  - 定义开机时要做什么、启动哪些服务                                       │
│  - 使用特殊的语法(不是 Shell 脚本!)                                    │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

12.2 init.rc 语法详解

12.2.1 基本结构

rc
# init.rc 由三种语句组成:

# 1. Action(动作)- 在特定触发条件下执行的命令集合
on <trigger>
    <command>
    <command>
    ...

# 2. Service(服务)- 定义后台运行的程序
service <name> <pathname> [<argument>]*
    <option>
    <option>
    ...

# 3. Import(导入)- 包含其他 rc 文件
import /path/to/other.rc

12.2.2 常用触发器 (Trigger)

rc
# ========== 系统阶段触发器 ==========
on early-init          # 早期初始化阶段
on init                # 基本初始化阶段
on late-init           # 后期初始化阶段
on boot                # 启动阶段
on post-fs             # 文件系统挂载后
on post-fs-data        # /data 分区挂载后

# ========== 属性触发器 ==========
on property:ro.boot_completed=1     # 属性值变为指定值时触发
on property:sys.boot_completed=1

# ========== 组合触发器 ==========
on property:sys.usb.config=mtp && property:sys.usb.configfs=1

12.2.3 常用命令

rc
# ========== 文件/目录操作 ==========
mkdir /data/mydir 0755 system system    # 创建目录
chmod 0660 /dev/mydev                   # 修改权限
chown system system /dev/mydev          # 修改所有者
write /sys/class/gpio/export 100        # 写入文件
copy /src/file /dest/file               # 复制文件
symlink /data/file /system/link         # 创建符号链接
rm /data/temp_file                      # 删除文件

# ========== 属性操作 ==========
setprop ro.my.property value            # 设置属性

# ========== 服务控制 ==========
start my_service                        # 启动服务
stop my_service                         # 停止服务
restart my_service                      # 重启服务
enable my_service                       # 启用服务

# ========== 内核模块 ==========
insmod /vendor/lib/modules/mymodule.ko  # 加载内核模块

# ========== 执行命令 ==========
exec /system/bin/mycommand              # 执行命令并等待完成
exec_background /system/bin/mycommand   # 后台执行

# ========== 挂载 ==========
mount ext4 /dev/block/xxx /mnt          # 挂载分区

12.2.4 服务选项

rc
service my_daemon /vendor/bin/my_daemon arg1 arg2
    # ========== 身份设置 ==========
    user system                  # 以 system 用户运行
    group system audio video     # 用户组

    # ========== 运行类型 ==========
    class main                   # 服务类别(main, core, hal 等)
    oneshot                      # 只运行一次,不重启
    disabled                     # 默认禁用,需要手动 start

    # ========== 重启控制 ==========
    critical                     # 关键服务,崩溃4次就重启设备
    onrestart restart other_svc  # 本服务重启时,也重启其他服务

    # ========== 能力 ==========
    capabilities NET_ADMIN       # Linux capabilities

    # ========== Socket ==========
    socket my_socket stream 0660 system system  # 创建 socket

    # ========== 文件描述符 ==========
    writepid /dev/cpuset/system-background/tasks

12.3 实际配置示例

12.3.1 添加开机执行的命令

rc
# 在开机完成后执行一些命令
on property:sys.boot_completed=1
    # 设置 CPU 性能模式
    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor performance

    # 启动自定义服务
    start my_custom_service

    # 设置属性
    setprop vendor.my.feature.enabled 1

12.3.2 添加自定义服务

rc
# 定义一个自定义服务
service my_daemon /vendor/bin/my_daemon
    class main
    user system
    group system
    disabled                    # 默认不启动

# 在特定条件下启动
on property:persist.vendor.my.daemon.enable=1
    start my_daemon

on property:persist.vendor.my.daemon.enable=0
    stop my_daemon

12.3.3 开机执行一次性脚本

rc
# 定义一次性执行的服务
service init_once /vendor/bin/init_once.sh
    class main
    user root
    group root
    oneshot                     # 只执行一次
    disabled

# 在 /data 分区挂载后执行
on post-fs-data
    start init_once

12.4 属性系统

12.4.1 属性类型

┌───────────────────────────────────────────────────────────────────────────┐
│                            属性前缀说明                                   │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  前缀            特点                          示例                       │
│  ─────────────────────────────────────────────────────────────────────── │
│  ro.*           只读,只能在启动时设置一次      ro.build.version.sdk      │
│  persist.*      持久化,重启后保留              persist.sys.timezone      │
│  sys.*          系统属性                        sys.boot_completed        │
│  ctl.*          控制属性,用于启停服务          ctl.start, ctl.stop       │
│  vendor.*       厂商定义的属性                  vendor.xxx.xxx            │
│  init.*         init 进程使用                   init.svc.<name>           │
│                                                                           │
│  service 状态属性:init.svc.<service_name>                                │
│  - stopped: 服务已停止                                                    │
│  - starting: 服务正在启动                                                 │
│  - running: 服务正在运行                                                  │
│  - restarting: 服务正在重启                                               │
│  - stopping: 服务正在停止                                                 │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

12.4.2 属性操作命令

bash
# 获取属性
adb shell getprop ro.build.version.sdk
adb shell getprop                           # 获取所有属性

# 设置属性(只能设置非 ro.* 属性)
adb shell setprop persist.my.property value

# 监控属性变化
adb shell watchprops

# 通过属性控制服务
adb shell setprop ctl.start my_service
adb shell setprop ctl.stop my_service

12.5 Amlogic init.rc 文件结构

device/amlogic/ross/
├── init.amlogic.rc              # ★ Amlogic 主配置文件
├── init.amlogic.usb.rc          # USB 相关配置
├── init.amlogic.wifi.rc         # WiFi 配置
├── init.amlogic.board.rc        # 板级配置
├── ueventd.amlogic.rc           # 设备节点权限配置
└── fstab.amlogic                # 分区挂载表

system/core/rootdir/
├── init.rc                      # ★ Android 核心 init.rc
└── init.zygote64.rc            # Zygote 配置

# 加载顺序
init.rc
  └── import init.amlogic.rc
        ├── import init.amlogic.usb.rc
        ├── import init.amlogic.wifi.rc
        └── import init.amlogic.board.rc

12.6 ueventd 配置(设备节点权限)

rc
# ueventd.amlogic.rc
# 格式: <设备路径> <权限> <用户> <用户组>

# 视频设备
/dev/amvideo            0666    system  system
/dev/ppmgr              0666    system  system
/dev/amstream_*         0666    system  system

# 音频设备
/dev/audiodsp0          0660    system  audio
/dev/snd/*              0660    system  audio

# 串口
/dev/ttyS*              0666    system  system

# I2C
/dev/i2c-*              0660    system  system

# GPIO
/dev/gpiochip*          0660    system  system

12.7 第八阶段检验


13. 第九阶段:HAL 硬件抽象层

📚 详细文档: HAL 硬件抽象层开发指南

目标:理解 HAL 的作用,能阅读和修改 HAL 代码

13.1 HAL 简介

┌───────────────────────────────────────────────────────────────────────────┐
│                           HAL 是什么?                                    │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  HAL (Hardware Abstraction Layer) = 硬件抽象层                            │
│                                                                           │
│  作用:                                                                   │
│  - 定义统一的硬件接口                                                     │
│  - 隔离 Android 框架和具体硬件                                            │
│  - 不同硬件只需实现相同接口                                               │
│                                                                           │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                      Android Framework                            │   │
│  │              (不需要知道具体硬件是怎么工作的)                      │   │
│  └─────────────────────────────┬─────────────────────────────────────┘   │
│                                │ 统一的 HAL 接口                          │
│                                ▼                                          │
│  ┌──────────────┬──────────────┬──────────────┬──────────────┐           │
│  │ Audio HAL    │ Camera HAL   │ Display HAL  │ Sensors HAL  │           │
│  │              │              │              │              │           │
│  │ 厂商A实现    │ 厂商A实现    │ 厂商A实现    │ 厂商A实现    │           │
│  │ 或           │ 或           │ 或           │ 或           │           │
│  │ 厂商B实现    │ 厂商B实现    │ 厂商B实现    │ 厂商B实现    │           │
│  └──────────────┴──────────────┴──────────────┴──────────────┘           │
│                                │                                          │
│                                ▼ 调用驱动                                 │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                       Linux Kernel 驱动                           │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

13.2 HAL 演进历史

┌───────────────────────────────────────────────────────────────────────────┐
│                            HAL 演进史                                     │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  Android 7.x 及更早         Android 8.0 ~ 13         Android 14+          │
│  ─────────────────         ──────────────────       ──────────────        │
│                                                                           │
│  Legacy HAL                 Binderized HAL           AIDL HAL             │
│  (传统 HAL)                 (HIDL)                                        │
│                                                                           │
│  ┌───────────┐             ┌───────────┐           ┌───────────┐         │
│  │ Framework │             │ Framework │           │ Framework │         │
│  └─────┬─────┘             └─────┬─────┘           └─────┬─────┘         │
│        │                         │                       │                │
│        │ dlopen                  │ Binder IPC            │ Binder IPC     │
│        │ (同进程)                │ (跨进程)              │ (跨进程)        │
│        ▼                         ▼                       ▼                │
│  ┌───────────┐             ┌───────────┐           ┌───────────┐         │
│  │ HAL .so   │             │HAL Service│           │HAL Service│         │
│  │ (动态库)  │             │ (独立进程)│           │ (独立进程)│         │
│  └───────────┘             └───────────┘           └───────────┘         │
│                                                                           │
│  问题:                     改进:                   改进:               │
│  HAL崩溃会导致              进程隔离                 更现代的接口语言     │
│  Framework崩溃              可独立更新               性能更好             │
│                             更好的安全性             统一到AIDL           │
│                                                                           │
│  注意:Android 14 上,Amlogic 的很多 HAL 还是用 HIDL 实现的               │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

13.3 Amlogic HAL 代码结构

hardware/amlogic/
├── audio/                       # ★ 音频 HAL
│   ├── audio_hal/               # 主要实现
│   │   ├── audio_hw.c           # 核心代码
│   │   ├── audio_hw.h
│   │   └── Android.bp
│   └── audio_policy/            # 音频策略

├── gralloc/                     # 图形内存分配 HAL

├── hwcomposer/                  # ★ 显示合成 HAL
│   ├── hwc2/                    # HWC2 实现
│   └── common/

├── hdmicec/                     # HDMI CEC HAL

├── tv_input/                    # TV 输入 HAL

├── media/                       # 媒体相关
│   └── amvdec/                  # 视频解码

├── thermal/                     # 热管理 HAL

└── power/                       # 电源管理 HAL

13.4 Audio HAL 详解

13.4.1 Audio HAL 架构

┌───────────────────────────────────────────────────────────────────────────┐
│                          Audio HAL 架构                                   │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                    应用 (播放音乐/视频)                           │   │
│  └─────────────────────────────┬─────────────────────────────────────┘   │
│                                │ MediaPlayer API                          │
│                                ▼                                          │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                    AudioFlinger (Framework)                       │   │
│  │                    负责音频混合、路由                             │   │
│  └─────────────────────────────┬─────────────────────────────────────┘   │
│                                │ HIDL 接口                                │
│                                ▼                                          │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                      Audio HAL                                    │   │
│  │  ┌───────────────────────────────────────────────────────────┐   │   │
│  │  │                  audio_hw.c                                │   │   │
│  │  │                                                            │   │   │
│  │  │  adev_open()           - 打开音频设备                      │   │   │
│  │  │  adev_open_output_stream() - 打开输出流                    │   │   │
│  │  │  out_write()           - 写入音频数据                      │   │   │
│  │  │  adev_open_input_stream()  - 打开输入流(录音)            │   │   │
│  │  │  in_read()             - 读取音频数据                      │   │   │
│  │  │  adev_set_parameters() - 设置参数                          │   │   │
│  │  │                                                            │   │   │
│  │  └───────────────────────────────────────────────────────────┘   │   │
│  └─────────────────────────────┬─────────────────────────────────────┘   │
│                                │ 调用驱动                                 │
│                                ▼                                          │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                      ALSA 驱动                                    │   │
│  │                                                                   │   │
│  │  /dev/snd/pcmC0D0p   - 播放设备                                   │   │
│  │  /dev/snd/pcmC0D0c   - 录音设备                                   │   │
│  │  /dev/snd/controlC0  - 控制设备                                   │   │
│  │                                                                   │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

13.4.2 Audio HAL 关键代码

c
/* hardware/amlogic/audio/audio_hal/audio_hw.c */

/* 音频设备结构体 */
struct aml_audio_device {
    struct audio_hw_device hw_device;  /* 继承标准接口 */

    /* Amlogic 特有的成员 */
    int hdmi_format;
    int speaker_mute;
    // ...
};

/* 打开音频设备 */
static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
    struct aml_audio_device *adev;

    /* 分配设备结构 */
    adev = calloc(1, sizeof(struct aml_audio_device));

    /* 设置标准接口函数 */
    adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    adev->hw_device.common.module = (hw_module_t *)module;
    adev->hw_device.common.close = adev_close;

    adev->hw_device.init_check = adev_init_check;
    adev->hw_device.set_parameters = adev_set_parameters;
    adev->hw_device.open_output_stream = adev_open_output_stream;
    adev->hw_device.open_input_stream = adev_open_input_stream;
    // ...

    /* 初始化硬件 */
    // ...

    *device = &adev->hw_device.common;
    return 0;
}

/* 写入音频数据(播放) */
static ssize_t out_write(struct audio_stream_out *stream,
                         const void *buffer, size_t bytes)
{
    struct aml_stream_out *out = (struct aml_stream_out *)stream;

    /* 将数据写入 ALSA 设备 */
    int ret = pcm_write(out->pcm, buffer, bytes);

    if (ret != 0) {
        ALOGE("pcm_write failed: %s", pcm_get_error(out->pcm));
        return -1;
    }

    return bytes;
}

13.5 Display HAL (HWComposer) 详解

┌───────────────────────────────────────────────────────────────────────────┐
│                        HWComposer 工作流程                                │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  1. SurfaceFlinger 收集所有图层 (Layers)                                  │
│     - 状态栏图层                                                          │
│     - 应用窗口图层                                                        │
│     - 导航栏图层                                                          │
│     - 视频图层                                                            │
│                                                                           │
│  2. 调用 HWComposer.validateDisplay()                                     │
│     - HWC 决定每个图层用什么方式合成:                                    │
│       - DEVICE:用硬件合成(性能好)                                      │
│       - CLIENT:用 GPU 合成(通用性好)                                   │
│                                                                           │
│  3. 如果有 CLIENT 图层,SurfaceFlinger 用 GPU 合成                        │
│                                                                           │
│  4. 调用 HWComposer.presentDisplay()                                      │
│     - 显示最终合成结果                                                    │
│                                                                           │
│  Amlogic 特有:                                                           │
│  - OSD 图层:用于 Android UI                                              │
│  - Video 图层:用于视频播放(硬件解码直接输出)                           │
│  - HDMI TX:输出到电视                                                    │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

13.6 HAL 调试技巧

bash
# 查看 HAL 服务是否运行
adb shell ps -A | grep android.hardware

# 查看 HAL 日志
adb logcat | grep -i audio_hw
adb logcat | grep -i hwc

# 查看音频设备
adb shell cat /proc/asound/cards
adb shell cat /proc/asound/pcm

# 查看显示状态
adb shell dumpsys SurfaceFlinger
adb shell dumpsys display

# 查看 HAL 接口版本
adb shell lshal

13.7 第九阶段检验


14. 第十阶段:Android Framework

📚 详细文档: Android Framework 开发指南 | SELinux 安全策略指南

目标:了解 Android 框架层,能进行基本的系统定制

14.1 Framework 架构

┌───────────────────────────────────────────────────────────────────────────┐
│                        Android Framework 层                               │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  Framework 层是 Android 的核心,提供:                                    │
│  - 应用程序 API                                                           │
│  - 系统服务                                                               │
│  - 进程间通信机制                                                         │
│                                                                           │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                       system_server 进程                          │   │
│  │                                                                   │   │
│  │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │   │
│  │   │ Activity     │  │ Window       │  │ Package      │           │   │
│  │   │ Manager      │  │ Manager      │  │ Manager      │           │   │
│  │   │ Service      │  │ Service      │  │ Service      │           │   │
│  │   │              │  │              │  │              │           │   │
│  │   │ 管理Activity │  │ 管理窗口     │  │ 管理应用     │           │   │
│  │   │ 生命周期     │  │ 显示层级     │  │ 安装卸载     │           │   │
│  │   └──────────────┘  └──────────────┘  └──────────────┘           │   │
│  │                                                                   │   │
│  │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │   │
│  │   │ Power        │  │ Input        │  │ Display      │           │   │
│  │   │ Manager      │  │ Manager      │  │ Manager      │           │   │
│  │   │ Service      │  │ Service      │  │ Service      │           │   │
│  │   │              │  │              │  │              │           │   │
│  │   │ 管理电源     │  │ 管理输入     │  │ 管理显示器   │           │   │
│  │   │ 休眠唤醒     │  │ 按键触摸     │  │ 分辨率亮度   │           │   │
│  │   └──────────────┘  └──────────────┘  └──────────────┘           │   │
│  │                                                                   │   │
│  │   ... 还有很多其他服务 ...                                        │   │
│  │                                                                   │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                           │
│  其他系统进程:                                                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                    │
│  │ surfaceflinger│  │ audioserver  │  │ mediaserver  │                    │
│  │              │  │              │  │              │                    │
│  │ 图形合成     │  │ 音频混合     │  │ 媒体播放     │                    │
│  └──────────────┘  └──────────────┘  └──────────────┘                    │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

14.2 Binder IPC 机制

┌───────────────────────────────────────────────────────────────────────────┐
│                          Binder 通信原理                                  │
├───────────────────────────────────────────────────────────────────────────┤
│                                                                           │
│  Binder 是 Android 的进程间通信 (IPC) 机制                                │
│                                                                           │
│  为什么不用 Linux 原生 IPC?                                              │
│  - Socket:开销大,需要两次数据拷贝                                       │
│  - 共享内存:难以管理                                                     │
│  - Binder:只需一次数据拷贝,性能好,安全性高                             │
│                                                                           │
│  ┌───────────────┐                           ┌───────────────┐            │
│  │   App 进程    │                           │ system_server │            │
│  │               │                           │               │            │
│  │ ActivityMgr   │                           │ Activity      │            │
│  │  .getService()│                           │ ManagerService│            │
│  │   (Proxy)     │                           │  (实现)       │            │
│  └───────┬───────┘                           └───────▲───────┘            │
│          │                                           │                    │
│          │ transact()                      onTransact()                   │
│          │                                           │                    │
│          ▼                                           │                    │
│  ┌───────────────────────────────────────────────────────────────────┐   │
│  │                      /dev/binder                                  │   │
│  │                     (内核 Binder 驱动)                            │   │
│  │                                                                   │   │
│  │  - 进程间数据拷贝(内存映射,只需一次拷贝)                        │   │
│  │  - 安全检查(UID/PID 验证)                                       │   │
│  │  - 引用计数和生命周期管理                                         │   │
│  │                                                                   │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

14.3 常见定制场景

14.3.1 修改系统设置默认值

java
/* frameworks/base/packages/SettingsProvider/res/values/defaults.xml */

<!-- 修改默认亮度 -->
<integer name="def_screen_brightness">102</integer>

<!-- 修改默认音量 -->
<integer name="def_music_volume">10</integer>

<!-- 修改默认时区 -->
<string name="def_timezone">Asia/Shanghai</string>

14.3.2 添加系统属性

java
/* frameworks/base/core/java/android/os/Build.java */

// 添加自定义属性
public static final String CUSTOM_PROPERTY =
    SystemProperties.get("ro.vendor.custom.property", "default");

14.3.3 修改系统行为

java
/* 示例:修改默认 Launcher */
/* frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java */

// 在 resolveIntent 方法中修改默认应用解析逻辑

14.4 Framework 代码位置

frameworks/
├── base/                        # ★ 核心框架
│   ├── core/
│   │   ├── java/android/        # Android API
│   │   │   ├── app/             # Activity, Service, Application
│   │   │   ├── content/         # ContentProvider, Intent
│   │   │   ├── view/            # View 系统
│   │   │   ├── widget/          # 控件
│   │   │   └── os/              # 系统相关
│   │   ├── jni/                 # JNI 层
│   │   └── res/                 # 系统资源
│   ├── services/                # ★ 系统服务
│   │   └── core/java/com/android/server/
│   │       ├── am/              # ActivityManager
│   │       ├── wm/              # WindowManager
│   │       ├── pm/              # PackageManager
│   │       └── ...
│   └── packages/
│       └── SettingsProvider/    # 设置默认值

├── native/
│   └── services/
│       └── surfaceflinger/      # 图形合成

└── av/                          # 音视频
    ├── media/
    └── services/
        ├── audioflinger/        # 音频混合
        └── mediaserver/         # 媒体服务

14.5 Framework 调试

bash
# 查看系统服务状态
adb shell dumpsys activity           # Activity 状态
adb shell dumpsys window             # 窗口状态
adb shell dumpsys package            # 包管理
adb shell dumpsys power              # 电源管理
adb shell dumpsys input              # 输入状态

# 查看所有服务
adb shell service list

# 重启 system_server(相当于软重启)
adb shell stop
adb shell start

# 查看 Framework 日志
adb logcat -s ActivityManager
adb logcat -s WindowManager

14.6 第十阶段检验


15. 实践项目

📚 相关文档: 调试与性能优化指南 | 显示子系统指南 | 音频子系统指南

15.1 入门项目

序号项目涉及知识难度
1Hello World 内核模块内核模块开发★☆☆☆☆
2修改开机 LogoBootloader★☆☆☆☆
3添加开机动画init.rc★★☆☆☆
4GPIO LED 控制设备树、驱动★★☆☆☆
5自定义遥控器键值设备树、KeyLayout★★☆☆☆

15.2 进阶项目

序号项目涉及知识难度
1I2C 传感器驱动驱动、设备树、HAL★★★☆☆
2自定义系统服务Framework★★★☆☆
3预装应用编译系统★★☆☆☆
4音频通路调试Audio HAL★★★☆☆
5HDMI CEC 开发HAL、驱动★★★☆☆

15.3 高级项目

序号项目涉及知识难度
1自定义 LauncherFramework、应用开发★★★★☆
2视频硬解优化HAL、Codec★★★★☆
3新板子适配全栈知识★★★★★

16. 常见问题与解答

Q1: 编译时内存不足怎么办?

bash
# 减少并行任务数
make -j4   # 改用 4 个任务

# 增加 swap 空间
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 编辑 /etc/fstab 添加:
/swapfile none swap sw 0 0

Q2: adb devices 看不到设备怎么办?

bash
# 1. 检查 USB 连接
lsusb  # 应该能看到 Amlogic 设备

# 2. 检查 udev 规则
sudo vim /etc/udev/rules.d/51-android.rules
# 添加:
SUBSYSTEM=="usb", ATTR{idVendor}=="1b8e", MODE="0666"

# 3. 重启 udev
sudo udevadm control --reload-rules
sudo service udev restart

# 4. 重启 adb
adb kill-server
adb start-server
adb devices

Q3: 驱动加载后看不到设备节点?

bash
# 1. 检查模块是否加载成功
lsmod | grep your_module
dmesg | tail -20  # 查看是否有错误

# 2. 检查设备树是否匹配
cat /proc/device-tree/your_device/compatible

# 3. 检查 SELinux
getenforce  # 如果是 Enforcing
setenforce 0  # 临时关闭测试

Q4: 设备树修改后不生效?

bash
# 1. 确保重新编译了设备树
make dtbs

# 2. 确保烧录了新的 dtb
fastboot flash dtb xxx.dtb
# 或者重新烧录完整镜像

# 3. 检查设备树是否正确加载
adb shell cat /proc/device-tree/your_node/status

17. 学习资源推荐

📚 本项目文档索引: S905X5M Android 14 开发文档 - 包含所有子系统和开发指南

17.1 书籍推荐

阶段书名说明
Linux 基础《鸟哥的 Linux 私房菜》Linux 入门经典
C 语言《C Primer Plus》C 语言入门
内核开发《Linux 设备驱动程序》(LDD3)驱动开发必读
内核深入《深入理解 Linux 内核》内核原理
Android《深入理解 Android》系列Android 系统分析

17.2 在线资源

资源链接说明
Android 官方文档source.android.com官方权威
内核文档kernel.org/doc内核官方文档
eLinuxelinux.org嵌入式 Linux Wiki
Stack Overflowstackoverflow.com问答社区

17.3 开发工具

工具用途
VS Code代码编辑,推荐插件:C/C++, Remote-SSH
Source Insight代码阅读(付费)
Android StudioAndroid 应用开发
Wireshark网络抓包分析
Logic Analyzer硬件信号分析

附录:快速参考

常用路径

内容路径
内核源码common/common14-5.15/
设备树common/common14-5.15/arch/arm64/boot/dts/amlogic/
Amlogic 驱动common/common14-5.15/drivers/amlogic/
设备配置device/amlogic/ross/
HALhardware/amlogic/
Frameworkframeworks/base/
编译输出out/target/product/ross/

常用命令

bash
# 编译
source build/envsetup.sh && lunch ross-userdebug
make -j$(nproc)

# 调试
adb shell dmesg              # 内核日志
adb logcat                   # Android 日志
adb shell dumpsys            # 系统状态

# 文件操作
adb push local remote
adb pull remote local
adb shell

# 模块操作
insmod xxx.ko
rmmod xxx
lsmod

# 属性操作
getprop ro.build.version.sdk
setprop persist.xxx value

文档版本: 2.0 (新手友好版)更新日期: 2025-12-12目标平台: Amlogic S905X5M (S7D) Android 14目标读者: 零基础新手

上次更新于: