samsung uboot fastboot command
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
do
{
continue_from_disconnect = 0;
/* Initialize the board specific support */
if (0 == fastboot_init(&interface))
{
int poll_status, board_poll_status;
/* If we got this far, we are a success */
ret = 0;
timeout_endtime = get_ticks();
timeout_endtime += timeout_ticks;
LCD_turnon();
while (1)
{
uint64_t current_time = 0;
poll_status = fastboot_poll();
if (1 == check_timeout)
current_time = get_ticks();
/* Check if the user wanted to terminate with ^C */
if ( ((poll_status != FASTBOOT_OK) && (ctrlc())) || gflag_reboot)
{
printf("Fastboot ended by user\n");
continue_from_disconnect = 0;
break;
}
if (FASTBOOT_ERROR == poll_status)
{
/* Error */
printf("Fastboot error \n");
break;
}
else if (FASTBOOT_DISCONNECT == poll_status)
{
/* break, cleanup and re-init */
printf("Fastboot disconnect detected\n");
continue_from_disconnect = 1;
break;
}
else if ((1 == check_timeout) &&
(FASTBOOT_INACTIVE == poll_status))
{
/* No activity */
if (current_time >= timeout_endtime)
{
printf("Fastboot inactivity detected\n");
break;
}
}
else
{
/* Something happened */
/* Actual works of parsing are done by rx_handler */
if (1 == check_timeout)
{
/* Update the timeout endtime */
timeout_endtime = current_time;
timeout_endtime += timeout_ticks;
}
}
board_poll_status = board_fastboot_poll();
if (BOARD_FASTBOOT_DISCONNECT == board_poll_status)
{
printf("Fastboot disconnect detected by board action\n");
continue_from_disconnect = 0;
break;
}
} /* while (1) */
}
/* Reset the board specific support */
fastboot_shutdown();
LCD_setfgcolor(0x000010);
LCD_setleftcolor(0x000010);
LCD_setprogress(100);
/* restart the loop if a disconnect was detected */
} while (continue_from_disconnect);
上面是 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,那么就代表此时接收到的是数据。
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 defined(CONFIG_RAMDUMP_MODE)
if (is_ramdump) {
is_ramdump = 0;
start_ramdump((void *)buffer);
}
#endif
}
/* 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 命令中完成数据的接收工作,如此就可以解决这个问题。