一、充电动画的启动入口
充电动画是通过通过启动 kpoc_charger.rc的方式启动
kpoc_charger.rc的内容:
on charger
start kpoc_charger //充电打印服务
service kpoc_charger /system/bin/kpoc_charger
class charger //充电服务
所以充电动画是在lk kernel都启动后才启动的服务。
二、charger 服务
1.charger服务的启动入口是main.cpp,具体代码如下
int main(__attribute__((unused))int argc, __attribute__((unused))char *argv[])
{
KPOC_LOGI("charging mainargc=%d,argv=%s\n",argc,argv);
set_draw_anim_mode(1);
pthread_mutex_init(&lights_mutex, NULL);
setpriority(PRIO_PROCESS, 0, -20);
FILE *oom_adj = fopen("/proc/self/oom_score_adj", "w");
if (oom_adj) {
fputs("-17", oom_adj);
fclose(oom_adj);
}
//stop_backlight();
KPOC_LOGI("stop_backlight 1\n");
//充电动画的初始化,1.打开fb :dev/graphics/fb0 framebuffer驱动 2.充电类型的选择 mtk里面有两个充电类型一个老的一个是新的,呈现的界面不同而且。
bootlogo_init();
alarm_control();
//实现充电动画的逻辑,创建一个draw_with_interval线程来渲染画布,并通过dev/graphics/fb0 来更新显示画面 ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo)
charging_control();
unsigned int i;
for (i=0; i< ARRAY_SIZE(pwrkeys); i++)
KPOC_LOGI("pwrkeys[%d]:%d\n",i,pwrkeys[i]);
//loop 监听按键开关,随时停止动画,启动设备
key_control(pwrkeys, ARRAY_SIZE(pwrkeys)); //will loop inside
return 0;
}
2.
void bootlogo_init()
{
KPOC_LOGI("[ChargingAnimation: %s %d]\n",__FUNCTION__,__LINE__);
sync_anim_version();//设置动画走的是哪个版本 old version new version
anim_init(); //初始化
}
3.charging_animation.cpp ->anim_init()
void anim_init()
{
KPOC_LOGI("anim_init");
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]\n",__FUNCTION__,__LINE__);
}
//挂载文件系统,确定动画走的是哪个版本
anim_logo_init();
//根据前面设置的版本不同选择不同的分支
if (draw_anim_mode == (DRAW_ANIM_MODE_FB)) {
anim_fb_init();//初始化屏幕信息
} else {
anim_surface_init();//初始化屏幕信息
}
}
4.
void anim_logo_init(void)
{
// read and de-compress logo data here
int fd = 0;
int len = 0;
struct fstab_rec* rec;
fstab = fs_mgr_read_fstab_default();//文件挂载
if (!fstab) {
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("failed to open fstab\n");
}
error_flag = 1;
return;
}
//挂载logo.bin文件
rec = fs_mgr_get_entry_for_mount_point(fstab, LOGO_MNT_POINT);
if (rec == NULL) {
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("failed to get entry for %s\n", LOGO_MNT_POINT);
}
error_flag = 1;
return;
}
// "rec->blk_device" is the path 块设备
fd = open(rec->blk_device, O_RDONLY);
// get logo patition from fstab end
if(fd < 0)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]open logo partition device file fail, errno = %d \n",__FUNCTION__,__LINE__ , errno);
}
goto error_return;
}
logo_addr = (unsigned int*)malloc(LOGO_BUFFER_SIZE);
if(logo_addr == NULL)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]allocate logo buffer fail, size=0x%08x \n",__FUNCTION__,__LINE__ , LOGO_BUFFER_SIZE);
}
goto error_return;
}
// (1) skip the image header
len = read(fd, logo_addr, 512);
if (len < 0)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]read from logo addr for 512B is failed! \n",__FUNCTION__,__LINE__);
}
goto error_return;
}
// get the image
len = read(fd, logo_addr, LOGO_BUFFER_SIZE - 512);
if (len < 0)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]read from logo addr for buffer is failed! \n",__FUNCTION__,__LINE__);
}
goto error_return;
}
close(fd);
//set_anim_version 改变了show_animationm_ver的值
//pinfo[0] 是代表图片量多少, pinfo[1]代表总图片size
if (show_animationm_ver > 0)
{
unsigned int *pinfo = (unsigned int*)logo_addr;
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]pinfo[0]=%d, pinfo[1]=%d\n", __FUNCTION__,__LINE__, pinfo[0], pinfo[1]);
}
if ((show_animationm_ver == VERION_WIRELESS_CHARGING_ANIMATION) && (pinfo[0] < ANIM_V2_LOGO_NUM))
{
set_anim_version(VERION_NEW_ANIMATION);
}
//对log数量进行判断,如果是小于39个,就采用旧的动画模式
if (pinfo[0] < ANIM_V1_LOGO_NUM)
{
kernel_logo_position = ANIM_V0_LOGO_NUM - 1;
set_anim_version(VERION_OLD_ANIMATION);
}
}
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d]show_animationm_ver = :%d",__FUNCTION__,__LINE__ ,show_animationm_ver);
}
return;
error_return:
if(fd >= 0)
{
close(fd);
}
free_fstab();
sleep(3);
error_flag = 1;
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[libshowlogo: %s %d] error return !!!\n",__FUNCTION__,__LINE__);
}
// to prevent interlace operation with MD reset
}
至此图片的初始化结束,总结的来说就是为了获取到logo.bin文件的地址logo_addr,而logo的文件是有哪些呢?
alps\vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\rules.mk
RESOURCE_OBJ_LIST += \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_battery.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_low_battery.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_charger_ov.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_0.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_1.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_2.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_3.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_4.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_5.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_6.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_7.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_8.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_9.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_num_percent.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_01.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_02.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_03.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_04.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_05.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_06.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_07.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_08.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_09.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_animation_10.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_01.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_02.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_03.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_04.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_05.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_06.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_07.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_08.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_09.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_10_10.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_bg.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_img.raw \
$(BOOT_LOGO_DIR)/$(BASE_LOGO)/$(BASE_LOGO)_bat_100.raw
这里总37张图片,再加上lk和boot的logo logo.bin解析后所有的图片就是39张。图片的数量和图片的顺序很总要,后面动画的显示就是根据电池电量来加载第几张图片,一旦顺序错乱就会产生图片加载问题。
5.
void charging_control()
{
int ret = 0;
pthread_attr_t attr, attrd, attrl;
pthread_t uevent_thread, draw_thread, light_thread;
//charging led control 充电led灯控制
if (!is_charging_source_available()) {
lights_exit();
}
pthread_mutex_init(&mutexlstate, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_attr_init(&attr);
pthread_attr_init(&attrd);
pthread_attr_init(&attrl);
inDraw = 0;
ret = pthread_create(&uevent_thread, &attr, uevent_thread_routine, NULL);//创建一个event线程
if (ret != 0)
{
KPOC_LOGI("create uevt pthread failed.\n");
exit_charger(EXIT_ERROR_SHUTDOWN);
}
firstTime = 1;
ret = pthread_create(&draw_thread, &attrd, draw_thread_routine, NULL);//创建一个充电线程
if (ret != 0)
{
KPOC_LOGI("create draw pthread failed.\n");
exit_charger(EXIT_ERROR_SHUTDOWN);
}
}
6.
static void* draw_thread_routine(__attribute__((unused))void *arg)
{
int i, bc, bc_offset = 0;
int fd_fb = -1, err =0;
char buf[PROPERTY_VALUE_MAX];
char filename[32] = {0};
do {
KPOC_LOGI("draw thread working2...\n");
// move here to avoid suspend when syncing with surfaceflinger
if(firstTime){
// make sure charging source online when in KPOC mode
// add 2s tolerance
if(wait_until(is_charging_source_available,
charging_source_waiting_duration_ms,
charging_source_waiting_interval_ms))
{
KPOC_LOGI("wait until charging source available\n");
}else{
KPOC_LOGI("charging source not available for %d ms at KPOC starup\n",
charging_source_waiting_duration_ms);
}
firstTime = 0;
}
inDraw = 1;
// check the bc offest value
bc = get_capacity();
//开始画充电动画
draw_with_interval(bootlogo_show_charging, bc, nChgAnimDuration_msec, nCbInterval_msec);
stop_backlight();
// @@@ draw fb again to refresh ddp
bootlogo_show_charging(bc, 1);
/* make fb blank */
snprintf(filename, sizeof(filename), "/dev/graphics/fb0");
fd_fb = open(filename, O_RDWR);
if (fd_fb < 0) {
KPOC_LOGI("Failed to open fb0 device: %s", strerror(errno));
}
err = ioctl(fd_fb, FBIOBLANK, FB_BLANK_POWERDOWN);
if (err < 0) {
KPOC_LOGI("Failed to blank fb0 device: %s", strerror(errno));
}
if (fd_fb >= 0)
close(fd_fb);
request_suspend(true);
inDraw = 0;
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
} while(1);
pthread_exit(NULL);
return NULL;
}
7.根据电池电量的不同,显示不同的充电动画,低于10%的电池容量显示低电充电,10%-90%就是一般 100% 显示一张充满点的图片
void bootlogo_show_charging(int capacity, int cnt)
{
KPOC_LOGI("[ChargingAnimation: %s %d]%d, %d",__FUNCTION__,__LINE__, capacity, cnt);
if (get_battnotify_status())
{
KPOC_LOGI("[ChargingAnimation: %s %d] show_charger_error_logo, get_battnotify_status()= %d \n",__FUNCTION__,__LINE__, get_battnotify_status());
show_charger_ov_logo();
return;
}
if (showLowBattLogo)
{
KPOC_LOGI("[ChargingAnimation: %s %d] show_low_battery , showLowBattLogo = %d \n",__FUNCTION__,__LINE__,showLowBattLogo);
show_low_battery(); //低电充电
return;
}
show_battery_capacity(capacity);//显示充电容量
}
8.show_animation_common.c show_battery_capacity ->fill_animation_battery_by_ver->fill_animation_battery_new
void fill_animation_battery_new(int capacity, void *fill_addr, void * dec_logo_addr, void * logo_addr, LCM_SCREEN_T phical_screen)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_animation_common fill_animation_battery_new: %s %d]capacity : %d\n",__FUNCTION__,__LINE__, capacity);
}
//充电容量是100%的情况
if (capacity >= 100) {
//show_logo(37); // battery 100
//FULL_BATTERY_INDEX=37
fill_animation_logo(FULL_BATTERY_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);
}
//充电容量小于10%的情况
else if (capacity < 10) {
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_animation_common: %s %d]charging_low_index = %d\n",__FUNCTION__,__LINE__, charging_low_index);
}
charging_low_index ++ ;
//LOW_BAT_ANIM_START_0=25 NUMBER_PIC_PERCENT=14 NUMBER_PIC_START_0=4
//充电log
fill_animation_logo(LOW_BAT_ANIM_START_0 + charging_low_index, fill_addr, dec_logo_addr, logo_addr,phical_screen);
//充电动画主题画面
fill_animation_number(NUMBER_PIC_START_0 + capacity, 1, fill_addr, logo_addr, phical_screen);
//充电百分比
fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
if (charging_low_index >= 9) charging_low_index = 0;
}
//除了上述两种情况的其他情况
else {
unsigned int capacity_grids = 0;
static RECT_REGION_T battery_rect = {CAPACITY_LEFT,CAPACITY_TOP,CAPACITY_RIGHT,CAPACITY_BOTTOM};
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_animation_common: %s %d]capacity_grids : %d,charging_animation_index = %d\n"
,__FUNCTION__,__LINE__, capacity_grids,charging_animation_index);
}
//background
fill_animation_logo(ANIM_V1_BACKGROUND_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);
fill_animation_line(ANIM_LINE_INDEX, capacity_grids, fill_addr, logo_addr, phical_screen);
//fill_animation_number(NUMBER_PIC_START_0 + (capacity/10), 0, fill_addr, logo_addr, phical_screen);
//fill_animation_number(NUMBER_PIC_START_0 + (capacity%10), 1, fill_addr, logo_addr, phical_screen);
//fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
if (capacity <= 90)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_animation_common: %s %d]capacity : %d,charging_animation_index = %d\n"
,__FUNCTION__,__LINE__, capacity,charging_animation_index);
}
RECT_REGION_T top_animation_rect = {TOP_ANIMATION_LEFT, capacity_grids - (TOP_ANIMATION_BOTTOM - TOP_ANIMATION_TOP), TOP_ANIMATION_RIGHT, capacity_grids};
//top_animation_rect.bottom = capacity_grids;
//top_animation_rect.top = capacity_grids - top_animation_height;
charging_animation_index++;
//show_animation_dynamic(15 + charging_animation_index, top_animation_rect, top_animation_addr);
fill_animation_dynamic(BAT_ANIM_START_0 + charging_animation_index, top_animation_rect, fill_addr,
top_animation_addr, logo_addr, phical_screen);
if (charging_animation_index >= 9) charging_animation_index = 0;
}
}
}
总结:这个函数比较重要,这里面制定了充电动画的显示界面,其实充电动画的图片的显示就是一个画布,fill_animation_logo fill_animation_number fill_animation_dynamic 这些函数加载一张张图片,按照画布的方式显示。所以一旦发现出现错位问题就是修改rect的四个顶点。
8.fill_animation_logo->fill_rect_with_content->fill_rect_with_content_by_16bit_argb8888
void fill_rect_with_content_by_16bit_argb8888(unsigned short *fill_addr, RECT_REGION_T rect, unsigned int *src_addr, LCM_SCREEN_T phical_screen)
{
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_logo_common: %s %d]\n",__FUNCTION__,__LINE__);
}
int virtual_width = phical_screen.needAllign == 0? phical_screen.width:phical_screen.allignWidth;
int virtual_height = phical_screen.height;
int i = 0;
int j = 0;
unsigned short * dst_addr = fill_addr;
unsigned int * color_addr = src_addr;
for(i = rect.top; i < rect.bottom; i++)
{
for(j = rect.left; j < rect.right; j++)
{
switch (phical_screen.rotation)
{
case 90:
color_addr = src_addr++;
//翻转90 i j 互换
dst_addr = fill_addr + (virtual_width * j + virtual_width - i - 1);
break;
case 270:
color_addr = src_addr++;
dst_addr = fill_addr + ((virtual_width * (virtual_height - j - 1)+ i));
break;
case 180:
// adjust fill in address
color_addr = src_addr++;
//由于图片是翻转180,virtual_width*virtual_height 开始的位置,每增加一个i 就减去virtual_width*i
dst_addr = fill_addr + virtual_width * (virtual_height - i)- j-1-(virtual_width-phical_screen.width);
break;
default:
color_addr = src_addr++;
dst_addr = fill_addr + virtual_width * i + j;
}
if(11 == phical_screen.blue_offset) {
*dst_addr = ARGB8888_TO_BGR565(*color_addr);
} else {
*dst_addr = ARGB8888_TO_RGB565(*color_addr);
}
if((i == rect.top && j == rect.left) || (i == rect.bottom - 1 && j == rect.left) ||
(i == rect.top && j == rect.right - 1) || (i == rect.bottom - 1 && j == rect.right - 1)){
if (MTK_LOG_ENABLE == 1) {
KPOC_LOGI("[show_logo_common]dst_addr= 0x%08x, color_addr= 0x%08x, i= %d, j=%d\n", *dst_addr, *color_addr, i , j);
}
}
}
}
}
总结:上面的函数的意义就是把图片的一个个argb数据映射到framebuffer驱动里面,最后通过ioctl(fb_fd, FBIOPUT_VSCREENINFO,&vinfo)来更新framebuffer驱动的显示。
充电动画比较难点在于图片的位置摆放
小技巧:1)打开画图软件,选择 查看->缩放->自定义,将图片放到到800%
2)选择 查看->缩放->显示网格
这样就可以看到一个一个的像素
确定rect的 left,top,right,bottom