Android内核学习之三----------Power源码分析学习(1)

本文详细介绍了 Android 4.4 版本之后 Power 系统采用的 Linux power_supply 架构,深入分析了 power_supply 驱动层的工作原理,包括其初始化过程、uevent 回调函数的作用及实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Android内核学习


-----Power源码分析学习(1)

1.    前言

最近学习了一下Android的Power源码,虽然还没学习通透,但是有点感觉了,怕后面忘了东西,就边学便把东西记录下来吧。如果有大神再致电一二那更是感激不尽了

Android4.4以后Power系统的使用了Linux power supply架构,这个架构的使用又是头疼的地方。不懂再Linux中怎么样使得服务将消息放到socket里面,然后客户端又怎么样通过socket获取到消息。不过还好,这几天看源码的过程有了一些进步,很多地方不再怵怕C和C++了。碰到系统函数就查一下意义,然后看看别人的思路,自己再继续走下去,还是有点收获。后面再把电池系统看一下,因为还有不少地方没明白。这次看的时候把这些个搞明白,顺便记录下学习路程。

2.    Power框架概况

    “ Android4.4的电池管理功能用于管理电池的充、放电功能。整个电池管理的部分包括Linux电池驱动、Android电池服务、电池属性和参数、电池曲线优化四个部分”。这段话是一篇博客中提到的android电池管理的功能部分,是我们后续学习的一个主要根据。

       下图是电池模块的系统结构。这个模块为我们提供了很多有用的信息,在看源码的时候不知道下一步该怎么走的时候这幅图提供了一个很大的帮助。(图是盗来的,URL下回补上去)



3.    power_supply驱动层学习

在学习android的电池驱动以前,需要学习一下power_supply驱动。该驱动的目的就是让用户空间来读取系统内核空间的电池信息。电池信息可以分为AC,battery和usb等。该驱动是通过sys文件系统与用户进行交互。目录为/sys/class/power_supply/。而/sys/class/power_supply/XXX的每个子目录表示一种供电设备的名称。


上图右边方框是该驱动的主要文件,方框里面的文件是我们将要学习的驱动文件。

(1)power_supply_core.c

==============》》init

static int __init power_supply_class_init(void)

{

         power_supply_class = class_create(THIS_MODULE, "power_supply");

         if (IS_ERR(power_supply_class))

                   return PTR_ERR(power_supply_class);

         power_supply_class->dev_uevent = power_supply_uevent;

         power_supply_init_attrs(&power_supply_dev_type);

         return 0;

}

 

 












这是子模块的初始化函数入口,class_creat在sys下创建了一个名为power_supply的class。然后赋值给power_supply_class其中power_supply_class是一个名为class的struct,定义在XXX/kernel/common/include/linux/device.h中。将power_supply_uevent作为回调函挂载在power_supply_class的dev_uevent上。这个回调函数就是当底层发生变化的时候将信息反馈到用户空间。这个函数极为重要,定义在power_supply_sysfs.c中,下面将会分析。后面的power_supply_init_attrs操作是填充device_tepe结构体的,该函数的定义也在power_supply_sysfs.c中。

(2)power_supply_sysfs.c

================》》power_supply_uevent



int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)

{

         struct power_supply *psy = dev_get_drvdata(dev);

         int ret = 0, j;

         char *prop_buf;

         char *attrname;

         dev_dbg(dev, "uevent\n");

         if (!psy || !psy->dev) {

                   dev_dbg(dev, "No power supply yet\n");

                   return ret;

         }

         dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);

         ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);

         if (ret)

                   return ret;

         prop_buf = (char *)get_zeroed_page(GFP_KERNEL);

         if (!prop_buf)

                   return -ENOMEM;

         for (j = 0; j < psy->num_properties; j++) {

                   struct device_attribute *attr;

                   char *line;

                   attr = &power_supply_attrs[psy->properties[j]];

                   ret = power_supply_show_property(dev, attr, prop_buf);

                   if (ret == -ENODEV || ret == -ENODATA) {

                            /* When a battery is absent, we expect -ENODEV. Don't abort;

                               send the uevent with at least the the PRESENT=0 property */

                            ret = 0;

                            continue;

                   }

                   if (ret < 0)

                            goto out;

                   line = strchr(prop_buf, '\n');

                   if (line)

                            *line = 0;

                   attrname = kstruprdup(attr->attr.name, GFP_KERNEL);

if (!attrname) {

                            ret= -ENOMEM;

                            gotoout;

                   }

                   dev_dbg(dev,"prop %s=%s\n", attrname, prop_buf);

                   ret= add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);

                   kfree(attrname);

                   if(ret)

                            gotoout;

         }

 

out:

         free_page((unsignedlong)prop_buf);

         return ret;

}

                        

 


上述的函数就是挂载到dev_uevent上的回调函数。首先是dev_get_drvdata,这个函数是返回驱动的指针,但是我没搞明白为什么可以吧struct device的指针返回给power_supply。然后add_uevent_var(env, "POWER_SUPPLY_NAME=%s",psy->name),这个地方就是将该函数添加发送信息到uevent,也就是往用户控件发送信息。如果psy->name为battery,则发送数据中有一个字符串为:POWER_SUPPLY_NAME=battery。后面的for循环中是将power_supply_attrs中的对应的属性和值,以POWER_SUPPLY_XXXX=xxxx的方式添加到发送的数据中。这个回调函数就是做了这个操作,即将系统中的变化数据组织成特定的格式,通过uevent发送出去(有人讲到使用socket发送的,可能我没走到那么深,后面慢慢完善吧)。具体的发送可以查看振片博客:

http://blog.youkuaiyun.com/pillarbuaa/article/details/9062115

(3)power_supply_core.c

===============》》power_supply_init_attrs


void power_supply_init_attrs(struct device_type*dev_type)

{

         int i;

         dev_type->groups= power_supply_attr_groups;

         for (i = 0;i < ARRAY_SIZE(power_supply_attrs); i++)

                   __power_supply_attrs[i]= &power_supply_attrs[i].attr;

}



上述函数应该主要用来填充device_type了。在core.c中还有其他一些辅助函数,这些函数在后面的学习中还会用到,这里将他们一一列举出来:

static int __power_supply_changed_work(struct device*dev, void *data)

static void power_supply_changed_work(structwork_struct *work)

void power_supply_changed(struct power_supply *psy)

static int __power_supply_am_i_supplied(struct device*dev, void *data)

int power_supply_am_i_supplied(struct power_supply*psy)

static int __power_supply_is_system_supplied(structdevice *dev, void *data)

int power_supply_is_system_supplied(void)

int power_supply_set_battery_charged(structpower_supply *psy)

static int power_supply_match_device_by_name(structdevice *dev, void *data)

struct power_supply *power_supply_get_by_name(char*name)

int power_supply_powers(struct power_supply *psy,struct device *dev)

int power_supply_register(struct device *parent,struct power_supply *psy)

static void __exit power_supply_class_exit(void)


未完待续……


继续努力,Keep moving!!!





<think>嗯,用户这次的问题很专业,是关于Android 7.1系统在关机充电状态下更新U-Boot的可行性。从提供的引用来看,用户应该是在进行嵌入式开发或系统移植工作,特别是RK3399和msm8909平台的修改经验。 用户引用的三段代码很有价值:引用[1]展示了U-Boot中BQ3060电量计驱动的修改,引用[2]是msm8909平台开机流程的aboot.c修改,引用[3]涉及关机充电的LED状态机。这些恰好构成解决关机充电时更新U-Boot的关键要素。 需要特别注意技术难点在于:关机充电时系统处于充电固件(charger mode)而非完整U-Boot环境。引用[2]中aboot.c的修改表明,高通平台通过target_pause_for_battery_charge()实现充电流程挂载,这正是突破口。 解决方案应该分三层考虑:首先是硬件上确保BootROM支持USB下载模式(如Rockchip的MaskROM模式),其次是软件上修改charger固件识别升级指令,最后是创建特殊的升级镜像格式。引用[1]中bq3060驱动添加的isexistbat标志位启示我们可以通过电池状态检测触发特殊模式。 用户可能还隐含了“不拆机更新”的需求,所以重点设计USB通信方案。考虑到Android 7.1的旧内核版本,建议采用USB Gadget框架实现CDC ACM虚拟串口,这样比开发MSC协议更稳妥。 测试环节要特别提醒风险,因为错误的U-Boot刷写会导致设备变砖。准备在回复中强调必须保留恢复分区,并给出具体的hexdump验证命令。用户既然能修改U-Boot源码,应该具备基本开发能力,但安全提示绝不能少。 最后生成的扩展问题要聚焦在嵌入式开发方向:fastboot协议扩展和BootROM机制是自然延伸,充电状态机优化则呼应引用[3]的LED控制问题。这些都能帮助用户深化系统移植工作。</think>在 Android 7.1 设备上实现**关机充电时更新 U-Boot**是可行的,但需深度修改系统底层代码,涉及**Bootloader 流程重构**和**充电固件增强**。以下是具体方案: --- ### **技术可行性分析** 1. **硬件基础** - 设备需支持 **USB OTG 或 UART 烧写模式**(如 Rockchip 的 MaskROM 模式[^1]) - 关机充电时 SoC 仍维持 **BootROM 运行**(如高通 PBL 或 Rockchip Miniloader) 2. **系统限制突破** - 默认充电流程仅调用 `charger` 服务(显示动画/电量)[^3] - **需劫持充电流程**,注入 U-Boot 更新逻辑 --- ### **实现步骤(以 Rockchip RK3399 + Android 7.1 为例)** #### **步骤 1:修改充电流程入口** 在 `aboot.c` 中重定向充电检测逻辑,使其可跳转到更新模式: ```c // android/bootable/bootloader/lk/app/aboot/aboot.c if (device.charger_screen_enabled && target_pause_for_battery_charge()) { // 原始充电流程 } else if (check_update_key() || usb_update_request()) { // 新增条件 launch_uboot_update(); // 跳转到U-Boot更新流程 } ``` *引用自高通平台修改逻辑[^2]* #### **步骤 2:扩展 U-Boot 的充电模式** 在 U-Boot 驱动层添加**升级指令监听**: ```c // u-boot/drivers/power/fuel_gauge/fg_bq3060.c void battery_check_update(void) { if (battery->isexistbat && usb_phy_connected()) { // 检测USB连接 if (usb_detect_update_cmd()) { // 监听特定升级指令 enter_flash_mode(); // 进入刷写模式 } } } ``` *参考电池检测逻辑[^1]* #### **步骤 3:构建升级协议** 通过 USB 实现简易通信协议: | 指令类型 | 字节序列 | 功能 | |----------|----------|------| | 握手信号 | `0x55 0xAA` | 建立连接 | | 擦除命令 | `0xDE 0xAD` | 擦除U-Boot分区 | | 数据包 | `0xDA + [size] + [data]` | 传输固件 | | 校验命令 | `0xBE 0xEF` | 验证固件完整性 | #### **步骤 4:修改充电界面交互** 在 `charger` 服务中添加升级提示: ```diff # frameworks/base/services/core/java/com/android/server/BatteryService.java public void onChargingStarted() { if (checkUsbUpdateMode()) { showUpdateScreen(); // 显示"检测到升级请求" } else { showNormalCharging(); } } ``` --- ### **关键挑战与解决方案** 1. **电源稳定性** - 问题:刷写时断电导致设备变砖 - 方案: - 刷写前强制 `battery->capacity >= 50%`[^1] - 实现**双备份 U-Boot**(主备分区切换) 2. **安全验证** - 问题:未签名固件可能植入恶意代码 - 方案: ```c if (!verify_signature(update_data)) { abort_update(); // 拒绝未签名固件 } ``` 3. **通信可靠性** - 问题:USB 传输错误 - 方案: - 添加 CRC32 校验:$$ \text{CRC} = \sum_{i=0}^{n} \text{data}[i] \oplus 0xEDB88320 $$ - 每 512 字节应答一次 ACK --- ### **操作流程示例** ```mermaid sequenceDiagram PC->>Device: 发送握手信号(0x55AA) Device->>PC: 返回设备ID PC->>Device: 发送擦除指令(0xDEAD) Device->>Device: 备份当前U-Boot Device->>PC: 返回就绪状态 loop 数据传输 PC->>Device: 数据包(0xDA + size + data) Device->>PC: ACK + CRC校验结果 end PC->>Device: 提交执行指令(0xBEEF) Device->>Device: 烧写新固件并重启 ``` --- ### **风险提示** 1. **变砖风险** - 错误修改 `aboot.c` 可能导致设备无法启动 - 建议保留 **Recovery 模式**作为救砖通道 2. **电池管理冲突** - 更新过程需禁用充电 IC 的看门狗(如 BQ3060 的 WD 寄存器[^1]) > ✅ **验证方法**: > 使用 `rkdeveloptool` 读取 U-Boot 版本: > ```bash > rkdeveloptool rl 0x4000 0x2000 /tmp/uboot.bin > strings /tmp/uboot.bin | grep "U-Boot" > ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值