产品需要给到客户去自定义开机logo并且不同的客户logo也不一样。所以我们需要同一个版本客户可以自定义开机logo。并需要方便的替换开机logo的图片。
可以参考RK吴工的博客【rockchip Android平台动态替换开机logo的实现】
我们的平台是rk3326 andorid9属于安卓低版本,并不支持在BoardConfig.mk直接添加分区。那我们就在分区表直接定义一个分区(不同的平台做法其实都差不多就是添加一个分区而已)
diff --git a/rk3326_pie/parameter.txt b/rk3326_pie/parameter.txt index f5eeb10..e4318c3 100755 --- a/rk3326_pie/parameter.txt +++ b/rk3326_pie/parameter.txt -00@0x00964c00(nvrom),-@0x0096cc00(userdata:grow) +000@0x00964c00(nvrom),0x00008000@0x0096cc00(logo),-@0x0096d400(userdata:grow) uuid:system=af01642c-9b84-11e8-9b2a-234eb5e198a0
然后我们开机的时候通过ls -l dev/block/by-name下会有一个logo分区。
因为RK已经实现了你有logo分区就会直接读logo分区里面的信息。有的平台是没有实现的可以去掉他原本加载logo图片的位置,然后使用(linxu根据分区名字获取分区信息的函数比如RK使用的函数是int part_get_info_by_name(struct blk_desc *dev_desc,const char *name, disk_partition_t *info); 返回的就是disk_partition_t 分区的一个结构体)
追一下part_get_info_by_name函数实际调用的是那个linux的自带的读分区函数
int part_get_info_by_name(struct blk_desc *dev_desc, const char *name,
disk_partition_t *info)
{
struct part_driver *part_drv;
int ret;
int i;
part_drv = part_driver_lookup_type(dev_desc);
if (!part_drv)
return -1;
for (i = 1; i < part_drv->max_entries; i++) {
ret = part_drv->get_info(dev_desc, i, info);
if (ret != 0) {
/* no more entries in table */
break;
}
if (strcmp(name, (const char *)info->name) == 0) {
/* matched */
return i;
}
}
return -1;
}
实际调用的是part_drv->get_info,并匹配每一个分区的info->name跟"logo"匹配
匹配成功就返回getinfo的(info)结构体
.get_info = part_get_info_ptr(part_get_info_rkparm),
static int part_get_info_rkparm(struct blk_desc *dev_desc, int idx,
disk_partition_t *info)
{
struct list_head *node;
struct rkparm_part *p = NULL;
int part_num = 1;
int ret = 0;
if (idx < 1) {
printf("%s Invalid partition no.%d\n", __func__, idx);
return -EINVAL;
}
if (list_empty(&parts_head) ||
(dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum)))
ret = rkparm_init_param(dev_desc, &parts_head);
if (ret) {
printf("%s Invalid rkparm partition\n", __func__);
return -1;
}
list_for_each(node, &parts_head) {
p = list_entry(node, struct rkparm_part, node);
if (idx == part_num)
break;
part_num ++;
}
if (part_num > idx) {
printf("%s Invalid partition no.%d\n", __func__, idx);
return -EINVAL;
}
info->start = p->start;
info->size = p->size;
info->blksz = dev_desc->blksz;
sprintf((char *)info->name, "%s", p->name);
strcpy((char *)info->type, "U-Boot");
info->bootable = 0;
return 0;
}
以上追下来发现其实,是根据idx来获取分区信息的比如我们的程序都是烧录在emmc里面,在获取logo分区的时候就是在emmc循环匹配name。
lrwxrwxrwx 1 root root 21 2017-08-04 17:00 apd -> /dev/block/mmcblk2p17
lrwxrwxrwx 1 root root 20 2017-08-04 17:00 backup -> /dev/block/mmcblk2p9
lrwxrwxrwx 1 root root 20 2017-08-04 17:00 boot -> /dev/block/mmcblk2p7
lrwxrwxrwx 1 root root 21 2017-08-04 17:00 cache -> /dev/block/mmcblk2p11
lrwxrwxrwx 1 root root 20 2017-08-04 17:00 dtb -> /dev/block/mmcblk2p4
lrwxrwxrwx 1 root root 20 2017-08-04 17:00 dtbo -> /dev/block/mmcblk2p5
lrwxrwxrwx 1 root root 21 2017-08-04 17:00 frp -> /dev/block/mmcblk2p16
lrwxrwxrwx 1 root root 21 2017-08-04 17:00 logo -> /dev/block/mmcblk2p19
可以看到这个index是19也就是for i=19的时候get part info 获取的分区结构体中的name就可以和logo匹配上。
我们来看一下get_info获取到的分区
typedef struct disk_partition {
lbaint_t start; /* # of first block in partition */ //起始地址
lbaint_t size; /* number of blocks in partition */ //一块扇区大小
ulong blksz; /* block size in bytes */ //分区大小
uchar name[PART_NAME_LEN]; /* partition name */ //分区名
uchar type[PART_TYPE_LEN]; /* string type description */
int bootable; /* Active/Bootable flag is set */
#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */
#endif
#ifdef CONFIG_PARTITION_TYPE_GUID
char type_guid[UUID_STR_LEN + 1]; /* type GUID as string, if exists */
#endif
#ifdef CONFIG_DOS_PARTITION
uchar sys_ind; /* partition type */
#endif
} disk_partition_t
在我们创建分区的时候美一个分区都会有一个这个结构体里面记录了分区的信息
我们主要是要根据logo 匹配 name 获取到分区的起始地址 disk_partition -> start
有了分区起始地址我们就可以直接读分区把bmp数据拿出来给到fb就可以直接显示了。
读分区使用的函数是blk_dread 返回的是viod*的数据
贴上补丁
static int init_resource_list(struct resource_img_hdr *hdr)
{
struct resource_entry *entry;
void *content;
int size;
int ret;
int e_num;
int offset = 0;
int resource_found = 0;
struct blk_desc *dev_desc;
struct bmp_header *header;
disk_partition_t part_info;
char *boot_partname = PART_BOOT;
/*
* Primary detect AOSP format image, try to get resource image from
* boot/recovery partition. If not, it's an RK format image and try
* to get from resource partition.
*/
#ifdef CONFIG_ANDROID_BOOT_IMAGE
struct andr_img_hdr *andr_hdr;
#endif
if (hdr) {
if (resource_image_check_header(hdr))
return -EEXIST;
content = (void *)((char *)hdr
+ (hdr->c_offset) * RK_BLK_SIZE);
for (e_num = 0; e_num < hdr->e_nums; e_num++) {
size = e_num * hdr->e_blks * RK_BLK_SIZE;
entry = (struct resource_entry *)(content + size);
add_file_to_list(entry, offset);
}
return 0;
}
dev_desc = rockchip_get_bootdev();
if (!dev_desc) {
printf("%s: dev_desc is NULL!\n", __func__);
return -ENODEV;
}
hdr = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
if (!hdr) {
printf("%s: out of memory!\n", __func__);
return -ENOMEM;
}
#ifdef CONFIG_ANDROID_BOOT_IMAGE
/* Get boot mode from misc */
#ifndef CONFIG_ANDROID_AB
if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
boot_partname = PART_RECOVERY;
#endif
/* Read boot/recovery and chenc if this is an AOSP img */
#ifdef CONFIG_ANDROID_AB
char slot_suffix[3] = {0};
if (rk_avb_get_current_slot(slot_suffix)) {
ret = -ENODEV;
goto out;
}
boot_partname = android_str_append(boot_partname, slot_suffix);
if (!boot_partname) {
ret = -EINVAL;
goto out;
}
#endif
ret = part_get_info_by_name(dev_desc, boot_partname, &part_info);
if (ret < 0) {
printf("%s: failed to get %s part, ret=%d\n",
__func__, boot_partname, ret);
/* RKIMG can support part table without 'boot' */
goto next;
}
/*
* Only read header and check magic, is a AOSP format image?
* If so, get resource image from second part.
*/
andr_hdr = (void *)hdr;
ret = blk_dread(dev_desc, part_info.start, 1, andr_hdr);
if (ret != 1) {
printf("%s: failed to read %s hdr, ret=%d\n",
__func__, part_info.name, ret);
ret = -EIO;
goto out;
}
ret = android_image_check_header(andr_hdr);
if (!ret) {
debug("%s: Load resource from %s second pos\n",
__func__, part_info.name);
/* Read resource from second offset */
offset = part_info.start * RK_BLK_SIZE;
offset += andr_hdr->page_size;
offset += ALIGN(andr_hdr->kernel_size, andr_hdr->page_size);
offset += ALIGN(andr_hdr->ramdisk_size, andr_hdr->page_size);
offset = offset / RK_BLK_SIZE;
resource_found = 1;
}
next:
#endif
/*
* If not found resource image in AOSP format images(boot/recovery part),
* try to read RK format images(resource part).
*/
if (!resource_found) {
debug("%s: Load resource from resource part\n", __func__);
/* Read resource from Rockchip Resource partition */
boot_partname = PART_RESOURCE;
ret = part_get_info_by_name(dev_desc, boot_partname, &part_info);
if (ret < 0) {
printf("%s: failed to get resource part, ret=%d\n",
__func__, ret);
goto out;
}
offset = part_info.start;
}
/* Only read header and check magic */
ret = blk_dread(dev_desc, offset, 1, hdr);
if (ret != 1) {
printf("%s: failed to read resource hdr, ret=%d\n",
__func__, ret);
ret = -EIO;
goto out;
}
ret = resource_image_check_header(hdr);
if (ret < 0) {
ret = -EINVAL;
goto out;
}
content = memalign(ARCH_DMA_MINALIGN,
hdr->e_blks * hdr->e_nums * RK_BLK_SIZE);
if (!content) {
printf("%s: failed to alloc memory for content\n", __func__);
ret = -ENOMEM;
goto out;
}
/* Read all entries from resource image */
ret = blk_dread(dev_desc, offset + hdr->c_offset,
hdr->e_blks * hdr->e_nums, content);
if (ret != (hdr->e_blks * hdr->e_nums)) {
printf("%s: failed to read resource entries, ret=%d\n",
__func__, ret);
ret = -EIO;
goto err;
}
for (e_num = 0; e_num < hdr->e_nums; e_num++) {
size = e_num * hdr->e_blks * RK_BLK_SIZE;
entry = (struct resource_entry *)(content + size);
add_file_to_list(entry, offset);
}
ret = 0;
printf("Load FDT from %s part\n", boot_partname);
/*
* Add logo.bmp from "logo" parititon
*
* We provide a "logo" partition for user to store logo.bmp
* and update from kernel user space dynamically.
*/
if (part_get_info_by_name(dev_desc, PART_LOGO, &part_info) >= 0) {
struct resource_file *file;
struct list_head *node;
header = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
if (!header) {
ret = -ENOMEM;
goto err;
}
ret = blk_dread(dev_desc, part_info.start, 1, header);
if (ret != 1) {
ret = -EIO;
goto err2;
}
if (header->signature[0] != 'B' ||
header->signature[1] != 'M') {
ret = 0;
goto err2;
}
entry = malloc(sizeof(*entry));
if (!entry) {
ret = -ENOMEM;
goto err2;
}
memcpy(entry->tag, ENTRY_TAG, sizeof(ENTRY_TAG));
memcpy(entry->name, "logo.bmp", sizeof("logo.bmp"));
entry->f_size = get_unaligned_le32(&header->file_size);
entry->f_offset = 0;
/* Delete exist "logo.bmp", then add new */
list_for_each(node, &entrys_head) {
file = list_entry(node,
struct resource_file, link);
if (!strcmp(file->name, entry->name)) {
list_del(&file->link);
free(file);
break;
}
}
add_file_to_list(entry, part_info.start);
free(entry);
printf("Load \"logo.bmp\" from logo part\n");
ret = 0;
err2:
free(header);
}
err:
free(content);
out:
free(hdr);
return ret;
}
上述补丁blk_dread(dev_desc, part_info.start, 1, header);会去读一个文件头判断是否是bmp文件。
我们可以使用od 指令去读一下分区可以看到bmp图片的文件头是BM然后+一下图片分辨率什么的信息
对应的4D42的ascii码表就是’B’‘M’
拓展一下bmp的文件结构体这个结构体的内容就是bmp图片中的文件头
typedef struct bmp_header {
/* Header */
char signature[2]; // 'b' 'm'
unsigned int file_size; //文件大小
unsigned int reserved;
unsigned int data_offset; //要显示的图片data偏移量
/* InfoHeader */
unsigned int size; //
unsigned int width; //图片分辨率
unsigned int height;
unsigned short planes;
unsigned short bit_count; //位深
unsigned int compression;
unsigned int image_size;
unsigned int x_pixels_per_m;
unsigned int y_pixels_per_m;
unsigned int colors_used;
unsigned int colors_important;
/* ColorTable */
} __attribute__ ((packed)) bmp_header_t;