作为一名在MTK平台摸爬滚打多年的驱动工程师,我最近遇到了一个看似简单却令人抓狂的问题——充电图标不显示。这个案例完美诠释了"魔鬼藏在细节中"的道理,也让我对MTK平台的充电管理机制有了更深入的理解。
问题的表象很简单:当使用某些第三方充电器时,系统能够正常充电(电流电压检测正常),但状态栏就是不显示充电图标。通过logcat查看系统日志,发现充电状态被错误地识别为"非充电"状态(st=3)。这种情况在用户端会造成严重误解,明明设备在充电,用户却以为没有充上电。
MTK平台的充电器类型检测是一个多阶段的复杂过程:
在MT6580/MT6735等常见MTK平台中,这个逻辑主要实现在:
code复制drivers/misc/mediatek/charger/mt65xx/charger_type.c
通过分析内核日志和代码,发现问题出在类型识别环节:
c复制static enum charger_type mtk_charger_detect(int *type)
{
// ...硬件检测逻辑...
switch(detected_type) {
case STANDARD_HOST:
case CHARGING_HOST:
case STANDARD_CHARGER:
case APPLE_2_1A_CHARGER:
case APPLE_1_0A_CHARGER:
case APPLE_0_5A_CHARGER:
// 处理已知类型
break;
default:
return; // 问题点:未处理非标充电器
}
// 更新sysfs节点
update_charger_type_node(type);
}
当遇到非标准充电器时,驱动直接return而没有更新type节点状态,导致上层获取到的充电状态始终为st=3(非充电状态)。
最直接的修复方式是在switch-case中添加对非标充电器的处理:
c复制switch(detected_type) {
// ...原有case...
default:
*type = NONSTANDARD_CHARGER; // 新增定义
pr_info("Detected non-standard charger, type=%d\n", detected_type);
break;
}
同时需要在头文件中添加对应的类型定义:
c复制#define NONSTANDARD_CHARGER 100
仅仅识别出非标充电器还不够,还需要确保状态正确同步到上层:
c复制void update_charger_type_node(int type)
{
struct power_supply *psy = power_supply_get_by_name("battery");
if (!psy) return;
union power_supply_propval val;
val.intval = (type != NONSTANDARD_CHARGER) ? type : STANDARD_CHARGER;
power_supply_set_property(psy, POWER_SUPPLY_PROP_TYPE, &val);
}
bash复制# 修复后可通过以下命令验证
adb shell cat /sys/class/power_supply/battery/type
考虑到各种边缘情况,建议增加以下处理:
c复制#define VOLTAGE_FLUCTUATION 50 // 允许50mV波动
if (abs(actual_voltage - expected_voltage) > VOLTAGE_FLUCTUATION) {
pr_warn("Voltage fluctuation detected, retrying...");
msleep(100);
// 重新检测
}
c复制static int evaluate_charger_capability(int voltage, int current)
{
// 根据电压电流评估充电器实际能力
if (current >= 1500) return AC_CHARGER;
if (current >= 500) return USB_CHARGER;
return WEAK_CHARGER;
}
为确保修复效果,需要设计全面的测试用例:
| 测试场景 | 测试方法 | 预期结果 |
|---|---|---|
| 原装充电器 | 使用MTK官方充电器 | 显示充电图标 |
| 非标快充 | 使用第三方QC3.0充电器 | 显示充电图标 |
| 电脑USB | 连接PC USB 3.0端口 | 显示USB充电图标 |
| 异常电压 | 使用输出电压波动的充电器 | 正确识别/告警 |
通过内核日志可以深入分析充电识别过程:
bash复制# 查看充电识别相关日志
adb shell dmesg | grep -E "charger|battery|power_supply"
# 关键日志标记
[ 12.345678] mtk_charger: detected charger type=5 (APPLE_2_1A_CHARGER)
[ 12.345679] power_supply battery: type = 5
[ 12.345680] battery: charger = 1
经过修复后,对不同充电器的测试结果:
code复制[ 15.123456] mtk_charger: Detected non-standard charger, type=15
[ 15.123457] power_supply battery: type set to STANDARD_CHARGER
[ 15.123458] battery: charger = 1
状态栏正确显示充电图标
code复制[ 18.654321] mtk_charger: detected charger type=3 (STANDARD_CHARGER)
[ 18.654322] battery: charger = 1
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 充电图标不显示 | 1. 类型识别失败 2. 状态未同步 |
检查dmesg日志中的charger type |
| 图标显示但无充电电流 | 1. 充电电路故障 2. 限流设置错误 |
测量VBUS电压 检查current_max节点 |
| 间歇性断开 | 1. 接触不良 2. 电压波动 |
更换数据线 监控/sys/class/power_supply/... |
c复制// 建议增加超时机制
#define DETECT_TIMEOUT 3000 // 3秒
unsigned long start = jiffies;
while (time_before(jiffies, start + msecs_to_jiffies(DETECT_TIMEOUT))) {
if (detect_charger_type() != UNKNOWN)
break;
msleep(100);
}
bash复制# 确保上层应用有权限读取节点
chmod 0644 /sys/class/power_supply/battery/*
chown system:system /sys/class/power_supply/battery/*
c复制// 对非标充电器采用保守策略
if (type == NONSTANDARD_CHARGER) {
set_charging_current(1000); // 限制为1A
enable_thermal_monitoring(true);
}
c复制static bool need_redetection(enum charger_type old, enum charger_type new)
{
// 只有当类型发生实质变化时才重新检测
if (old == UNKNOWN || new == UNKNOWN) return true;
if ((old == STANDARD_HOST || old == CHARGING_HOST) &&
(new == STANDARD_HOST || new == CHARGING_HOST)) {
return false;
}
return old != new;
}
c复制static enum charger_type last_valid_type;
if (current_type != UNKNOWN) {
last_valid_type = current_type;
} else {
// 使用上次有效结果
current_type = last_valid_type;
}
这个案例给我的深刻教训是:在驱动开发中,特别是电源管理这种关键子系统,必须考虑所有可能的边界条件。那些"不应该出现"的情况,在实际用户环境中恰恰是最常出现的。