Qcom ARM平台Windows驱动(三):S4 Hibernate异常关机问题分析与修复

Qcom ARM平台Windows驱动(三):S4 Hibernate 异常关机问题分析与修复

日期: 2026-03-21
系列: Qcom ARM平台Windows驱动开发
问题版本: BH202 v1043 (qcom-bh202-20250114 → bh202-debug-2025-0310)
适用: Windows ARM 驱动开发者、电源管理调试工程师


1. 问题概述

1.1 现象描述

项目 描述
问题 S4 Hibernate 测试过程中发生异常关机 (Abnormal Shutdown)
发生概率 几乎 100% 必现
复现条件 即使没有 SD 卡在位也会发生
时间窗口 S4 开始后约 1 分钟内

1.2 问题背景

此问题的引入背景是为了修复以下问题:

  • v1042 问题: 设备在 D0 状态保持,无法进入 D3
  • v1042 问题: 快速插拔卡时不弹出卡

为了修复这些问题,v1043 进行了多项修改,但意外引入了 S4 Hibernate 异常关机的新问题。


2. 根因分析

2.1 核心问题定位

根本原因: pofx_request_d3 的 timeout 时间从 5 秒大幅降低到 1 秒

2.2 代码变更对比

v1042 版本 (有问题之前的版本):

1
2
3
4
5
6
7
8
9
10
11
12
// cardinterface.c
int pofx_request_d3(bht_dev_ext_t *pdx, int timeout){
int wait_time_s;
// ...
if(timeout){
wait_time_s = timeout; // timeout 是秒数
util_init_waitloop(pdx, wait_time_s*1000, 0, &wait); // 乘以 1000 转毫秒
}
}

// 调用处
pofx_request_d3(pdx, 5); // 传入 5 秒

v1043 版本 (引入问题的版本):

1
2
3
4
5
6
7
8
9
10
// cardinterface.c
int pofx_request_d3(bht_dev_ext_t *pdx, int timeout_ms){ // ★ 参数名改为 _ms
// ...
if(timeout_ms){
util_init_waitloop(pdx, timeout_ms, 0, &wait); // ★ 直接使用,不再乘以 1000
}
}

// 调用处
pofx_request_d3(pdx, 1000); // ★ 传入 1000 毫秒 = 1 秒

2.3 问题分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
┌─────────────────────────────────────────────────────────────────────┐
│ S4 Hibernate 异常关机根因分析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ S4 Hibernate 过程中 Windows 需要做的事情: │
│ ────────────────────────────────────────────────────────── │
│ │
│ 1. 暂停所有应用程序 │
│ 2. 通知所有驱动程序准备进入 S4 │
│ 3. ★ 将内存内容写入 hiberfil.sys 文件 (最耗时!) │
│ 4. 等待所有驱动程序确认进入 D3 │
│ 5. 关闭电源 │
│ │
│ ────────────────────────────────────────────────────────────────── │
│ │
│ 问题时序: │
│ ───────── │
│ │
│ T0: Windows 开始 S4 hibernate │
│ │ │
│ ▼ │
│ T1: 调用 ScsiAdapterPower(StorPowerActionHibernate, D3) │
│ │ │
│ ▼ │
│ T2: 驱动调用 req_pre_enter_d3() │
│ │ │
│ ▼ │
│ T3: card_power_off() → pofx_request_d3(pdx, 1000) │
│ │ ★ 仅等待 1 秒 │
│ ▼ │
│ T4: ⏰ 1 秒 timeout! (StorPort 正在写 hiberfil.sys,还很忙) │
│ │ │
│ ▼ │
│ T5: card_request_d3 标志位被强制清 0 │
│ │ │
│ ▼ │
│ T6: 返回 -1 (失败) │
│ │ │
│ ▼ │
│ T7: 驱动认为已进入 D3,但 StorPort 还在等待确认 │
│ │ │
│ ▼ │
│ T8: 💥 Windows 电源管理器检测到状态不一致 │
│ │ │
│ ▼ │
│ T9: Abnormal Shutdown (异常关机) │
│ │
└─────────────────────────────────────────────────────────────────────┘

2.4 代码证据链

证据 1: 超时后强制清标志位:

1
2
3
4
5
6
// cardinterface.c - pofx_request_d3()
if (os_atomic_read(&gHwResTbl.card_request_d3)){
PrintDebug("%s: timeout! card_request_d3 not response\n", __FUNCTION__);
os_atomic_set(&gHwResTbl.card_request_d3, 0); // ★ 强制清 0!
return -1; // error
}

证据 2: 第二次调用会直接返回:

1
2
3
4
5
6
// cardinterface.c - pofx_request_d3()
pofx_active = os_atomic_read(&pdx->pofx_active);
if(!pofx_active){
PrintDebug("%s: pofx already idle, exit \n", __FUNCTION__);
return 0; // ★ 第二次调用直接返回!StorPort 未被通知
}

证据 3: S4 过程中有两次 card_power_off 调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// pmfunc.c
void req_enter_d3(bht_dev_ext_t *pdx)
{
func_autotimer_stop(pdx);
card_power_off(&(pdx->card), FALSE); // ★ 第一次调用
// ...
}

void req_pre_enter_d3(bht_dev_ext_t *pdx)
{
if (card_stop_infinite(&pdx->card, FALSE, NULL) == FALSE)
card_power_off(&(pdx->card), TRUE); // ★ 第二次调用
// ...
}

3. 修复方案

3.1 推荐方案:动态调整 Timeout

根据系统电源状态使用不同的 timeout 值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// cardinterface.c - 修复后的 card_power_off()

void card_power_off(sd_card_t *card, bool directly)
{
PrintDebug("Enter %s directy=%d\n", __FUNCTION__, directly);

if (directly)
goto next;

if (card_resume_sleep(card, FALSE) == FALSE)
goto next;

if (card_stop_infinite(card, FALSE, NULL) == FALSE)
goto next;

next:
if ((card->state != CARD_STATE_POWEROFF) || host_get_vdd1_state(card->host))
host_poweroff(card->host, card->card_type);

// Thomas add: BH202 ARM should use pofx->D3 to power off card.
// ★ 修复: 根据系统电源状态使用不同的 timeout
bht_dev_ext_t *pdx = card->host->pdx;

// S3/S4/RTD3 过程中使用 5 秒,其他场景用 1 秒
int timeout_ms = (pdx->pm_state.s3s4_entered ||
pdx->pm_state.rtd3_entered) ? 5000 : 1000;

pofx_request_d3(pdx, timeout_ms);

card->state = CARD_STATE_POWEROFF;
card->has_built_inf = FALSE;

PrintDebug("Exit %s\n", __FUNCTION__);
}

3.2 方案说明

场景 Timeout 原因
S3/S4 Hibernate 5000 ms (5秒) 系统电源管理需要更长时间协调
Runtime D3 (RTD3) 5000 ms (5秒) StorPort 可能正在处理请求
正常工作 1000 ms (1秒) 快速响应,用户体验

3.3 状态检查条件

1
2
3
4
5
6
7
// pm_state_t 结构
typedef struct {
bool s3s4_entered; // S3/S4 进入标志
bool rtd3_entered; // Runtime D3 进入标志
bool rtd3_en; // RTD3 使能
// ...
} pm_state_t;

4. 修复验证

4.1 测试用例

测试项 操作 预期结果
S4 Hibernate 进入 S4 → 观察 无异常关机,正常休眠
S4 Resume S4 → 唤醒 正常恢复,卡功能正常
RTD3 空闲 10 秒 进入 D3,无异常唤醒
快速插拔 快速插拔 SD 卡 正常识别,无卡住

4.2 日志验证点

修复后,在 S4 过程中应看到以下日志:

1
2
3
4
5
[PM] Enter card_power_off directy=0
[PM] s3s4_entered=1, timeout_ms=5000 // ★ 使用 5 秒 timeout
[PM] PoFxRequestD3: RefCount 1 -> 0
[PM] PoFxIdleComponent called
[PM] Exit card_power_off

5. 问题总结

5.1 根因总结

项目 描述
根本原因 PoFx timeout 参数单位变更导致实际等待时间大幅缩短
触发条件 S4 Hibernate 过程中 StorPort 响应延迟
影响范围 所有 S4 测试场景 (100% 复现)
问题定位 参数单位变更: 秒 → 毫秒

5.2 经验教训

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────────────┐
│ 代码修改经验教训 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. ★ 参数单位变更必须配套修改所有调用处 │
│ ───────────────────────────────────────── │
│ 问题: timeout 参数从 int timeout 改为 int timeout_ms │
│ 后果: 调用处的 "5" 从 5 秒变成了 5 毫秒 │
│ │
│ 2. ★ 电源管理 timeout 必须考虑系统电源状态 │
│ ───────────────────────────────────────── │
│ S3/S4: StorPort 可能忙于写 hiberfil.sys │
│ Runtime: StorPort 可能有待处理请求 │
│ 正常: 快速响应即可 │
│ │
│ 3. ★ 修改前必须评估对所有调用路径的影响 │
│ ───────────────────────────────────────── │
│ card_power_off() 在多处被调用 │
│ 每处调用可能有不同的 timeout 需求 │
│ │
│ 4. ★ 添加参数单位到参数名中 │
│ ───────────────────────────────────────── │
│ 好: timeout_ms, timeout_s, wait_count │
│ 差: timeout, delay, wait │
│ │
└─────────────────────────────────────────────────────────────────────┘

5.3 修复代码片段

完整修复代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// cardinterface.c - 修复 pofx_request_d3 调用

void card_power_off(sd_card_t *card, bool directly)
{
// ... (省略无关代码) ...

// ★ 修复: 根据系统电源状态选择合适的 timeout
bht_dev_ext_t *pdx = card->host->pdx;

// 在 S3/S4/RTD3 场景下使用更长的 timeout
// 因为 StorPort 可能在这些场景下响应较慢
int timeout_ms = (pdx->pm_state.s3s4_entered ||
pdx->pm_state.rtd3_entered) ? 5000 : 1000;

pofx_request_d3(pdx, timeout_ms);

// ... (省略无关代码) ...
}

6. 相关代码改动记录

6.1 v1042 → v1043 变更摘要

文件 变更 目的 副作用
cardinterface.c timeout 5→1秒 快速响应拔卡 引入 S4 问题
cardinterface.c timeout 参数名改为 _ms 代码规范 配合参数变更
pmfunc.c D3 进入流程调整 改善 D0 卡住问题 无明显副作用
irqhandler.c 中断检查移除 改善卡检测 无明显副作用

6.2 后续修复

版本 修复内容
v1044+ 恢复 timeout 5秒,添加 s3s4_entered 检查

7. 调试技巧

7.1 快速定位类似问题

当遇到 S4/S3 异常关机时,检查以下内容:

1
2
3
4
5
// 检查清单
1. pofx_request_d3 的 timeout 设置
2. s3s4_entered 标志是否正确设置
3. StorPort 响应时间是否足够
4. 中断处理是否在 S4 过程中被正确处理

7.2 日志宏

1
2
3
4
5
6
7
// 添加调试日志
#if DBG
DbgInfo("PoFx: s3s4_entered=%d, rtd3_entered=%d, timeout=%d\n",
pdx->pm_state.s3s4_entered,
pdx->pm_state.rtd3_entered,
timeout_ms);
#endif

8. 参考信息

8.1 相关文档

8.2 版本信息

版本 日期 描述
qcom-bh202-20250114-rc1 2025-01-14 v1042,稳定版本
bh202-debug-2025-0310-release 2025-03-10 v1043,引入问题
v1044+ 2025-xx-xx 修复版本

9. 总结

9.1 关键要点

要点 说明
单位一致性 参数单位变更必须同步修改所有调用处
场景感知 电源管理 timeout 应根据系统状态动态调整
测试覆盖 每次修改后测试 S3/S4/RTD3 所有场景
日志记录 添加关键路径日志便于问题定位

9.2 修复确认

  • S4 Hibernate 测试通过
  • S4 Resume 测试通过
  • RTD3 测试通过
  • 快速插拔测试通过
  • 原有 D0 卡住问题未复现

文档生成日期: 2026-03-21
问题版本: BH202 v1043
修复版本: BH202 v1044+