在Android/Linux开发中,开机 Logo 是设备品牌的标志,经常需要根据不同的客户或应用场景进行定制。对于 RK3568 平台,开机 Logo 通常是打包在固件的 img 文件中,这意味着每次更换 Logo 都需要重新编译并烧录整个固件,这无疑增加了开发的复杂性和浪费时间。本文将介绍一种在 RK3568 Android/Linux 系统中动态更换 U-Boot 和 Kernel Logo 的方法。
系列文章:
Android存储分区与Rockchip平台的分区命名及U-Boot配置
Rockchip u-boot阶段命令行和代码方式读取u盘内容并解析
参考:
Android存储分区与Rockchip平台的分区命名及U-Boot配置
需求背景
传统的方法要求我们在每次更换 Logo 时重新编译并烧录固件。这不仅耗时,还是耗时^^ , 所以我们来解决这个问题。
解决方案
目标是在设备已经运行的情况下,动态地更换开机 Logo。为此,我提出了以下方案:
- 新建一个分区(或者用已有分区):在这个分区中存放 Logo 文件。当设备启动时,系统会首先尝试从这个分区加载 Logo。如果分区中没有 Logo 文件,系统会回退到默认的 Logo。
- 修改 U-Boot 源代码:在 U-Boot 的启动过程中,我们修改了 Logo 的加载逻辑。首先尝试从新建的分区加载 Logo,如果失败,则从默认的资源文件中加载。
实现细节
用已有分区存放logo
首先我们需要确定 Logo 文件的存放位置。由于 system 和 vendor 分区是只读的,而 data 分区在恢复出厂设置后会被清空,所以我决定新建一个分区来存放 Logo 文件(其实我之前就建好了 你们搞的话可以用系统已有分区比如/cache,对应的是mmc 0:a)。
rk3568_r:/ $ df Filesystem 1K-blocks Used Available Use% Mounted on tmpfs 1001716 824 1000892 1% /dev tmpfs 1001716 12 1001704 1% /mnt /dev/block/mmcblk0p11 11760 144 11132 2% /metadata /dev/block/dm-0 956964 954064 0 100% / /dev/block/dm-5 692224 96 692128 1% /mnt/scratch overlay 692224 96 692128 1% /system overlay 692224 96 692128 1% /vendor overlay 692224 96 692128 1% /odm overlay 692224 96 692128 1% /product overlay 692224 96 692128 1% /system_ext tmpfs 1001716 0 1001716 0% /apex tmpfs 1001716 264 1001452 1% /linkerconfig /dev/block/mmcblk0p10 364504 916 351792 1% /cache /dev/block/mmcblk0p12 3952 364 3468 10% /mnt/private /dev/block/dm-6 25528320 43916 25353332 1% /data tmpfs 1001716 0 1001716 0% /data_mirror /dev/fuse 25528320 43916 25353332 1% /storage/emulated
在Rockchip平台上,分区的命名和配置通常是由设备树(DTB/DTBO)定义的。例如,mmc 0:c中的0代表第0个存储设备,而c是一个十六进制数,代表12,所以它对应于第12个分区。
OK 选好分区之后,我们在分区底下新建一个文件夹比如custom_logo
,放入logo.bmp
和logo_kernel.bmp
。
rk3568_r:/mnt/private/custom_logo $ ls logo.bmp logo_kernel.bmp rk3568_r:/mnt/private/custom_logo $
如果你们非要用自定义分区的方法可以在CSDN搜找到,这里不再赘述。
修改 U-Boot 源代码
U-Boot 中加载 Logo 的代码位于 /u-boot/drivers/video/drm/rockchip_display.c
中的 load_bmp_logo
方法。我们的策略是先尝试从新建的分区加载 Logo,如果失败,则从默认的资源文件中加载。对了 我验证过Linux和Android 通用!! 只需要换分区就OK了。
以下是修改后的代码片段:
static int load_bmp_logo(struct logo_info *logo, const char *bmp_name) { #define BUFFER_SIZE 128 #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE struct rockchip_logo_cache *logo_cache; struct bmp_header *header; void *dst = NULL, *pdst; char cmd[BUFFER_SIZE] = {"0"}; int size, len; int ret = 0; int reserved = 0; if (!logo || !bmp_name) return -EINVAL; logo_cache = find_or_alloc_logo_cache(bmp_name); if (!logo_cache) return -ENOMEM; if (logo_cache->logo.mem) { memcpy(logo, &logo_cache->logo, sizeof(*logo)); return 0; } header = malloc(RK_BLK_SIZE); if (!header) return -ENOMEM; /*len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); if (len != RK_BLK_SIZE) { ret = -EINVAL; goto free_header; }*/ //---add start sprintf(cmd, "ext4load mmc 0:c 0x%p custom_logo/%s %x", header,bmp_name, RK_BLK_SIZE); printf("load_bmp_logo 尝试从MMC加载 %s...\n", bmp_name); if(run_command(cmd, 0)){ len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); if (len != RK_BLK_SIZE) { printf("load_bmp_logo 从资源文件加载 %s 失败\n", bmp_name); ret = -EINVAL; goto free_header; } } //---add end logo->bpp = get_unaligned_le16(&header->bit_count); logo->width = get_unaligned_le32(&header->width); logo->height = get_unaligned_le32(&header->height); reserved = get_unaligned_le32(&header->reserved); if (logo->height < 0) logo->height = -logo->height; size = get_unaligned_le32(&header->file_size); if (!can_direct_logo(logo->bpp)) { if (size > MEMORY_POOL_SIZE) { printf("failed to use boot buf as temp bmp buffer\n"); ret = -ENOMEM; goto free_header; } pdst = get_display_buffer(size); } else { pdst = get_display_buffer(size); dst = pdst; } /*len = rockchip_read_resource_file(pdst, bmp_name, 0, size); if (len != size) { printf("failed to load bmp %s\n", bmp_name); ret = -ENOENT; goto free_header; }*/ //---add start memset(cmd, 0, BUFFER_SIZE); sprintf(cmd, "ext4load mmc 0:c 0x%p custom_logo/%s %x", pdst, bmp_name, size); if(run_command(cmd, 0)){ len = rockchip_read_resource_file(pdst, bmp_name, 0, size); if (len != size) { printf("load_bmp_logo 加载bmp %s 失败\n", bmp_name); printf("failed to load bmp %s\n", bmp_name); ret = -ENOENT; goto free_header; } } //---add end if (!can_direct_logo(logo->bpp)) { int dst_size; /* * TODO: force use 16bpp if bpp less than 16; */ logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp; dst_size = logo->width * logo->height * logo->bpp >> 3; dst = get_display_buffer(dst_size); if (!dst) { ret = -ENOMEM; goto free_header; } if (bmpdecoder(pdst, dst, logo->bpp)) { printf("failed to decode bmp %s\n", bmp_name); ret = -EINVAL; goto free_header; } flush_dcache_range((ulong)dst, ALIGN((ulong)dst + dst_size, CONFIG_SYS_CACHELINE_SIZE)); logo->offset = 0; logo->ymirror = 0; } else { logo->offset = get_unaligned_le32(&header->data_offset); if (reserved == BMP_PROCESSED_FLAG) logo->ymirror = 0; else logo->ymirror = 1; } logo->mem = dst; memcpy(&logo_cache->logo, logo, sizeof(*logo)); free_header: free(header); return ret; #else return -EINVAL; #endif }
调试
接入串口 按ctrl+c 进入uboot命令行模式,输入ext4ls mmc 0:c /custom_logo
就能看到我们push 进去的2个custom logo图片了 OK 准备工作就绪 刷uboot.img即可。
=> ext4ls mmc 0:c /custom_logo <DIR> 4096 . <DIR> 4096 .. 170326 logo.bmp 170326 logo_kernel.bmp ----------下面3行只是调试显示而已 并没什么关系 => rockchip_show_bmp logo_kernel.bmp load_bmp_logo 尝试从MMC加载 logo_kernel.bmp... 512 bytes read in 44 ms (10.7 KiB/s) 170326 bytes read in 48 ms (3.4 MiB/s) VOP VP0 enable Smart0[654x258->654x258@633x411] fmt[2] addr[0x7dfa7000] => rockchip_show_bmp logo.bmp VOP VP0 enable Smart0[654x258->654x258@633x411] fmt[2] addr[0x7df2a000] => rockchip_show_logo VOP VP0 enable Smart0[654x258->654x258@633x411] fmt[2] addr[0x7df2a000] =>
其他发现, 我看日志的时候发现load_kernel_bmp_logo函数根本没走? 然后看了注释才发现 , 好吧没什么卵关系 继续。
/* Note: used only for rkfb kernel driver */ static int load_kernel_bmp_logo(struct logo_info *logo, const char *bmp_name)
验证
接入串口,看调试打印日志 可以看到是先读取的logo.bmp 然后读取的logo_kernel.bmp ,对应的也就是uboot logo和kernel logo。搞定! 如果没有这2个custom logo文件 也会走系统默认的。
## Baudrate 1500000 bps not supported himport_r: can't insert "baudrate=1500000" into hash table dwmmc@fe2b0000: 1, dwmmc@fe2c0000: 2, sdhci@fe310000: 0 Bootdev(atags): mmc 0 MMC0: HS200, 200Mhz PartType: EFI boot mode: recovery (misc) FIT: No fdt blob boot mode: None Android 11.0, Build 2021.7, v2 Found DTB in boot part DTB: rk-kernel.dtb HASH(c): OK ANDROID: fdt overlay OK I2c0 speed: 100000Hz vsel-gpios- not found! Error: -2 vdd_cpu init 900000 uV PMIC: RK8090 (on=0x40, off=0x00) vdd_logic init 900000 uV vdd_gpu init 900000 uV vdd_npu init 900000 uV io-domain: OK Model: Rockchip RK3568 EVB3568 Board load_bmp_logo 尝试从MMC加载 logo.bmp... .............. Fdt Ramdisk skip relocation ## Booting Android Image at 0x0027f800 ... Kernel load addr 0x00280000 size 32057 KiB RAM disk load addr 0x0a200000 size 804 KiB ## Flattened Device Tree blob at 0x0a100000 Booting using the fdt blob at 0x0a100000 XIP Kernel Image from 0x00280000 to 0x00280000 ... OK 'reserved-memory' linux,cma: addr=10000000 size=800000 'reserved-memory' ramoops@110000: addr=110000 size=f0000 Using Device Tree in place at 000000000a100000, end 000000000a1223a7 load_bmp_logo 尝试从MMC加载 logo_kernel.bmp...
疑问
有个疑问就是 这么改完之后,kernel logo 如果是24位深的,logo 会倒过来 8位却不会?
然后 根据我查资料得出 ,8-bit BMP 图像与 24-bit BMP 图像在存储结构上有所不同,特别是在颜色表和像素数据的处理上。这些差异可能影响到图像的渲染方式和是否需要进行垂直翻转。
因此,如果不进行特殊处理,BMP 图像在显示时会被倒置。
在代码中,有这样一段:
if (reserved == BMP_PROCESSED_FLAG) logo->ymirror = 0; else logo->ymirror = 1;
这里的 ymirror
似乎是用来处理这个问题的。当 ymirror
设置为 1 时,图像会被垂直翻转,从而正常显示。而 BMP_PROCESSED_FLAG
似乎是一个标志,用来表示 BMP 图像是否已经经过处理(例如已经被翻转过)。
如果发现 24 位深的 BMP Logo 显示是倒置的,可能需要检查以下几点:
- BMP 文件的存储方式:确保你的 BMP 文件是自下而上存储的。如果它已经是自上而下存储的,那么你不需要进行任何翻转。
BMP_PROCESSED_FLAG
的设置:确保这个标志正确地表示了 BMP 图像是否已经经过处理。ymirror
的使用:确保在渲染图像时正确地使用了ymirror
标志。
为了解决这个问题,可以考虑总是设置 ymirror
为 1(或根据实际情况进行设置),或者在创建 BMP 文件时,确保它是自上而下存储的(都用8位的)。
总结
通过我上述方法,我们成功地实现了在 RK3568 Android/Linux 系统中动态更换 U-Boot 和 Kernel Logo 的功能。这不仅简化了开发流程,还为设备固件提供了更大的灵活性。