286 lines
11 KiB
Bash
Executable File
286 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
||
#
|
||
# Chromebox 10 代 - Linux 音频硬件拓扑收集脚本
|
||
# 用于 C1a 任务:在 Linux 下收集硬件拓扑信息
|
||
#
|
||
# 用法: ./collect_linux_audio_topology.sh [输出文件]
|
||
# 默认输出到: audio_topology/collected/audio_topology_linux_$(hostname)_$(date).txt
|
||
#
|
||
# 建议: 用 sudo 运行以获取 dmesg/journalctl;pactl/wpctl 需图形会话,
|
||
# 若用 sudo 会尝试以 SUDO_USER 身份调用,否则可在桌面终端直接运行
|
||
|
||
set -e
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
OUTPUT_DIR="${SCRIPT_DIR}/collected"
|
||
mkdir -p "$OUTPUT_DIR"
|
||
OUTPUT="${1:-${OUTPUT_DIR}/audio_topology_linux_$(hostname)_$(date +%Y%m%d_%H%M%S).txt}"
|
||
|
||
TMP_DIR=$(mktemp -d)
|
||
trap "rm -rf $TMP_DIR" EXIT
|
||
|
||
log() {
|
||
echo "[$(date +%H:%M:%S)] $*"
|
||
}
|
||
|
||
section() {
|
||
echo "" >> "$OUTPUT"
|
||
echo "========================================" >> "$OUTPUT"
|
||
echo "### $1" >> "$OUTPUT"
|
||
echo "========================================" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
}
|
||
|
||
run_cmd() {
|
||
local desc="$1"
|
||
shift
|
||
echo "# $desc" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
if "$@" >> "$OUTPUT" 2>&1; then
|
||
true
|
||
else
|
||
echo "(命令返回非零,可能部分信息缺失)" >> "$OUTPUT"
|
||
fi
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
}
|
||
|
||
log "开始收集 Linux 音频硬件拓扑..."
|
||
log "输出文件: $OUTPUT"
|
||
|
||
: > "$OUTPUT"
|
||
echo "# Chromebox 10 代 - Linux 音频硬件拓扑" >> "$OUTPUT"
|
||
echo "# 收集时间: $(date -Iseconds)" >> "$OUTPUT"
|
||
echo "# 主机: $(hostname)" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
|
||
# --- 系统信息 ---
|
||
section "系统信息"
|
||
run_cmd "uname -a" uname -a
|
||
run_cmd "内核版本" uname -r
|
||
run_cmd "/proc/version" cat /proc/version
|
||
if command -v alsactl &>/dev/null; then
|
||
run_cmd "ALSA 版本" alsactl --version 2>/dev/null || echo "alsactl 无版本信息"
|
||
fi
|
||
|
||
# --- PCI 音频设备 ---
|
||
section "PCI 音频相关设备"
|
||
if command -v lspci &>/dev/null; then
|
||
run_cmd "lspci -nn | grep -i audio (含设备 ID,便于查驱动/quirks)" bash -c "lspci -nn | grep -i audio || echo '(无匹配)'"
|
||
run_cmd "lspci | grep -i audio" bash -c "lspci | grep -i audio || echo '(无匹配)'"
|
||
run_cmd "lspci -v | grep -A 15 -i audio" bash -c "lspci -v 2>/dev/null | grep -A 15 -i audio || echo '(无匹配)'"
|
||
fi
|
||
|
||
# --- 播放/录音设备列表 ---
|
||
section "ALSA 设备列表"
|
||
if command -v aplay &>/dev/null; then
|
||
run_cmd "aplay -l (播放设备)" aplay -l
|
||
run_cmd "aplay -L (ALSA 设备描述)" aplay -L 2>/dev/null || true
|
||
else
|
||
echo "# aplay 未安装" >> "$OUTPUT"
|
||
fi
|
||
if command -v arecord &>/dev/null; then
|
||
run_cmd "arecord -l (录音设备)" arecord -l
|
||
fi
|
||
run_cmd "cat /proc/asound/cards" cat /proc/asound/cards
|
||
run_cmd "cat /proc/asound/pcm (PCM 设备明细)" cat /proc/asound/pcm
|
||
|
||
# --- /proc/asound 目录结构 ---
|
||
section "/proc/asound 目录结构"
|
||
run_cmd "ls -la /proc/asound" ls -la /proc/asound
|
||
for card_dir in /proc/asound/card[0-9]*; do
|
||
if [ -d "$card_dir" ]; then
|
||
card=$(basename "$card_dir")
|
||
echo "# $card 目录内容:" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
ls -la "$card_dir" 2>/dev/null >> "$OUTPUT" || true
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
|
||
# --- Codec 信息 (HDA) ---
|
||
section "HDA Codec 信息"
|
||
for codec in /proc/asound/card*/codec*; do
|
||
if [ -f "$codec" ]; then
|
||
echo "# $codec" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
cat "$codec" >> "$OUTPUT" 2>/dev/null || echo "(无法读取)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
if ! ls /proc/asound/card*/codec* &>/dev/null; then
|
||
echo "(未找到 codec 文件)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- ELD 内容 (HDMI 显示器音频能力,对 HDMI 无声排查很重要) ---
|
||
section "HDMI ELD 内容"
|
||
for eld in /proc/asound/card*/eld*; do
|
||
if [ -f "$eld" ]; then
|
||
echo "# $eld" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
cat "$eld" >> "$OUTPUT" 2>/dev/null || echo "(无法读取,或 ELD 为空/未连接)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
|
||
# --- /sys/class/sound 拓扑 ---
|
||
section "/sys/class/sound 设备"
|
||
if [ -d /sys/class/sound ]; then
|
||
run_cmd "ls -la /sys/class/sound" ls -la /sys/class/sound
|
||
for card in /sys/class/sound/card[0-9]*; do
|
||
if [ -d "$card" ]; then
|
||
name=$(basename "$card")
|
||
echo "# $name 设备属性:" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
for f in id number; do
|
||
if [ -f "$card/$f" ]; then
|
||
echo "$f: $(cat "$card/$f" 2>/dev/null)" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# --- 内核日志中的音频相关信息 ---
|
||
section "内核日志 (dmesg) 音频相关"
|
||
echo "# dmesg | grep -iE 'snd|hda|audio|codec|hdmi'" >> "$OUTPUT"
|
||
echo "# 提示: 若此处显示「需要 root」,请用 sudo 重新运行本脚本以获取完整日志" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
dmesg 2>/dev/null | grep -iE 'snd|hda|audio|codec|hdmi' >> "$OUTPUT" || echo "(需要 root 或 dmesg 不可用)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
|
||
# --- Kaisa HDMI / SOF IPC 基线(辅助采集;用户态排障见 docs/linux-hdmi/OPERATION_PipeWire_Kaisa_HDMI.md)---
|
||
section "SOF / HDMI / IPC 基线 (dmesg)"
|
||
echo "# dmesg | grep -iE 'sof|STREAM_PCM|ipc failed|ipc tx|pcm[0-9]|ASoC|iDisp|hdmi' | tail -120" >> "$OUTPUT"
|
||
echo "# 用于对照 SOF/IPC 与 dmesg;建议 sudo 运行本脚本以读全量 dmesg" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
dmesg 2>/dev/null | grep -iE 'sof|STREAM_PCM|ipc failed|ipc tx|pcm[0-9]|ASoC|iDisp|hdmi' | tail -120 >> "$OUTPUT" || echo "(需要 root 或 dmesg 不可用)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
|
||
section "HDMI 试听命令提示(人工执行)"
|
||
{
|
||
echo "# 在 aplay -L 中查找 HDMI / DisplayPort 设备后,对每个候选 plughw 试播(Ctrl+C 结束):"
|
||
echo "# speaker-test -D plughw:卡号,设备号 -c 2 -l 1"
|
||
echo "# 若 dmesg 已出现 SOF IPC / PARAMS 类错误,用户态换 sink 通常无法单独修复。"
|
||
} >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
|
||
# --- 可选: journalctl 内核日志 (补充 dmesg,含启动后日志) ---
|
||
section "journalctl 内核日志 (SOF/音频,可选补充)"
|
||
if command -v journalctl &>/dev/null; then
|
||
echo "# journalctl -b -k --no-pager | grep -iE 'sof|snd|hda|audio|hdmi' | tail -80" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
journalctl -b -k --no-pager 2>/dev/null | grep -iE 'sof|snd|hda|audio|hdmi' | tail -80 >> "$OUTPUT" || echo "(journalctl 需 root 或不可用)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
else
|
||
echo "(journalctl 未安装)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- 可选: ACPI NHLT 表 (Coreboot 下常缺失,影响 SOF) ---
|
||
section "ACPI NHLT 表"
|
||
if [ -d /sys/firmware/acpi/tables ]; then
|
||
echo "# ls /sys/firmware/acpi/tables/ | grep -i nhlt" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
ls /sys/firmware/acpi/tables/ 2>/dev/null | grep -i nhlt >> "$OUTPUT" || echo "(未找到 NHLT 表,Coreboot 下常见)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
else
|
||
echo "(/sys/firmware/acpi/tables 不可用)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- 可选: sof-logger (若已安装,可查看 SOF 固件 trace) ---
|
||
section "sof-logger (可选)"
|
||
# sudo 下同时检查 SUDO_USER 的 ~/.local/bin(用户可能安装于此)
|
||
if [ -n "${SUDO_USER:-}" ]; then
|
||
USER_HOME=$(getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6)
|
||
[ -n "$USER_HOME" ] && [ -d "$USER_HOME/.local/bin" ] && export PATH="$USER_HOME/.local/bin:$PATH"
|
||
fi
|
||
if command -v sof-logger &>/dev/null; then
|
||
echo "# sof-logger -t 2>/dev/null | tail -30 (DMA trace,无需 ldc 文件)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
SOF_LOG=$(timeout 3 sof-logger -t 2>/dev/null | tail -30) || true
|
||
if [ -z "$SOF_LOG" ]; then
|
||
echo "(sof-logger 执行超时或需 root/debugfs)" >> "$OUTPUT"
|
||
elif echo "$SOF_LOG" | grep -q "Usage sof-logger"; then
|
||
echo "(sof-logger 输出 Usage:通常表示 debugfs 未挂载或需 root 权限读取 /sys/kernel/debug/sof/)" >> "$OUTPUT"
|
||
else
|
||
echo "$SOF_LOG" >> "$OUTPUT"
|
||
fi
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
else
|
||
echo "(sof-logger 未安装,可从 thesofproject/sof-bin 或发行版 firmware-sof 相关包安装)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- 音频服务/会话 ---
|
||
section "音频服务 (PulseAudio / PipeWire)"
|
||
echo "# 提示: sudo 下会以原用户身份调用 pactl/pw-cli,需设置 XDG_RUNTIME_DIR 以连接用户会话" >> "$OUTPUT"
|
||
RUN_PACTL() {
|
||
if [ -n "${SUDO_USER:-}" ] && [ -d "/run/user/$(id -u "$SUDO_USER" 2>/dev/null)" ]; then
|
||
runuser -u "$SUDO_USER" -- env XDG_RUNTIME_DIR="/run/user/$(id -u "$SUDO_USER")" "$@"
|
||
else
|
||
"$@"
|
||
fi
|
||
}
|
||
if command -v pactl &>/dev/null; then
|
||
run_cmd "pactl info" RUN_PACTL pactl info 2>/dev/null || run_cmd "pactl info (直接调用)" pactl info 2>/dev/null || true
|
||
run_cmd "pactl list cards (含 HDMI/3.5mm 等 profile)" RUN_PACTL pactl list cards 2>/dev/null || true
|
||
run_cmd "pactl list sinks short" RUN_PACTL pactl list sinks short 2>/dev/null || true
|
||
fi
|
||
if command -v pw-cli &>/dev/null; then
|
||
run_cmd "pw-cli list-objects Device" RUN_PACTL pw-cli list-objects Device 2>/dev/null || true
|
||
fi
|
||
if command -v wpctl &>/dev/null; then
|
||
run_cmd "wpctl status (WirePlumber)" RUN_PACTL wpctl status 2>/dev/null || true
|
||
fi
|
||
|
||
# --- 已加载的音频相关内核模块 ---
|
||
section "音频相关内核模块"
|
||
run_cmd "lsmod | grep -iE 'snd|hda|sound'" bash -c "lsmod | grep -iE 'snd|hda|sound' || echo '(无匹配)'"
|
||
|
||
# --- 混音器状态 ---
|
||
section "ALSA 混音器状态"
|
||
if command -v amixer &>/dev/null; then
|
||
run_cmd "amixer -c 0 contents (或 -c default)" amixer -c 0 contents 2>/dev/null || amixer contents 2>/dev/null || echo "(amixer 执行失败)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- SOF 固件与拓扑 ---
|
||
section "SOF 固件 (Intel Sound Open Firmware)"
|
||
if [ -f /sys/kernel/debug/sof/fw_version ]; then
|
||
echo "# /sys/kernel/debug/sof/fw_version (需 root)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
cat /sys/kernel/debug/sof/fw_version 2>/dev/null >> "$OUTPUT" || echo "(无法读取)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
echo "" >> "$OUTPUT"
|
||
fi
|
||
if [ -d /lib/firmware/intel ]; then
|
||
echo "# ls -la /lib/firmware/intel/ (sof 相关)" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
ls -la /lib/firmware/intel/ 2>/dev/null | grep -E 'sof|avs' >> "$OUTPUT" || ls -la /lib/firmware/intel/ >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
for tplg_dir in /lib/firmware/intel/sof /lib/firmware/intel/sof-tplg; do
|
||
if [ -d "$tplg_dir" ]; then
|
||
echo "# SOF 拓扑: $tplg_dir" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
find "$tplg_dir" -type f -name '*tplg*' 2>/dev/null | head -30 >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
done
|
||
else
|
||
echo "(未找到 /lib/firmware/intel)" >> "$OUTPUT"
|
||
fi
|
||
|
||
# --- 机器/主板识别 (ACPI) ---
|
||
section "机器识别 (主板/机型)"
|
||
if [ -f /sys/class/dmi/id/board_name ]; then
|
||
echo "# DMI 主板信息:" >> "$OUTPUT"
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
for f in /sys/class/dmi/id/board_name /sys/class/dmi/id/product_name /sys/class/dmi/id/sys_vendor; do
|
||
[ -f "$f" ] && echo "$(basename $f): $(cat "$f" 2>/dev/null)" >> "$OUTPUT"
|
||
done
|
||
echo "\`\`\`" >> "$OUTPUT"
|
||
fi
|
||
|
||
log "收集完成,结果已保存到: $OUTPUT"
|