zoukankan      html  css  js  c++  java
  • Android 8.1 关机充电动画(一)模式选择

    system:Android 8.1
    platform:RK3326/PX30
    uboot
    kernel


    Android 8.1 关机充电动画(一)模式选择
    Android 8.1 关机充电动画(二)Uboot模式
    Android 8.1 关机充电动画(三)Android模式


    前言

    关机充电的动画可以在u-boot或者Androidcharger模式工作,这是两个相互独立的部分,RK平台上需要在设备树进行配置。顾名思义u-boot下的charger模式,系统仍然运行在loader下,并未启动内核。
    Androidcharger模式下,在引导程序运行期间会传递参数给内核,则会启动内核并加载内核模块,同时安卓系统也不会启动,系统最终只工作charger模式。这里简单记录一下,在uboot中都做了哪些工作,根据什么条件选择工作的模式。

    配置

    设备树要增加节点charge-animation,这个rk的文档里有相应的说明,在配置下面设备树的配置中,我已经加上了注释,对照这个看就行,不过所有属性只适用u-boot下的关机充电,而不适用用Android下的关机充电,这个具体会在后面加以说明。

    这里暂时把已经启动内核之后再进入charger的模式称作Android下的关机充电,可能有些不妥,但是先这样区分吧

    具体的配置如下;
    rockchip,uboot-charge-on = <0>; ,rockchip,android-charge-on = <1>; 这两个属性用于选择后续的程序会进入到哪一种模式工作,后面在代码中会加以区分。

    	charge-animation {
    		compatible = "rockchip,uboot-charge";
    		rockchip,uboot-charge-on = <0>; // 是否在U-Boot进行充电
    		rockchip,android-charge-on = <1>; // 是否在Android进行充电
    		rockchip,uboot-exit-charge-level = <5>; // U-Boot充电时,允许开机的最低电量
    		rockchip,uboot-exit-charge-voltage = <3600>; // U-Boot充电时,允许开机的最低电压
    		rockchip,screen-on-voltage = <3400>; // U-Boot充电时,允许点亮屏幕的最低电压
    		rockchip,uboot-low-power-voltage = <3350>; // U-Boot无条件强制进入充电模式的最低电压
    		rockchip,system-suspend = <0>; // 灭屏时进入trust进行低功耗待机
    		rockchip,auto-off-screen-interval = <10>;// 亮屏超时后自动灭屏,单位秒。(如果没有这个属性,则默认15s)
    		rockchip,auto-wakeup-interval = <0>; // 休眠自动唤醒时间,单位秒。(如果值为0或没有这个属性,则禁止休眠自动唤醒)
    		rockchip,auto-wakeup-screen-invert = <0>; // 休眠自动唤醒的时候,是否让屏幕产生亮/灭效果
    		status = "okay";
    	};
    

    代码分析

    可以定位到u-boot/drivers/power/charge_animation.c,目前只分析一下函数static int charge_animation_show(struct udevice *dev),具体代码见最后的附录
    这个函数会在启动内核之前进行一个简单的检测;

     1. Extrem low power charge?
     2. Preboot cmd?
     3. Valid boot mode?
     4. U-Boot charge enabled by dts config?
     5. Screen off before charge?
     6. Enter charge !
    

    分别是1 检测低电量2 检测启动命令3 检测启动模式4 检测设备树配置,这四项检测如果不符合会直接return,然后无法成功进入6 Enter charge的状态。具体的可以在代码里面慢慢看,至于第五点,不太清楚了。这里先把第四点的部分相关的代码抠出来,如下:

    	/* charge mode */
    	pdata->uboot_charge =
    		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
    	pdata->android_charge =
    		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
    
    	/* Enter android charge, set property for kernel */
    	if (pdata->android_charge) {
    		env_update("bootargs", "androidboot.mode=charger");
    	}
    	/* Not enable U-Boot charge, exit */
    	if (!pdata->uboot_charge) {
    		debug("exit charge, due to not enable uboot charge
    ");
    		return 0;
    	}
    

    可以看到这里有两种情况

    1. uboot_charge如果在设备树里配置为开启,则无法进入Android 关机充电模式了,然后最终程序会进入到while(1)不断循环显示充电动画,检测到相应事件动作之后才会退出充电进入系统,或者掉电关机;
    2. android_charge在设备树中设置开启,并且 uboot_charge设置为关闭的情况下,会设置内核的启动参数env_update("bootargs", "androidboot.mode=charger"),之后uboot会启动内核,并传参数给内核,告诉他,要进入Android关机充电的模式

    总结

    这里篇幅较短,主要分析了在u-bootcharge_animation_show的部分工作流程即如何选择最终充电的模式,下面会简单介绍uboot下关机充电Android下关机充电的动画定制。

    附录

    代码可能已经更新请参考u-boot/drivers/power/charge_animation.c

    static int charge_animation_show(struct udevice *dev)
    {
    	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
    	struct charge_animation_priv *priv = dev_get_priv(dev);
    	const struct charge_image *image = priv->image;
    	struct udevice *pmic = priv->pmic;
    	struct udevice *fg = priv->fg;
    	const char *preboot = env_get("preboot");
    	int image_num = priv->image_num;
    	bool ever_lowpower_screen_off = false;
    	bool screen_on = true;
    	ulong show_start = 0, charge_start = 0, debug_start = 0;
    	ulong delta;
    	ulong ms = 0, sec = 0;
    	int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_SHOW_RESET;
    	int soc, voltage, current, key_state;
    	int i, charging = 1, ret;
    	int boot_mode;
    	int first_poll_fg = 1;
    
    /*
     * Check sequence:
     *
     * 1. Extrem low power charge?
     * 2. Preboot cmd?
     * 3. Valid boot mode?
     * 4. U-Boot charge enabled by dts config?
     * 5. Screen off before charge?
     * 6. Enter charge !
     *
     */
    	if (!fuel_gauge_bat_is_exist(fg)) {
    		printf("Exit charge: battery is not exist
    ");
    		return 0;
    	}
    
    	/* Extrem low power charge */
    	ret = charge_extrem_low_power(dev);
    	if (ret < 0) {
    		printf("extrem low power charge failed, ret=%d
    ", ret);
    		return ret;
    	}
    
    	/* If there is preboot command, exit */
    	if (preboot && !strstr(preboot, "dvfs")) {
    		printf("Exit charge: due to preboot cmd '%s'
    ", preboot);
    		return 0;
    	}
    
    	/* Not valid charge mode, exit */
    #ifdef CONFIG_RKIMG_BOOTLOADER
    	boot_mode = rockchip_get_boot_mode();
    	if ((boot_mode != BOOT_MODE_CHARGING) &&
    	    (boot_mode != BOOT_MODE_UNDEFINE)) {
    		printf("Exit charge: due to boot mode
    ");
    		return 0;
    	}
    #endif
    
    	/* Not charger online, exit */
    	charging = fuel_gauge_get_chrg_online(fg);
    	if (charging <= 0) {
    		printf("Exit charge: due to charger offline
    ");
    		return 0;
    	}
    
    	/* Enter android charge, set property for kernel */
    	if (pdata->android_charge) {
    		env_update("bootargs", "androidboot.mode=charger");
    		printf("Android charge mode
    ");
    	}
    
    	/* Not enable U-Boot charge, exit */
    	if (!pdata->uboot_charge) {
    		printf("Exit charge: due to not enable uboot charge
    ");
    		return 0;
    	}
    
    	voltage = fuel_gauge_get_voltage(fg);
    	if (voltage < 0) {
    		printf("get voltage failed: %d
    ", voltage);
    		return -EINVAL;
    	}
    
    	/* If low power, turn off screen */
    	if (voltage <= pdata->screen_on_voltage + 50) {
    		screen_on = false;
    		ever_lowpower_screen_off = true;
    		charge_show_bmp(NULL);
    	}
    
    	/* Auto wakeup */
    	if (pdata->auto_wakeup_interval) {
    		printf("Auto wakeup: %dS
    ", pdata->auto_wakeup_interval);
    		autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
    	}
    
    /* Give a message warning when CONFIG_IRQ is not enabled */
    #ifdef CONFIG_IRQ
    	printf("Enter U-Boot charging mode
    ");
    #else
    	printf("Enter U-Boot charging mode(without IRQ)
    ");
    #endif
    
    	charge_start = get_timer(0);
    	delta = get_timer(0);
    
    	/* Charging ! */
    	while (1) {
    		/*
    		 * At the most time, fuel gauge is usually a i2c device, we
    		 * should avoid read/write all the time. We had better set
    		 * poll seconds to update fuel gauge info.
    		 */
    		if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS)
    			goto show_images;
    
    		delta = get_timer(0);
    
    		debug("step1 (%d)... 
    ", screen_on);
    
    		/*
    		 * Most fuel gauge is I2C interface, it shouldn't be interrupted
    		 * during tansfer. The power key event depends on interrupt, so
    		 * so we should disable local irq when update fuel gauge.
    		 */
    		local_irq_disable();
    
    		/* Step1: Is charging now ? */
    		charging = fuel_gauge_get_chrg_online(fg);
    		if (charging <= 0) {
    			printf("Not charging, online=%d. Shutdown...
    ",
    			       charging);
    
    			/* wait uart flush before shutdown */
    			mdelay(5);
    
    			/* PMIC shutdown */
    			pmic_shutdown(pmic);
    
    			printf("Cpu should never reach here, shutdown failed !
    ");
    			continue;
    		}
    
    		debug("step2 (%d)... show_idx=%d
    ", screen_on, show_idx);
    
    		/* Step2: get soc and voltage */
    		soc = fuel_gauge_get_soc(fg);
    		if (soc < 0 || soc > 100) {
    			printf("get soc failed: %d
    ", soc);
    			continue;
    		}
    
    		voltage = fuel_gauge_get_voltage(fg);
    		if (voltage < 0) {
    			printf("get voltage failed: %d
    ", voltage);
    			continue;
    		}
    
    		current = fuel_gauge_get_current(fg);
    		if (current == -ENOSYS) {
    			printf("get current failed: %d
    ", current);
    			continue;
    		}
    		first_poll_fg = 0;
    
    		local_irq_enable();
    
    show_images:
    		/*
    		 * Just for debug, otherwise there will be nothing output which
    		 * is not good to know what happen.
    		 */
    		if (!debug_start)
    			debug_start = get_timer(0);
    		if (get_timer(debug_start) > 20000) {
    			debug_start = get_timer(0);
    			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d
    ",
    			       get_timer(0)/1000, soc, voltage,
    			       current, charging, screen_on);
    		}
    
    		/*
    		 * If ever lowpower screen off, force screen_on=false, which
    		 * means key event can't modify screen_on, only voltage higher
    		 * then threshold can update screen_on=true;
    		 */
    		if (ever_lowpower_screen_off)
    			screen_on = false;
    
    		/*
    		 * Auto turn on screen when voltage higher than Vol screen on.
    		 * 'ever_lowpower_screen_off' means enter while loop with
    		 * screen off.
    		 */
    		if ((ever_lowpower_screen_off) &&
    		    (voltage > pdata->screen_on_voltage)) {
    			ever_lowpower_screen_off = false;
    			screen_on = true;
    			show_idx = IMAGE_SHOW_RESET;
    		}
    
    		/*
    		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
    		 * When short key pressed event trigged, we will set show_idx
    		 * as IMAGE_SHOW_RESET which updates images index from start_idx
    		 * that calculate by current soc.
    		 */
    		if (show_idx == IMAGE_SHOW_RESET) {
    			for (i = 0; i < image_num - 2; i++) {
    				/* Find out which image we start to show */
    				if ((soc >= image[i].soc) &&
    				    (soc < image[i + 1].soc)) {
    					start_idx = i;
    					break;
    				}
    
    				if (soc >= 100) {
    					start_idx = image_num - 2;
    					break;
    				}
    			}
    
    			debug("%s: show_idx=%d, screen_on=%d
    ",
    			      __func__, show_idx, screen_on);
    
    			/* Mark start index and start time */
    			show_idx = start_idx;
    			show_start = get_timer(0);
    		}
    
    		debug("step3 (%d)... show_idx=%d
    ", screen_on, show_idx);
    
    		/* Step3: show images */
    		if (screen_on) {
    			/* Don't call 'charge_show_bmp' unless image changed */
    			if (old_show_idx != show_idx) {
    				old_show_idx = show_idx;
    				debug("SHOW: %s
    ", image[show_idx].name);
    				charge_show_bmp(image[show_idx].name);
    			}
    			/* Re calculate timeout to off screen */
    			if (priv->auto_screen_off_timeout == 0)
    				priv->auto_screen_off_timeout = get_timer(0);
    		} else {
    			priv->auto_screen_off_timeout = 0;
    
    			system_suspend_enter(pdata);
    		}
    
    		mdelay(5);
    
    		/* Every image shows period */
    		if (get_timer(show_start) > image[show_idx].period) {
    			show_start = get_timer(0);
    			/* Update to next image */
    			show_idx++;
    			if (show_idx > (image_num - 2))
    				show_idx = IMAGE_SHOW_RESET;
    		}
    
    		debug("step4 (%d)... 
    ", screen_on);
    
    		/*
    		 * Step4: check key event.
    		 *
    		 * Short key event: turn on/off screen;
    		 * Long key event: show logo and boot system or still charging.
    		 */
    		key_state = check_key_press(dev);
    		if (key_state == KEY_PRESS_DOWN) {
    			old_show_idx = IMAGE_SHOW_RESET;
    
    			/* NULL means show nothing, ie. turn off screen */
    			if (screen_on)
    				charge_show_bmp(NULL);
    
    			/*
    			 * Clear current image index, and show image
    			 * from start_idx
    			 */
    			show_idx = IMAGE_SHOW_RESET;
    
    			/*
    			 * We turn off screen by charge_show_bmp(NULL), so we
    			 * should tell while loop to stop show images any more.
    			 *
    			 * If screen_on=false, means this short key pressed
    			 * event turn on the screen and we need show images.
    			 *
    			 * If screen_on=true, means this short key pressed
    			 * event turn off the screen and we never show images.
    			 */
    			if (screen_on)
    				screen_on = false;
    			else
    				screen_on = true;
    		} else if (key_state == KEY_PRESS_LONG_DOWN) {
    			/* Only long pressed while screen off needs screen_on true */
    			if (!screen_on)
    				screen_on = true;
    
    			/* Is able to boot now ? */
    			if (soc < pdata->exit_charge_level) {
    				printf("soc=%d%%, threshold soc=%d%%
    ",
    				       soc, pdata->exit_charge_level);
    				printf("Low power, unable to boot, charging...
    ");
    				show_idx = image_num - 1;
    				continue;
    			}
    
    			if (voltage < pdata->exit_charge_voltage) {
    				printf("voltage=%dmv, threshold voltage=%dmv
    ",
    				       voltage, pdata->exit_charge_voltage);
    				printf("Low power, unable to boot, charging...
    ");
    				show_idx = image_num - 1;
    				continue;
    			}
    
    			/* Success exit charging */
    			printf("Exit charge animation...
    ");
    			charge_show_logo();
    			break;
    		} else {
    			/* Do nothing */
    		}
    
    		debug("step5 (%d)... 
    ", screen_on);
    
    		/* Step5: Exit by ctrl+c */
    		if (ctrlc()) {
    			if (voltage >= pdata->screen_on_voltage)
    				charge_show_logo();
    			printf("Exit charge, due to ctrl+c
    ");
    			break;
    		}
    	}
    
    	if (pdata->auto_wakeup_interval)
    		autowakeup_timer_uninit();
    
    	ms = get_timer(charge_start);
    	if (ms >= 1000) {
    		sec = ms / 1000;
    		ms = ms % 1000;
    	}
    
    	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv
    ",
    	       sec, ms, soc, voltage);
    
    	return 0;
    }
    
  • 相关阅读:
    [转帖]译文:如何使用SocketAsyncEventArgs类(How to use the SocketAsyncEventArgs class)
    如何建立一个“绑定友好的”usercontrol--wpf
    安卓学习(三)
    安卓学习(二)
    Android学习1
    用伪代码梳理springboot
    用伪代码梳理javaweb从零开始
    用伪代码梳理spring源码
    java如何写出简洁代码
    JAVA修复微信官方SDK支付XXE漏洞
  • 原文地址:https://www.cnblogs.com/unclemac/p/12783400.html
Copyright © 2011-2022 走看看