FastBoot协议版本0.4
fastboot协议是一种通过USB连接与bootloaders通讯的机制。它被设计的非常容易实现,能够用于多种设备和运行Linux、Windows或者OSX的主机。
基本需求(Basic Requirements)
- 两个端点,一个输入端,一个输出端。
- 对于全速(full-speed)USB,最大包尺寸必须是64个字节;
对于高速(hign-speed)USB,最大包尺寸必须是512个字节。 - 协议完全是主机驱动(SSW注:相对于设备客户端而言),并且同步的。
这与多通道、双向、异步的ADB协议不同。
传输和组帧(Transport and Framing)
步骤1、主机发送命令(Command)。
一个命令是一个ASCII字符串,并且只能包含在不大于64个字节的单个包内。
步骤2、客户端(SSW注:设备)用一个单个的不大于64个字节的包响应。
响应包开头四个字节是“OKAY”、“FAIL”、“DATA”或者“INFO”。
响应包剩余的字节可以包含ASCII格式的说明性信息。
a、INFO -> 剩余的60个字节包含说明信息(提供进度或者诊断信息)。
这些说明信息应该被显示,然后重复步骤2。
b、FAIL -> 指示请求的命令失败。
剩余的60个字节可以提供一个文本形式的失败原因呈现给用户。交互停止。
c、OKAY -> 指示请求的命令成功完成。跳转到步骤5。
d、DATA -> 请求的命令已经为数据阶段做好准备。
一个数据响应包是12个字节长,组织形式为DATA00000000,
其中8位十六进制的数字表示所传输数据的总大小。
步骤3、数据阶段。
根据命令的不同,主机或者客户端将发送指定大小的数据。
比指定长度短的包总是可接受的,零长度的包将被忽略。
这个阶段会一直持续,直到客户端已经发送或接收了上面数据响应包中指定大小的字节数为止。
步骤4、客户端用一个单个的不大于64个字节的包响应。
a、INFO -> 显示剩余的60个字节,然后返回到步骤4。
b、FAIL -> 显示剩余的60个字节(如果有的话)作为失败原因,命令失败,停止交互。
c、OKAY -> 成功。跳转到步骤5。
步骤5、命令执行成功。
结束交互。
示例会话(Example Session)
Host:主机 Client:客户端(设备)
Host: “getvar:version” 请求版本号
Client: “OKAY0.4” 返回版本为”0.4”
Host: “getvar:nonexistant” 请求未定义的变量
Client: “OKAY” 返回值为”“
Host: “download:00001234” 请求发送0x1234大小的字节数据
Client: “DATA00001234” 准备好接收数据
Host: < 0x1234 bytes > 发送数据
Client: “OKAY” 数据接收成功完成
Host: “flash:bootloader” 请求刷新数据到bootloader
Client: “INFOerasing flash” 指示状态/进度为“擦除flash”
“INFOwriting flash” 指示状态/进度为“写入flash”
“OKAY” 刷新成功完成
Host: “powerdown” 发送“关机”命令
Client: “FAILunknown command” 命令执行失败
命令参考(Command Reference)
- 命令参数以printf风格的转义序列表示。
- 命令是ASCII字符串,发送时不用引号(下面命令外使用引号仅仅为了在此文档中清楚的表达命令),
发送时也不以0字节结尾。
- 以小写字母开头的命令是为本规范保留的,OEM特定的命令不应该以小写字母开头,以防和规范的未来版本不兼容。
"getvar:%s" 从bootloader读取配置或版本变量。
变量的值在OKAY响应的后面返回。
"download:x" 写入数据到内存,供下面阐述的”boot“、”randisk“、”flash“等命令使用。
如果RAM有足够的空间,客户端将用”DATAx“回应;否则,将回应”FAIL“。
下载数据的大小会被记下来。
"verify:x" 发送一个数字签名去验证下载的数据。
如果bootloader是”secure(安全的)“,那么签名验证是必须的;
如果bootloader不是”secure“,”flash“和”boot“命令会忽略签名验证。
"flash:%s" 将之前下载的影像写入到指定的分区(如果可能的话)。
"erase:%s" 擦除指定的分区(将分区全部写成0xFFs)。
"boot" 之前下载的数据一个boot.img,应该按照boot.img的正常步骤被启动。
"continue" 继续正常启动工作(如果可能的话)。
"reboot" 重新启动设备。
"reboot-bootloader" 重新启动进入bootloader。
对于升级bootloader之后,用新的bootloader去升级其他分区的升级过程,
这个命令是很有用的。
"powerdown" 设备关机。
客户端变量(Client Variables)
命令”getvar:%s”用来读取客户端变量,客户端变量代表关于设备和运行于设备之上软件的各种信息。
当前已经定义的变量名称如下:
version FastBoot协议所支持的版本。
version-bootloader Bootloader的版本字符串。
version-baseband 基带(Baseband)软件的版本字符串。
product 产品名称。
serialno 产品序列号。
secure 如果值是”yes“,说明这是一个安全的bootloader,在它安装或启动映像之前,需要一个签名。
以小写字母开头的变量名被本规范保留,OEM特定的变量名不应该以小写字母开头。
ADB (Android Debug Bridge)
ADB是Android系统的调试协议,所有Android系统都支持它。
解析源码:
lk-refs-heads-master\app\aboot下的rules.mk可知
OBJS += \
$(LOCAL_DIR)/aboot.o \
$(LOCAL_DIR)/fastboot.o
主要包含aboot.c 和 fastboot.c
在aboot.c 中
APP_START(aboot)
.init = aboot_init,
APP_END
aboot_init的实现如下:
可知只要用户按下BACK按键就跳过boot_linux_from_flash
,进入到fastboot.
void aboot_init(const struct app_descriptor *app)
{
if (keys_get_state(KEY_BACK) != 0)
goto fastboot;
boot_linux_from_flash();
dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
"to fastboot mode.\n");
fastboot:
udc_init(&surf_udc_device);
fastboot_register("boot", cmd_boot);
fastboot_register("erase:", cmd_erase);
fastboot_register("flash:", cmd_flash);
fastboot_register("continue", cmd_continue);
fastboot_publish("product", "swordfish");
fastboot_publish("kernel", "lk");
fastboot_init((void*) SCRATCH_ADDR, 100 * 1024 * 1024);
udc_start();
}
调用usb初始化,并注册fastboot的boot/erase/flash/continue/product/kernel等命令,并fastboot_init
,新建thread来接受pc发过来的命令
int fastboot_init(void *base, unsigned size)
{
thread_t *thr;
dprintf(INFO, "fastboot_init()\n");
download_base = base;
download_max = size;
event_init(&usb_online, 0, EVENT_FLAG_AUTOUNSIGNAL);
event_init(&txn_done, 0, EVENT_FLAG_AUTOUNSIGNAL);
in = udc_endpoint_alloc(UDC_TYPE_BULK_IN, 512);
if (!in)
goto fail_alloc_in;
out = udc_endpoint_alloc(UDC_TYPE_BULK_OUT, 512);
if (!out)
goto fail_alloc_out;
fastboot_endpoints[0] = in;
fastboot_endpoints[1] = out;
req = udc_request_alloc();
if (!req)
goto fail_alloc_req;
if (udc_register_gadget(&fastboot_gadget))
goto fail_udc_register;
fastboot_register("getvar:", cmd_getvar);
fastboot_register("download:", cmd_download);
fastboot_publish("version", "0.5");
thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);
thread_resume(thr);
return 0;
fail_udc_register:
udc_request_free(req);
fail_alloc_req:
udc_endpoint_free(out);
fail_alloc_out:
udc_endpoint_free(in);
fail_alloc_in:
return -1;
}
这个函数又注册getvar/download命令,并建立thread接受pc发过来的执行,thread的callback函数是fastboot_handler
:
static int fastboot_handler(void *arg)
{
for (;;) {
event_wait(&usb_online);
fastboot_command_loop();
}
return 0;
}
这个函数是循环,如果usb接收到命令,就调用fastboot_command_loop
来解析命令并调用命令的执行函数
static void fastboot_command_loop(void)
{
struct fastboot_cmd *cmd;
int r;
dprintf(INFO,"fastboot: processing commands\n");
again:
while (fastboot_state != STATE_ERROR) {
r = usb_read(buffer, 64);
if (r < 0) break;
buffer[r] = 0;
dprintf(INFO,"fastboot: %s\n", buffer);
for (cmd = cmdlist; cmd; cmd = cmd->next) {
if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
continue;
fastboot_state = STATE_COMMAND;
cmd->handle((const char*) buffer + cmd->prefix_len,
(void*) download_base, download_size);
if (fastboot_state == STATE_COMMAND)
fastboot_fail("unknown reason");
goto again;
}
fastboot_fail("unknown command");
}
fastboot_state = STATE_OFFLINE;
dprintf(INFO,"fastboot: oops!\n");
}
所有的命令都在cmdlist 这个列表中,调用memcmp来比较pc发的命令和fastboot的命令是否相等,如果相等就调用handle处理,
也就是我们fastboot_register
时候的第二个参数.
在fastboot中我们一般通过fastboot_register
来注册命令
fastboot_register("erase:", cmd_erase);
我们来看看fastboot_register
的实现