linux显示启动logo源码分析以及修改显示logo

Posted 正在起飞的蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux显示启动logo源码分析以及修改显示logo相关的知识,希望对你有一定的参考价值。

1、linux显示启动logo整个流程分析

(1)logo图片在内核源码中是以ppm格式的文件保存,在编译内核时会把ppm格式的文件自动转换成.c文件,在c文件中会构造一个struct linux_logo结构体变量,
变量名字就是文件的名字,struct linux_logo结构体在内核中用来表示一副logo图像信息;
(2)内核在初始化显示器后,会按照配置去匹配合适的struct linux_logo结构体,也就是查找logo图片;
(3)将logo图像显示到默认屏幕上;

2、linux显示启动logo源码分析

2.1、全局变量fb_logo

(1)显示启动logo的代码在fbmem.c中实现,使用struct logo_data结构体来描述启动logo相关的信息;
(2)成员logo表示启动logo图像的具体信息,其他的变量和图像的格式和图像深度有关系;

2.2、struct linux_logo结构体

#define LINUX_LOGO_MONO		1	/* monochrome black/white */
#define LINUX_LOGO_VGA16	2	/* 16 colors VGA text palette */
#define LINUX_LOGO_CLUT224	3	/* 224 colors */
#define LINUX_LOGO_GRAY256	4	/* 256 levels grayscale */

(1)struct linux_logo结构体就是内核中用来描述logo图片的,这个结构体不用我们去构造,是有ppm文件转换成c文件时自动生成;
(2)logo图像有4中格式,其中LINUX_LOGO_CLUT224应用最为广泛,所占空间也最小。LINUX_LOGO_CLUT224类型的图像颜色种类不会超过224中,颜色的具体信息保存在clut成员表示的颜色对照表中,成员data
指向由颜色对照表索引构成的图像数据。
(3)LINUX_LOGO_CLUT224类型的图像空间计算,比如:对于长宽分别是150和100的 Logo,如果bpp为24 使用颜色对照表方式存储这幅Logo最多需要( 150×100+224 × 3)=15672字节,而如果直接
存储 Logo的所有像素信息,就需要(150×100×3)=45000字节。
(4)通常情况下,内核对于bpp大于等于8的情况,会使用颜色对照表的方式存放Logo ,而bpp小于8时,使用颜色对照表反而可能占用更多空间,因此直接将像素信息存放在成员data中;

2.3、函数调用关系

s3cfb_probe()	//这是显示器驱动的prob函数
	fb_prepare_logo()	//查找合适的logo
		fb_get_color_depth()	//获取显示器的颜色深度
		fb_find_logo()	//真正找到合适的logo图像,也就是struct linux_logo结构体
		fb_prepare_extra_logos()	//设置附加的logo数据,不是必须的
	
	fb_set_cmap()	//设置显示屏的颜色表
	
	fb_show_logo()	//显示logo图像
		fb_show_logo_line()	//构建struct fb_image结构体
			fb_do_show_logo()	//执行logo显示
				info->fbops->fb_imageblit()	//调用显示驱动的fb_imageblit()方法,这里是实际操作硬件的
		fb_show_extra_logos()	//显示附加的logo数据,非必须

2.4、fb_prepare_logo()函数

int fb_prepare_logo(struct fb_info *info, int rotate)

	//计算颜色深度
	int depth = fb_get_color_depth(&info->var, &info->fix);
	unsigned int yres;

	memset(&fb_logo, 0, sizeof(struct logo_data));

	if (info->flags & FBINFO_MISC_TILEBLITTING ||
	    info->flags & FBINFO_MODULE)
		return 0;

	if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) 
		depth = info->var.blue.length;
		if (info->var.red.length < depth)
			depth = info->var.red.length;
		if (info->var.green.length < depth)
			depth = info->var.green.length;
	

	if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) 
		/* assume console colormap */
		depth = 4;
	

	/* 根据颜色深度返回找到的struct linux_logo结构体 */
	fb_logo.logo = fb_find_logo(depth);

	if (!fb_logo.logo) 
		return 0;
	

	//判断是否旋转
	if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
		yres = info->var.yres;
	else
		yres = info->var.xres;

	//判断logo图像的高是否超过可视屏幕的高
	if (fb_logo.logo->height > yres) 
		fb_logo.logo = NULL;
		return 0;
	

	/* 根据logo图像类型设置图像深度 */
	if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
		fb_logo.depth = 8;
	else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
		fb_logo.depth = 4;
	else
		fb_logo.depth = 1;


 	if (fb_logo.depth > 4 && depth > 4) 
 		switch (info->fix.visual) 
 		case FB_VISUAL_TRUECOLOR:
 			fb_logo.needs_truepalette = 1;
 			break;
 		case FB_VISUAL_DIRECTCOLOR:
 			fb_logo.needs_directpalette = 1;
 			fb_logo.needs_cmapreset = 1;
 			break;
 		case FB_VISUAL_PSEUDOCOLOR:
 			fb_logo.needs_cmapreset = 1;
 			break;
 		
 	

	return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);

(1)调用fb_get_color_depth()函数,根据info->var和info->fix计算图像深度,并返回给局部变量depth;
(2)调用fb_find_logo()函数,根据图像深度何也找到合适的Logo图像数据并返回给fb_logo.logo;
(3)如果找到了合适的图像数据,则根据 Logo 图像数据的类型给fb_logo.depth赋值
(4)如果由fb_logo.depth和depth 都大于 4,则根据 info->fix且sual 的值设置 fb_logo 剩下的成员:若 info->fix且sual 为 FB_VISUAL_TRUECOLOR,则由1__logo 的 needs二刷刷e出成员被设置,若为 FB_VISUAL_TR四COLOR,则 needs_directpalette 和 needs_cmapreset 均被设置,若为 FB_VISUAL_PSEUDOCOLOR,则 needs_cmapreset 被设置;
(5)调用fb_prepare_extra_logos()函数设置附加Logo数据;

2.5、fb_find_logo()函数

const struct linux_logo * __init_refok fb_find_logo(int depth)

	const struct linux_logo *logo = NULL;

	if (nologo)
		return NULL;

	if (depth >= 1) 
#ifdef CONFIG_LOGO_LINUX_MONO
		/* Generic Linux logo */
		logo = &logo_linux_mono;
#endif

	
	
	if (depth >= 4) 
#ifdef CONFIG_LOGO_LINUX_VGA16
		/* Generic Linux logo */
		logo = &logo_linux_vga16;
#endif

	
	
	if (depth >= 8) 
#ifdef CONFIG_LOGO_LINUX_CLUT224
		/* Generic Linux logo */
		logo = &logo_linux_clut224;
#endif

//X210开发板的logo图像数据
#ifdef CONFIG_LOGO_X210_CLUT224
/* x210 android logo */
logo = &logo_x210_clut224;
#endif

	
	
	return logo;

(1)根据depth去查找合适的logo数据,这里的struct linux_logo结构体是由ppm文件转换成C语言文件得到的;
(2)注意这里的查找有后覆盖性,当查找到合适的struct linux_logo结构体函数不会返回,会把所有的结构体都查找一遍,最后一个
匹配的会被返回;

2.6、fb_show_logo_line()函数

static int fb_show_logo_line(struct fb_info *info, int rotate,
			     const struct linux_logo *logo, int y,
			     unsigned int n)

	u32 *palette = NULL, *saved_pseudo_palette = NULL;
	unsigned char *logo_new = NULL, *logo_rotate = NULL;
	struct fb_image image;

	/* Return if the frame buffer is not mapped or suspended */
	if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
	    info->flags & FBINFO_MODULE)
		return 0;

	image.depth = 8;
	image.data = logo->data;	//图像数据

	//若needs_cmapreset被设置,则根据logo配置颜色表,设置颜色表
	//最终会调用info->fbops中的fb_setcmap()或fb_setcolreg()方法
	if (fb_logo.needs_cmapreset)
		fb_set_logocmap(info, logo);

	//若needs_truepalette或 needs_directpalette被设置,则根据logo分配并设置调色板
	if (fb_logo.needs_truepalette ||
	    fb_logo.needs_directpalette) 
		palette = kmalloc(256 * 4, GFP_KERNEL);
		if (palette == NULL)
			return 0;

		if (fb_logo.needs_truepalette)
			fb_set_logo_truepalette(info, logo, palette);
		else
			fb_set_logo_directpalette(info, logo, palette);

		//备份info中原调色板内容,并设置为新的调色板
		saved_pseudo_palette = info->pseudo_palette;
		info->pseudo_palette = palette;
	

	//对于depth <=4 的情况,既不使用颜色表,也不使用调色板,
	//image.data中直接存入像素信息,而不是颜色表或者调色板的索引
	if (fb_logo.depth <= 4) 
		logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);
		if (logo_new == NULL) 
			kfree(palette);
			if (saved_pseudo_palette)
				info->pseudo_palette = saved_pseudo_palette;
			return 0;
		
		image.data = logo_new;
		fb_set_logo(info, logo, logo_new, fb_logo.depth);
	

	//logo显示的起始坐标
	image.dx = 0;
	image.dy = y;
	
	//logo图像的宽高
	image.width = logo->width;
	image.height = logo->height;

	//根据旋转方式调整图像
	if (rotate) 
		logo_rotate = kmalloc(logo->width *
				      logo->height, GFP_KERNEL);
		if (logo_rotate)
			fb_rotate_logo(info, logo_rotate, &image, rotate);
	

	//执行logo显示
	fb_do_show_logo(info, &image, rotate, n);

	kfree(palette);

	//恢复原调色板内容
	if (saved_pseudo_palette != NULL)
		info->pseudo_palette = saved_pseudo_palette;
	kfree(logo_new);
	kfree(logo_rotate);
	return logo->height;

传参注释
info帧缓冲设备描述结构
rotate屏幕旋转方式
logo待显示的 Logo 图像 logo(实际上是 fb_logo.logo)
ylogo显示起始坐标的y值
n系统 CPU 数量

3、制作ppm文件

//安装工具包
sudo apt-get install netpbm

//将名字为logo.png的图像,转换成logo_linux_clut224.ppm文件
pngtopnm logo.png | ppmquant -fs 224 | pnmtoplainpnm > logo_linux_clut224.ppm

(1)上面是利用命令pngtopnm & ppmquant & pnmtoplainpnm来将png格式图像转换成ppm格式图像,如果linux中没有相关命令则要先安装命令包;
(2)在上面的命令中,注意图片文件的名字和你实际的对应上,放到内核源码中放ppm格式文件的目录下;

4、ppm文件自动转换成C源文件

obj-$(CONFIG_LOGO)			+= logo.o
obj-$(CONFIG_LOGO_LINUX_MONO)		+= logo_linux_mono.o

obj-$(CONFIG_SPU_BASE)			+= logo_spe_clut224.o

//添加宏进行控制
obj-$(CONFIG_LOGO_X210_CLUT224)         += logo_x210_clut224.o 

# Use logo-cfiles to retrieve list of .c files to be built
logo-cfiles = $(notdir $(patsubst %.$(2), %.c, \\
              $(wildcard $(srctree)/$(src)/*$(1).$(2))))

# Mono logos
extra-y += $(call logo-cfiles,_mono,pbm)

# VGA16 logos
extra-y += $(call logo-cfiles,_vga16,ppm)

# 224 Logos
extra-y += $(call logo-cfiles,_clut224,ppm)

# Gray 256
extra-y += $(call logo-cfiles,_gray256,pgm)

pnmtologo := scripts/pnmtologo

# Create commands like "pnmtologo -t mono -n logo_mac_mono -o ..."
quiet_cmd_logo = LOGO    $@
	cmd_logo = $(pnmtologo) \\
			-t $(patsubst $*_%,%,$(notdir $(basename $<))) \\
			-n $(notdir $(basename $<)) -o $@ $<

$(obj)/%_mono.c: $(src)/%_mono.pbm $(pnmtologo) FORCE
	$(call if_changed,logo)

$(obj)/%_vga16.c: $(src)/%_vga16.ppm $(pnmtologo) FORCE
	$(call if_changed,logo)

#将ppm格式转换成c语言文件
$(obj)/%_clut224.c: $(src)/%_clut224.ppm $(pnmtologo) FORCE
	$(call if_changed,logo)

$(obj)/%_gray256.c: $(src)/%_gray256.pgm $(pnmtologo) FORCE
	$(call if_changed,logo)

# Files generated that shall be removed upon make clean
clean-files := *.o *_mono.c *_vga16.c *_clut224.c *_gray256.c

(1)上面是内核中放ppm格式文件目录的Makefile,在匹配规则中,有将各种pbm、ppm等格式的图像转换成C语言文件的规则;
(2)规则的匹配是根据文件的后缀名来匹配,所以我们要注意对ppm文件的名字命名要以"_clut224"结尾;
(3)通过宏控制要编译哪些ppm格式的文件;

5、转换得到的logo_linux_clut224.c文件


static unsigned char logo_x210_clut224_data[] __initdata = 
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	······


static unsigned char logo_x210_clut224_clut[] __initdata = 
	0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x19, 0x09, 0x18, 0x36, 0x14,
	······


const struct linux_logo logo_x210_clut224 __initconst = 
	.type		= LINUX_LOGO_CLUT224,		//图像类型
	.width		= 800,						//宽
	.height		= 480,						//高
	.clutsize	= 102,						//颜色对照表大小
	.clut		= logo_x210_clut224_clut,	//颜色对照表
	.data		= logo_x210_clut224_data	//Logo 图像数据
;

(1)主要就是构建一个struct linux_logo结构体,这是内核用来描述logo图像信息的结构体;
(2)注意这个结构体的名字是和文件名一致的;

6、ppm文件如何与内核源码关联

(1)ppm格式转换成c语言文件,是根据文件的后缀名去Makefile中进行规则的匹配,自动完成编译;
(2)C语言文件中主体就是struct linux_logo结构体,名字和c文件的名字一致;
(3)在内核源码中就是匹配转换得到的struct linux_logo结构体;

7、替换内核启动logo图像

(1)制作ppm格式的文件,注意文件名字的后缀要符合Makefile的匹配规则,并且图像大小不能超过显示屏的分辨率;
(2)修改ppm文件同级的Makefile文件,按照其他的ppm文件的添加方式,通过宏进行控制;
(3)在内核源码的fb_find_logo()函数中,添加新生成的struct linux_logo结构体变量;

8、修改启动logo图像显示位置

修改fb_show_logo_line()函数中logo显示的起始坐标;

以上是关于linux显示启动logo源码分析以及修改显示logo的主要内容,如果未能解决你的问题,请参考以下文章

ARM开发板 嵌入式Linux 修改开机启动LOGO

Linux内核启动logo

Android源码发开记录-修改开机logo启动页、开机动画

Android开机logo简单修改方法

分析easyswoole源码

修改linux内核开机logo并居中全屏显示