AI 技术博客
Android全栈10 分钟阅读4722

【全栈第20课】驱动→HAL→App 全链路 bring-up(阶段三收官)

Android 全栈工程师进阶教程 第20课。驱动→HAL→App 全链路 bring-up(阶段三收官)。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。

> 本文是《Android 全栈工程师进阶教程》系列第 20 课。完整 26 课见 GitHub 仓库

【全栈第20课】驱动→HAL→App 全链路 bring-up(阶段三收官)


0. 这节课你将搞懂什么

把 L9-L18 串成一条完整竖线:硬件 ← DTS ← 驱动 ← HAL ← framework ← App。理解接一颗新外设(以触控/sensor 为例)从硬件到 App 的完整 bring-up 流程。 学完你能回答:给我一颗新 sensor,我该按什么顺序把它接进系统、让 App 能用?HAL 怎么和驱动连起来(HAL 怎么读到驱动的数据)?完整一条"硬件→App"的链路上每一层各干什么?

1. 背景:bring-up 是 BSP 的看家本领

"bring-up" = 把一个新硬件从"焊在板上没人理"做到"App 能正常使用"。这是 BSP 工程师的核心交付。它需要把前面所有课的能力串起来——单独会写驱动、会写 HAL 不够,要能把整条链路打通。

2. 全局图:完整竖线(串 L9-L18)

App                                          ← L2/L12(getSystemService / 经 framework 调)
 │ Binder
Framework 服务 / HalController               ← L10(framework 调 HAL)
 │ Binder(system→vendor)
HAL(vendor 进程, IHelloHal)                  ← L9(AIDL HAL),L11(sepolicy)
 │ open("/dev/hello") + read/ioctl           ← 本课重点:HAL 怎么读驱动
Kernel Driver(字符/I2C 驱动)                 ← L14(字符)/L16(I2C)
 │ file_operations.read / regmap_read 读寄存器
 │ 中断 schedule_work 上报                     ← L17
硬件(I2C sensor IC)
 ▲ DTS 描述这块硬件, compatible 匹配驱动        ← L15
 ▲ 电源管理 息屏关 sensor                       ← L18

3. driver→HAL 的连接点(本课核心)

前面 L9 的 HAL 是空壳(返回固定字符串),L14-L18 的驱动是孤立的。本课讲它俩怎么连:HAL 不直接碰硬件寄存器,而是通过驱动暴露给用户空间的接口拿数据:

驱动暴露方式HAL 怎么读适用
字符设备 /dev/xxxopen + read/ioctl(L14)通用
sysfs /sys/class/xxx/valueread 这个文件简单状态/配置
input 子系统 /dev/input/eventX读 input event(标准)触控/按键
iio 子系统 /sys/bus/iio/...iio 标准接口传感器

HAL 读到原始数据后,通过 AIDL 上报给 framework。

示意(HAL 读字符设备节点):

// HAL 实现里(L9 的 HelloHal),从驱动节点读数据
int readSensorFromDriver(int* out) {
    int fd = open("/dev/hello", O_RDONLY);   // 打开 L14 驱动建的节点
    if (fd < 0) { LOG(ERROR) << "open /dev/hello 失败(驱动加载了吗?sepolicy 放行了吗?)"; return -1; }
    char buf[64] = {0};
    ssize_t n = read(fd, buf, sizeof(buf)-1); // 读驱动数据(驱动的 hello_read → copy_to_user)
    close(fd);
    if (n <= 0) return -1;
    *out = atoi(buf);
    return 0;
}

> HAL open /dev 节点需要 sepolicy 放行(L11):allow hal_hello_default hello_device:chr_file rw_file_perms;,否则 avc denied。

4. 前置准备

  • 依赖 L9-L18 全部(HAL、字符/I2C 驱动、中断、电源、sepolicy)。
  • 本课偏集成/串联,产出全链路文档 + HAL 读驱动节点示意。

5. 动手:一颗假 sensor 的 bring-up 八步(BSP 标准流程)

按这个顺序接一颗新硬件(每步对应一课):

  1. 看原理图:确认 IC 挂哪条 I2C、地址、中断脚 GPIO、供电 regulator。(硬件)
  2. DTS 描述硬件:i2c@48 节点,compatible/reg/interrupts/供电。(L15/L16)
  3. 写 kernel 驱动:i2c_driver,probe 读 CHIPID 验证通信。(L16)
  4. 中断上报:数据就绪中断 → workqueue 读寄存器 → 暴露 /dev 或 input/iio。(L17)
  5. 电源管理:suspend 关 sensor,resume 恢复。(L18)
  6. 驱动编 .ko 进 vendor,加进 modules.load 开机加载。(L13)
  7. 写 HAL:open 驱动节点 read 数据(本课);注册 + vintf + sepolicy。(L9/L11)
  8. framework 接入 + App:framework 调 HAL,App getSystemService。(L10/L2)

每一步都验证(下一步前先确认这一步通):probe 成功?getevent 有数据?HAL lshal 起了?

6. 看底层:数据从硬件到 App 走了几跳

sensor IC 产生数据
 → 拉中断脚(L17)→ 驱动上半部 schedule_work
 → 下半部 regmap_read 读 IC 寄存器(L16)→ 存到驱动缓冲 / input event
 → HAL open /dev 节点 read 到数据(本课)
 → HAL 通过 AIDL 上报 → framework(跨 Binder,L10)
 → framework 通过 Binder 给 App(L2)
 → App 拿到数据

一个数据点,从硬件到 App,经过:I2C 总线 → 内核驱动 → /dev 节点 → HAL → 两次 Binder → App。理解这条链,任何一层出问题你都知道去哪查。

7. 编译 & 运行(验证状态如实)

# 逐层验证(bring-up 的标准做法:从下往上一层层确认通)
adb shell dmesg | grep -i "hello.*chipid"   # 1. 驱动 probe 成功?(L16)
adb shell cat /proc/interrupts | grep hello # 2. 中断来了?(L17)
adb shell lshal | grep hello                # 3. HAL 起了?(L9/L11)
adb shell /vendor/bin/hello_hal_client      # 4. framework 能调 HAL?(L10)
# App 端验证最终能拿到数据

> 验证状态(诚实):本课为串联/集成说明课;各单课的代码已分别验证(驱动真编译、HAL aidl 生成、framework 调用写法)。完整真机 bring-up 一颗硬件需真实 IC + 真机刷机,未实跑。

8. 踩坑提醒

  1. 跳步:bring-up 必须从下往上一层层验证通,跳过中间层(比如驱动还没 probe 就去调 HAL)只会浪费时间。
  2. HAL 读 /dev 节点 sepolicy 没放行:avc denied,HAL 读不到驱动数据(L11)。
  3. 驱动 .ko 没加进 modules.load:开机不加载,等于没装(L13)。
  4. 数据格式在某层变了:每层传数据要约定好格式,一层解析错全错。每层打印验证。
  5. 权限校验放错层:App 权限要在 framework 层校验(HAL 拿到的是 system uid,L12)。

9. 常见问题分析与定位(全链路 bring-up 实战)

9.1 常见问题清单

  1. App 拿不到 sensor 数据(整条链不通)
  2. 数据有但不对/不更新
  3. 某一层之后就断了
  4. 概率性丢数据
  5. bring-up 卡在某一层不知道是硬件还是软件

9.2 分析与定位

核心方法:从下往上逐层确认"数据到这层了没"

# 第1层 硬件/I2C:IC 通信正常?
adb shell i2cdetect -y <bus>                 # IC 在总线上吗(L16)
# 第2层 驱动:probe 成功?中断来?数据读到?
adb shell dmesg | grep -i hello              # probe/读数据日志
adb shell cat /proc/interrupts | grep hello  # 中断计数涨吗(L17)
adb shell cat /dev/hello                      # 驱动节点有数据吗
# 第3层 HAL:起了?读到驱动数据?
adb shell lshal | grep hello                 # HAL 状态(L9/L11)
adb logcat | grep HelloHal                   # HAL 读驱动日志
# 第4层 framework→App
adb logcat | grep -iE "HelloManagerService|<App>"

哪层日志断了,问题就在那层和上一层之间。 这是 bring-up 定位的黄金法则。

① 整条链不通 — 从下往上查,第一个"没数据"的层就是断点。i2cdetect 都扫不到 → 硬件层(L16/L23)。

② 数据不对/不更新 — 每层打印数据值,看哪层进去对、出来错。中断不更新 → L17 中断没触发或下半部没读。

③ 某层之后断 — 那层日志有、下一层没有 → 卡在这两层之间的接口(驱动→HAL 查 sepolicy+节点;HAL→framework 查 Binder+服务名)。

④ 概率性丢 — 中断丢(L17 下半部慢)、I2C 偶发失败(L16)、HAL 读时机问题。

⑤ 不知硬件还是软件 — 用 i2cdetect/示波器(L23)划清界:硬件层通了就是软件,不通就是硬件。

9.3 定位决策树

全链路 bring-up 出问题
├─ 从下往上逐层确认数据到没到:
│   i2cdetect(硬件) → dmesg probe/中断(驱动) → cat /dev/hello(节点) → lshal(HAL) → logcat(framework/App)
├─ 第一个"没数据"的层 = 断点
├─ 硬件层不通(i2cdetect 扫不到) → 示波器(L23):上电/地址/排线
├─ 驱动→HAL 断 → sepolicy(L11)+节点权限
├─ HAL→framework 断 → Binder+服务名(L10)
└─ 数据不对 → 每层打印值找哪层错

10. 小结(阶段三收官)

  1. bring-up = 把硬件从"焊在板上"做到"App 能用",串起 L9-L18 全部能力。
  2. 八步标准流程:原理图→DTS→驱动 probe→中断上报→电源→编 .ko→HAL→framework+App。
  3. driver→HAL 连接点:HAL 通过 /dev 节点 / sysfs / input / iio 读驱动数据,不直接碰寄存器。
  4. 定位黄金法则:从下往上逐层确认"数据到这层了没",第一个断的层就是问题点。
  5. 排查口诀:i2cdetect→dmesg→/dev节点→lshal→logcat,哪层没数据卡在上层之后。

🎓 阶段三(Kernel+驱动,L13-L20)收官:你已掌握从 DTS 描述硬件、写 kernel 驱动、经 HAL 上报、一路到 App 的完整 bring-up 能力。

下节预告

L21:Bootloader→kernel→init 开机全流程 —— 进入阶段四(实战)。从按电源键到看见桌面,中间发生了什么?开不了机怎么定位?

评论