Fastboot 协议解析与底层通信实现
Fastboot 是 Bootloader 与 PC 之间用来刷写分区、查询设备状态的轻量级协议。本文深入解析 Fastboot 协议格式、USB 传输层、命令实现细节,并演示如何用 C 实现一个最小化的 Fastboot 客户端。
一、Fastboot 是什么?
当你在终端敲下:
fastboot flash boot boot.img
fastboot reboot
幕后到底发生了什么?
Fastboot 是一个 应用层协议,运行在 USB Bulk Transfer 之上,客户端在 PC,服务端在 Android 设备的 Bootloader(或 userspace fastbootd)中。它的设计极度简洁,适合在资源受限的 Bootloader 环境中运行。
二、协议格式
Fastboot 协议非常简单,所有消息都是文本格式。
客户端 → 设备(命令)
直接发送 ASCII 字符串,最长 64 字节:
"getvar:version"
"download:00100000"
"flash:boot"
"reboot"
"erase:userdata"
设备 → 客户端(响应)
固定 4 字节前缀 + 内容(总长 ≤ 64 字节):
| 前缀 | 含义 |
|---|---|
OKAY |
命令成功完成,后续可有附加信息 |
FAIL |
命令失败,后续 60 字节是错误描述 |
INFO |
信息(进度),客户端打印后继续等 |
DATA |
设备准备好接收 N 字节数据(N 是 8 字节十六进制) |
TEXT |
文本信息(较新协议) |
一次完整的交互示例
以 flash boot boot.img 为例:
[Client] -> "download:01000000" (准备发送 16 MB 数据)
[Device] -> "DATA01000000" (Ready,16 MB)
[Client] -> <16 MB 二进制数据>
[Device] -> "OKAY" (数据接收完毕)
[Client] -> "flash:boot"
[Device] -> "INFO writing 'boot' partition" (进度)
[Device] -> "OKAY" (刷写完成)
三、USB 传输层
Fastboot 使用 USB 2.0/3.0 Bulk Endpoint,Interface Class/SubClass/Protocol 固定:
bInterfaceClass = 0xFF (Vendor Specific)
bInterfaceSubClass = 0x42 ('B')
bInterfaceProtocol = 0x03 (Fastboot)
每个 Fastboot Interface 有 2 个 Bulk 端点:
EP1 OUT (Host -> Device) 传输命令和数据
EP1 IN (Device -> Host) 传输响应
设备通过这一组 Endpoint 提供服务,PC 端通过 libusb 或 WinUSB 与之通信。
在设备端识别
# 设备进入 fastboot 模式后查看 USB
lsusb -d 18d1:4ee0 -v | grep -A4 Interface
输出示例:
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 66
bInterfaceProtocol 3
四、设备端的 Fastboot 实现
设备端的 Fastboot 通常实现在 Bootloader(LK)中。核心代码在 AOSP 的 bootable/bootloader/lk/app/aboot/fastboot.c。
主循环框架
static void cmd_handler(const char *cmd, void *arg) {
char buf[64];
if (strncmp(cmd, "download:", 9) == 0) {
unsigned size = strtoul(cmd + 9, NULL, 16);
if (size > MAX_DOWNLOAD_SIZE) {
fastboot_fail("size too large");
return;
}
// 通告主机可以发数据
snprintf(buf, sizeof(buf), "DATA%08x", size);
usb_write(buf, strlen(buf));
// 接收数据
usb_read(download_buffer, size);
download_size = size;
fastboot_okay("");
}
else if (strncmp(cmd, "flash:", 6) == 0) {
cmd_flash(cmd + 6);
}
else if (strncmp(cmd, "erase:", 6) == 0) {
cmd_erase(cmd + 6);
}
else if (strncmp(cmd, "getvar:", 7) == 0) {
cmd_getvar(cmd + 7);
}
else if (strcmp(cmd, "reboot") == 0) {
fastboot_okay("");
msleep(100);
reboot_device(NORMAL);
}
else if (strcmp(cmd, "reboot-bootloader") == 0) {
fastboot_okay("");
msleep(100);
reboot_device(FASTBOOT_MODE);
}
else {
fastboot_fail("unknown command");
}
}
void fastboot_handler(void) {
char cmd[64];
while (1) {
int n = usb_read(cmd, sizeof(cmd) - 1);
if (n < 0) continue;
cmd[n] = 0;
printf("fastboot: cmd '%s'\n", cmd);
cmd_handler(cmd, NULL);
}
}
flash 命令的内部实现
static void cmd_flash(const char *arg) {
struct partition_info pi;
if (find_partition(arg, &pi) < 0) {
fastboot_fail("partition not found");
return;
}
if (download_size == 0) {
fastboot_fail("no data downloaded");
return;
}
if (download_size > pi.size) {
fastboot_fail("image too large for partition");
return;
}
// 如果是 sparse image (ext4 大文件),特殊处理
if (is_sparse_image(download_buffer, download_size)) {
if (write_sparse_image(&pi, download_buffer, download_size) < 0) {
fastboot_fail("sparse write failed");
return;
}
} else {
// 普通块写
if (mmc_write(pi.offset, download_buffer, download_size) < 0) {
fastboot_fail("write failed");
return;
}
}
fastboot_okay("");
}
getvar 的实现
getvar 是 Fastboot 的"读寄存器"命令,客户端用它来探查设备能力。
struct fastboot_var {
const char *name;
const char *value;
};
static struct fastboot_var fb_vars[] = {
{ "version", "0.4" },
{ "version-bootloader", BOOTLOADER_VERSION },
{ "product", DEVICE_NAME },
{ "secure", "yes" },
{ "unlocked", "no" },
{ "slot-count", "2" },
{ "current-slot", "a" },
{ "max-download-size", "0x20000000" }, // 512 MB
{ NULL, NULL }
};
static void cmd_getvar(const char *name) {
if (strcmp(name, "all") == 0) {
// 'getvar all' 用 INFO 逐条返回
for (int i = 0; fb_vars[i].name; i++) {
char buf[128];
snprintf(buf, sizeof(buf), "%s:%s",
fb_vars[i].name, fb_vars[i].value);
fastboot_info(buf);
}
fastboot_okay("");
return;
}
// 特殊变量:partition-size:<part>
if (strncmp(name, "partition-size:", 15) == 0) {
struct partition_info pi;
if (find_partition(name + 15, &pi) < 0) {
fastboot_fail("partition not found");
return;
}
char buf[32];
snprintf(buf, sizeof(buf), "0x%llx", pi.size);
fastboot_okay(buf);
return;
}
for (int i = 0; fb_vars[i].name; i++) {
if (strcmp(name, fb_vars[i].name) == 0) {
fastboot_okay(fb_vars[i].value);
return;
}
}
fastboot_fail("unknown variable");
}
五、Sparse Image:省带宽的妙招
system.img 这样的 ext4 镜像可能有 3GB,但实际数据只有 1GB,剩下都是空洞。整块发太浪费,Android 设计了 sparse 格式。
Sparse 格式
#define SPARSE_HEADER_MAGIC 0xed26ff3a
struct sparse_header {
uint32_t magic; // 0xed26ff3a
uint16_t major_version; // 1
uint16_t minor_version; // 0
uint16_t file_hdr_sz; // 28
uint16_t chunk_hdr_sz; // 12
uint32_t blk_sz; // 块大小 (通常 4096)
uint32_t total_blks; // 总块数 (含空洞)
uint32_t total_chunks; // chunk 数
uint32_t image_checksum;
};
struct chunk_header {
uint16_t chunk_type; // RAW=0xCAC1, FILL=0xCAC2, DONT_CARE=0xCAC3, CRC=0xCAC4
uint16_t reserved;
uint32_t chunk_sz; // 此 chunk 占用的块数
uint32_t total_sz; // 总字节(含 header)
};
四种 chunk 类型:
| 类型 | 用途 |
|---|---|
| RAW (0xCAC1) | 原始数据,后面跟 chunk_sz × blk_sz 字节 |
| FILL (0xCAC2) | 填充,后面跟 4 字节模式,扩展为 chunk_sz × blk_sz |
| DONT_CARE | 不关心(空洞),无 payload |
| CRC (0xCAC4) | 校验信息 |
设备端解析
int write_sparse_image(struct partition_info *pi,
uint8_t *data, size_t size) {
struct sparse_header *sh = (struct sparse_header *)data;
if (sh->magic != SPARSE_HEADER_MAGIC)
return -1;
uint8_t *p = data + sh->file_hdr_sz;
uint64_t cur_offset = pi->offset;
for (uint32_t i = 0; i < sh->total_chunks; i++) {
struct chunk_header *ch = (struct chunk_header *)p;
uint64_t bytes = (uint64_t)ch->chunk_sz * sh->blk_sz;
switch (ch->chunk_type) {
case 0xCAC1: // RAW
mmc_write(cur_offset,
p + sh->chunk_hdr_sz, bytes);
break;
case 0xCAC2: { // FILL
uint32_t fill = *(uint32_t*)(p + sh->chunk_hdr_sz);
fill_and_write(cur_offset, fill, bytes);
break;
}
case 0xCAC3: // DONT_CARE - 跳过
break;
}
cur_offset += bytes;
p += ch->total_sz;
}
return 0;
}
六、用 C 实现一个 minimal Fastboot 客户端
下面是一个用 libusb 实现的迷你 Fastboot 客户端,支持 getvar 和 reboot:
// minifastboot.c
// 编译: gcc minifastboot.c -lusb-1.0 -o minifastboot
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#define FB_INTERFACE_CLASS 0xFF
#define FB_INTERFACE_SUBCLASS 0x42
#define FB_INTERFACE_PROTOCOL 0x03
static int ep_in = -1, ep_out = -1, iface = -1;
static int find_fastboot_interface(libusb_device *dev,
struct libusb_device_handle *h) {
struct libusb_config_descriptor *cfg;
if (libusb_get_active_config_descriptor(dev, &cfg) < 0)
return -1;
for (int i = 0; i < cfg->bNumInterfaces; i++) {
const struct libusb_interface_descriptor *id =
&cfg->interface[i].altsetting[0];
if (id->bInterfaceClass == FB_INTERFACE_CLASS &&
id->bInterfaceSubClass == FB_INTERFACE_SUBCLASS &&
id->bInterfaceProtocol == FB_INTERFACE_PROTOCOL) {
for (int j = 0; j < id->bNumEndpoints; j++) {
const struct libusb_endpoint_descriptor *ep = &id->endpoint[j];
if ((ep->bmAttributes & 0x3) != LIBUSB_TRANSFER_TYPE_BULK)
continue;
if (ep->bEndpointAddress & 0x80) ep_in = ep->bEndpointAddress;
else ep_out = ep->bEndpointAddress;
}
iface = id->bInterfaceNumber;
libusb_free_config_descriptor(cfg);
return 0;
}
}
libusb_free_config_descriptor(cfg);
return -1;
}
static struct libusb_device_handle *open_fastboot_device(void) {
libusb_device **list;
ssize_t n = libusb_get_device_list(NULL, &list);
for (ssize_t i = 0; i < n; i++) {
struct libusb_device_handle *h;
if (libusb_open(list[i], &h) < 0) continue;
if (find_fastboot_interface(list[i], h) == 0) {
libusb_claim_interface(h, iface);
libusb_free_device_list(list, 1);
return h;
}
libusb_close(h);
}
libusb_free_device_list(list, 1);
return NULL;
}
static int send_command(struct libusb_device_handle *h, const char *cmd) {
int transferred;
return libusb_bulk_transfer(h, ep_out,
(unsigned char *)cmd, strlen(cmd),
&transferred, 5000);
}
static int read_response(struct libusb_device_handle *h, char *buf, int max) {
int transferred;
int ret = libusb_bulk_transfer(h, ep_in,
(unsigned char *)buf, max - 1,
&transferred, 5000);
if (ret == 0) buf[transferred] = 0;
return ret;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <command>\n", argv[0]);
return 1;
}
libusb_init(NULL);
struct libusb_device_handle *h = open_fastboot_device();
if (!h) {
fprintf(stderr, "No fastboot device found\n");
return 1;
}
if (send_command(h, argv[1]) < 0) {
fprintf(stderr, "send failed\n");
return 1;
}
// 循环读响应直到 OKAY 或 FAIL
char resp[65];
while (read_response(h, resp, sizeof(resp)) == 0) {
if (strncmp(resp, "OKAY", 4) == 0) {
printf("[OK] %s\n", resp + 4);
break;
}
if (strncmp(resp, "FAIL", 4) == 0) {
printf("[FAIL] %s\n", resp + 4);
break;
}
if (strncmp(resp, "INFO", 4) == 0) {
printf("[INFO] %s\n", resp + 4);
}
}
libusb_release_interface(h, iface);
libusb_close(h);
libusb_exit(NULL);
return 0;
}
使用:
sudo ./minifastboot getvar:version
# [OK] 0.4
sudo ./minifastboot getvar:product
# [OK] sm8550
sudo ./minifastboot reboot
# [OK]
七、Fastboot 命令对照表
| 命令 | 作用 |
|---|---|
getvar:<name> |
读取设备变量 |
getvar:all |
读取所有变量 |
download:<size> |
准备接收 size 字节数据 |
upload |
上传数据到 PC (oem unlock 等用) |
flash:<part> |
把已下载数据写入分区 |
erase:<part> |
擦除分区 |
boot |
临时用已下载数据启动(不写入) |
continue |
继续正常启动 |
reboot |
正常重启 |
reboot-bootloader |
重启回 fastboot |
reboot fastboot |
重启到 userspace fastboot (fastbootd) |
set_active:a/b |
切换 active slot |
oem unlock |
解锁 Bootloader (需 OEM 流程) |
flashing unlock |
现代解锁命令 |
八、userspace fastbootd
Android 10+ 把一部分 Fastboot 功能移到了用户态(super 分区相关操作),叫 fastbootd,运行在 recovery ramdisk 中。
# 重启进 fastbootd (需要 recovery 内核 + ramdisk)
adb reboot fastboot
# 这时候才能刷 dynamic partition
fastboot flash system system.img
fastboot flash vendor vendor.img
fastbootd 的协议和 Bootloader 的 Fastboot 完全相同,只是运行环境从 LK 换成了 Linux 用户态,可以处理更复杂的逻辑(比如 super 分区里的 LVM 操作)。
九、踩坑经验
- Windows 找不到设备:需要安装 Google USB Driver,或者用 Zadig 替换为 WinUSB
- download 超过 max-download-size:设备会拒绝,要分批下载(
getvar:max-download-size查询) - flash dynamic partition 失败:必须在 fastbootd 下,不是普通 fastboot
- erase userdata 后不能直接刷小镜像:userdata 分区大小巨大,但镜像只有几 MB,会被识别为损坏。需要
fastboot format userdata - 某些机型加固 Bootloader,不响应 oem unlock:被 OEM 替换为
flashing unlock,并且要先在开发者选项打开 OEM 解锁开关
十、总结
Fastboot 协议是一个工程典范:
- 极简:文本命令 + 4 字节前缀响应,几百行代码就能实现
- 灵活:
download + flash解耦,支持任意数据传输 - 可扩展:
getvar机制让客户端能动态发现设备能力 - 跨阶段:既能在 Bootloader 跑,也能在 Linux 用户态跑
理解 Fastboot 协议对做 OEM 工具、定制刷机流程、自动化测试非常有帮助。下篇我们继续讲 Recovery 模式 — Android 的另一套救命系统。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)