【全栈第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/xxx | open + read/ioctl(L14) | 通用 |
sysfs /sys/class/xxx/value | read 这个文件 | 简单状态/配置 |
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 标准流程)
按这个顺序接一颗新硬件(每步对应一课):
- 看原理图:确认 IC 挂哪条 I2C、地址、中断脚 GPIO、供电 regulator。(硬件)
- DTS 描述硬件:i2c@48 节点,compatible/reg/interrupts/供电。(L15/L16)
- 写 kernel 驱动:i2c_driver,probe 读 CHIPID 验证通信。(L16)
- 中断上报:数据就绪中断 → workqueue 读寄存器 → 暴露 /dev 或 input/iio。(L17)
- 电源管理:suspend 关 sensor,resume 恢复。(L18)
- 驱动编 .ko 进 vendor,加进 modules.load 开机加载。(L13)
- 写 HAL:open 驱动节点 read 数据(本课);注册 + vintf + sepolicy。(L9/L11)
- 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. 踩坑提醒
- 跳步:bring-up 必须从下往上一层层验证通,跳过中间层(比如驱动还没 probe 就去调 HAL)只会浪费时间。
- HAL 读 /dev 节点 sepolicy 没放行:avc denied,HAL 读不到驱动数据(L11)。
- 驱动 .ko 没加进 modules.load:开机不加载,等于没装(L13)。
- 数据格式在某层变了:每层传数据要约定好格式,一层解析错全错。每层打印验证。
- 权限校验放错层:App 权限要在 framework 层校验(HAL 拿到的是 system uid,L12)。
9. 常见问题分析与定位(全链路 bring-up 实战)
9.1 常见问题清单
- App 拿不到 sensor 数据(整条链不通)
- 数据有但不对/不更新
- 某一层之后就断了
- 概率性丢数据
- 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. 小结(阶段三收官)
- bring-up = 把硬件从"焊在板上"做到"App 能用",串起 L9-L18 全部能力。
- 八步标准流程:原理图→DTS→驱动 probe→中断上报→电源→编 .ko→HAL→framework+App。
- driver→HAL 连接点:HAL 通过 /dev 节点 / sysfs / input / iio 读驱动数据,不直接碰寄存器。
- 定位黄金法则:从下往上逐层确认"数据到这层了没",第一个断的层就是问题点。
- 排查口诀:i2cdetect→dmesg→/dev节点→lshal→logcat,哪层没数据卡在上层之后。
🎓 阶段三(Kernel+驱动,L13-L20)收官:你已掌握从 DTS 描述硬件、写 kernel 驱动、经 HAL 上报、一路到 App 的完整 bring-up 能力。
下节预告
L21:Bootloader→kernel→init 开机全流程 —— 进入阶段四(实战)。从按电源键到看见桌面,中间发生了什么?开不了机怎么定位?