diff --git a/.gitignore b/.gitignore index de6479e5d5..616d26a747 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,16 @@ chromiumos_kernel_v5.15_git_backup.tar.xz # 本地基线(含 topology / tar,体积与隐私不宜入库) audio_topology/baseline-stash/ +# 运行日志与一次性采集物(勿提交) +/_logs/ +/scripts/_logs/ +/report.txt +/audio_topology/*.txt +/audio_topology/new_reboot/ + +# 本地个人命令记录(勿提交) +/常用命令 + # 本机 sound/soc 等对照 diff(可选放入 reference/chromeos-ubuntu-sound-diffs/) reference/chromeos-ubuntu-sound-diffs/diff-u_*.txt diff --git a/_bmad-output/planning-artifacts/epics.md b/_bmad-output/planning-artifacts/epics.md index 3113959034..46108fdbdc 100644 --- a/_bmad-output/planning-artifacts/epics.md +++ b/_bmad-output/planning-artifacts/epics.md @@ -125,7 +125,7 @@ workflowType: epics ### Story 3.1:OPERATION 覆盖 pro-audio / EBUSY / IEC958 / 全自动 作为一名 **Kaisa 用户**, -我希望 **OPERATION_PipeWire_Kaisa_UCM_HiFi.md** 说明 **UCM2 / HiFi**、**`speaker-test -D pulse`**、**IEC958',N**、以及安装器脚本(disable + install), +我希望 **OPERATION_PipeWire_Kaisa_UCM_HiFi.md** 说明 **UCM2 / HiFi**、**`speaker-test -D pipewire`**、**IEC958',N**、以及安装器脚本(disable + install), 以便 **排障不靠口口相传**(**FR5**)。 **验收标准:** diff --git a/docs/INDEX.md b/docs/INDEX.md index 23cf429cdc..edefc454e0 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -5,6 +5,9 @@ | 文档 | 说明 | | ---- | ---- | | [linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md](linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md) | **主路线(交付)**:UCM2、HiFi、`60-kaisa-ucm.lua`、Jack-driven 端口、IEC958、安装 / 验收 / 排障 | +| [linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md](linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md) | 排障记录:重启后无声的根因链路(WirePlumber 状态恢复 / `set_hw_params`)与 `--fix` 一键恢复 | +| [linux-hdmi/LOCAL_Root_Cause_and_Fix.md](linux-hdmi/LOCAL_Root_Cause_and_Fix.md) | **本机判因(替代 Live)**:`kaisa-audio-doctor.sh` 流程、证据表、安装顺序、与 DEBUG 文档衔接 | +| [linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md](linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md) | **根因深读**:`default-routes` 极低 HDMI 音量(机制 A 实测)与 `set_hw_params`(机制 B)分工 | | [README.md](README.md) | 本目录说明 | **脚本(仓库根 `scripts/`)**:禁用 pro-audio 片段 — [`disable-kaisa-pro-audio-wireplumber.sh`](../scripts/disable-kaisa-pro-audio-wireplumber.sh);安装 UCM overlay — [`install-kaisa-ucm-overlay.sh`](../scripts/install-kaisa-ucm-overlay.sh)。 diff --git a/docs/README.md b/docs/README.md index 7689b441ed..64bd2de4d9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,7 +5,9 @@ | 文档 | 内容 | | ---- | ---- | | [linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md](linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md) | **主路线(交付)**:UCM2、HiFi、Jack-driven 端口、IEC958、安装/验收/排障 | +| [linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md](linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md) | 排障记录:重启后无声与 `--fix` 恢复的证据与判因路径 | +| [linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md](linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md) | 根因深读:`default-routes` 极低 HDMI 音量 vs `set_hw_params` | -附属文件:`linux-hdmi/wireplumber/50-kaisa-sof-rt5682-hdmi.conf`、`linux-hdmi/systemd-user/kaisa-hdmi-iec958-pipewire.service`。 +附属文件:`linux-hdmi/wireplumber/50-kaisa-sof-rt5682-hdmi.conf`、`linux-hdmi/systemd-user/kaisa-hdmi-iec958-pipewire.service`(旧 pro-audio 示例)、**`linux-hdmi/systemd-user/kaisa-ucm-hifi-boot-fix.service`(重启必现无声时登录自动 `--fix`)**。 仓库根 [README.md](../README.md) · 路径表 [REPO_INDEX.md](../REPO_INDEX.md)。 diff --git a/docs/linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md b/docs/linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md new file mode 100644 index 0000000000..0d1d40f451 --- /dev/null +++ b/docs/linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md @@ -0,0 +1,97 @@ +# 排障记录:重启后无声(sof-rt5682 / UCM HiFi)与 `--fix` 恢复 + +本文把一次已复现的问题“落盘”:**系统重启后没有声音**,但运行仓库脚本的 **`--fix` 模式后恢复有声**。目标是让后续排障有统一的证据与判因路径。 + +适用:Ubuntu + PipeWire + WirePlumber,声卡为 `sof-rt5682`,并已按主交付文档启用 **UCM2 + HiFi**。 + +--- + +## 1. 现象 + +- **重启后无声**:桌面看起来仍是 HiFi/HDMI,但测试音无声。 +- **运行修复工具后恢复**:执行 `./scripts/kaisa-audio-doctor.sh --fix` 后恢复有声。 + +--- + +## 2. 关键结论(根因链路) + +该问题至少包含两类机制: + +- **机制 A(确定)**:WirePlumber 会在重启后按持久化状态恢复默认路由/默认音量(`~/.local/state/wireplumber/default-nodes`、`default-routes`)。如果恢复到“错误的默认输出”或“极小音量”,就会表现为“无声”。 + - **本机已量化的一例**:`default-routes` 里 HDMI1 端口的 `channelVolumes` 曾被持久化为约 **0.063**(6% 线性增益),冷启动后等价于「几乎无声」;Port1 仍为 1.0。详见 [ROOTCAUSE_Reboot_Silent_Analysis.md](ROOTCAUSE_Reboot_Silent_Analysis.md)。 +- **机制 B(不稳定点)**:HDMI1(常见为 `pcm=2`)在某些时刻会出现驱动层打开失败(PipeWire 日志出现 `set_hw_params: 输入/输出错误`),即使 **Jack=on / ELD 有效** 也可能无声。 + +`--fix` 模式之所以能“立刻恢复”,本质是把上述两类状态做了一次“强制收敛”: + +- 重启用户音频栈(清理 error 节点与链路) +- 强制 `HiFi` profile +- 按 **Jack/ELD** 选择输出(只尝试 Jack=on 的 HDMI) +- 取消静音、把音量拉到 1.0 +- 打开对应 `IEC958',N` +- `pw-play` 做最短试播闭环 + +--- + +## 3. 如何采集证据(重启后第一时间) + +重启后,先不要手工乱改设置,直接跑一次诊断: + +```bash +cd "/home/jack/文档/chromebox_10th_audio_driver" +./scripts/kaisa-audio-doctor.sh +``` + +重点关注这几段输出: + +- **WirePlumber state**: + - `~/.local/state/wireplumber/default-nodes` + - `~/.local/state/wireplumber/default-routes` +- **HDMI Jack states + ELD controls**: + - `HDMI/DP,pcm=2/3/4 Jack` 是否为 on/off + - `ELD device=2/3/4` 是否有 bytes(为空通常代表该路未就绪) +- **PipeWire 日志**: + - 是否出现 `set_hw_params: 输入/输出错误` + +--- + +## 4. 一键恢复(可重复执行) + +```bash +cd "/home/jack/文档/chromebox_10th_audio_driver" +./scripts/kaisa-audio-doctor.sh --fix +``` + +说明: + +- `--fix` 会尽量只对“确实插线”的 HDMI(Jack=on)尝试输出;若所有 HDMI Jack 都是 off,会回退到 Port1(模拟)。 +- `--fix` 会把音量拉到 1.0 并取消静音,避免“看似路由正确但音量太小”的假无声。 + +--- + +## 5. 重启后**必现**无声:自动收敛(可选) + +若每次冷启动进桌面后都要手跑 `--fix` 才有声,多半是 **机制 A**(WirePlumber 把默认 sink / 路由恢复到不合适的状态)或 **机制 B**(错误节点需重启栈清空)。可以用登录后 **user systemd oneshot** 自动执行与 `--fix` 相同的逻辑,无需改内核。 + +1. 仓库已提供: + - `scripts/kaisa-audio-boot-fix.sh`(等待 `pactl` 就绪后调用 doctor) + - `scripts/kaisa-audio-doctor.sh --fix-only`(只做修复、不写完整诊断报告,适合开机跑) +2. 单元模板:[systemd-user/kaisa-ucm-hifi-boot-fix.service](systemd-user/kaisa-ucm-hifi-boot-fix.service)(内附 `sed` 安装说明)。 + +**副作用**:每次登录会 **重启用户 PipeWire 栈一次** 并 **把默认 sink 音量拉到 1.0**(与手动 `--fix` 相同)。若你希望保留「仅诊断、不重启」,不要启用该单元,继续手跑 `./scripts/kaisa-audio-doctor.sh --fix`。 + +--- + +## 6. LiveCD 验证如何使用本工具 + +在 Ubuntu Live 环境中(临时安装 UCM overlay 后),同样可以运行: + +```bash +./scripts/kaisa-audio-doctor.sh +./scripts/kaisa-audio-doctor.sh --fix +``` + +用于判断问题属于: + +- **Live 原生就存在**(偏内核/硬件/显示链路/线材),还是 +- **仅安装后出现**(偏 WirePlumber 状态恢复、策略或配置)。 + diff --git a/docs/linux-hdmi/LOCAL_Root_Cause_and_Fix.md b/docs/linux-hdmi/LOCAL_Root_Cause_and_Fix.md new file mode 100644 index 0000000000..e5ede80f3e --- /dev/null +++ b/docs/linux-hdmi/LOCAL_Root_Cause_and_Fix.md @@ -0,0 +1,135 @@ +# 本机定位根因与修复方案(替代 LiveCD) + +LiveCD 未通过或不便使用时,在**已安装的 Ubuntu + PipeWire** 本机上用仓库脚本与下表即可判因;本机可长期保留安装与 WirePlumber 持久化状态,证据比 Live 更完整。 + +**必读**:主交付步骤见 [OPERATION_PipeWire_Kaisa_UCM_HiFi.md](OPERATION_PipeWire_Kaisa_UCM_HiFi.md);历史与 IEC958 对照见 [HISTORY.md](../../HISTORY.md)。 + +--- + +## 1. 前置条件 + +- 在**桌面会话**、**普通用户**下操作(**不要** `sudo ./scripts/kaisa-audio-doctor.sh`)。若以 root 运行,`systemctl --user` 会离线,`pactl` 可能无法连接,日志会误报「无声音」。 +- 仓库根目录执行:`./scripts/kaisa-audio-doctor.sh`(可加 `-o ./_logs/my.log` 指定报告路径)。 + +--- + +## 2. 判因流程(与计划一致) + +```mermaid +flowchart TD + start[本机桌面用户运行 doctor] + checkFiles{系统路径 UCM+Lua 存在?} + checkUcm{alsaucm 列出 HiFi?} + checkPactl{pactl 有 HiFi profile?} + branchInstall[安装: disable pro-audio + install overlay] + branchUcm[修 UCM: sof-rt5682.conf / HiFi.conf] + branchWp[修 WirePlumber: 60-kaisa-ucm.lua 与互斥片段] + branchRuntime[运行时: doctor --fix / 查 state / journal] + start --> checkFiles + checkFiles -->|否| branchInstall + checkFiles -->|是| checkUcm + checkUcm -->|否| branchUcm + checkUcm -->|是| checkPactl + checkPactl -->|否| branchWp + checkPactl -->|是| branchRuntime +``` + +--- + +## 3. 证据与修复对照表 + +| 本机证据 | 根因归纳 | 修复方向 | +|----------|----------|----------| +| `/usr/share/alsa/ucm2/conf.d/sof-rt5682/sof-rt5682.conf`、`GoogleKaisa/.../HiFi.conf`、`/usr/share/wireplumber/main.lua.d/60-kaisa-ucm.lua` **缺失** | 未安装 overlay(与 Live 仅跑一键未 `--install` 同类) | 见下文 **§4 安装顺序** | +| `alsaucm -c sof-rt5682` **import/parse 失败** | UCM 语法或入口未指向 `GoogleKaisa/.../HiFi.conf` | 对照仓库 [`reference/ucm2/`](../../reference/ucm2/README.md);[HISTORY.md](../../HISTORY.md) §3.2:`SectionVerb` + `TQ "HiFi"`、避免 `disdevall` 等导致 PipeWire 不注册 profile | +| `alsaucm` **有 HiFi**,`pactl list cards` **无 `HiFi:`** | ACP 未走 UCM,或与强制 pro-audio 片段互斥 | [`scripts/disable-kaisa-pro-audio-wireplumber.sh`](../../scripts/disable-kaisa-pro-audio-wireplumber.sh)(必要时 `--system`);重装 [`scripts/install-kaisa-ucm-overlay.sh`](../../scripts/install-kaisa-ucm-overlay.sh);`systemctl --user restart wireplumber pipewire pipewire-pulse` | +| **`HiFi:` 已存在**,但无声 / 切换异常 / journal 有 `set_hw_params: 输入/输出错误` | WirePlumber 恢复的默认路由错误,或某路 HDMI PCM 打开失败 | [DEBUG_Reboot_No_Sound_and_Fix.md](DEBUG_Reboot_No_Sound_and_Fix.md);**`./scripts/kaisa-audio-doctor.sh --fix`**;仅插一根 HDMI 试 pcm=3/4 再切默认 sink | + +--- + +## 4. 未安装时的推荐安装顺序(本机) + +仅在 §3 第一行命中时执行: + +```bash +cd "/path/to/chromebox_10th_audio_driver" +./scripts/disable-kaisa-pro-audio-wireplumber.sh +# 若曾在 /etc/wireplumber 装过 kaisa 片段: +# ./scripts/disable-kaisa-pro-audio-wireplumber.sh --system + +./scripts/install-kaisa-ucm-overlay.sh +# 脚本会在可用时重启用户 PipeWire 栈;否则登录会话后手动: +# systemctl --user restart wireplumber pipewire pipewire-pulse + +./scripts/kaisa-audio-doctor.sh +``` + +然后按 [OPERATION](OPERATION_PipeWire_Kaisa_UCM_HiFi.md) §3 验收 `alsaucm` / `pactl list cards`。 + +--- + +## 5. HiFi 已生效时的「重启后无声 / HDMI 打开失败」 + +1. 先读 [DEBUG_Reboot_No_Sound_and_Fix.md](DEBUG_Reboot_No_Sound_and_Fix.md)(机制 A:持久化路由;机制 B:`set_hw_params`),以及 [ROOTCAUSE_Reboot_Silent_Analysis.md](ROOTCAUSE_Reboot_Silent_Analysis.md)(**机制 A 实测**:`default-routes` 里 HDMI 端口 `channelVolumes≈0.06` 导致假无声)。 +2. 查看 `~/.local/state/wireplumber/default-nodes`、`default-routes`(重点 **`grep channelVolumes`**)与 `journalctl --user -u pipewire -b`。 +3. **首选一键恢复**:`./scripts/kaisa-audio-doctor.sh --fix` 在本机实测中**执行后即可恢复正常出声与路由**(重启栈 + 强制 HiFi + 按 Jack 选路 + IEC958 + 试播);若问题复现,可**重复执行**,不必先改 UCM。 + +```bash +./scripts/kaisa-audio-doctor.sh --fix +``` + +**重启必现、不想每次手跑**:见 [DEBUG_Reboot_No_Sound_and_Fix.md §5](DEBUG_Reboot_No_Sound_and_Fix.md) — 使用 `scripts/kaisa-audio-boot-fix.sh` + [systemd-user/kaisa-ucm-hifi-boot-fix.service](systemd-user/kaisa-ucm-hifi-boot-fix.service) 在登录后自动执行与 `--fix` 等价的收敛(有音量拉满、重启音频栈等副作用,与手动 `--fix` 相同)。 + +### 5.1 重要澄清:物理 HDMI 口 ≠ 固定的 ALSA pcm 编号 + +在 **jack-Kaisa** 上观察到一个容易误判的现象: + +- 仅连接 **一个** 显示器时,即使把同一根 HDMI 线插到不同物理口,系统也可能始终表现为 **`pcm=2 Jack=on`**,并在 `pactl list cards` 中显示 **`HDMI/DP,pcm=2` available**。 +- 只有在连接 **两个** 显示器时,才更容易同时出现 `pcm=2` 与 `pcm=3` 两路都 `Jack=on/available`(从而可对比两路的稳定性差异)。 + +因此,做 A/B 验证时不要用「插 HDMI1 / 插 HDMI2」直接类比为「测试 `pcm=2` / `pcm=3`」;请以 **Jack/ELD 实测**为准: + +```bash +amixer -c0 cget "iface=CARD,name='HDMI/DP,pcm=2 Jack'" +amixer -c0 cget "iface=CARD,name='HDMI/DP,pcm=3 Jack'" +amixer -c0 cget "iface=CARD,name='HDMI/DP,pcm=4 Jack'" +``` + +(可选)再看 ELD 是否非空: + +```bash +amixer -c0 cget "iface=PCM,name='ELD',device=2" +amixer -c0 cget "iface=PCM,name='ELD',device=3" +amixer -c0 cget "iface=PCM,name='ELD',device=4" +``` + +`values=on` + `ELD values>0` 的 pcm,才是当前实际连着显示器的输出链路。 + +--- + +## 6. 交付物(便于他人复核) + +1. `./_logs/kaisa-audio-doctor_*.log`(修复前后各一份更佳)。 +2. 注明命中 §3 中哪一行,以及是否已执行 §4 / §5。 + +--- + +## 7. 本仓库所在机器一次实测摘要(2026-04-09) + +在 **jack-Kaisa** 上以用户 `jack` 运行 `kaisa-audio-doctor.sh` 的报告见: + +- `_logs/kaisa-audio-doctor_local-plan-jack_20260409_130048.log` + +**归类**:§3 **第四行**(运行时 / 驱动打开失败),**不是**「未安装」分支。 + +**依据**: + +- 三处系统文件均存在;`alsaucm` 列出 `HiFi`;`pactl` **活动配置为 HiFi**,多路 `HiFi__hw_sofrt5682_*__sink` 已注册。 +- `journalctl` 中 **`HiFi__hw_sofrt5682_2__sink`**(HDMI1 / pcm=2)反复出现 **`set_hw_params: 输入/输出错误`**;默认 sink 曾指向该节点时易表现为无声或节点 `error`。 +- `~/.config/wireplumber/.../50-kaisa-sof-rt5682-hdmi.conf.disabled` 已存在,pro-audio 强制片段已禁用。 + +**修复结果**:随后执行 **`kaisa-audio-doctor.sh --fix`** 后,**本机实际使用已恢复正常**(与 DEBUG 文档中「`--fix` 可立刻恢复有声」的结论一致)。说明当时问题主要是 **栈内错误节点 / 默认 sink 与 Jack 路由未收敛**,而不是「必须改 UCM 才能好」。 + +**若日后再次无声**:可再跑 `--fix`;若 journal 仍反复出现某一路 `set_hw_params: 输入/输出错误`,再按 DEBUG 文档做单 HDMI、换口、对照 [HISTORY.md](../../HISTORY.md) IEC958。 + +`--fix` 运行记录:`_logs/kaisa-audio-doctor_local-plan-jack-fix_20260409_130131.log`。 diff --git a/docs/linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md b/docs/linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md index 46e376becb..9514754aea 100644 --- a/docs/linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md +++ b/docs/linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md @@ -80,7 +80,7 @@ pactl set-card-profile "$(pactl list cards short | awk '/cml_rt5682/ {print $2; ### 3.4 出声测试(走 PipeWire) ```bash -speaker-test -D pulse -c2 -t sine -f 440 -l 3 +speaker-test -D pipewire -c2 -t sine -f 440 -l 3 ``` 若你想强制把测试音打到某一路(避免默认 sink 干扰),先取出 sink 名: @@ -177,6 +177,13 @@ speaker-test -D hw:0,3 -c2 -r48000 -t sine systemctl --user start pipewire pipewire-pulse wireplumber ``` +### 5.4 每次重启进桌面后都要手跑 `--fix` 才有声 + +多为 WirePlumber 持久化默认路由不当或 PipeWire 残留 error 节点;证据与判因见 [DEBUG_Reboot_No_Sound_and_Fix.md](DEBUG_Reboot_No_Sound_and_Fix.md)。 + +- **临时**:`./scripts/kaisa-audio-doctor.sh --fix` +- **自动(接受与 `--fix` 相同副作用:重启用户音频栈、默认 sink 音量拉满)**:按 [DEBUG §5](DEBUG_Reboot_No_Sound_and_Fix.md) 安装 `kaisa-ucm-hifi-boot-fix.service`,使用 [`scripts/kaisa-audio-boot-fix.sh`](../../scripts/kaisa-audio-boot-fix.sh)。 + --- ## 6. 卸载 / 回退 UCM overlay @@ -198,5 +205,5 @@ systemctl --user restart wireplumber pipewire pipewire-pulse ./scripts/disable-kaisa-pro-audio-wireplumber.sh ``` -仓库仍保留相关 pro-audio 脚本仅供对照与临时排障;仓库主交付以本文为准。 +本仓库已收敛为 UCM/HiFi 主线;pro-audio 仅作为概念与历史背景出现在 `HISTORY.md`。 diff --git a/docs/linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md b/docs/linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md new file mode 100644 index 0000000000..86d35f9a30 --- /dev/null +++ b/docs/linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md @@ -0,0 +1,67 @@ +# 根因分析:重启后「无声」与本机状态对照 + +本文在已启用 **UCM HiFi** 的前提下,把「重启必现无声 / 手跑 `--fix` 又好」拆成**可验证**的两条链路,并给出比「整栈重启」更轻的验证手段。 + +--- + +## 1. 机制 A(本机已证实):WirePlumber 持久化了**极低的路由音量** + +WirePlumber 把每张声卡、每个**输出端口**的 `channelVolumes` 写在: + +`~/.local/state/wireplumber/default-routes` + +在本机(jack-Kaisa)与历史日志 `kaisa-audio-doctor_local-plan-jack_20260409_130048.log` 中,同一文件里出现: + +- **HDMI1**(UCM 端口名在文件里常编码为 `\oOut\c\sHDMI1`):`channelVolumes=0.062991671264172;0.062991671264172`(约 **6.3%** 线性增益,主观上接近无声) +- **Port1**:`channelVolumes=1.0;1.0`(正常) + +冷启动后 WirePlumber **按该文件恢复**各端口的音量;若当前默认输出或某条路由落在 HDMI1 上,就会表现为 **「路由看起来对、就是没声」**。这与 [DEBUG_Reboot_No_Sound_and_Fix.md](DEBUG_Reboot_No_Sound_and_Fix.md) 中的**机制 A**一致,并把「极小音量」落实为**可 grep 的数字**。 + +**`--fix` 为何能立刻好**:`kaisa-audio-doctor.sh` 的 `apply_fix` 会对当前默认 sink 执行 `wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0`,相当于把**当前输出**从持久化的低音量里拉出来;若再配合切到 Jack=on 的 HDMI / 重启栈,就会把「错路由 + 低音量 + error 节点」一并收敛。 + +**更轻的验证(不必先重启栈)**: + +```bash +grep -n channelVolumes ~/.local/state/wireplumber/default-routes +wpctl get-volume @DEFAULT_AUDIO_SINK@ +``` + +若 `default-routes` 里某 HDMI 行 `< 0.2` 而你觉得应该能听见,可先: + +```bash +wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 +# 或在声音设置里把对应输出滑条拉高,观察 default-routes 是否被写回合理值 +``` + +**根因追问(仍开放)**:为何 HDMI1 会被写成 ~0.06?可能是 GNOME/滑条与 WirePlumber 路由音量的交互、某次误触、或显示刻度与存储线性值不一致。**产品侧**可在 deb 文档中建议用户检查该文件;**工程侧**可评估是否在登录后只做「路由音量下限裁剪」而非整栈重启(需单独实现与测试)。 + +--- + +## 2. 机制 B(日志已见):某路 HDMI PCM `set_hw_params` 打开失败 + +`journalctl --user -u pipewire` 中出现: + +- `spa.alsa: set_hw_params: 输入/输出错误` +- `alsa_output....HiFi__hw_sofrt5682_2__sink`(pcm=2 / HDMI1)`suspended -> error` + +即 **驱动/时序层** 在打开 `hw:0,2` 时失败。脚本里已优先尝试 pcm=3/4 再 2(见 `detect_available_pcm` 注释)。这与 DEBUG 中的**机制 B**一致。 + +**根因追问**:需内核/SOF/HDMI 音频路径上进一步对照(单线插拔、EDID、HWE 小版本),超出用户态 deb 能「根治」的范围。 + +--- + +## 3. `default-nodes` 在说什么(辅助理解) + +`~/.local/state/wireplumber/default-nodes` 里常见多行 `default.configured.audio.sink.N=...`,表示曾配置过的默认 sink 候选;**当前真正生效的默认 sink** 仍以 `pactl info` / `wpctl status` 为准。机制 A 的「假无声」可以发生在 **default-nodes 指向 Port1,但某应用或路由仍走 HDMI1 且音量为 0.06** 的组合下,因此排查时 **务必看 `default-routes` 里各端口的 channelVolumes**。 + +--- + +## 4. 与交付策略的关系 + +| 手段 | 针对 | +|------|------| +| 查 / 清 / 修正 `default-routes` 中的低 `channelVolumes` | **机制 A**(用户态、可文档化) | +| `--fix` / 登录 oneshot | **A + B** 一并收敛(重、但稳) | +| 内核/驱动跟进 | **机制 B** 的根本缩小 | + +继续「找根本原因」时,建议优先在本机复现一次重启后、无声当下执行 **§1 的 grep**,确认是否仍为 HDMI 某行 `channelVolumes≈0.06`;若是,则**首要根因已落在 WirePlumber 持久化音量**,再决定是否做轻量修复脚本或上游 issue(WirePlumber/GNOME 音量模型)。 diff --git a/docs/linux-hdmi/systemd-user/kaisa-ucm-hifi-boot-fix.service b/docs/linux-hdmi/systemd-user/kaisa-ucm-hifi-boot-fix.service new file mode 100644 index 0000000000..2b039b3b52 --- /dev/null +++ b/docs/linux-hdmi/systemd-user/kaisa-ucm-hifi-boot-fix.service @@ -0,0 +1,24 @@ +# 重启后若「必现无声」,可启用本 user 单元:登录后自动执行与 +# kaisa-audio-doctor.sh --fix 相同的收敛(实际调用 scripts/kaisa-audio-boot-fix.sh)。 +# +# 安装(把下面 @REPO@ 换成仓库绝对路径,或复制后手工改 ExecStart 一行): +# mkdir -p ~/.config/systemd/user +# sed "s|@REPO@|$HOME/文档/chromebox_10th_audio_driver|g" kaisa-ucm-hifi-boot-fix.service > ~/.config/systemd/user/kaisa-ucm-hifi-boot-fix.service +# chmod +x .../scripts/kaisa-audio-boot-fix.sh .../scripts/kaisa-audio-doctor.sh +# systemctl --user daemon-reload +# systemctl --user enable --now kaisa-ucm-hifi-boot-fix.service +# +# 日志:~/.local/state/kaisa-audio/last-boot-fix.log(或环境变量 KAISA_BOOT_FIX_LOG) +# +[Unit] +Description=Kaisa sof-rt5682: post-login audio convergence (UCM HiFi, same as doctor --fix) +After=wireplumber.service pipewire-pulse.service pipewire.service +Wants=wireplumber.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@REPO@/scripts/kaisa-audio-boot-fix.sh + +[Install] +WantedBy=default.target diff --git a/next.md b/next.md index b1f475c284..65d6dff3da 100644 --- a/next.md +++ b/next.md @@ -1,83 +1,25 @@ -## LiveCD 验证:UCM(HiFi / Jack-driven)方案 +# 下一步(建议以 docs 主线为准) -目标:在 Ubuntu Live 环境里验证 **本仓库的 UCM2 + HiFi** 是否能实现: +本仓库当前主交付已收敛到 **UCM/HiFi(Jack-driven HDMI)** 主线;操作与验收请优先阅读: -- HDMI **插入才显示为输出** -- HDMI **测试音正常**、切换正常 +- `docs/linux-hdmi/OPERATION_PipeWire_Kaisa_UCM_HiFi.md` -### 1) Live 原生基线(不装任何东西) +排障与根因分析: -目的:确认 Live 的内核/驱动本身没把 HDMI 搞死(排除“纯驱动级”问题)。 +- `docs/linux-hdmi/DEBUG_Reboot_No_Sound_and_Fix.md` +- `docs/linux-hdmi/LOCAL_Root_Cause_and_Fix.md` +- `docs/linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md` + +建议的一键诊断(在桌面会话、普通用户下运行): ```bash -uname -a -aplay -l -amixer -c0 sset 'IEC958',0 on -amixer -c0 sset 'IEC958',1 on -amixer -c0 sset 'IEC958',2 on -speaker-test -D pulse -c2 -t sine -f 440 -l 2 +./scripts/kaisa-audio-doctor.sh --verify +./scripts/kaisa-audio-doctor.sh --fix --verify ``` -说明: - -- 若 `-D pulse` 不通,再用 ALSA 直连(例如 `speaker-test -D hw:0,3 ...`),但**不要在 PipeWire 运行时**直连 `hw:0,N`,否则会遇到 `EBUSY` 或把状态搅乱。 - -### 2) 在 Live 里临时安装本仓库 UCM 方案 - -有两种方式,选你方便的: - -- **方式 A(推荐)**:把仓库放到另一只 U 盘/同一只启动盘的可写分区里,Live 启动后挂载再执行脚本 -- **方式 B**:Live 里 `git clone` 仓库(需要网络) - -在仓库根目录执行: +单显示器环境下建议使用“只测已连接输出”的验证,避免误测未连接的 HDMI PCM: ```bash -./scripts/install-kaisa-ucm-overlay.sh -systemctl --user restart wireplumber pipewire pipewire-pulse +./scripts/kaisa-audio-doctor.sh --fix --verify --only-connected --retries 10 ``` -说明: - -- Live 环境通常**没有**你的 pro-audio 方案,所以一般**不需要**跑 `disable-kaisa-pro-audio-wireplumber.sh`(跑了也多半是空操作)。 - -### 3) 验收 UCM 是否真的生效(关键) - -```bash -alsaucm -c sof-rt5682 list _verbs -pactl list cards -``` - -预期: - -- `alsaucm` 应列出 `HiFi` -- `pactl list cards` 的 **配置文件** 里应出现 `HiFi:` - -若 `HiFi:` 已出现但活动配置不是 HiFi,手动切一次: - -```bash -pactl set-card-profile "$(pactl list cards short | awk '/cml_rt5682/ {print $2; exit}')" HiFi -``` - -### 4) 验收“插线才出现 HDMI” + 出声 - -- **拔掉 HDMI**:GNOME 输出列表应只剩模拟(或 HDMI 变不可用) -- **插上 HDMI**:应出现 HDMI1/HDMI2(以及显示器名),并可用测试音验证 - -终端验收(可选): - -```bash -pactl list short sinks -speaker-test -D pulse -c2 -t sine -f 440 -l 3 -``` - -### 5) 常见坑(Live 特有) - -- **user systemd 不完整**:`systemctl --user restart ...` 失败时,注销/重登 Live 会话再试一次 -- **误用 ALSA 直连**:PipeWire 运行时对 `hw:0,N` 直连会 `EBUSY`;优先 `-D pulse` - ---- - -如果需要我远程判定“Live 是否真正验证了 UCM 方案”,请贴: - -- `pactl list cards`(包含“配置文件”那段) -- 插拔 HDMI 前后 `pactl list short sinks` 的变化 \ No newline at end of file diff --git a/scripts/kaisa-audio-boot-fix.sh b/scripts/kaisa-audio-boot-fix.sh new file mode 100755 index 0000000000..bea33d95cd --- /dev/null +++ b/scripts/kaisa-audio-boot-fix.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# 登录后跑一次与 kaisa-audio-doctor --fix 相同的收敛,缓解「重启后 WirePlumber 恢复错误默认路由 / +# PipeWire 残留 error 节点」导致的必现无声。供 systemd --user 调用。 +# +# 环境变量: +# KAISA_REPO 仓库根目录(未设置则从本脚本位置推断) +set -euo pipefail + +REPO_ROOT="${KAISA_REPO:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/kaisa-audio" +LOG="${KAISA_BOOT_FIX_LOG:-${STATE_DIR}/last-boot-fix.log}" +mkdir -p "$(dirname "$LOG")" + +if [[ "${EUID}" -eq 0 ]]; then + echo "kaisa-audio-boot-fix: refuse to run as root" >&2 + exit 1 +fi + +# 等 PipeWire/Pulse 套接字就绪(冷启动桌面可能较慢) +for _ in $(seq 1 45); do + if [[ -n "${XDG_RUNTIME_DIR:-}" && -S "${XDG_RUNTIME_DIR}/pulse/native" ]] && pactl info &>/dev/null; then + break + fi + sleep 1 +done + +exec "${REPO_ROOT}/scripts/kaisa-audio-doctor.sh" --fix-only -o "$LOG" diff --git a/scripts/kaisa-audio-doctor.sh b/scripts/kaisa-audio-doctor.sh new file mode 100755 index 0000000000..d4bccc6866 --- /dev/null +++ b/scripts/kaisa-audio-doctor.sh @@ -0,0 +1,800 @@ +#!/usr/bin/env bash +# Kaisa (sof-rt5682) audio one-shot diagnostics (UCM/HiFi mainline). +# Collects high-signal state across ALSA/UCM2, PipeWire, WirePlumber, and policy/state files. +set -euo pipefail + +ts="$(date +%Y%m%d_%H%M%S)" +out="" +fix=0 +fix_only=0 +verify=0 +only_pcm="" +retries=1 +only_connected=0 +log_dir="${KAISA_LOG_DIR:-./_logs}" + +usage() { + cat <<'EOF' +Usage: + ./scripts/kaisa-audio-doctor.sh [--fix] [--fix-only] [--verify] [--only-pcm N] [--only-connected] [--retries N] [-o|--out /path/to/log] + +Modes: + default Diagnostics only (no changes) + --fix Best-effort recovery, then full diagnostics report + --fix-only Same recovery as --fix, then exit (for login/boot automation; use with -o for a short log) + --verify More detailed verification (route/ports/playback + kernel error window), best used right after boot + +Options: + --only-pcm N Restrict fix/verify to a single PCM (0/2/3/4). Useful for single-monitor A/B tests. + --only-connected Restrict fix/verify to HDMI PCMs that are currently connected (Jack=on + ELD non-empty). + --retries N Playback retries per PCM (default 1). On failure, restart user audio services then retry. + + Recovery steps: + - restart user PipeWire/WirePlumber + - set card profile to HiFi + - choose an "available" HDMI port (prefer pcm=3/4, then pcm=2), else fallback to Analog (pcm=0) + - enable matching IEC958 switch (pcm=2->IEC958,0; pcm=3->IEC958,1; pcm=4->IEC958,2) + - quick playback test (timeout) + +Notes: + - Run as your desktop user (NOT via sudo). + - This script assumes card name contains "cml_rt5682_def". +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --fix-only) fix=1; fix_only=1; shift ;; + --fix) fix=1; shift ;; + --verify) verify=1; shift ;; + --only-pcm) only_pcm="${2:-}"; shift 2 ;; + --only-connected) only_connected=1; shift ;; + --retries) retries="${2:-}"; shift 2 ;; + -o|--out) out="${2:-}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + --) shift; break ;; + -*) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + *) + # Backward compatible: treat the first positional argument as output path. + if [[ -z "${out}" ]]; then + out="$1" + shift + else + echo "Unexpected argument: $1" >&2 + usage >&2 + exit 2 + fi + ;; + esac +done + +out="${out:-${log_dir}/kaisa-audio-doctor_${ts}.log}" +mkdir -p "$(dirname "$out")" + +exec > >(tee "$out") 2>&1 + +hr() { printf '\n%s\n' "================================================================================"; } +sec() { hr; printf '%s\n' "$1"; hr; } +cmd() { printf '\n$ %s\n' "$*"; "$@"; } +maybe() { printf '\n$ %s\n' "$*"; "$@" || true; } +note() { printf '\n[NOTE] %s\n' "$*"; } +warn() { printf '\n[WARN] %s\n' "$*"; } + +is_int() { [[ "${1:-}" =~ ^[0-9]+$ ]]; } + +get_kaisa_card_name() { + pactl list cards short 2>/dev/null | awk '/cml_rt5682_def/ {print $2; exit}' +} + +amixer_find_numid() { + # Find numid by matching a single control line from `amixer -c0 controls`. + # Usage: + # amixer_find_numid "iface=PCM" "name='ELD'" "device=2" + # amixer_find_numid "iface=CARD" "name='HDMI/DP,pcm=2 Jack'" + local want_iface="${1:-}" + local want_name="${2:-}" + local want_device="${3:-}" + amixer -c0 controls 2>/dev/null | awk -v wi="$want_iface" -v wn="$want_name" -v wd="$want_device" ' + { + ok=1 + if (wi!="" && $0 !~ wi) ok=0 + if (wn!="" && $0 !~ wn) ok=0 + if (wd!="" && $0 !~ wd) ok=0 + if (ok) { + # line begins with: numid=XX,iface=... + if (match($0, /numid=[0-9]+/)) { + s=substr($0, RSTART, RLENGTH) + sub(/^numid=/, "", s) + print s + exit 0 + } + } + } + ' +} + +amixer_cget_by_control() { + # Best-effort cget using stable identifiers; falls back to numid lookup. + # For Jack: iface=CARD + exact name + # For ELD: iface=PCM + name='ELD' + device=N + local iface="${1:?iface}" + local name="${2:?name}" + local device="${3:-}" + + local numid + if [[ -n "$device" ]]; then + numid="$(amixer_find_numid "iface=${iface}" "name='${name}'" "device=${device}" || true)" + else + numid="$(amixer_find_numid "iface=${iface}" "name='${name}'" "" || true)" + fi + if [[ -n "$numid" ]]; then + maybe amixer -c0 cget "numid=${numid}" + else + warn "Could not find numid for iface=${iface} name='${name}' device=${device:-}" + fi +} + +read_jack_on_for_pcm() { + # Returns 0 if Jack is on, 1 otherwise/unknown. + # Uses stable name->numid resolution. + local pcm="${1:?pcm}" + local name="HDMI/DP,pcm=${pcm} Jack" + local numid + numid="$(amixer_find_numid "iface=CARD" "name='${name}'" "" || true)" + [[ -n "$numid" ]] || return 1 + # Example output contains ": values=on/off" + amixer -c0 cget "numid=${numid}" 2>/dev/null | awk ' + BEGIN { ok=0; seen=0 } + $1==":" && $2 ~ /^values=/ { + seen=1 + sub(/^values=/,"",$2) + if ($2=="on" || $2=="1") ok=1 + else ok=0 + exit + } + END { + if (seen && ok) exit 0 + exit 1 + } + ' +} + +eld_bytes_len_for_pcm() { + # Return byte count for ELD device=N (0 if empty/unknown). + local pcm="${1:?pcm}" + local numid + numid="$(amixer_find_numid "iface=PCM" "name='ELD'" "device=${pcm}" || true)" + [[ -n "$numid" ]] || { echo 0; return 0; } + amixer -c0 cget "numid=${numid}" 2>/dev/null | awk ' + BEGIN { printed=0 } + /values=/ { + # Example: "; type=BYTES,...,values=36" + if (match($0, /values=[0-9]+/)) { + s=substr($0, RSTART, RLENGTH) + sub(/^values=/,"",s) + print s + printed=1 + exit + } + } + END { if (!printed) print 0 } + ' || true +} + +wait_for_hdmi_ready_pcm() { + # Best-effort wait until HDMI pcm looks "ready enough": + # - Jack ON + # - ELD has bytes + # - ALSA subdevices avail > 0 (if we can parse) + local pcm="${1:?pcm}" + local timeout_s="${2:-6}" + local i + for i in $(seq 1 "$timeout_s"); do + if read_jack_on_for_pcm "$pcm"; then + local eld + eld="$(eld_bytes_len_for_pcm "$pcm" || echo 0)" + local avail total + avail="$(alsa_pcm_subdevices_available "$pcm" || true)" + total="$(alsa_pcm_subdevices_total "$pcm" || true)" + if [[ "$eld" -gt 0 ]]; then + if [[ -n "$avail" && -n "$total" && "$total" -gt 0 ]]; then + if [[ "$avail" -gt 0 ]]; then + return 0 + fi + else + # If we can't parse subdevices, treat Jack+ELD as enough. + return 0 + fi + fi + fi + sleep 1 + done + return 1 +} + +connected_hdmi_pcms() { + # Return space-separated HDMI PCMs that appear connected: Jack=on AND ELD has bytes. + # Ordered by preference (3/4/2). + local out=() + local pcm + for pcm in 3 4 2; do + if read_jack_on_for_pcm "$pcm"; then + local eld + eld="$(eld_bytes_len_for_pcm "$pcm" || echo 0)" + if [[ "$eld" -gt 0 ]]; then + out+=("$pcm") + fi + fi + done + echo "${out[*]-}" +} + +detect_available_pcm() { + # Prefer HDMI2/HDMI3 over HDMI1 because pcm=2 is known flaky on some setups. + # Returns one of: 3 4 2 0 (fallback to analog). + local card_section + card_section="$(pactl list cards 2>/dev/null | sed -n '/cml_rt5682_def/,+220p' || true)" + if echo "$card_section" | awk 'BEGIN{ok=1} /pcm=3, available/ {ok=0} END{exit ok}'; then echo 3; return 0; fi + if echo "$card_section" | awk 'BEGIN{ok=1} /pcm=4, available/ {ok=0} END{exit ok}'; then echo 4; return 0; fi + if echo "$card_section" | awk 'BEGIN{ok=1} /pcm=2, available/ {ok=0} END{exit ok}'; then echo 2; return 0; fi + echo 0 +} + +alsa_pcm_subdevices_total() { + # Return total subdevices for a given DEV from `aplay -l` (expects "子设备: X/Y" or "Subdevices: X/Y"). + local dev="${1:?dev}" + aplay -l 2>/dev/null | awk -v dev="$dev" ' + $0 ~ ("device " dev ":") {inblk=1} + inblk && ($0 ~ /子设备:/ || $0 ~ /Subdevices:/) { + # formats: "子设备: 0/1" or "Subdevices: 0/1" + gsub(/[^0-9\/]/,"",$0) + split($0,a,"/") + print a[2] + exit 0 + } + inblk && $0 ~ /^card [0-9]+:/ {inblk=0} + ' +} + +alsa_pcm_subdevices_available() { + # Return available subdevices count X from X/Y for a given DEV. + local dev="${1:?dev}" + aplay -l 2>/dev/null | awk -v dev="$dev" ' + $0 ~ ("device " dev ":") {inblk=1} + inblk && ($0 ~ /子设备:/ || $0 ~ /Subdevices:/) { + gsub(/[^0-9\/]/,"",$0) + split($0,a,"/") + print a[1] + exit 0 + } + inblk && $0 ~ /^card [0-9]+:/ {inblk=0} + ' +} + +iec958_index_for_pcm() { + case "${1:-}" in + 2) echo 0 ;; + 3) echo 1 ;; + 4) echo 2 ;; + *) echo "" ;; + esac +} + +sink_name_for_pcm() { + local pcm="${1:?pcm}" + echo "alsa_output.pci-0000_00_1f.3-platform-cml_rt5682_def.HiFi__hw_sofrt5682_${pcm}__sink" +} + +sink_exists() { + local sink="${1:?sink}" + pactl list short sinks 2>/dev/null | awk -v s="$sink" '$2==s {found=1} END{exit !found}' +} + +kernel_tail_since_ts() { + # Print kernel SOF/ASoC HDMI related lines since given timestamp. + # ts format: "YYYY-MM-DD HH:MM:SS" + local since_ts="${1:?since_ts}" + journalctl -k -b --since "$since_ts" --no-pager 2>/dev/null | \ + grep -nE 'sof-audio|sof_ipc3_pcm_hw_params|ipc tx error|STREAM_PCM_PARAMS|ASoC error|set_hw_params|HDMI[0-9]|pcm[0-9]+' 2>/dev/null | \ + tail -n 200 || true +} + +restart_user_audio_services() { + maybe systemctl --user restart pipewire pipewire-pulse wireplumber + maybe sleep 2 +} + +verify_one_pcm() { + local pcm="${1:?pcm}" + local since_ts="${2:?since_ts}" + local sink + sink="$(sink_name_for_pcm "$pcm")" + + echo + echo "=== VERIFY: pcm=$pcm ===" + + if [[ "$pcm" -ne 0 ]]; then + local jack="off" + if read_jack_on_for_pcm "$pcm"; then jack="on"; fi + local eld + eld="$(eld_bytes_len_for_pcm "$pcm" || echo 0)" + local avail total + avail="$(alsa_pcm_subdevices_available "$pcm" || true)" + total="$(alsa_pcm_subdevices_total "$pcm" || true)" + echo "Jack=$jack ELD_bytes=$eld Subdevices=${avail:-?}/${total:-?}" + fi + + if ! sink_exists "$sink"; then + warn "Sink missing in PipeWire: $sink" + note "pactl list short sinks (for reference):" + maybe pactl list short sinks + return 2 + fi + + note "Routing to sink: $sink" + maybe pactl set-default-sink "$sink" + maybe pactl info | sed -n '/默认音频入口/p' + maybe wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 + maybe wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 + + local iec + iec="$(iec958_index_for_pcm "$pcm")" + if [[ -n "$iec" ]]; then + maybe amixer -c0 sset "IEC958,${iec}" on + fi + + note "pw-play (attempt, 5s timeout)" + local attempt=1 + local max_attempts="${retries}" + if ! is_int "$max_attempts" || [[ "$max_attempts" -lt 1 ]]; then + max_attempts=1 + fi + local ok=0 + while [[ "$attempt" -le "$max_attempts" ]]; do + note "pw-play attempt ${attempt}/${max_attempts}" + if timeout -k 1s 5s pw-play /usr/share/sounds/alsa/Front_Center.wav; then + note "pw-play: OK (exit 0)" + ok=1 + break + fi + warn "pw-play: FAILED (non-zero exit)" + if [[ "$attempt" -lt "$max_attempts" ]]; then + note "Restarting user audio services before retry" + restart_user_audio_services + fi + attempt=$((attempt+1)) + done + + note "Kernel error window since $since_ts (tail)" + local k + k="$(kernel_tail_since_ts "$since_ts")" + if [[ -n "$k" ]]; then + warn "Kernel shows possible SOF/ASoC/HDMI issues in this window:" + printf '%s\n' "$k" + return 1 + fi + + if [[ "$ok" -eq 1 ]]; then + return 0 + fi + return 3 +} + +verify_audio() { + sec "VERIFY mode (more detailed validation)" + note "This runs active route + playback attempts and captures kernel error windows." + note "Best run right after boot and BEFORE any manual --fix, to catch first-open races." + + local since_ts + since_ts="$(date '+%F %T')" + echo "verify_start_ts=$since_ts" + + sec "VERIFY snapshot (user services / routing / objects)" + maybe systemctl --user status pipewire pipewire-pulse wireplumber --no-pager + maybe pactl info + maybe pactl list short cards + maybe pactl list cards | sed -n '/cml_rt5682_def/,+220p' + maybe pactl list short sinks + maybe wpctl status + + sec "VERIFY WirePlumber state files (default-*)" + maybe ls -la ~/.local/state/wireplumber + maybe sed -n '1,240p' ~/.local/state/wireplumber/default-profile + maybe sed -n '1,240p' ~/.local/state/wireplumber/default-nodes + maybe sed -n '1,240p' ~/.local/state/wireplumber/default-routes + + sec "VERIFY ALSA readiness (Jack/ELD/subdevices)" + maybe aplay -l + echo + echo "HDMI/DP Jack + ELD + Subdevices:" + local pcm + for pcm in 2 3 4; do + local jack="off" + if read_jack_on_for_pcm "$pcm"; then jack="on"; fi + local eld + eld="$(eld_bytes_len_for_pcm "$pcm" || echo 0)" + local avail total + avail="$(alsa_pcm_subdevices_available "$pcm" || true)" + total="$(alsa_pcm_subdevices_total "$pcm" || true)" + echo "pcm=$pcm Jack=$jack ELD_bytes=$eld Subdevices=${avail:-?}/${total:-?}" + done + + sec "VERIFY per-sink playback attempts (with kernel windows)" + local preferred + preferred="$(detect_available_pcm)" + note "Order preference: detected=$preferred then 3,4,2 then 0" + local pcms=() + if [[ -n "$only_pcm" ]]; then + pcms+=("$only_pcm") + elif [[ "$only_connected" -eq 1 ]]; then + local connected + connected="$(connected_hdmi_pcms)" + if [[ -n "$connected" ]]; then + note "Restricting VERIFY targets due to --only-connected: ${connected}" + # shellcheck disable=SC2206 + pcms+=($connected) + else + warn "--only-connected set but no connected HDMI PCMs detected; falling back to pcm=0" + pcms+=(0) + fi + else + pcms+=("$preferred") + for pcm in 3 4 2 0; do + [[ "$pcm" == "$preferred" ]] || pcms+=("$pcm") + done + fi + + local ok_any=0 + local ok_pcm="" + local ret + for pcm in "${pcms[@]}"; do + if [[ "$pcm" -ne 0 ]]; then + if ! wait_for_hdmi_ready_pcm "$pcm" 6; then + note "pcm=$pcm not ready (Jack/ELD/subdevices); skipping verify attempt" + continue + fi + fi + + verify_one_pcm "$pcm" "$since_ts" || ret=$? + ret="${ret:-0}" + if [[ "$ret" -eq 0 ]]; then + ok_any=1 + ok_pcm="$pcm" + note "VERIFY succeeded on pcm=$pcm; stopping further attempts to avoid triggering flaky paths." + break + fi + ret="" + # Move the window forward so each attempt isolates new kernel lines. + since_ts="$(date '+%F %T')" + sleep 1 + done + + sec "VERIFY summary" + if [[ "$ok_any" -eq 1 ]]; then + note "At least one sink attempt completed with no kernel error lines in its window (pcm=${ok_pcm})." + note "Default sink is kept at this successful pcm to preserve working audio." + note "If you still have silence but no kernel errors: focus on routing/monitor input/volume persistence." + else + warn "No sink attempt was clean. If windows show ipc tx error -5 / hw_params failures, this points to kernel/SOF." + fi +} + +apply_fix() { + sec "FIX mode (best-effort recovery)" + + if [[ "${EUID}" -eq 0 ]]; then + warn "You are running as root. Fix mode must run as your desktop user (no sudo)." + warn "Abort fix to avoid writing state into the wrong user session." + return 1 + fi + + if [[ -z "${XDG_RUNTIME_DIR-}" || ! -d "${XDG_RUNTIME_DIR-}" ]]; then + warn "XDG_RUNTIME_DIR is missing; likely not in a desktop session. Abort fix." + return 1 + fi + + note "Restarting user audio services" + restart_user_audio_services + + note "Post-restart quick state (forensics)" + maybe pactl info | sed -n '/默认音频入口/p' + maybe pactl list cards | sed -n '/cml_rt5682_def/,+120p' + maybe aplay -l + + local card + card="$(get_kaisa_card_name)" + if [[ -z "$card" ]]; then + warn "Could not find card name (expected match: cml_rt5682_def). Abort fix." + return 1 + fi + + note "Forcing profile to HiFi on card: $card" + maybe pactl set-card-profile "$card" HiFi + maybe sleep 1 + + local preferred_pcm + preferred_pcm="$(detect_available_pcm)" + note "Detected available pcm (from port availability): $preferred_pcm (preference: 3/4/2, fallback 0)" + + # Try a small ordered set. Start with "available" one, but if ALSA reports 0/1 subdevices or playback fails, + # fall back to other HDMI PCMs (3/4/2) then analog (0). + local candidates=() + if [[ -n "$only_pcm" ]]; then + candidates+=("$only_pcm") + note "Restricting FIX candidates due to --only-pcm: $only_pcm" + elif [[ "$only_connected" -eq 1 ]]; then + local connected + connected="$(connected_hdmi_pcms)" + if [[ -n "$connected" ]]; then + note "Restricting FIX candidates due to --only-connected: ${connected}" + # shellcheck disable=SC2206 + candidates+=($connected) + # Always keep analog fallback last, in case HDMI opens but remains silent. + candidates+=(0) + else + warn "--only-connected set but no connected HDMI PCMs detected; falling back to pcm=0" + candidates+=(0) + fi + else + candidates+=("$preferred_pcm") + for p in 3 4 2 0; do + [[ "$p" == "$preferred_pcm" ]] || candidates+=("$p") + done + fi + + local pcm sink iec avail total + for pcm in "${candidates[@]}"; do + # For HDMI outputs, only try PCMs whose Jack is currently ON. + if [[ "$pcm" -ne 0 ]]; then + if ! wait_for_hdmi_ready_pcm "$pcm" 6; then + note "pcm=$pcm not ready (Jack/ELD/subdevices); skipping" + continue + fi + fi + + # Skip clearly broken HDMI devices (subdevices available == 0). + if [[ "$pcm" -ne 0 ]]; then + avail="$(alsa_pcm_subdevices_available "$pcm" || true)" + total="$(alsa_pcm_subdevices_total "$pcm" || true)" + if [[ -n "$avail" && -n "$total" && "$total" -gt 0 && "$avail" -eq 0 ]]; then + warn "pcm=$pcm appears unavailable at ALSA level (subdevices ${avail}/${total}); skipping" + continue + fi + fi + + sink="alsa_output.pci-0000_00_1f.3-platform-cml_rt5682_def.HiFi__hw_sofrt5682_${pcm}__sink" + if [[ "$pcm" -eq 0 ]]; then + note "Trying fallback Analog (Port1) sink: $sink" + else + note "Trying HDMI pcm=$pcm sink: $sink" + fi + + maybe pactl set-default-sink "$sink" + maybe pactl info | sed -n '/默认音频入口/p' + maybe wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 + maybe wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 + + iec="$(iec958_index_for_pcm "$pcm")" + if [[ -n "$iec" ]]; then + note "Enabling IEC958 for pcm=$pcm -> IEC958,$iec" + maybe amixer -c0 sset "IEC958,${iec}" on + fi + + note "Quick playback test on pcm=$pcm (timeout -k 1s 5s), retries=${retries}" + local attempt=1 + local max_attempts="${retries}" + if ! is_int "$max_attempts" || [[ "$max_attempts" -lt 1 ]]; then + max_attempts=1 + fi + local since_ts + since_ts="$(date '+%F %T')" + while [[ "$attempt" -le "$max_attempts" ]]; do + note "pw-play attempt ${attempt}/${max_attempts} (pcm=$pcm)" + if timeout -k 1s 5s pw-play /usr/share/sounds/alsa/Front_Center.wav; then + note "Playback command returned success on pcm=$pcm" + note "Post-play quick checks (kernel/user logs)" + maybe journalctl --user -u pipewire -b --no-pager | tail -n 40 + local k + k="$(kernel_tail_since_ts "$since_ts")" + if [[ -n "$k" ]]; then + warn "Kernel window since $since_ts shows possible SOF/ASoC issues:" + printf '%s\n' "$k" + fi + return 0 + fi + warn "Playback test failed on pcm=$pcm (attempt ${attempt}/${max_attempts})" + local k + k="$(kernel_tail_since_ts "$since_ts")" + if [[ -n "$k" ]]; then + warn "Kernel window since $since_ts shows possible SOF/ASoC issues:" + printf '%s\n' "$k" + fi + if [[ "$attempt" -lt "$max_attempts" ]]; then + note "Restarting user audio services before retry" + restart_user_audio_services + since_ts="$(date '+%F %T')" + fi + attempt=$((attempt+1)) + done + warn "Playback test failed on pcm=$pcm; trying next candidate" + done + + warn "Fix mode could not find a working sink automatically. Check cables/monitor input and re-run with only one HDMI plugged." + return 1 +} + +sec "Kaisa audio doctor (sof-rt5682) — report: $out" +maybe uname -a +maybe date +maybe id + +sec "Session sanity (THIS OFTEN EXPLAINS 'no sound')" +echo +echo "If you run this as root / without a logged-in desktop session:" +echo "- systemctl --user will be offline" +echo "- /run/user/\$UID may not exist" +echo "- PipeWire/WirePlumber won't be running" +echo "- ALSA may show 'no soundcards found'" +echo +echo "Current:" +maybe bash -lc 'echo "USER=$USER UID=$UID HOME=$HOME XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR-}"' +maybe bash -lc 'if [ -n "${XDG_RUNTIME_DIR-}" ]; then ls -ld "${XDG_RUNTIME_DIR}" 2>/dev/null || true; else echo "XDG_RUNTIME_DIR is empty"; fi' +maybe bash -lc 'test -S "${XDG_RUNTIME_DIR-}/pipewire-0" && echo "pipewire socket: OK" || echo "pipewire socket: MISSING"' +maybe bash -lc 'test -S "${XDG_RUNTIME_DIR-}/pulse/native" && echo "pulse native socket: OK" || echo "pulse native socket: MISSING"' +echo +if [[ "${EUID}" -eq 0 ]]; then + echo "WARNING: running as root (EUID=0). For best results, run as your desktop user WITHOUT sudo:" + echo " ./scripts/kaisa-audio-doctor.sh" +fi + +if [[ "$fix" -eq 1 ]]; then + apply_fix || true +fi + +if [[ "$fix_only" -eq 1 ]]; then + exit 0 +fi + +if [[ "$verify" -eq 1 ]]; then + verify_audio || true +fi + +sec "Versions (PipeWire / WirePlumber / ALSA utils)" +maybe pipewire --version +maybe wireplumber --version +maybe wpctl --version +maybe pactl --version +maybe pw-play --version +maybe speaker-test --version +maybe alsaucm --version +maybe amixer --version +maybe aplay --version + +sec "User services status" +maybe systemctl --user is-system-running +maybe systemctl --user status pipewire pipewire-pulse wireplumber --no-pager +maybe systemctl --user status pipewire.socket wireplumber.socket --no-pager + +sec "ALSA enumeration" +maybe aplay -l +sec "ALSA PCM list (check pipewire/default/pulse)" +maybe aplay -L + +sec "UCM sanity" +maybe alsaucm -c sof-rt5682 list _verbs +maybe alsaucm -c sof-rt5682 list _devices + +sec "IEC958 switches (all 0/1/2)" +maybe amixer -c0 sget 'IEC958',0 +maybe amixer -c0 sget 'IEC958',1 +maybe amixer -c0 sget 'IEC958',2 + +sec "HDMI Jack states (on/off) + ELD controls" +amixer_cget_by_control CARD "HDMI/DP,pcm=2 Jack" +amixer_cget_by_control CARD "HDMI/DP,pcm=3 Jack" +amixer_cget_by_control CARD "HDMI/DP,pcm=4 Jack" +amixer_cget_by_control PCM "ELD" 2 +amixer_cget_by_control PCM "ELD" 3 +amixer_cget_by_control PCM "ELD" 4 + +sec "Installed files (system paths)" +maybe ls -l /usr/share/alsa/ucm2/conf.d/sof-rt5682/sof-rt5682.conf +maybe ls -l /usr/share/alsa/ucm2/GoogleKaisa/sof-rt5682/HiFi.conf +maybe ls -l /usr/share/wireplumber/main.lua.d/60-kaisa-ucm.lua +maybe ls -l /usr/share/wireplumber/main.lua.d/60-kaisa-ucm.lua.disabled + +sec "Potential conflicting WirePlumber snippets (user/system)" +maybe ls -la ~/.config/wireplumber/wireplumber.conf.d +maybe ls -la ~/.config/wireplumber/wireplumber.conf.d/*kaisa* 2>/dev/null +maybe ls -la /etc/wireplumber/wireplumber.conf.d 2>/dev/null +maybe ls -la /etc/wireplumber/wireplumber.conf.d/*kaisa* 2>/dev/null + +sec "WirePlumber state (profile / nodes / routes)" +maybe ls -la ~/.local/state/wireplumber +maybe sed -n '1,200p' ~/.local/state/wireplumber/default-profile +maybe sed -n '1,200p' ~/.local/state/wireplumber/default-nodes +maybe sed -n '1,200p' ~/.local/state/wireplumber/default-routes + +sec "WirePlumber default-routes: persisted volume sanity" +routes="${HOME}/.local/state/wireplumber/default-routes" +if [[ -f "$routes" ]]; then + low_lines="$(awk -F'channelVolumes=' ' + NF >= 2 { + rest = $2 + sub(/;.*/, "", rest) + v = rest + 0.0 + if (v > 0 && v < 0.25) print $0 + } + ' "$routes" 2>/dev/null || true)" + if [[ -n "$low_lines" ]]; then + warn "Persisted channelVolumes below ~0.25 in default-routes (can sound like silence after reboot):" + printf '%s\n' "$low_lines" + note "See docs/linux-hdmi/ROOTCAUSE_Reboot_Silent_Analysis.md" + else + note "No suspiciously low channelVolumes lines in default-routes (threshold 0.25)." + fi +else + note "No default-routes file (yet)." +fi + +sec "PipeWire card / profile / ports (focus: cml_rt5682_def)" +maybe pactl list cards short +maybe pactl list cards | sed -n '/cml_rt5682_def/,+220p' + +sec "Sinks (PipeWire) + default sink" +maybe pactl info +maybe pactl list short sinks +maybe wpctl status + +sec "Kernel hints (SOF/HDMI hw_params IPC errors)" +echo +echo "Note: if you see PipeWire 'set_hw_params: Input/output error', the real cause is often in kernel logs." +echo " This section tries to summarize SOF/ASoC HDMI failures (e.g. ipc tx error -5 for pcm2 HDMI1)." + +kernel_snip="$( + journalctl -k -b --no-pager 2>/dev/null | \ + grep -nE 'sof-audio|sof_ipc3_pcm_hw_params|ipc tx error|STREAM_PCM_PARAMS|ASoC error|HDMI1|pcm2' 2>/dev/null | \ + tail -n 120 || true +)" +if [[ -n "${kernel_snip}" ]]; then + warn "Kernel log shows SOF/ASoC HDMI failures (tail):" + printf '%s\n' "${kernel_snip}" + note "If it mentions: sof_ipc3_pcm_hw_params: pcm2 (HDMI1) ... ipc failed ... -5" + note "then this is a kernel/SOF issue (not UCM/WirePlumber). Capture full: journalctl -k -b | grep -nE 'sof-audio|ipc tx error|pcm2|HDMI1'" +else + note "No matching kernel SOF/HDMI error lines found (or insufficient permission to read kernel journal)." +fi + +sec "Quick playback tests (non-destructive)" +echo +echo "Note: if ALSA 'pulse' PCM is missing, do NOT use: speaker-test -D pulse" +echo "Try these instead (they use PipeWire):" +echo +echo "Tip: these are wrapped with a short timeout to avoid hanging." +echo " (uses: timeout -k 1s 5s ... -> TERM then KILL)" +maybe timeout -k 1s 5s speaker-test -D pipewire -c2 -t sine -f 440 -l 1 +maybe timeout -k 1s 5s speaker-test -D default -c2 -t sine -f 440 -l 1 +maybe timeout -k 1s 5s pw-play /usr/share/sounds/alsa/Front_Center.wav +maybe timeout -k 1s 5s paplay /usr/share/sounds/alsa/Front_Center.wav + +sec "Recent logs (journalctl --user, current boot)" +maybe journalctl --user -u wireplumber -b --no-pager -n 200 +maybe journalctl --user -u pipewire -b --no-pager -n 200 +maybe journalctl --user -u pipewire-pulse -b --no-pager -n 200 + +sec "Hints" +cat <<'EOF' +- If HDMI ports show "not available", verify cable/monitor input/EDID and re-plug. +- If profile keeps reverting after reboot, compare: + - ~/.local/state/wireplumber/default-profile + - /usr/share/wireplumber/main.lua.d/60-kaisa-ucm.lua (device.profile) + - any *kaisa* snippets under ~/.config/wireplumber/ or /etc/wireplumber/ +- If set_hw_params errors appear in logs for a given pcm (2/3/4), test only ONE HDMI at a time and switch sink accordingly. +EOF + +hr +echo "Done. Report saved to: $out" diff --git a/scripts/kaisa-hdmi-stability-probe.sh b/scripts/kaisa-hdmi-stability-probe.sh new file mode 100755 index 0000000000..8d60c3ac8d --- /dev/null +++ b/scripts/kaisa-hdmi-stability-probe.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash +# Kaisa (sof-rt5682) HDMI stability probe. +# Goal: one script to reproduce/quantify "reboot -> silent -> later OK" and capture +# kernel SOF IPC(-5)/ASoC errors with monotonic timestamps. +# +# What it does (non-destructive, no service stop by default): +# - Repeatedly tries playback on HiFi HDMI sinks (pcm=2/3/4 as requested) +# - After each attempt, scrapes *new* kernel lines for SOF/ASoC HDMI hw_params failures +# - Records ALSA subdevices availability, Jack/ELD states, default sink, and sinks list +# +# Logs: +# ./_logs/kaisa-hdmi-stability-probe_.log (or $KAISA_LOG_DIR) +# +# Notes: +# - Run as desktop user (no sudo). Needs access to kernel journal for best results. +# - If journalctl -k is restricted, the script will still log PipeWire errors and ALSA state. +set -euo pipefail + +ts="$(date +%Y%m%d_%H%M%S)" +log_dir="${KAISA_LOG_DIR:-./_logs}" +out="${log_dir}/kaisa-hdmi-stability-probe_${ts}.log" + +duration_s="${KAISA_PROBE_DURATION_S:-120}" +interval_s="${KAISA_PROBE_INTERVAL_S:-2}" +pcms="${KAISA_PROBE_PCMS:-2,3}" # comma-separated list: 2,3,4 +audio_file="${KAISA_PROBE_AUDIO_FILE:-/usr/share/sounds/alsa/Front_Center.wav}" + +usage() { + cat <<'EOF' +Usage: + ./scripts/kaisa-hdmi-stability-probe.sh [--duration S] [--interval S] [--pcms 2,3,4] + +Env overrides: + KAISA_LOG_DIR + KAISA_PROBE_DURATION_S + KAISA_PROBE_INTERVAL_S + KAISA_PROBE_PCMS + KAISA_PROBE_AUDIO_FILE + +Example: + KAISA_PROBE_DURATION_S=180 KAISA_PROBE_PCMS=2,3 ./scripts/kaisa-hdmi-stability-probe.sh +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) usage; exit 0 ;; + --duration) duration_s="${2:?seconds}"; shift 2 ;; + --interval) interval_s="${2:?seconds}"; shift 2 ;; + --pcms) pcms="${2:?list}"; shift 2 ;; + *) echo "Unknown arg: $1" >&2; usage >&2; exit 2 ;; + esac +done + +mkdir -p "$log_dir" +exec > >(tee "$out") 2>&1 + +hr() { printf '\n%s\n' "================================================================================"; } +sec() { hr; printf '%s\n' "$1"; hr; } +note() { printf '\n[NOTE] %s\n' "$*"; } +warn() { printf '\n[WARN] %s\n' "$*"; } + +kernel_grep_re='sof-audio|sof_ipc3_pcm_hw_params|ipc tx error|STREAM_PCM_PARAMS|ASoC error|HDMI1|HDMI2|HDMI3|pcm2|pcm3|pcm4' +pipewire_grep_re='set_hw_params|suspended -> error|Start error|hw_sofrt5682_[234]__sink' + +get_default_sink() { + pactl info 2>/dev/null | awk -F': ' '/^默认音频入口:|^Default Sink:/{print $2; exit}' +} + +list_sinks_short() { + pactl list short sinks 2>/dev/null || true +} + +alsa_dump_subdevices() { + aplay -l 2>/dev/null | awk ' + BEGIN { dev=""; } + $0 ~ /^card [0-9]+: / { next } + $0 ~ /, device [0-9]+: / { + match($0, /device [0-9]+:/) + dev=substr($0, RSTART+7, RLENGTH-8) + gsub(/[^0-9]/,"",dev) + print $0 + next + } + $0 ~ /(子设备:|Subdevices:)/ { + print " " $0 + next + } + ' +} + +amixer_dump_jack_eld() { + # Stable numids for this platform are often: Jack 10/16/22; ELD 15/21/27. + # If they differ, doctor script can resolve dynamically; here we best-effort. + for n in 10 16 22 15 21 27; do + echo + echo "\$ amixer -c0 cget numid=$n" + amixer -c0 cget "numid=$n" 2>/dev/null || true + done +} + +kernel_tail_since() { + # Print kernel lines since a monotonic timestamp (string like "[ 78.312861]") + # We do not rely on exact parse; we simply filter journalctl -k -b -o short-monotonic output + # by lexicographic comparison on the bracketed numeric value. + local since="${1:-}" + if ! journalctl -k -b -o short-monotonic --no-pager &>/dev/null; then + note "Cannot read kernel journal (journalctl -k)." + return 0 + fi + if [[ -z "$since" ]]; then + journalctl -k -b -o short-monotonic --no-pager | grep -E "$kernel_grep_re" | tail -n 80 || true + return 0 + fi + journalctl -k -b -o short-monotonic --no-pager | awk -v since="$since" ' + # since is like "[ 78.312861]" + function num(s, x) { + x=s + gsub(/[\[\]]/,"",x) + gsub(/^[[:space:]]+/,"",x) + return x+0.0 + } + { + tag="" + if (match($0, /^\[[^]]+\]/)) { + tag=substr($0, RSTART, RLENGTH) + } + if (tag != "" && num(tag) >= num(since)) print $0 + } + ' | grep -E "$kernel_grep_re" | tail -n 120 || true +} + +now_monotonic() { + # Current kernel journal last monotonic tag, best-effort. + # journalctl -o short-monotonic lines start with: "[ 3.588851] ..." + # which may tokenize as "[" "3.588851]" ... so we must extract up to the first "]". + journalctl -k -b -o short-monotonic --no-pager 2>/dev/null | tail -n 1 | \ + awk '{ if (match($0, /^\[[^]]+\]/)) print substr($0, RSTART, RLENGTH); }' || true +} + +play_on_pcm() { + local pcm="${1:?pcm}" + local sink="alsa_output.pci-0000_00_1f.3-platform-cml_rt5682_def.HiFi__hw_sofrt5682_${pcm}__sink" + echo + echo "\$ pactl set-default-sink $sink" + pactl set-default-sink "$sink" 2>/dev/null || true + echo "\$ pw-play $audio_file" + timeout -k 1s 5s pw-play "$audio_file" 2>/dev/null || true +} + +sec "Kaisa HDMI stability probe — report: $out" +echo "\$ date"; date +echo "\$ uname -a"; uname -a +echo "\$ id"; id +echo +note "duration_s=$duration_s interval_s=$interval_s pcms=$pcms audio_file=$audio_file" +note "Tip: run right after reboot, before any manual audio tweaks." + +sec "Baseline: ALSA + Jack/ELD + sinks" +echo "\$ aplay -l"; aplay -l || true +echo +echo "\$ aplay -l (subdevices summary)"; alsa_dump_subdevices || true +sec "Jack/ELD (best-effort numid dump)" +amixer_dump_jack_eld +sec "PipeWire sinks" +echo "\$ pactl info"; pactl info || true +echo "\$ pactl list short sinks"; list_sinks_short + +start_epoch="$(date +%s)" +end_epoch="$((start_epoch + duration_s))" +iter=0 + +last_mon="$(now_monotonic)" +note "Kernel monotonic cursor (initial): ${last_mon:-}" + +sec "Loop" +while [[ "$(date +%s)" -lt "$end_epoch" ]]; do + iter="$((iter+1))" + echo + hr + echo "[ITER $iter] time=$(date) default_sink=$(get_default_sink || true)" + + # Try play on each requested pcm + IFS=',' read -r -a pcm_arr <<< "$pcms" + for pcm in "${pcm_arr[@]}"; do + pcm="$(echo "$pcm" | tr -d '[:space:]')" + [[ -n "$pcm" ]] || continue + play_on_pcm "$pcm" + done + + echo + echo "[ITER $iter] sinks:" + list_sinks_short + + echo + echo "[ITER $iter] new kernel hints since ${last_mon:-}:" + new_kernel="$(kernel_tail_since "${last_mon:-}")" + if [[ -n "${new_kernel}" ]]; then + warn "Kernel SOF/ASoC lines (tail):" + printf '%s\n' "${new_kernel}" + else + echo "(none)" + fi + + # Advance cursor + last_mon="$(now_monotonic)" + + sleep "$interval_s" +done + +sec "Done" +note "Saved: $out" +note "If you hit failures, also capture: journalctl -k -b -o short-monotonic | grep -nE '$kernel_grep_re' | tail -n 200" diff --git a/wireplumber/main.lua.d/60-kaisa-ucm.lua b/wireplumber/main.lua.d/60-kaisa-ucm.lua index 158d0a32b0..817c58feff 100644 --- a/wireplumber/main.lua.d/60-kaisa-ucm.lua +++ b/wireplumber/main.lua.d/60-kaisa-ucm.lua @@ -8,7 +8,10 @@ -- WirePlumber 无法匹配 device.profile,最终会把活动配置留在 off,且 pactl set-card-profile HiFi -- 报「无此实体」。此时请保持 "pro-audio" 先恢复输出;待 install-kaisa-ucm-overlay + 重启后 pactl 已出现 -- HiFi: 时,再把本变量改为 "HiFi" 并重启用户 pipewire 栈。 -local KAISA_WP_DEVICE_PROFILE = "pro-audio" -- 或 "HiFi" +-- +-- 当前仓库主交付为 UCM/HiFi;当你已验证 `pactl list cards` 的「配置文件」里出现 `HiFi:`, +-- 就应默认固定为 "HiFi",避免重启后回退到 stereo-fallback(只剩 3.5mm)。 +local KAISA_WP_DEVICE_PROFILE = "HiFi" -- 或 "pro-audio"(仅用于临时排障对照) table.insert(alsa_monitor.rules, { matches = {