1 fastboot协议
fastboot 协议是一种通过 usb 连接 pc 和 bootloader 的机制。他被设计的非常容易实现,能够用于多种设备和运行于 Linux、Windows 或者 OSX 的主机。下面将会讲述 pc 和 bootloader 如何通信,以 fastboot flash 命令举例。
1.1 pc 端
假设烧写的镜像名为 uboot.bin,烧写的分区名为 bootloader。fastboot flash 命令并会被解析为多个命令。
步骤一:pc 发送命令 getvar:partition-type:bootloader
pc 发送的第一个命令,获取分区类型,如果你是某种特殊的类型,如 “ext4”。那么 pc 端会发送擦除这个分区的命令,然后在烧写一个 ext4 的类型的镜像进去。此处要烧写的分区是 bootloader,他没有特殊的格式,那么 uboot 就没有回应。
步骤二:pc 发送命令 getvar:max-download-size
在bootloader 中有一个接受数据的 buffer。这条命令就是获取这个 buffer 的大小。如果 buffer 太小那么 pc 就会把镜像拆分,分段发送。
步骤三:pc 发送命令 download:01fbe8b2
给出这条命令的时候就是告诉 bootloader,我(pc)现在要发送数据了。接着就是数据的传输过程了。download 命令后面的数据表示发送的数据的大小。
步骤四:pc 发送命令 flash:bootloader
告诉 bootloader 将发送的数据写到 bootloader 分区中去。如果在写数据的时候发现分区大小比数据大小要小,那么此时就会报错。
1.2 bootloader 的回应
bootloader 是被动响应 pc 发送过来的指令。它给 pc 回应信息是一个不大于 64 个字节的包响应, 响应包开头四个字节是 “OKAY”、“FAIL”、“DATA” 或者 “INFO”。
a.INFO:表示信心应该被显示
b.FAIL :表示命令执行失败
c.OKAY:表示命令执行成功
d.DATA:表示请求的命令已经为数据阶段做好准备
对于 pc 端发送过来的命令,bootloader 都会发送响应消息。响应消息的头部就是上述4个中的一个,具体用哪个是具体情况而定。
2 fastboot 命令
github 上的 uboot 源码中是没有实现 fastboot 的。这部分是由芯片厂商提供的。此处说的是三星在 uboot 中实现的 fastboot 命令。命令的入口函数为 do_fastboot。因为 usb 设备方面的初始化由芯片厂商完成,所以 usb初始化部分将不是本次讨论的重点。我们将重点关注 fastboot 的流程和其 download 机制。
2.1 fastboot 的 main loop
1 | do |
上面是 do_fastboot() 函数的主体部分。当调用 do_fastbboot() 函数时给出了第三个参数时候,那么这个值将被解析为超时时间,但是一般都没有传入第三个参数,所以关于超时的检测可以自动屏蔽了。fastboot_init() 函数,完成 usb 硬件设备相关的的初始化。设置了 serial number 以及接受数据的 buffer 的大小。此处的 buffer 和在 rx_handler() 中提及的 buffer 不同。
while(1) 循环是一直在运行的,除非是检测到了 ctrl+c。通过检测某个中断位是否置 1,判断 pc 是否有消息发送过来,若是有命令发送过来那么将执行 rx_handler() 函数。这个功能是通过函数 fastboot_poll() 函数来实现的。
2.2 rx_handler()
这个函数有两个参数,第一个参数 const unsigned char *buffer 表示接收到的数据在内存中的地址,第二个参数 unsigned int buffer_size 表示接收到的数据大小。 从这个函数的结构来看,它起初并不知道传进来的是命令还是数据,所以命令和数据都是通过一个 buffer 来接受的。那么他是如何实现下载这项功能的呢?
2.2.1 数据下载
在前面说过 pc 端在发送数据之前先发送了 download:01fbe8b2 命令,后面接的是即将发送的数据的大小。 rx_handler() 函数会去处理 download 命令,将即将接收的数据大小放在静态变量 download_size 中。 download 命令处理完成,rx_handler() 函数返回。pc 端开始发送数据,buffer 填充满了之后再调用 rx_handler()函数,此时 download_size 的值不为 0,那么就代表此时接收到的是数据。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 if (download_size)
{
/* Something to download */
if (buffer_size)
{
/* Handle possible overflow */
unsigned int transfer_size = download_size - download_bytes;
if (buffer_size < transfer_size)
transfer_size = buffer_size;
/* Save the data to the transfer buffer */
memcpy (interface.transfer_buffer + download_bytes,
buffer, transfer_size);
download_bytes += transfer_size;
/* Check if transfer is done */
if (download_bytes >= download_size)
{
/* Reset global transfer variable, Keep download_bytes because it will be used in the next possible flashing command */
download_size = 0;
if (download_error)
{
/* There was an earlier error */
sprintf(response, "ERROR");
}
else
{
/* Everything has transferred,
send the OK response */
sprintf(response, "OKAY");
}
fastboot_tx_status(response, strlen(response), FASTBOOT_TX_ASYNC);
printf("\ndownloading of %d bytes finished\n", download_bytes);
LCD_setprogress(0);
if (is_ramdump) {
is_ramdump = 0;
start_ramdump((void *)buffer);
}
}
/* Provide some feedback */
if (download_bytes && download_size && 0 == (download_bytes & (0x100000 - 1)))
{
/* Some feeback that the download is happening */
if (download_error)
printf("X");
else
printf(".");
if (0 == (download_bytes % (80 * 0x100000))) printf("\n");
LCD_setfgcolor(0x2E8B57);
LCD_setprogress(download_bytes / (download_size/100));
}
}
else
{
/* Ignore empty buffers */
printf("Warning empty download buffer\n");
printf("Ignoring\n");
}
ret = 0;
}
上面是 fastboot 下载数据代码的一部分,其中 download_bytes 表示已经接收的数据的大小。当 download_bytes 大于或等于 download_size 表示数据接收完成,此时将 download_size 置为0,那么就退出了数据接收模式。因为接收数据和接收命令使用的是同一个 buffer,导致buffer不可能太大,所以在接收比较大的image的时候就比较慢,例如刷入system.img。通过修改 buffer 的大小或者在 download 命令中完成数据的接收工作,如此就可以解决这个问题。
This is copyright.