http://blog.sina.com.cn/s/blog_a6559d920101k09x.html
一、android正常开机流程、关机充电流程
在写这篇文章之前我们先看两个流程:正常开机流程,关机充电系统启动流程
1、正常开机流程,按开机键。
可大致分成三部分
(1)、OS_level:UBOOT、kenrel、init这三步完成系统启动;
(2)、Android_level:这部分完成android部的初始化;
(3)、Home Screen:这部分就是我们看到的launcher部分。
2、关机充电系统启动流程
二、关机充电逻辑硬件逻辑
1、插入DC,charger IC从硬件上唤醒系统,相当于长按开机键开机。
下面这部分是charger IC连接系统的控制部分。
三、软件逻辑。
DC插入,其实相当于关机状态下“按开机键”开机。第一步要走UBOOT、kernel 、androidinit这一流程。
1、UBOOT
a:如何判断是DC插入;
b:设定setenv("bootargs", "androidboot.mode=charger"),androidboot.mode这个参数相当重要,这个参数决定系统是正常启动、还是关机充电状态。
Uboot/board/samsung/smdk4212/smkd4212.c
- int
board_late_init (void) - {
-
int keystate = 0; -
printf("check start mode\n"); -
if ((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212) - ||
(*(int *)0x10020808==0x19721212)) //(1)、检查是否有DC插入; - {
-
setenv ("bootargs", "");//(2)、没有DC插入; -
} else {//DC插入 -
int tmp=*(int *)0x11000c08; -
*(int *)0x10020800=*(int *)0x10020804=0x19721212; -
*(int *)0x11000c08=(tmp&(~0xc000))|0xc000; -
udelay(10000); -
if ((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) { -
setenv ("bootargs", "androidboot.mode=charger");//(3)、设定bootargs为charger状态 -
printf("charger mode\n"); -
} else { -
setenv ("bootargs", ""); -
} -
*(int *)0x11000c08=tmp; -
} - #ifdef
CONFIG_CPU_EXYNOS4X12 -
int charge_status=CheckBatteryLow();//(4)、检查电池电量; -
keystate=board_key_check();//(5)、检查按键状态; -
// fuse bootloader -
if(second_boot_info != 0) { -
boot_symbol=1; -
INF_REG2_REG =0x8; -
run_command(CONFIG_BOOTCMD_FUSE_BOOTLOADER, NULL); -
} -
if((INF_REG4_REG == 0xd)) { -
// reboot default -
char buf[10]; -
sprintf(buf, "%d", CONFIG_BOOTDELAY); -
setenv ("bootdelay", buf); -
setenv ("reserved", NULL); -
saveenv(); -
} else if((INF_REG4_REG == 0xe) || keystate == (0x1 | 0x2)) {//(6)、按键进入fastboot模式; -
// reboot bootloader -
boot_symbol=1; -
INF_REG2_REG =0x8; -
printf("BOOTLOADER - FASTBOOT\n"); -
setenv ("reserved", "fastboot"); -
setenv ("bootdelay", "0"); -
} else if((INF_REG4_REG == 0xf) || keystate == (0x1 | 0x2 | 0x4)) {//(7)、按键进入recovery模式; -
// reboot recovery -
printf("BOOTLOADER - RECOVERY\n"); -
boot_symbol=1; -
INF_REG2_REG =0x8; -
setenv ("reserved", CONFIG_BOOTCMD_RECOVERY); -
setenv ("bootdelay", "0"); -
} else -
if(keystate == (0x1 | 0x4) || second_boot_info != 0 || partition_check()) {//(8)、按键进入卡升级模式; -
// 2nd boot -
printf("BOOTLOADER - 2ND BOOT DEVICE\n"); -
boot_symbol=1; -
INF_REG2_REG =0x8; -
setenv ("bootcmd", CONFIG_BOOTCOMMAND); -
setenv ("reserved", CONFIG_BOOTCMD_FUSE_RELEASE); -
setenv ("bootdelay", "0"); -
} else {//(9)、正常启动; -
// normal case -
char buf[10]; -
sprintf(buf, "%d", CONFIG_BOOTDELAY); -
setenv ("bootdelay", buf); -
} -
INF_REG4_REG = 0; -
return 0; - }
(1)、检查是否有DC插入;
- if
((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212) -
(*(int *)0x10020808==0x19721212))
这部分检查寄存器的值。
(2)、没有DC插入;
(3)、设定bootargs为charger状态
- if
((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) { -
setenv ("bootargs", "androidboot.mode=charger");
这是这部分的重点,如果能过寄存器判断是DC插入,把androidboot.mode设定为charger状态。
以下这部分根据需要加入,通过判断不同的情况进入不同的功能,如fastboot\revovery…………,这部分不做详细解释。
(4)、检查电池电量;
(5)、检查按键状态;
(6)、按键进入fastboot模式;
(7)、按键进入recovery模式;
(8)、按键进入卡升级模式
(9)、正常启动;
2、kernel
这部分和正常启动是一样的。
3、init
前面所有的描述其实只有一点和正常启动不太一样,那就是在UBOOT中把androidboot.mode设定为charger状态,内核正常流程启动,然后到init时要对charger这种状态处理。
system\core\init\init.c
- int
main(int argc, char **argv) - {
-
……………… -
action_for_each_trigger("early-init", action_add_queue_tail); -
-
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); -
queue_builtin_action(property_init_action, "property_init"); -
queue_builtin_action(keychord_init_action, "keychord_init"); -
queue_builtin_action(console_init_action, "console_init"); //(1)、显示initlogo.rle,也就是android第二张图片; -
queue_builtin_action(set_init_properties_action, "set_init_properties"); -
-
-
action_for_each_trigger("init", action_add_queue_tail); -
-
-
if (strcmp(bootmode, "charger") != 0) {//(2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化; -
action_for_each_trigger("early-fs", action_add_queue_tail); -
action_for_each_trigger("fs", action_add_queue_tail); -
action_for_each_trigger("post-fs", action_add_queue_tail); -
action_for_each_trigger("post-fs-data", action_add_queue_tail); -
} -
-
queue_builtin_action(property_service_init_action, "property_service_init"); -
queue_builtin_action(signal_init_action, "signal_init"); -
queue_builtin_action(check_startup_action, "check_startup"); -
-
if (!strcmp(bootmode, "charger")) {//(3)、如果为charger,则调用charger.c。 -
action_for_each_trigger("charger", action_add_queue_tail); -
} else { -
action_for_each_trigger("early-boot", action_add_queue_tail); -
action_for_each_trigger("boot", action_add_queue_tail); -
} - ……………………
- }
(1)、显示initlogo.rle,也就是android第二张图片;
queue_builtin_action(console_init_action,"console_init");调用console_init_action
- static
int console_init_action(int nargs, char **args) - {
-
int fd; -
char tmp[PROP_VALUE_MAX]; -
if (console[0]) { -
snprintf(tmp, sizeof(tmp), "/dev/%s", console); -
console_name = strdup(tmp); -
} -
fd = open(console_name, O_RDWR); -
if (fd >= 0) -
have_console = 1; -
close(fd); -
if( load_565rle_image(INIT_IMAGE_FILE) ) {//这里定义rle文件的名称#define INIT_IMAGE_FILE "/initlogo.rle" -
fd = open("/dev/tty0", O_WRONLY); -
if (fd >= 0) {//如果没有这张图片,就显示android字样,在屏幕左上角; -
const char *msg; -
msg = "\n" -
"\n" -
"\n" // console is 40 cols x 30 lines -
"\n" -
"\n" -
"\n" -
"\n" -
"\n" -
"\n" -
"\n" -
" A N D R O I D "; -
write(fd, msg, strlen(msg)); -
close(fd); -
} -
} -
return 0; - }
(2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化;
-
- if
(strcmp(bootmode, "charger") != 0) { -
action_for_each_trigger("early-fs", action_add_queue_tail); -
action_for_each_trigger("fs", action_add_queue_tail); -
action_for_each_trigger("post-fs", action_add_queue_tail); -
action_for_each_trigger("post-fs-data", action_add_queue_tail); - }
(3)、如果为charger,则调用charger.c
- action_for_each_trigger("charger",
action_add_queue_tail);
我们在后面细分charger这部分。
4、charger.c
这部分就是我们充电部分,充电画面显示的实现。
system\core\charger\charger.c
- int
main(int argc, char **argv) - {
- ………………
-
klog_set_level(CHARGER_KLOG_LEVEL); -
dump_last_kmsg(); -
LOGI("--------------- STARTING CHARGER MODE ---------------\n"); -
-
gr_init(); -
gr_font_size(&char_width, &char_height); //(1)、初始化graphics,包括buf大小; -
-
ev_init(input_callback, charger);//(2)初始化按键; -
- fd
= uevent_open_socket(64*1024, true); -
if (fd >= 0) { -
fcntl(fd, F_SETFL, O_NONBLOCK); -
ev_add_fd(fd, uevent_callback, charger); -
} -
-
charger->uevent_fd = fd; -
coldboot(charger, "/sys/class/power_supply", "add");//(3)、创建/sys/class/power_supply结点,把socket信息通知应用层; -
- ret
= res_create_surface("charger/battery_fail", &charger->surf_unknown); -
if (ret < 0) { -
LOGE("Cannot load image\n"); -
charger->surf_unknown = NULL; -
} -
for (i = 0; i < charger->batt_anim->num_frames; i++) {//(4)、这里是显示charger logo,res_create_surface显示图片函数; -
struct frame *frame = &charger->batt_anim->frames[i]; -
ret = res_create_surface(frame->name, &frame->surface); -
if (ret < 0) { -
LOGE("Cannot load image %s\n", frame->name); -
-
charger->batt_anim->num_frames = 0; -
charger->batt_anim->num_cycles = 1; -
break; -
} -
} - ev_sync_key_state(set_key_callback,
charger); -
gr_fb_blank(true); -
-
charger->next_screen_transition = now - 1; -
charger->next_key_check = -1; -
charger->next_pwr_check = -1; -
reset_animation(charger->batt_anim); -
kick_animation(charger->batt_anim); -
event_loop(charger);//(5)、event_loop循环,电池状态,检测按键是否按下; -
return 0; -
- }
(1)、初始化graphics,包括buf大小
android/bootable/recovery/minui/graphics.c
gr_init():minui/graphics.c[settty0 to graphic mode, openfb0],设制tty0为图形模式,打开fb0;
- int
gr_init(void) - {
-
gglInit(&gr_context); -
GGLContext *gl = gr_context; -
gr_init_font(); -
gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC); -
if (gr_vt_fd < 0) { -
// This is non-fatal; post-Cupcake kernels don't have tty0. -
perror("can't open /dev/tty0"); -
-
} else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) { -
// However, if we do open tty0, we expect the ioctl to work. -
perror("failed KDSETMODE to KD_GRAPHICS on tty0"); -
gr_exit(); -
return -1; -
} -
gr_fb_fd = get_framebuffer(gr_framebuffer); -
if (gr_fb_fd < 0) { -
gr_exit(); -
return -1; -
} -
get_memory_surface(&gr_mem_surface); -
fprintf(stderr, "framebuffer: fd %d (%d x %d)\n", -
gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); -
-
gr_active_fb = 0; -
set_active_framebuffer(0); -
gl->colorBuffer(gl, &gr_mem_surface); -
gl->activeTexture(gl, 0); -
gl->enable(gl, GGL_BLEND); -
gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); -
gr_fb_blank(true); -
gr_fb_blank(false); -
return 0; -
- }
(2)android/bootable/recovery/minui/events.c
ev_init():minui/events.c[open /dev/input/event*]打开/dev/input/event*
这部分是在,充电状态下,按键操作的初始化,比如:短按显示充电logo,长按开机,初始化代码如下。
- int
ev_init(ev_callback input_cb, void *data) - {
-
DIR *dir; -
struct dirent *de; -
int fd; -
dir = opendir("/dev/input");//打开驱动结点; -
if(dir != 0) { -
while((de = readdir(dir))) { -
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; - //
fprintf(stderr,"/dev/input/%s\n", de->d_name); -
if(strncmp(de->d_name,"event",5)) continue; -
fd = openat(dirfd(dir), de->d_name, O_RDONLY); -
if(fd < 0) continue; -
-
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) { -
close(fd); -
continue; -
} -
-
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) { -
close(fd); -
continue; -
} -
ev_fds[ev_count].fd = fd; -
ev_fds[ev_count].events = POLLIN; -
ev_fdinfo[ev_count].cb = input_cb; -
ev_fdinfo[ev_count].data = data; -
ev_count++; -
ev_dev_count++; -
if(ev_dev_count == MAX_DEVICES) break; -
} -
} -
return 0; - }
(3)、创建/sys/class/power_supply结点,把socket信息通知应用层
uevent_open_socket这个函数是通过kobject_uevent的方式通知的应用层,就是往一个socket广播一个消息,只需要在应用层打开socket监听NETLINK_KOBJECT_UEVENT组的消息,就可以收到了,主要是创建了socket接口获得uevent的文件描述符,然后触发/sys/class/power_supply目录及其子目录下的uevent,然后接受并创建设备节点,至此设备节点才算创建。
(4)、这里显示charger logo,res_create_surface显示图片函数;
res_create_surface:minui/resource.c[create surfaces for all bitmapsused later, include icons, bmps]
创建surface为所以的位图,包括图标、位图。
(5)、event_loop循环,电池状态,检测按键是否按下;
5、event_loop
- static
void event_loop(struct charger *charger) - {
-
int ret; -
while (true) { -
int64_t now = curr_time_ms();//(1)、获得当前时间; -
LOGV("[%lld] event_loop()\n", now); -
handle_input_state(charger, now);//(2)、检查按键状态; -
handle_power_supply_state(charger, now);// (3)、检查DC是否拔出; -
-
update_screen_state(charger, now);//(4)、对按键时间状态标志位的判断,显示不同电量的充电logo; -
wait_next_event(charger, now); -
} - }
(1)、获得当前时间;
(2)、检查按键状态;
- static
void handle_input_state(struct charger *charger, int64_t now) - {
-
process_key(charger, KEY_POWER, now); -
if (charger->next_key_check != -1 && now > charger->next_key_check) -
charger->next_key_check = -1; - }
- 我们再看下:process_key(charger,
KEY_POWER, now); - static
void process_key(struct charger *charger, int code, int64_t now) - {
- ………………
-
if (code == KEY_POWER) { -
if (key->down) { -
int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; -
if (now >= reboot_timeout) {//如果长按power键,就重新启动,也就是重启开机; -
LOGI("[%lld] rebooting\n", now); -
android_reboot(ANDROID_RB_RESTART, 0, 0);//重启命令; -
} -
……………… -
} -
-
key->pending = false; - }
(3)、检查DC是否拔出;
handle_power_supply_state(charger, now);
- static
void handle_power_supply_state(struct charger *charger, int64_t now) - {
-
if (charger->num_supplies_online == 0) { -
if (charger->next_pwr_check == -1) { -
charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; -
LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n", -
now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); -
} else if (now >= charger->next_pwr_check) { -
LOGI("[%lld] shutting down\n", now); -
android_reboot(ANDROID_RB_POWEROFF, 0, 0);//如果DC拔出,则关机; -
} - ………………
- }
(4)、对按键时间状态标志位的判断,显示不同电量的充电logo;
这个函数比较长了,其实做用就是:我们在状态的过程中,充电logo的电量是要增加的,比如电量是20%时,要从第一格开始闪烁;如果是80%时,则要从第三格开始闪烁,电量显示就是通过这个函数来计算实现的。
- static
void update_screen_state(struct charger *charger, int64_t now) - {
-
struct animation *batt_anim = charger->batt_anim; -
int cur_frame; -
int disp_time; -
-
if (!batt_anim->run || now < charger->next_screen_transition) -
return; -
-
-
if (batt_anim->cur_cycle == batt_anim->num_cycles) { -
reset_animation(batt_anim); -
charger->next_screen_transition = -1; -
gr_fb_blank(true); -
LOGV("[%lld] animation done\n", now); -
return; -
} -
-
disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; -
-
-
if (batt_anim->cur_frame == 0) { -
int batt_cap; -
int ret; -
-
LOGV("[%lld] animation starting\n", now); -
batt_cap = get_battery_capacity(charger); -
if (batt_cap >= 0 && batt_anim->num_frames != 0) { -
int i; -
-
-
for (i = 1; i < batt_anim->num_frames; i++) { -
if (batt_cap < batt_anim->frames[i].min_capacity) -
break; -
} -
batt_anim->cur_frame = i - 1; -
-
-
disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2; -
} -
-
batt_anim->capacity = batt_cap; -
} -
-
-
if (batt_anim->cur_cycle == 0) -
gr_fb_blank(false); -
-
-
redraw_screen(charger); -
-
-
if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) { -
LOGV("[%lld] animation missing or unknown battery status\n", now); -
charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; -
batt_anim->cur_cycle++; -
return; -
} -
-
-
charger->next_screen_transition = now + disp_time; -
-
-
batt_anim->cur_frame++; -
-
-
while (batt_anim->cur_frame < batt_anim->num_frames && -
batt_anim->frames[batt_anim->cur_frame].level_only) -
batt_anim->cur_frame++; -
if (batt_anim->cur_frame >= batt_anim->num_frames) { -
batt_anim->cur_cycle++; -
batt_anim->cur_frame = 0; -
-
-
} - }
下面是不能容量时显示logo的函数:
- static
struct frame batt_anim_frames[] = { -
{ -
.name = "charger/battery_0", -
.disp_time = 750, -
.min_capacity = 0, -
}, -
{ -
.name = "charger/battery_1", -
.disp_time = 750, -
.min_capacity = 20, -
}, -
{ -
.name = "charger/battery_2", -
.disp_time = 750, -
.min_capacity = 40, -
}, -
{ -
.name = "charger/battery_3", -
.disp_time = 750, -
.min_capacity = 60, -
}, -
{ -
.name = "charger/battery_4", -
.disp_time = 750, -
.min_capacity = 80, -
.level_only = true, -
}, -
{ -
.name = "charger/battery_5", -
.disp_time = 750, -
.min_capacity = BATTERY_FULL_THRESH, -
}, - };