进水检测框架 (water_detect) 模块分析
一、模块概述
1.1 功能定位
water_detect 是华为 MATE X5 的 进水检测统一管理框架,主要功能是:
1.2 核心设计理念
架构模式:中间件/框架层
┌────────────────────────────────────────┐
│ 应用层 (充电/音频/系统模块) │
│ ↓ 查询进水状态 │
├────────────────────────────────────────┤
│ water_detect 框架层 (本模块) │
│ - ops管理 │
│ - 事件分发 │
│ - 状态汇总 │
│ - DMD/Uevent上报 │
│ ↓ 调用检测接口 │
├────────────────────────────────────────┤
│ 检测驱动层 (注册ops) │
│ - USB DP/DN (FSA9685/Scharger) │
│ - USB ID (FSA4480) │
│ - USB GPIO (water_check模块) │
│ - Audio DP/DN │
└────────────────────────────────────────┘
1.3 设计目标
解耦合:检测方法与业务逻辑分离
可扩展:新增检测方法只需注册ops
集中管理:统一状态维护和上报接口
灵活配置:DTS配置启用/禁用检测方法
二、核心架构
2.1 模块结构图
┌──────────────────────────────────────────────────────┐
│ water_detect 框架层 │
├──────────────────────────────────────────────────────┤
│ 初始化层 │
│ ├─ water_detect_init: 创建框架实例 │
│ ├─ water_detect_parse_dts: 解析使能配置 │
│ └─ power_event监听注册 (POWER_BNT_WD) │
├──────────────────────────────────────────────────────┤
│ Ops管理层 │
│ ├─ water_detect_ops_register │
│ │ ├─ 类型解析 (type_name → type) │
│ │ ├─ 存储ops[type] │
│ │ └─ total_ops计数 │
│ └─ 支持4种检测类型: │
│ ├─ WD_TYPE_USB_DP_DN │
│ ├─ WD_TYPE_USB_ID │
│ ├─ WD_TYPE_USB_GPIO │
│ └─ WD_TYPE_AUDIO_DP_DN │
├──────────────────────────────────────────────────────┤
│ 事件处理层 │
│ ├─ water_detect_notifier_call │
│ │ ├─ POWER_NE_WD_DETECT_BY_* → 触发检测 │
│ │ ├─ POWER_NE_WD_REPORT_DMD → DMD上报 │
│ │ └─ POWER_NE_WD_REPORT_UEVENT → Uevent上报 │
│ └─ water_detect_monitor (检测执行) │
│ ├─ 检查工作模式 (关机充电/工厂模式禁用) │
│ ├─ 调用ops->is_water_intruded() │
│ ├─ 更新status状态位图 │
│ └─ 触发DMD + Uevent上报 │
├──────────────────────────────────────────────────────┤
│ 状态管理层 │
│ ├─ status: 位图 (bit0-3对应4种检测类型) │
│ │ ├─ bit=1: 对应类型检测到进水 │
│ │ └─ bit=0: 对应类型未检测到进水 │
│ ├─ water_detect_set_intruded_status │
│ │ ├─ WD_OP_SET: status |= (1 << type) │
│ │ └─ WD_OP_CLR: status &= ~(1 << type) │
│ └─ water_detect_get_intruded_status (对外接口) │
│ └─ return (status > 0) ? true : false │
├──────────────────────────────────────────────────────┤
│ 上报层 │
│ ├─ water_detect_report_dmd │
│ │ └─ DMD: POWER_DSM_ERROR_NO_WATER_CHECK_IN_USB │
│ └─ water_detect_report_uevent │
│ └─ POWER_UI_NE_WATER_STATUS │
└──────────────────────────────────────────────────────┘
2.2 工作流程
系统启动
↓
water_detect_init
├─ 创建框架实例
├─ 解析DTS使能配置
└─ 注册power_event监听
↓
各检测驱动加载
├─ FSA9685: water_detect_ops_register(&usb_dp_dn_ops)
├─ FSA4480: water_detect_ops_register(&usb_id_ops)
├─ water_check: water_detect_ops_register(&usb_gpio_ops)
└─ Audio: water_detect_ops_register(&audio_dp_dn_ops)
↓
运行时检测触发
├─ USB插入 → POWER_NE_WD_DETECT_BY_USB_DP_DN
├─ USB ID变化 → POWER_NE_WD_DETECT_BY_USB_ID
├─ GPIO中断 → POWER_NE_WD_DETECT_BY_USB_GPIO
└─ 音频插入 → POWER_NE_WD_DETECT_BY_AUDIO_DP_DN
↓
water_detect_notifier_call
├─ 检查使能配置 (enable.usb_dp_dn等)
└─ 调用water_detect_monitor
↓
water_detect_monitor
├─ 检查工作模式 (关机充电/工厂模式→跳过)
├─ 调用ops[type]->is_water_intruded()
├─ 进水: status |= (1 << type)
│ ├─ report_uevent(WD_NON_STBY_MOIST)
│ └─ report_dmd(type_name)
└─ 干燥: status &= ~(1 << type)
↓
应用层查询
└─ water_detect_get_intruded_status() → true/false
三、关键数据结构
3.1 检测类型枚举
enum water_detect_type {
WD_TYPE_BEGIN = 0,
WD_TYPE_USB_DP_DN = 0,
WD_TYPE_USB_ID,
WD_TYPE_USB_GPIO,
WD_TYPE_AUDIO_DP_DN,
WD_TYPE_END,
};
3.2 Ops操作接口
struct water_detect_ops {
const char *type_name;
void *dev_data;
int (*is_water_intruded)(void *);
};
3.3 使能配置
struct water_detect_enable {
unsigned int usb_dp_dn;
unsigned int usb_id;
unsigned int usb_gpio;
unsigned int audio_dp_dn;
};
3.4 框架设备结构
struct water_detect_dev {
struct notifier_block nb;
struct water_detect_enable enable;
struct water_detect_ops *ops[4];
unsigned int total_ops;
unsigned int status;
};
3.5 进水状态枚举
enum water_detect_intruded_status {
WD_NON_STBY_DRY,
WD_NON_STBY_MOIST,
WD_STBY_DRY,
WD_STBY_MOIST,
};
四、核心功能实现
4.1 Ops注册机制
int water_detect_ops_register(struct water_detect_ops *ops)
{
int type;
if (!g_water_detect_dev || !ops || !ops->type_name)
return -EPERM;
type = water_detect_get_type(ops->type_name);
if (type < 0)
return -EPERM;
g_water_detect_dev->ops[type] = ops;
g_water_detect_dev->total_ops++;
hwlog_info("total_ops=%d type=%d:%s ops register ok\n",
total_ops, type, ops->type_name);
return 0;
}
注册示例:
static struct water_detect_ops fsa9685_water_ops = {
.type_name = "usb_dp_dn",
.dev_data = &fsa9685_dev,
.is_water_intruded = fsa9685_is_water_intruded,
};
water_detect_ops_register(&fsa9685_water_ops);
static struct water_detect_ops usb_gpio_water_ops = {
.type_name = "usb_gpio",
.dev_data = &water_check_dev,
.is_water_intruded = usb_gpio_is_water_intruded,
};
water_detect_ops_register(&usb_gpio_water_ops);
4.2 检测执行
static void water_detect_monitor(struct water_detect_dev *l_dev,
enum water_detect_type type)
{
struct water_detect_ops *l_ops = NULL;
if (water_detect_check_type(type))
return;
if (water_detect_is_disabled(l_dev))
return;
l_ops = l_dev->ops[type];
if (!l_ops || !l_ops->is_water_intruded)
return;
if (l_ops->is_water_intruded(l_ops->dev_data)) {
water_detect_set_intruded_status(WD_OP_SET, type);
water_detect_report_uevent(WD_NON_STBY_MOIST);
water_detect_report_dmd(water_detect_get_type_name(type));
} else {
water_detect_set_intruded_status(WD_OP_CLR, type);
}
}
4.3 状态管理 - 位图操作
static void water_detect_set_intruded_status(unsigned int mode,
unsigned int type)
{
struct water_detect_dev *l_dev = water_detect_get_dev();
if (mode == WD_OP_SET)
l_dev->status |= (1 << type);
if (mode == WD_OP_CLR)
l_dev->status &= (~(1 << type));
}
bool water_detect_get_intruded_status(void)
{
struct water_detect_dev *l_dev = water_detect_get_dev();
return (l_dev->status > 0) ? true : false;
}
状态位图示例:
初始状态: status = 0b0000 (全部干燥)
USB_DP_DN检测到进水:
status |= (1 << 0) → 0b0001
USB_GPIO检测到进水:
status |= (1 << 2) → 0b0101
查询进水状态:
water_detect_get_intruded_status() → true (status=0b0101 > 0)
USB_DP_DN恢复干燥:
status &= ~(1 << 0) → 0b0100
查询进水状态:
water_detect_get_intruded_status() → true (status=0b0100 > 0, USB_GPIO仍进水)
USB_GPIO恢复干燥:
status &= ~(1 << 2) → 0b0000
查询进水状态:
water_detect_get_intruded_status() → false (status=0 全部干燥)
4.4 工作模式过滤
static bool water_detect_is_disabled(struct water_detect_dev *l_dev)
{
if (power_cmdline_is_powerdown_charging_mode()) {
hwlog_info("water detect disabled on pd charging mode\n");
return true;
}
if (power_cmdline_is_factory_mode()) {
hwlog_info("water detect disabled on factory mode\n");
return true;
}
return false;
}
禁用原因:
关机充电:系统简化运行,减少复杂检测
工厂模式:产线测试可能触发误报,影响生产
4.5 事件处理
static int water_detect_notifier_call(struct notifier_block *nb,
unsigned long event, void *data)
{
struct water_detect_dev *l_dev = water_detect_get_dev();
switch (event) {
case POWER_NE_WD_REPORT_DMD:
water_detect_report_dmd((char *)data);
break;
case POWER_NE_WD_REPORT_UEVENT:
water_detect_report_uevent(*(unsigned int *)data);
break;
case POWER_NE_WD_DETECT_BY_USB_DP_DN:
if (l_dev->enable.usb_dp_dn)
water_detect_monitor(l_dev, WD_TYPE_USB_DP_DN);
break;
case POWER_NE_WD_DETECT_BY_USB_ID:
if (l_dev->enable.usb_id)
water_detect_monitor(l_dev, WD_TYPE_USB_ID);
break;
case POWER_NE_WD_DETECT_BY_USB_GPIO:
if (l_dev->enable.usb_gpio)
water_detect_monitor(l_dev, WD_TYPE_USB_GPIO);
break;
case POWER_NE_WD_DETECT_BY_AUDIO_DP_DN:
if (l_dev->enable.audio_dp_dn)
water_detect_monitor(l_dev, WD_TYPE_AUDIO_DP_DN);
break;
}
return NOTIFY_OK;
}
4.6 DMD上报
static void water_detect_report_dmd(const char *buf)
{
char dsm_buff[128] = { 0 };
if (buf)
snprintf(dsm_buff, 127, "water check is triggered : %s\n", buf);
else
snprintf(dsm_buff, 127, "water check is triggered\n");
power_dsm_report_dmd(POWER_DSM_BATTERY,
POWER_DSM_ERROR_NO_WATER_CHECK_IN_USB, dsm_buff);
}
DMD内容示例:
DMD_NO: 926002xxx
Content: water check is triggered : usb_dp_dn
4.7 Uevent上报
static void water_detect_report_uevent(unsigned int flag)
{
int data = flag;
power_ui_event_notify(POWER_UI_NE_WATER_STATUS, &data);
}
Uevent用途:
通知SystemUI显示进水警告
通知充电模块禁止充电
更新设置菜单进水状态
五、DTS配置示例
5.1 完整配置
huawei_charger: huawei,charger {
compatible = "huawei,charger";
status = "ok";
/* 进水检测使能配置
* 0: 禁用
* 1: 启用
*/
// USB D+/D- 检测 (FSA9685/Scharger芯片)
wd_by_usb_dp_dn = <1>;
// USB ID引脚检测 (FSA4480芯片)
wd_by_usb_id = <1>;
// USB GPIO检测 (water_check模块)
wd_by_usb_gpio = <1>;
// 音频D+/D- 检测
wd_by_audio_dp_dn = <0>; // 禁用
};
5.2 配置策略
| 检测类型 | 芯片依赖 | 推荐配置 | 说明 |
|---|
| usb_dp_dn | FSA9685/Scharger | 1 | USB充电芯片集成检测 |
| usb_id | FSA4480 | 1 | TypeC芯片ID检测 |
| usb_gpio | water_check模块 | 1 | 独立GPIO检测 |
| audio_dp_dn | Audio芯片 | 0/1 | 根据是否有耳机孔 |
5.3 多检测方法组合
策略1:单一检测
wd_by_usb_dp_dn = <1>; // 仅使用充电芯片检测
wd_by_usb_id = <0>;
wd_by_usb_gpio = <0>;
策略2:双重检测
wd_by_usb_dp_dn = <1>; // 充电芯片检测
wd_by_usb_gpio = <1>; // GPIO独立检测
// 双重保险,提高可靠性
策略3:全方位检测
wd_by_usb_dp_dn = <1>;
wd_by_usb_id = <1>;
wd_by_usb_gpio = <1>;
wd_by_audio_dp_dn = <1>;
// 覆盖USB+音频,最大化保护
六、典型应用场景
6.1 USB进水检测 - 多方法协同
用户插入USB充电器,接口有水分
T=0ms: USB物理插入
T=10ms: FSA9685芯片检测D+/D-阻抗异常
→ power_event_notify(POWER_NE_WD_DETECT_BY_USB_DP_DN)
→ water_detect_notifier_call
→ water_detect_monitor(WD_TYPE_USB_DP_DN)
→ fsa9685_ops->is_water_intruded() → 返回1 (进水)
→ status |= 0b0001
→ report_uevent(WD_NON_STBY_MOIST)
→ report_dmd("usb_dp_dn")
T=20ms: water_check模块GPIO检测
→ power_event_notify(POWER_NE_WD_DETECT_BY_USB_GPIO)
→ water_detect_monitor(WD_TYPE_USB_GPIO)
→ usb_gpio_ops->is_water_intruded() → 返回1 (进水)
→ status |= 0b0100
→ report_uevent(WD_NON_STBY_MOIST)
→ report_dmd("usb_gpio")
T=30ms: 充电模块查询进水状态
→ water_detect_get_intruded_status() → true
→ 禁止充电
→ 显示进水警告: "USB接口检测到液体,请擦干后充电"
T=10min: 用户擦干USB接口
→ FSA9685再次检测: 返回0 (干燥)
→ status &= ~0b0001 → status=0b0100
→ GPIO仍检测到微量水分
→ 仍禁止充电
T=20min: 水分完全蒸发
→ GPIO检测: 返回0 (干燥)
→ status &= ~0b0100 → status=0b0000
→ water_detect_get_intruded_status() → false
→ 允许充电
6.2 工厂模式跳过检测
产线测试场景
T=0s: 设备进入工厂模式
→ power_cmdline_is_factory_mode() → true
T=1s: 测试人员插入USB
→ POWER_NE_WD_DETECT_BY_USB_DP_DN
→ water_detect_monitor
→ water_detect_is_disabled() → true
→ 直接返回,不执行检测
T=2s: 测试充电功能
→ water_detect_get_intruded_status() → false
→ 允许充电测试
→ 避免环境湿度导致测试失败
6.3 多检测方法冗余
极端场景: FSA9685芯片故障
T=0s: USB进水
T=0.01s: FSA9685检测 (芯片故障)
→ is_water_intruded() → 返回0 (误判为干燥)
→ status保持0b0000
T=0.02s: USB_GPIO检测 (正常工作)
→ is_water_intruded() → 返回1 (正确检测)
→ status |= 0b0100
T=0.03s: 充电模块查询
→ water_detect_get_intruded_status() → true
→ 成功禁止充电
→ 冗余设计避免单点故障
七、与其他模块协作
7.1 检测驱动注册
FSA9685/Scharger驱动:
→ water_detect_ops_register(&usb_dp_dn_ops)
→ ops[WD_TYPE_USB_DP_DN] = &usb_dp_dn_ops
FSA4480驱动:
→ water_detect_ops_register(&usb_id_ops)
→ ops[WD_TYPE_USB_ID] = &usb_id_ops
water_check模块:
→ water_detect_ops_register(&usb_gpio_ops)
→ ops[WD_TYPE_USB_GPIO] = &usb_gpio_ops
7.2 检测触发流程
USB驱动 (检测到插入)
↓
power_event_notify(POWER_NE_WD_DETECT_BY_USB_DP_DN)
↓
water_detect_notifier_call
↓
water_detect_monitor(WD_TYPE_USB_DP_DN)
↓
ops[WD_TYPE_USB_DP_DN]->is_water_intruded()
↓
FSA9685驱动检测函数
└─ 读取寄存器
└─ 判断阻抗/电容
└─ 返回 1(进水) / 0(干燥)
7.3 状态查询
bool is_water = water_detect_get_intruded_status();
if (is_water) {
disable_charging();
show_water_warning();
}
bool is_water = water_detect_get_intruded_status();
if (is_water) {
disable_headphone_detection();
protect_audio_codec();
}
7.4 依赖接口
| 模块 | 接口 | 用途 |
|---|
| power_event | power_event_bnc_register | 监听进水检测事件 |
| power_dsm | power_dsm_report_dmd | 上报DMD故障 |
| power_ui | power_ui_event_notify | 通知UI显示进水状态 |
| power_cmdline | power_cmdline_is_* | 查询工作模式 |
| power_dts | power_dts_read_u32_compatible | 解析DTS配置 |
八、关键技术要点
8.1 Ops注册机制
优势:
解耦:框架与检测实现分离
可扩展:新增检测方法无需修改框架
统一管理:集中维护状态和上报
实现:
struct water_detect_ops *ops[4];
static struct water_detect_ops my_ops = {
.type_name = "usb_dp_dn",
.is_water_intruded = my_detect_func,
};
water_detect_ops_register(&my_ops);
8.2 位图状态管理
位图设计:
优势:
节省内存 (4个bool → 4个bit)
原子操作 (位运算)
快速查询 (status > 0)
8.3 事件驱动架构
事件源 → power_event_notify → water_detect_notifier → 检测执行
优势:
松耦合:事件源与检测逻辑分离
灵活触发:任何模块都可触发检测
统一处理:集中的事件分发
8.4 工作模式过滤
关机充电/工厂模式禁用检测:
if (power_cmdline_is_powerdown_charging_mode())
return;
if (power_cmdline_is_factory_mode())
return;
原因:
关机充电:简化系统,减少复杂逻辑
工厂模式:避免环境湿度干扰产线测试
8.5 多检测方法融合
单一方法 vs 多方法:
单一方法:
- 优点: 简单、功耗低
- 缺点: 单点故障风险
多方法融合:
- 优点: 冗余保护、可靠性高
- 缺点: 复杂度增加
本框架策略:
支持1-4种方法同时使能
DTS灵活配置
任一方法检测到进水即触发保护
九、调试与诊断
9.1 日志输出
echo "water_detect" > /sys/kernel/debug/dynamic_debug/control
[water_detect] total_ops=3 type=0:usb_dp_dn ops register ok
[water_detect] total_ops=3 type=2:usb_gpio ops register ok
[water_detect] water detect disabled on factory mode
9.2 状态查询
cat /sys/class/hw_power/water_detect/status
cat /sys/class/power_dsm/dsm_battery/dsm_dump
9.3 模拟测试
static ssize_t water_detect_test_store(...)
{
power_event_notify(POWER_BNT_WD,
POWER_NE_WD_DETECT_BY_USB_GPIO, NULL);
}
echo 1 > /sys/class/hw_power/water_detect/test
十、总结
核心价值
统一框架:为多种进水检测方法提供统一管理接口
松耦合设计:框架与检测实现分离,易扩展
集中管理:统一状态维护、DMD上报、Uevent通知
灵活配置:DTS配置启用/禁用检测方法
冗余保护:支持多检测方法并行,提高可靠性
技术亮点
Ops注册机制:类似Linux驱动模型,解耦框架与实现
位图状态管理:高效存储和查询多检测点状态
事件驱动架构:通过power_event实现松耦合触发
工作模式过滤:关机充电/工厂模式智能禁用
对外统一接口:water_detect_get_intruded_status()简化查询
架构模式
适用场景
多芯片平台:不同产品使用不同检测芯片
冗余检测:同时使用多种方法提高可靠性
灵活配置:不同产品形态选择不同检测方法
统一管理:集中处理进水状态查询和上报
设计理念
分层解耦:框架层 ↔ 驱动层 ↔ 硬件层
开放扩展:新增检测方法仅需注册ops
集中决策:框架统一维护进水状态
简化应用:对外提供简单的bool查询接口
十一、与water_check的区别
| 模块 | 角色 | 类比 |
|---|
| water_detect | 框架/中间件 | 像Linux内核的VFS层,提供统一文件系统接口 |
| water_check | 具体实现 | 像ext4/ntfs文件系统,实现具体检测逻辑 |
water_detect 的价值
✅ 统一接口:应用层只需调用一个API
✅ 状态汇总:多种检测方法OR逻辑
✅ 解耦合:应用层不依赖具体检测实现
✅ 可扩展:新增检测方法无需改应用层
water_check 的价值
✅ GPIO硬件检测:多位置监控
✅ 中断驱动:实时响应
✅ 去抖动:3秒延迟过滤误触发
✅ 多点联动:检测多位置同时进水
✅ 保护动作:BATFET断开等
两者关系:water_check 是 water_detect 框架下的一个具体实现,类似于"接口"与"实现类"的关系。框架负责管理,实现负责干活。