1. 进入手机/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040/leds/ 或 sys/class/leds/目录list如下
pwr-red,pwr-green,pwr-blue //控制充电led
l-key-red,l-key-green,l-key-blue //控制left button led
m-key-red,m-key-green,m-key-blue //控制middle button led
r-key-red,r-key-green,r-key-blue //控制right button led
lcd-backlight //控制lcd backlight led
设置其中brightness的值,可以调整各led的显示亮度
2. https://wiki.sonyericsson.net/androiki/Fuel_Gauge (SysFS interfaces)
Riogrande
Disable charging: adb shell "echo 0 > /sys/ab8500_chargalg/chargalg",用于得到没有充电的log
Enable AC dedicated charger: adb shell "echo 1 > /sys/ab8500_chargalg/chargalg"
Enable PC-USB and USB wall charger: adb shell "echo 2 > /sys/ab8500_chargalg/chargalg"
Qualcom
Disable charging: echo 2 > /sys/module/pm8921_charger/parameters/disabled
Enable charging: echo 0 > /sys/module/pm8921_charger/parameters/disabled
3. Kernel 有log输出
"SysRq : Emergency Remount R/O"
“Emergency Remount complete” 位于函数do_emergency_remount@kernel/fs/super.c
“Shutting down AS3676” 位于函数 as3676_shutdown@kernel/drivers/leds/leds-as3676.c
"CPU1: shutdown"
"Power down" 位于函数kernel_power_off@kernel/kernel/sys.c
Java 有log输出
“Performing low-level shutdown...” 位于函数 rebootOrShutdown@frameworks/base/core/java/com/android/internal/app/shutdownthread.java
4. 手机手动关机的过程
[JAVA framework]interceptPowerKeyDown和Runnable mPowerLongPress@frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
[JAVA framework]shutdown dialog 在frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java
[JAVA framework]Shutdown process 在frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
Through beginShutdownSequence(), actual shutdown process is done in run().
Broadcasts ACTION_SHUTDOWN Intent
Calls shutdown of ActivityManager service
Calls disable of Bluetooth service
Calls Radio(false) of Phone service
Calls shutdown of Mount service
Vibrate for a while
Calls Power.shutdown(); //这个是关键
4.1 具体分析Power.shutdown如何从user spaced到kenrel space
[JAVA]其中Power.shutdown@frameworks/base/core/java/android/os/power.java
[JNI] android_os_Power_shutdown@frameworks/base/core/jni/andorid_os_power.cpp
[Library] android_reboot(ANDROID_RB_POWEROFF, 0, 0)@system/core/libcutils/android_reboot.c
[Library] reboot(RB_POWER_OFF)@kernel/bionic/libc/unistd/reboot.c
[Library] _reboot@kernel/bionic/libc/arch-arm/syscalls/__reboot.S 其中bionic/libc/include/sys/linux-syscalls.h中定义了#define __NR_reboot (__NR_SYSCALL_BASE + 88)这与kernel的定义就一致了,会产生系统中断88,调用kernel的sys_reboot
4.2 reboot系统调用:
[Kernel] kernel/arch/arm/include/asm/unistd.h 有#define __NR_reboot (__NR_SYSCALL_BASE+ 88);
[Kernel] kernel/arch/arm/kernel/calls.s 有CALL(sys_reboot) /*88*/
[Kernel] kernel/arch/arm/kernel/entry-common.s 会引用call.s 对sys_call_table(系统调用表)进行定义
[Kernel] kernel/kernel/sys.c中有定义SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),定义了reboot该系统调用
[Kernel] 如果是Power off 则会调用kernel_power_off和do_exit(0)@kernel/kernel/sys.c
[Kernel] kernel_power_off-->kernel_shutdown_prepare-->device_shutdown[关闭非系统设备 比如:AS3676 会调用as3676_shutdown@kernel/drivers/leds/leds-as3676.c]和 sysdev_shutdown[关闭所有系统设备]
[Kernel] 最后machine_power_off
5. LowBat interrupt trigger; 当电池电压低于某个值(在那设置?abx500_fg_parameters.lowbat_threshold的值@kernel/arch/arm/mach-ux500/board-mop500-bm.c)会触发如下中断函数
1). [KERNEL]ab8500_fg_lowbatf_handler@kernel/drivers/power/ab8500_fg.c 有log: "Battery voltage is below LOW threshold", 其中为了避免是电池电压偶尔的下降,需要多次检测(ab8500_fg_low_bat_work 有log: ”Battery voltage still LOW“),从而最终确认是否真的是电池电压不够了,
2). [KERNEL]最后会调用 ab8500_fg_check_capacity_limits->power_supply_changed@power_supply_core.c->power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);另外需要说明下,实际上任何电池温度和电量的变化都会触发 power_supply_changed.
3). [KERNEL]在kernel/drivers/power/power_supply_core.c中power_supply_changed_work->power_supply_update_leds@kernel/drivers/power/power_supply_leds.c会设置power leds的颜色
4). [KERNEL]谁发event通知java层电源低?????
在power_supply_class_init@kernel/drivers/power/power_supply_core.c中有
power_supply_class = class_create(THIS_MODULE, "power_supply"); [正好与Java层的mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply")一致]
还有power_supply_class->dev_uevent = power_supply_uevent;
power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);会调用到power_supply_uevent@kernel/drivers/power/power_supply_sysfs.c 但是如何调用过来的,没分析清楚??
//ab8500_fg_check_capacity_limits->sysfs_notify(&di->fg_kobject, NULL, "charge_now");
6. Java层的处理过程,[现在需要确认谁触发了ActivityMangagerService@frameworks/base/core/java/android/app/ActivityManager.java]
1). [JAVA]BatteryService.java@framework/base/services/java/com/android/server, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
2). [JAVA][email protected], 会发送intent {Intent.ACTION_REQUEST_SHUTDOWN} 注意:除了电量百分比为0,同时还需要没有充电的情况下,才能触发shutdown, 因为有时候连着充电器,但有可能没有充电(比如高温的情况)。这样还能解决一个问题:就是如果充电器提供的电流不能满足手机的超负荷应用咋办?我们知道如果手机边充电边玩负载大的游戏,则系统会不断从电池拽电流,也就是根本没充电,最后电池电量耗光到0触发shutdown
3). [JAVA]ActivityMangagerService 会下发 intent{ act=android.intent.action.ACTION_REQUEST_SHUTDOWN}给com.android.server.ShutdownActivity
4). [JAVA]ShutdownActivity@frameworks/base/services/java/com/android/server/ShutdownActivity.java 接收到 intent 开始触发oncreate-> ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
5). [JAVA]shutdown@frameworks/base/core/java/com/android/internal/app/ShutdownThread.java 会调用 beginShutdownSequence(context);其中有
sInstance.mCpuWakeLock.setReferenceCounted(false); //sure never fall asleep
sInstance.mScreenWakeLock.setReferenceCounted(false); //sure screen stays on
6). [JAVA]sInstance.start();->run()
7. BatteryService.java具体分析,会接收kernel的uevent消息,并加以处理
a. framework/base/services/java/com/android/server/BatteryService.java, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
b. [email protected]函数会被 mPowerSupplyObserver和mInvalidChargerObserver调用,两者都是UEventObserver.
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply"); //在uevent监听事件中增加该匹配 "SUBSYSTEM=power_supply" 的UEventObserver,一旦有匹配的uevent收到,会调用mPowerSupplyObserver->onUEvent->update
c. [email protected]函数会得到最新的电池状态信息; update->native_update[JNI]->android_server_BatteryService_update@frameworks/base/services/jni/com_android_server_BatteryService.cpp
d. com_android_server_BatteryService.cpp中 实际是读取的 #define POWER_SUPPLY_PATH "/sys/class/power_supply" 的内容,
e. 顺便分析下C 向 JAVA如何传递数据,比如 private int [email protected], 在com_android_server_BatteryService.cpp中有
jfieldID mBatteryStatus;
gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
f. JAVA的update->JNI的native_update,然后Batteryservice.java中的关于电池状态信息的变量会在 native_update中赋值,根据获取的电池状态,决定是否shutdownIfNoPower.
8. 可以直接修改ab8500的寄存器 https://wiki.sonyericsson.net/androiki/AB8500_Debug_registers_in_DebugFS
~ $ adb shell
# cd /data
# mkdir debug
# mount -t debugfs nodev debug
# cd debug/ab8500
bank: AB8500_SYS_CTRL2_BLOCK 0x02
address: AB8500_LOW_BAT_REG 0x03
value: 3.65v 0b110110 0x36[6:1] 0x1[0]enable lowbat function -> 0x6D 对应 3.65v
3.575v 0b110011 0x33[6:1] 0x1[0]enable lowbat function -> 0x67 对应 3.575v
3.55v 0b110010 0x32[6:1] 0x1[0]enable lowbat function -> 0x65 对应 3.55v //
3.35v 0b100010 0x2A[6:1] 0x1[0]enable lowbat function -> 0x45 对应 3.35v //bad
3.25v 0b100110 0x26[6:1] 0x1[0]enable lowbat function -> 0x4D 对应 3.25v //bad
// READ/WRITE OUT VALUE OF REGISTER 0x0203(LowBat : Low battery feature management.)
# echo 0x2 > register-bank
# echo 0x3 > register-address
# cat register-value
# echo 0x67 > register-value
9. 修改 ab8500_fg_parameters@/kernel/arch/arm/mach-ux500/board-mop500-bm.c中的 fg.lowbat_threshold = 3350, 检查手机电池状态sys/class/power_supply/ab8500_fg中可以查看voltage_now,etc
10. 只修改fg.lowbat_threshold = 3350,同时修改 cap_tbl_A_100mA数据使之对应, 会导致电池capacity减小,不是最佳solution
****************************************************************************************************************************************************
以下是对应的qualcom platform 分析的kernel 部分
十九、Battery voltage alarm
1. 最小门限电压如何定义和设置
@kernel/drivers/power/pm8921-charger.c
struct pm8921_chg_chip.alarm_low_mv //the battery alarm voltage low
@kernel/arch/arm/mach-msm/board-sony_viskan_huashan-pmic.c
static struct pm8921_charger_platform_data pm8921_chg_pdata.alarm_low_mv = 3400, //低电触发
.alarm_high_mv = 4000, //高电触发
2. 如何设置trigger the alarm of battery low voltage
@kernel/drivers/power/pm8921-charger.c
pm8921_charger_probe-->pm8921_charger_configure_batt_alarm
-->pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_LOWER_COMPARATOR,chip->alarm_low_mv); //往寄存器里面写入最低电压门限
pm8xxx_batt_alarm_register_notifier(&alarm_notifier);@kernel/drivers/mfd/pm8xxx-batt-alarm.c //设置触发回调函数
需要说明的是该函数只是往 battery alarm 中断触发函数链表中添加一个响应函数
3. Battery alarm中断如何设置和触发
a. 设备注册 @kernel/drivers/mfd/pm8921-core.c
static struct mfd_cell batt_alarm_cell = {
.resources = batt_alarm_cell_resources, //PM8921_BATT_ALARM_IRQ 是其中断资源号PM8921_IRQ_BLOCK_BIT(5, 6)
}
b. 驱动注册 @kernel/drivers/mfd/pm8xxx-batt-alarm.c
pm8xxx_batt_alarm_probe-->request_irq(chip->irq, pm8xxx_batt_alarm_isr,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, cdata->irq_name,chip); //中断注册函数为 pm8xxx_batt_alarm_isr
c. 如何触发 低电处理函数
当电池电压低于alarm_low_mv 或 高于alarm_high_mv 时硬件中断产生 --> [email protected] --> [email protected]
-->[email protected]
4. 如何把低电信息传递给user space
set_heartbeat_update_interval@kernel/drivers/mfd/pm8921-core.c //设置多少时间间隔触发update_heartbeat
实际上电池的状态没间隔心跳时间获取一次,然后传递给userspace
update_heartbeat 将得到电池容量百分比get_prop_batt_capacity, 然后power_supply_changed发送给user space.
二十、电池容量的计算
1. @kernel/arch/arm/mach-msm/board-sony_viskan_huashan-pmic.c
static struct pm8921_charger_platform_data pm8921_chg_pdata.max_voltage = MAX_VOLTAGE_MV,(4200) //the max voltage (mV) the battery should be charged up to
.min_voltage = 3200, //the voltage (mV) where charging method switches from trickle to fast. This is also the minimum voltage
2. 调试路径 可以设置最小电池电压值
/sys/devices/platform/msm_ssbi.0/pm8921-core/pm8921-charger/min_voltage_mv
或者/sys/bus/platform/drivers/pm8921-charger/pm8921-charger/min_voltage_mv
3. update_heartbeat函数会定时调用
a. update_temp_state --> get_prop_batt_temp
b .get_prop_batt_capacity --> voltage_based_capacity -->get_prop_battery_uvolts
c. check_soc_and_resume --> update_charge_state
二十一、如何触发低电关机
1. get_prop_batt_capacity@kernel/drivers/power/pm8921-charger.c
如果get_prop_battery_uvolts得到的电压值连续小于min_voltage_mv 次RETRY_NUM_FOR_FORCE_SHUTDOWN=5,就会触发
update_charge_state(BIT(DIS_BIT_CHG_SHUTDOWN),DIS_BIT_CHG_SHUT_MASK);该函数会disable 充电
2. 然后把电池容量为0的信息传递给userspace
[email protected] -->power_supply_changed@power_supply_core.c
-->power_supply_changed_work 另外说明下power_supply_update_leds 会根据电量改变led灯的显示颜色
-->psy->changed //如果property有任何改变则向user space 发送uevent.
-->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); //发送电池电量已经改变的信息给userspace
3. /sys/class/power_supply/battery如何创建的
a. [email protected]
chip->batt_psy.name = "battery",
chip->batt_psy.get_property = pm_batt_power_get_property,
rc = power_supply_register(chip->dev, &chip->usb_psy);
b. power_supply_register@power_supply_core.c
dev->class = power_supply_class; //class.name="power_suppl"
dev->type = &power_supply_dev_type; //最终会关联到static struct device_attribute power_supply_attrs@power_supply_sysfs.c
//并且static struct device_attribute power_supply_attrs必须和enum power_supply_property 里面的变量定义一致
rc = kobject_set_name(&dev->kobj, "%s", psy->name); //psy->name="battery"
rc = device_add(dev); //生成/sys/class/power_supply/battery/*.*
c. power_supply_class_init@power_supply_core.c
power_supply_class = class_create(THIS_MODULE, "power_supply");
power_supply_class->dev_uevent = power_supply_uevent;
power_supply_init_attrs(&power_supply_dev_type);
{
dev_type->groups = power_supply_attr_groups;//会关联到__power_supply_attrs和power_supply_attr_is_visible
//通过power_supply_attr_is_visible函数过滤只有[email protected]中的prop才会生成文件节点, 也就是/sys/class/power_supply/battery/下的文件
}
d. static enum power_supply_property [email protected]
4. User space 收到charging 的uevent事件后,会主动读取/sys/class/power_supply/battery/*.* 某些文件以获取充电相关的信息
-->power_supply_uevent@kernel/drivers/power/power_supply_sysfs.c
5. 如果电池的capacity=0,则触发user space低电关机,具体的流程与riogrande platform一样
以下是qualcom的8226平台的分析
二十六、低电关机(soc=0%和电压低于设置的低电压 都会触发低电关机)
1. 电压低于设置电压的处理
@msm-pm8226.dtsi中
pm8226_bms: qcom,bms{
qcom,low-soc-calculate-soc-threshold = <15>; //认为是低电压的soc(15%)
qcom,low-soc-calculate-soc-ms = <5000>; //低电压情况下,计算soc 的时间间隔5s
qcom,calculate-soc-ms = <20000>; //正常情况下,计算soc 的时间间隔20s
qcom,v-cutoff-uv = <3400000>;//用于计算soc
qcom,low-voltage-threshold = <3420000>;//用于设置关机低电压
}
2. 系统一开机会马上运行calculate_soc_work,然后间隔20s或5s 循环执行,只要系统不suspend,该schedule work会一直执行,如果s计算的soc有变化还会power_supply_changed上报给用户空间,如果此时soc=0%则用户空间会强制关机shut down.
3. 另外还有一种情况,就是系统已经suspend,但电池电压小于预先设置的门限电压,会触发notify也就是[email protected],如果是低电状态则继续调用configure_vbat_monitor_low,其中会wake_lock(&chip->low_voltage_wake_lock);让系统不再suspend,马上开始运行calculate_soc_work,也就是把soc=0%的信息赶快通知给user space,好执行低电关机。具体分析如下:
3.1 设置低电notify的相关参数,[email protected]
->[email protected]; 设置adc channel的监控设置
chip->vbat_monitor_params.low_thr = chip->low_voltage_threshold; //低电压门限设置
chip->vbat_monitor_params.high_thr = chip->max_voltage_uv - VBATT_ERROR_MARGIN; //高电压门限设置
chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE; //触发的类型 满足高低电压门限时触发
chip->vbat_monitor_params.channel = VBAT_SNS;
chip->vbat_monitor_params.btm_ctx = (void *)chip;
chip->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S; //Polling 时间
chip->vbat_monitor_params.threshold_notification = &btm_notify_vbat; //回调函数
rc = qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
3.2 @qpnp-adc-tm.c
notify_adc_tm_fn是其中关键的处理函数
->notify_clients(adc_tm)-->adc_tm->btm_param->threshold_notification(ADC_TM_LOW_STATE, adc_tm->btm_param->btm_ctx);
qpnp_adc_tm_probe中初始化INIT_WORK(&adc_tm->sensor[sen_idx].work, notify_adc_tm_fn);
初始化qpnp_adc_tm_low_thr_isr中断处理函数
rc = devm_request_irq(&spmi->dev, adc_tm->adc->adc_low_thr_irq,
qpnp_adc_tm_low_thr_isr,IRQF_TRIGGER_RISING, "qpnp_adc_tm_low_interrupt", adc_tm);
一旦电压低于chip->vbat_monitor_params.low_thr则触发qpnp_adc_tm_low_thr_isr,然后schedule_work(qpnp_adc_tm_low_thr_work);最后会调用qpnp_adc_tm_read_status,又会触发schedule_work(&adc_tm->sensor[sensor_num].work);即notify_adc_tm_fn函数,从而会发notify消息给BMS的btm_notify_vbat->configure_vbat_monitor_low和power_supply_changed