#!/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"