成功解决联想系列笔记本和ubuntu22.04版本的网卡声卡不兼容问题

新内核固件缺失修复案例:WiFi + 声卡

开始操作前本人使用USB共享网络:

适用场景:在 Ubuntu 22.04 LTS 上升级主线内核(6.x+)后,WiFi 或声卡消失。

电脑信息

项目 详情
型号 Lenovo 小新 Pro 16c IAH10 (XiaoXinPro-16c-IAH10)
系统 Ubuntu 22.04.5 LTS (Jammy)
内核 6.12.95-061295-generic(自行升级的主线内核)
网卡 Intel Wi-Fi 6 AX201 160MHz (8086:7740)
声卡 Intel Arrow Lake Audio (8086:7728),Conexant SN6140 解码器

通用根因

两个问题本质相同:新内核的驱动需要新版固件,但 Ubuntu 22.04 的固件包太旧

复制代码
┌────────────────────────────────────────────────────┐
│  Ubuntu 22.04 LTS (2022.04 发布)                    │
│  ├── linux-firmware          → 2022.03 版本         │
│  ├── firmware-sof-signed     → 2.0 (2022 年)        │
│  └── 内核 6.12 (2024.12)                           │
│       ├── iwlwifi 需要 API v90+  固件 → 缺失!      │
│       └── SOF 需要 Arrow Lake 固件   → 缺失!       │
└────────────────────────────────────────────────────┘

一、WiFi 修复:Intel AX201 固件缺失 (bz-b0-hr-b0)

故障现象

  • 系统只有有线网卡 (enx0a10d7dd5f41),完全没有 WiFi 接口
  • nmcli device status 看不到任何 wlan0 / wlp* 设备
  • ip link show 无无线网卡
  • rfkill list 显示 ideapad_wlan 未被软/硬屏蔽

排查过程

1. 确认硬件存在

bash 复制代码
$ lspci -nn -s 00:14.3
00:14.3 Network controller [0280]: Intel Corporation Device [8086:7740]

设备存在,PCI ID 为 8086:7740

2. 检查驱动状态

bash 复制代码
$ lsmod | grep iwlwifi
iwlwifi               647168  0          # ← 0 users,驱动没有绑定到设备
cfg80211             1413120  1 iwlwifi

$ nmcli device status
DEVICE             TYPE      STATE   CONNECTION
enx0a10d7dd5f41    ethernet  已连接  有线连接 1
lo                 loopback  未托管  --
# 没有任何 WiFi 设备

iwlwifi 模块已加载但未被任何设备使用 --- 驱动识别了设备但固件加载失败后退出了。

3. 查看内核日志(关键!)

bash 复制代码
$ cat /var/log/syslog | grep iwl

输出关键信息:

复制代码
iwlwifi 0000:00:14.3: Detected Intel(R) Wi-Fi 6 AX201 160MHz
iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-bz-b0-hr-b0-93.ucode failed with error -2
iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-bz-b0-hr-b0-92.ucode failed with error -2
iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-bz-b0-hr-b0-91.ucode failed with error -2
iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-bz-b0-hr-b0-90.ucode failed with error -2
iwlwifi 0000:00:14.3: no suitable firmware found!
iwlwifi 0000:00:14.3: minimum version required: iwlwifi-bz-b0-hr-b0-90
iwlwifi 0000:00:14.3: maximum version supported: iwlwifi-bz-b0-hr-b0-93
iwlwifi 0000:00:14.3: check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
  • 驱动需要 bz-b0-hr-b0 固件 API 版本 v90-v93
  • 内核按 93→92→91→90 顺序尝试加载,全部失败(error -2 = 文件不存在)

4. 确认本地固件缺失

bash 复制代码
$ ls /lib/firmware/iwlwifi-bz*
iwlwifi-bz-b0-fm-c0-92.ucode   iwlwifi-bz-b0-fm-c0.pnvm
iwlwifi-bz-b0-fm-c0-94.ucode   iwlwifi-bz-b0-gf-a0-92.ucode
iwlwifi-bz-b0-fm-c0-96.ucode   iwlwifi-bz-b0-gf-a0-94.ucode
                                iwlwifi-bz-b0-gf-a0-96.ucode
                                iwlwifi-bz-b0-gf-a0.pnvm

系统只有 fm-c0(FM Radio)和 gf-a0(Gale Force)变体固件,缺少 hr-b0(Harrison Peak)变体。

bash 复制代码
$ apt-cache policy linux-firmware
已安装:20220329.git681281e4-0ubuntu3.42    # 2022 年 3 月版本,太旧

5. 尝试过的失败方案

方案 结果 原因
git clone linux-firmware.git ❌ 超时 kernel.org 在国内极慢,100s 仅下载 280KB,总量 >1GB
curl kernel.org /plain/ 端点 ❌ 404 git.kernel.org 的 cgit 不给二进制固件文件返回 200
curl GitHub 镜像 ❌ 无法访问 github.com 无响应
Symlink gf-a0→hr-b0 ❌ 超时 Failed to start RT ucode: -110 (ETIMEDOUT),RF 模块硬件不兼容

6. 成功方案

从 Debian testing 下载 firmware-iwlwifi(仅 23MB,Tsighua 镜像速度快):

bash 复制代码
# 下载
curl -L "https://mirrors.tuna.tsinghua.edu.cn/debian/pool/non-free-firmware/f/firmware-nonfree/firmware-iwlwifi_20260519-1_all.deb" \
     -o /tmp/firmware-iwlwifi.deb

# 提取
dpkg-deb -x /tmp/firmware-iwlwifi.deb /tmp/fw-extract/

# 安装正确变体 + 版本的固件
sudo cp /tmp/fw-extract/usr/lib/firmware/intel/iwlwifi/iwlwifi-bz-b0-hr-b0-93.ucode /lib/firmware/
sudo cp /tmp/fw-extract/usr/lib/firmware/intel/iwlwifi/iwlwifi-bz-b0-hr-b0.pnvm /lib/firmware/

# 重载驱动
sudo modprobe -r iwlwifi
sudo modprobe iwlwifi

修复验证

bash 复制代码
$ dmesg | grep iwl | tail -10
iwlwifi: loaded firmware version 93.26a2cb89.0 bz-b0-hr-b0-93.ucode op_mode iwlmvm
iwlwifi: Detected RF HR B3, rfid=0x10a100
iwlwifi: base HW address: f8:cf:52:bc:41:ed
wlp0s20f3: renamed from wlan0
iwlwifi: Registered PHC clock: iwlwifi-PTP, with index: 0

$ nmcli device status
DEVICE             TYPE      STATE   CONNECTION
enx0a10d7dd5f41    ethernet  已连接  有线连接 1
wlp0s20f3          wifi      已断开  --          # ← WiFi 接口出现!
p2p-dev-wlp0s20f3  wifi-p2p  已断开  --
lo                 loopback  未托管  --

WiFi 根因分析

复制代码
┌─────────────────────────────────────────────────────┐
│  Intel AX201 网卡有 3 种 RF 模块变体:                │
│                                                       │
│  FM (FM Radio)    → iwlwifi-bz-b0-fm-c0-*.ucode     │
│  GF (Gale Force)  → iwlwifi-bz-b0-gf-a0-*.ucode     │
│  HR (Harrison Peak) → iwlwifi-bz-b0-hr-b0-*.ucode ← 这台机器 │
│                                                       │
│  不同 RF 变体固件互不兼容(硬件射频前端不同)           │
└─────────────────────────────────────────────────────┘
层级 问题
直接原因 硬盘上没有 iwlwifi-bz-b0-hr-b0-*.ucode 固件文件
为什么其它 AX201 机器没问题 大多数 AX201 使用 FM 或 GF 射频模块,Ubuntu 22.04 的 linux-firmware 包含这些变体,但没包含 HR 变体
根本原因 自行升级的 6.12 主线内核需要新版固件(API v90+),Ubuntu 22.04 官方 linux-firmware 包版本为 2022 年 3 月,太旧且缺 HR 变体

WiFi 固件命名规则解读

iwlwifi-bz-b0-hr-b0-93.ucode 为例:

字段 含义
iwlwifi Intel WiFi 固件前缀
bz 平台代号:Bz (Bazaar),对应 AX201/AX211 系列
b0 硅晶片步进版本 B0
hr RF 射频模块类型:HR = Harrison Peak
b0 RF 模块步进版本 B0
93 固件 API 版本号

二、声卡修复:SOF 固件缺失 (Arrow Lake)

故障现象

  • 系统设置中无声音输出/输入设备
  • PulseAudio 只有 auto_null(伪输出),没有真实声卡
  • aplay -l / arecord -l 报 "找不到音效卡"
  • cat /proc/asound/cards 显示 "--- no soundcards ---"

排查过程

1. 确认硬件存在

bash 复制代码
$ lspci -nn | grep audio
00:1f.3 Multimedia audio controller [0401]: Intel Corporation Device [8086:7728]

设备存在,PCI ID 为 8086:7728

2. 检查驱动状态

bash 复制代码
$ lsmod | grep sof
snd_sof_pci_intel_mtl    40960  0    # SOF 驱动已加载但 0 用户
snd_sof_intel_hda_generic  36864  1
...
snd_hda_intel          65536  0    # HDA 传统驱动也是 0 用户

SOF 和 HDA 驱动都已加载但未绑定到设备。

3. 查看内核日志(关键!)

bash 复制代码
$ sudo dmesg | grep -i sof

输出关键信息:

复制代码
sof-audio-pci-intel-mtl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
sof-audio-pci-intel-mtl 0000:00:1f.3: DMICs detected in NHLT tables: 2
sof-audio-pci-intel-mtl 0000:00:1f.3: SOF firmware and/or topology file not found.
sof-audio-pci-intel-mtl 0000:00:1f.3: Supported default profiles
sof-audio-pci-intel-mtl 0000:00:1f.3: - ipc type 1 (Requested):
sof-audio-pci-intel-mtl 0000:00:1f.3:  Firmware file: intel/sof-ipc4/arl/sof-arl.ri
sof-audio-pci-intel-mtl 0000:00:1f.3:  Topology file: intel/sof-ace-tplg/sof-hda-generic-2ch.tplg
sof-audio-pci-intel-mtl 0000:00:1f.3: Check if you have 'sof-firmware' package installed.
sof-audio-pci-intel-mtl 0000:00:1f.3: error: sof_probe_work failed err: -2
  • 驱动需要 intel/sof-ipc4/arl/sof-arl.ri --- Arrow Lake 固件
  • 需要 intel/sof-ace-tplg/sof-hda-generic-2ch.tplg --- 拓扑文件
  • error: -2 = ENOENT(文件不存在)

4. 确认本地固件缺失

bash 复制代码
$ ls /lib/firmware/intel/sof-ipc4/arl/
ls: 无法访问 '/lib/firmware/intel/sof-ipc4/arl/': 没有那个文件或目录

# 系统只有 MTL (Meteor Lake) 固件,缺 ARL (Arrow Lake)
$ ls /lib/firmware/intel/sof-ipc4/
mtl    # 只有 Meteor Lake

$ apt-cache policy firmware-sof-signed
已安装:2.0-1ubuntu4.7    # 2022 年版本,不包含 Arrow Lake

5. 成功方案

ARL 固件实际上就是 MTL 固件 --- Arrow Lake 和 Meteor Lake 共享相同的 DSP 固件二进制。新版 firmware-sof-signed 包的做法是创建符号链接。

bash 复制代码
# 创建 ARL 固件目录
sudo mkdir -p /lib/firmware/intel/sof-ipc4/arl/{intel-signed,community}

# 创建符号链接到已有 MTL 固件
sudo ln -sf intel-signed/sof-arl.ri /lib/firmware/intel/sof-ipc4/arl/sof-arl.ri
sudo ln -sf ../../mtl/intel-signed/sof-mtl.ri /lib/firmware/intel/sof-ipc4/arl/intel-signed/sof-arl.ri
sudo ln -sf ../../mtl/community/sof-mtl.ri /lib/firmware/intel/sof-ipc4/arl/community/sof-arl.ri

# 重载 SOF 驱动
sudo modprobe -r snd_sof_pci_intel_mtl snd_sof_intel_hda_common snd_sof
sudo modprobe snd_sof_pci_intel_mtl

修复验证

bash 复制代码
$ cat /proc/asound/cards
 0 [sofhdadsp      ]: sof-hda-dsp - sof-hda-dsp
                      LENOVO-83ND-XiaoXinPro16cIAH10-LNVNB161216

$ aplay -l
card 0: sofhdadsp [sof-hda-dsp], device 0: HDA Analog (*) []     # 扬声器
card 0: sofhdadsp [sof-hda-dsp], device 3: HDMI1 (*) []          # HDMI
card 0: sofhdadsp [sof-hda-dsp], device 4: HDMI2 (*) []
card 0: sofhdadsp [sof-hda-dsp], device 5: HDMI3 (*) []
card 0: sofhdadsp [sof-hda-dsp], device 31: Deepbuffer HDA Analog (*) []

$ arecord -l
card 0: sofhdadsp [sof-hda-dsp], device 0: HDA Analog (*) []    # 模拟麦克风
card 0: sofhdadsp [sof-hda-dsp], device 6: DMIC Raw (*) []      # 数字麦克风

dmesg 确认:

复制代码
sof-audio-pci-intel-mtl: Loaded firmware library: ADSPFW, version: 2.8.1.1
sof-audio-pci-intel-mtl: Booted firmware version: 2.8.1.1
snd_hda_codec_conexant ehdaudio0D0: SN6140: BIOS auto-probing.
snd_hda_codec_conexant ehdaudio0D0: autoconfig for SN6140:
    line_outs=1 (0x17/...) type:speaker
    hp_outs=1 (0x16/...)                    # 耳机
    inputs: Mic=0x19                        # 麦克风

声卡根因分析

层级 问题
直接原因 /lib/firmware/intel/sof-ipc4/arl/ 目录不存在
为什么其他 Meteor Lake 机器没问题 MTL 固件存在,且 ARL 固件就是 MTL 的符号链接
根本原因 内核 6.12 的 SOF 驱动根据 PCI ID (8086:7728) 判定为 Arrow Lake 平台,需要 arl/ 路径的固件;Ubuntu 22.04 的 firmware-sof-signed (v2.0) 不包含 Arrow Lake 目录

SOF 固件目录结构

复制代码
/lib/firmware/intel/sof-ipc4/
├── mtl/                          # Meteor Lake(已有)
│   ├── sof-mtl.ri → intel-signed/sof-mtl.ri
│   ├── intel-signed/
│   │   └── sof-mtl.ri           # 实际固件二进制 (879KB)
│   └── community/
│       └── sof-mtl.ri
│
└── arl/                          # Arrow Lake(需创建)
    ├── sof-arl.ri → intel-signed/sof-arl.ri
    ├── intel-signed/
    │   └── sof-arl.ri → ../../mtl/intel-signed/sof-mtl.ri  # → MTL 固件
    └── community/
        └── sof-arl.ri → ../../mtl/community/sof-mtl.ri

关键经验

通用原则

  1. 升级主线内核后必须同步升级固件:新驱动需要新版固件,旧发行版的固件包往往不匹配

  2. Ubuntu 22.04 的固件包普遍偏旧(均为 2022 年版本):

    • linux-firmware20220329
    • firmware-sof-signed2.0

    对于 2023+ 的 Intel 硬件平台,需要从 Debian testing 获取更新固件

  3. Debian 的拆分固件包是更好的下载源

    • firmware-iwlwifi(23MB)vs 整个 linux-firmware repo(>1GB)
    • firmware-sof-signed(2MB)包含完整的 SOF 固件
  4. 排查固件问题的标准流程

    bash 复制代码
    # 1. 确认硬件
    lspci -nn | grep -iE "network|audio"
    
    # 2. 看内核日志(关键!)
    sudo dmesg | grep -iE "iwlwifi|sof|firmware"
    
    # 3. 对比固件文件
    ls /lib/firmware/iwlwifi-*    # WiFi
    ls /lib/firmware/intel/sof-ipc4/*/  # 声卡
    
    # 4. 检查固件包版本
    apt-cache policy linux-firmware firmware-sof-signed

WiFi 专项

  • RF 变体必须完全匹配fm-c0/gf-a0/hr-b0 是不同射频硬件,固件不通用
  • WiFi 固件命名格式:iwlwifi-{平台}-{芯片步进}-{RF类型}-{RF步进}-{API版本}.ucode

声卡专项

  • SOF 固件跨平台共享:Arrow Lake 和 Meteor Lake 使用相同 DSP 固件,通过符号链接即可
  • 声卡固件路径:intel/sof-ipc4/{平台}/sof-{平台}.ri + 拓扑文件 intel/sof-ace-tplg/sof-hda-generic-*.tplg

一键修复脚本

fix-firmware.sh,可自动诊断并修复常见的固件缺失问题。

bash 复制代码
chmod +x fix-firmware.sh
sudo ./fix-firmware.sh

脚本内容:

复制代码
#!/bin/bash
#=============================================================================
# fix-firmware.sh --- 一键诊断并修复 Intel WiFi / 声卡固件缺失问题
#
# 适用场景:
#   - Ubuntu/Debian 系统,自行升级了主线内核
#   - WiFi 消失(iwlwifi 固件缺失)
#   - 声卡消失(SOF 固件缺失)
#
# 用法:
#   sudo ./fix-firmware.sh           # 自动诊断并修复
#   sudo ./fix-firmware.sh --dry-run # 仅诊断,不修复
#   sudo ./fix-firmware.sh --wifi    # 仅修复 WiFi
#   sudo ./fix-firmware.sh --audio   # 仅修复声卡
#=============================================================================

set -euo pipefail

# --- 颜色定义 ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# --- 配置 ---
DEBIAN_MIRROR="${DEBIAN_MIRROR:-https://mirrors.tuna.tsinghua.edu.cn/debian}"
FIRMWARE_IWLWIFI_URL="${DEBIAN_MIRROR}/pool/non-free-firmware/f/firmware-nonfree/firmware-iwlwifi_20260519-1_all.deb"
FIRMWARE_SOF_URL="${DEBIAN_MIRROR}/pool/non-free-firmware/f/firmware-sof/firmware-sof-signed_2025.12.2-2_all.deb"
TEMP_DIR=$(mktemp -d)
DRY_RUN=false
FIX_WIFI=true
FIX_AUDIO=true
FOUND_ISSUES=0
FIXED_ISSUES=0

# --- 辅助函数 ---

cleanup() {
    rm -rf "$TEMP_DIR"
}
trap cleanup EXIT

log_info()    { echo -e "${BLUE}[INFO]${NC} $*"; }
log_ok()      { echo -e "${GREEN}[OK]${NC} $*"; }
log_warn()    { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error()   { echo -e "${RED}[ERROR]${NC} $*"; }
log_section() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}"; }

check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_error "请用 sudo 运行此脚本:sudo $0 $*"
        exit 1
    fi
}

need_download() {
    # 检查系统是否能联网下载
    if ! command -v curl &>/dev/null && ! command -v wget &>/dev/null; then
        log_warn "未找到 curl 或 wget,将跳过需要下载的操作"
        return 1
    fi
    return 0
}

download() {
    local url="$1" output="$2"
    if command -v curl &>/dev/null; then
        curl -sL --max-time 120 "$url" -o "$output"
    elif command -v wget &>/dev/null; then
        wget -q --timeout=120 "$url" -O "$output"
    fi
}

# --- WiFi 诊断与修复 ---

diagnose_wifi() {
    log_section "WiFi 固件诊断"

    # 1. 找出所有 Intel WiFi PCI 设备
    local wifi_devs
    wifi_devs=$(lspci -nn 2>/dev/null | grep -i "Network controller.*Intel" || true)
    if [ -z "$wifi_devs" ]; then
        log_info "未检测到 Intel WiFi 硬件,跳过 WiFi 诊断"
        return 0
    fi

    echo "$wifi_devs" | while read -r line; do
        echo -e "  ${line}"
    done

    # 2. 快速检查:WiFi 接口是否已存在?
    #    如果接口已经正常工作,就不需要看历史 dmesg(旧日志可能误报)
    if ip link show 2>/dev/null | grep -q "wl"; then
        local wl_iface
        wl_iface=$(ip link show 2>/dev/null | grep -oP 'wl[^ :]+' | head -1)
        log_ok "WiFi 接口已存在 ($wl_iface),驱动工作正常,跳过"
        return 0
    fi

    # 3. WiFi 接口不存在 → 检查 iwlwifi 模块
    if ! lsmod | grep -q iwlwifi; then
        log_info "iwlwifi 模块未加载,尝试加载..."
        modprobe iwlwifi 2>/dev/null || true
        sleep 2
    fi

    # 4. 从 dmesg 提取缺失的固件文件名
    #    用本次启动后产生的日志(取最后出现的错误,避免旧日志干扰)
    local missing_fw
    missing_fw=$(dmesg 2>/dev/null | grep "iwlwifi.*Direct firmware load for.*failed" \
        | grep -oP 'iwlwifi-[^ ]+\.ucode' | sort -u || true)

    if [ -z "$missing_fw" ]; then
        # 也检查 /var/log/syslog,只取最近的启动日志
        missing_fw=$(grep "iwlwifi.*Direct firmware load for.*failed" /var/log/syslog 2>/dev/null \
            | grep "$(uname -r)" | grep -oP 'iwlwifi-[^ ]+\.ucode' | sort -u || true)
    fi

    # 5. 对于找到的 "缺失" 固件,检查是否已经存在且只是 API 版本不同
    #    例如 dmesg 说缺 -93,但 -96 已存在且驱动支持
    if [ -n "$missing_fw" ]; then
        local actually_missing=""
        for fw in $missing_fw; do
            # 检查这个文件是否现在已存在
            if [ -f "/lib/firmware/$fw" ]; then
                log_info "固件 $fw 已存在(可能已修复),跳过"
                continue
            fi
            # 检查同前缀的其他版本是否存在
            local fw_prefix fw_count
            fw_prefix=$(echo "$fw" | sed -E 's/-[0-9]+\.ucode$//')
            fw_count=$(ls "/lib/firmware/${fw_prefix}-"*.ucode 2>/dev/null | wc -l)
            if [ "$fw_count" -gt 0 ]; then
                log_info "固件 $fw 不存在,但有同系列其他版本,跳过"
                continue
            fi
            actually_missing="$actually_missing $fw"
        done
        missing_fw=$(echo "$actually_missing" | xargs -r echo)
    fi

    # 6. 也检查 "no suitable firmware found" 错误
    if [ -z "$missing_fw" ]; then
        local no_suitable
        no_suitable=$(dmesg 2>/dev/null | grep "iwlwifi.*no suitable firmware" | tail -1 || true)
        if [ -n "$no_suitable" ]; then
            log_warn "iwlwifi 报告 'no suitable firmware found'"
            local max_ver
            max_ver=$(dmesg 2>/dev/null | grep "maximum version supported:" | grep -oP 'iwlwifi-[^ ]+' | head -1 || true)
            if [ -n "$max_ver" ] && [ ! -f "/lib/firmware/$max_ver" ]; then
                missing_fw="$max_ver"
                log_info "需要固件: $missing_fw"
            fi
        fi
    fi

    if [ -z "$missing_fw" ]; then
        # 可能是模块重载后已修复,再检查一次 WiFi 接口
        if ip link show 2>/dev/null | grep -q "wl"; then
            log_ok "WiFi 接口已出现,修复成功"
            return 0
        fi
        log_info "未检测到明确的 WiFi 固件缺失,跳过"
        return 0
    fi

    echo -e "\n${YELLOW}检测到缺失的 WiFi 固件文件:${NC}"
    for fw in $missing_fw; do
        echo -e "  ${RED}✗${NC} $fw"
    done
    FOUND_ISSUES=$((FOUND_ISSUES + 1))

    # 提取固件前缀(去除版本号),确定需要的 pnvm 文件
    local fw_prefix
    fw_prefix=$(echo "$missing_fw" | head -1 | sed -E 's/-[0-9]+\.ucode$//')

    echo -e "\n${YELLOW}将修复以下固件文件:${NC}"
    echo "  → ${fw_prefix}-*.ucode 系列"
    echo "  → ${fw_prefix}.pnvm (射频配置)"

    if [ "$DRY_RUN" = true ]; then
        log_info "[DRY RUN] 将下载 firmware-iwlwifi 包并安装缺失的固件"
        return 0
    fi

    if ! need_download; then
        log_warn "无法联网下载,跳过 WiFi 固件修复"
        return 1
    fi

    # 下载 firmware-iwlwifi Debian 包
    log_info "下载 firmware-iwlwifi 包 (~23MB)..."
    local deb_file="$TEMP_DIR/firmware-iwlwifi.deb"
    if ! download "$FIRMWARE_IWLWIFI_URL" "$deb_file"; then
        log_error "下载失败,请检查网络连接"
        return 1
    fi
    log_ok "下载完成 ($(du -h "$deb_file" | cut -f1))"

    # 提取并安装固件
    log_info "提取并安装固件..."
    local extract_dir="$TEMP_DIR/iwlwifi-extract"
    mkdir -p "$extract_dir"
    dpkg-deb -x "$deb_file" "$extract_dir" 2>/dev/null

    # 查找需要的固件文件
    local installed=0
    local fw_base
    fw_base=$(echo "$missing_fw" | head -1)

    # 先查找所有匹配的 .ucode 文件
    local found_files
    found_files=$(find "$extract_dir" -name "${fw_prefix}*.ucode" -o -name "${fw_prefix}*.pnvm" 2>/dev/null || true)

    if [ -z "$found_files" ]; then
        log_warn "下载的包中未找到 ${fw_prefix}* 固件,尝试查找最接近的版本..."
        # 显示包里有什么
        find "$extract_dir" -name "iwlwifi-*${fw_prefix##iwlwifi-}*" 2>/dev/null | head -10 || true
        log_info "可用的 hr/gf/fm 变体:"
        find "$extract_dir" -name "iwlwifi-*" 2>/dev/null | grep -oP 'iwlwifi-[^-]+-[^-]+-[^-]+-[^-]+' | sort -u | head -20
    fi

    for fw_file in $found_files; do
        local fname
        fname=$(basename "$fw_file")
        cp "$fw_file" "/lib/firmware/$fname"
        log_ok "已安装: $fname"
        installed=$((installed + 1))
    done

    if [ "$installed" -eq 0 ]; then
        log_error "未找到匹配的固件文件"
        log_info "请手动从以下地址下载:"
        log_info "  https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
        return 1
    fi

    # 重载 iwlwifi 模块
    log_info "重载 iwlwifi 模块..."
    modprobe -r iwlwifi 2>/dev/null || true
    sleep 1
    modprobe iwlwifi 2>/dev/null || true
    sleep 3

    # 验证
    if ip link show 2>/dev/null | grep -q "wl"; then
        local wl_iface
        wl_iface=$(ip link show 2>/dev/null | grep -oP 'wl[^ :]+' | head -1)
        log_ok "WiFi 接口已出现: $wl_iface"
        FIXED_ISSUES=$((FIXED_ISSUES + 1))
    else
        log_warn "WiFi 接口尚未出现,可能需要重启"
        log_info "新的固件文件已安装:"
        ls -la /lib/firmware/${fw_prefix}* 2>/dev/null
        dmesg 2>/dev/null | grep -i iwl | tail -5
    fi
}

# --- 声卡诊断与修复 ---

diagnose_audio() {
    log_section "声卡固件诊断"

    # 1. 找出 Intel 音频 PCI 设备
    local audio_devs
    audio_devs=$(lspci -nn 2>/dev/null | grep -i "Audio.*Intel" || true)
    if [ -z "$audio_devs" ]; then
        log_info "未检测到 Intel 音频硬件,跳过声卡诊断"
        return 0
    fi

    echo "$audio_devs" | while read -r line; do
        echo -e "  ${line}"
    done

    # 2. 检查声卡是否存在
    if [ -f /proc/asound/cards ] && grep -q "\[.*\]" /proc/asound/cards 2>/dev/null; then
        local card_count
        card_count=$(grep -c "\[" /proc/asound/cards 2>/dev/null || echo 0)
        log_ok "已有 $card_count 张声卡在工作,跳过"
        cat /proc/asound/cards 2>/dev/null | while read -r l; do echo "  $l"; done
        return 0
    fi

    # 3. 从 dmesg 检查 SOF 固件错误
    local sof_err
    sof_err=$(dmesg 2>/dev/null | grep "SOF firmware and/or topology file not found" || true)
    if [ -z "$sof_err" ]; then
        sof_err=$(grep "SOF firmware and/or topology file not found" /var/log/syslog 2>/dev/null || true)
    fi

    if [ -z "$sof_err" ]; then
        log_info "未检测到 SOF 固件错误,跳过"
        return 0
    fi

    FOUND_ISSUES=$((FOUND_ISSUES + 1))

    # 4. 提取需要的固件路径
    local fw_path tplg_path fw_dir ipc_type
    fw_path=$(dmesg 2>/dev/null | grep "Firmware file:" | grep sof | tail -1 | grep -oP 'intel/[^ ]+' || true)
    tplg_path=$(dmesg 2>/dev/null | grep "Topology file:" | grep sof | tail -1 | grep -oP 'intel/[^ ]+' || true)
    if [ -z "$fw_path" ]; then
        fw_path=$(grep "Firmware file:" /var/log/syslog 2>/dev/null | grep sof | tail -1 | grep -oP 'intel/[^ ]+' || true)
    fi

    echo -e "\n${YELLOW}检测到 SOF 固件缺失:${NC}"
    echo -e "  ${RED}✗${NC} Firmware:  $fw_path"
    echo -e "  ${YELLOW}?${NC} Topology:  $tplg_path"

    if [ -z "$fw_path" ]; then
        log_warn "无法从 dmesg 提取固件路径"
        return 1
    fi

    # 5. 分析固件路径,确定需要的平台
    # 典型路径: intel/sof-ipc4/arl/sof-arl.ri
    local platform fw_dir_name fw_file_name
    fw_dir_name=$(dirname "/lib/firmware/$fw_path")
    fw_file_name=$(basename "$fw_path")
    platform=$(echo "$fw_path" | grep -oP 'sof-ipc4/\K[^/]+' || echo "unknown")

    echo -e "\n${YELLOW}分析:${NC}"
    echo "  平台:     $platform"
    echo "  目标路径: $fw_dir_name/"
    echo "  固件文件: $fw_file_name"

    # 6. 检查系统中是否有对应 IPC4 的其他平台固件可复用
    local existing_dirs
    existing_dirs=$(ls -d /lib/firmware/intel/sof-ipc4/*/ 2>/dev/null | grep -v "$platform" || true)

    if [ -z "$existing_dirs" ]; then
        log_warn "没有其他平台的 SOF 固件可复用"
        if need_download; then
            log_info "尝试从 Debian 下载 firmware-sof-signed..."
            if [ "$DRY_RUN" = false ]; then
                local deb_file="$TEMP_DIR/firmware-sof.deb"
                if download "$FIRMWARE_SOF_URL" "$deb_file"; then
                    log_ok "下载完成 ($(du -h "$deb_file" | cut -f1))"
                    local sofe_dir="$TEMP_DIR/sof-extract"
                    mkdir -p "$sofe_dir"
                    dpkg-deb -x "$deb_file" "$sofe_dir" 2>/dev/null

                    # 复制整个 sof-ipc4 和 sof-ace-tplg 目录
                    if [ -d "$sofe_dir/usr/lib/firmware/intel/sof-ipc4" ]; then
                        cp -rn "$sofe_dir/usr/lib/firmware/intel/sof-ipc4/"* /lib/firmware/intel/sof-ipc4/ 2>/dev/null || true
                        log_ok "SOF IPC4 固件已安装"
                    fi
                    if [ -d "$sofe_dir/usr/lib/firmware/intel/sof-ipc4-tplg" ]; then
                        cp -rn "$sofe_dir/usr/lib/firmware/intel/sof-ipc4-tplg/"* /lib/firmware/intel/sof-ace-tplg/ 2>/dev/null || true
                        log_ok "SOF 拓扑文件已安装"
                    fi
                else
                    log_error "下载失败,尝试手动修复..."
                fi
            fi
        fi
    fi

    # 7. 检查是否可以通过创建符号链接修复
    # SOF 的设计:同一代平台的 DSP 固件通常相同,新平台通过符号链接指向已有固件
    local target_dir="/lib/firmware/intel/sof-ipc4/$platform"

    if [ -d "$target_dir" ]; then
        log_ok "目标目录已存在: $target_dir"
    else
        log_info "创建 $target_dir 目录..."

        if [ "$DRY_RUN" = true ]; then
            log_info "[DRY RUN] 将创建符号链接到已有 MTL 固件"
            return 0
        fi

        # 找到一个已存在的参考平台(通常是 mtl)
        local ref_platform=""
        for ref in mtl lnl ptl; do
            if [ -d "/lib/firmware/intel/sof-ipc4/$ref/intel-signed" ]; then
                ref_platform="$ref"
                break
            fi
        done

        if [ -z "$ref_platform" ]; then
            log_error "找不到参考平台固件,请手动安装 firmware-sof-signed 包"
            return 1
        fi

        log_info "使用 $ref_platform 固件作为 $platform 的参考"

        mkdir -p "$target_dir/intel-signed" "$target_dir/community"

        # 创建符号链接
        ln -sf "../../$ref_platform/intel-signed/sof-$ref_platform.ri" \
            "$target_dir/intel-signed/sof-$platform.ri"
        ln -sf "../../$ref_platform/community/sof-$ref_platform.ri" \
            "$target_dir/community/sof-$platform.ri"
        ln -sf "intel-signed/sof-$platform.ri" \
            "$target_dir/sof-$platform.ri"

        log_ok "已创建 $platform 固件符号链接 → $ref_platform"

        # 显示目录结构
        find "$target_dir" -type l -ls 2>/dev/null | while read -r l; do
            echo "  $l"
        done
    fi

    # 重载 SOF 驱动
    log_info "重载 SOF 驱动..."
    for mod in snd_sof_pci_intel_mtl snd_sof_intel_hda_generic \
               snd_sof_intel_hda_common snd_sof_intel_hda \
               snd_sof_pci snd_sof snd_sof_utils snd_sof_intel_hda_mlink; do
        modprobe -r "$mod" 2>/dev/null || true
    done
    sleep 1
    modprobe snd_sof_pci_intel_mtl 2>/dev/null || true
    sleep 3

    # 验证
    if [ -f /proc/asound/cards ] && grep -q "\[.*\]" /proc/asound/cards 2>/dev/null; then
        log_ok "声卡已出现!"
        cat /proc/asound/cards 2>/dev/null | while read -r l; do echo "  $l"; done
        aplay -l 2>&1 | grep -E "card|device" | head -10 | while read -r l; do echo "  $l"; done
        FIXED_ISSUES=$((FIXED_ISSUES + 1))
    else
        log_warn "声卡尚未出现,请检查 dmesg:"
        dmesg 2>/dev/null | grep -iE "sof|snd.*audio" | tail -10 | while read -r l; do echo "  $l"; done
    fi
}

# --- 主流程 ---

main() {
    echo -e "${BOLD}${CYAN}"
    echo "╔══════════════════════════════════════════╗"
    echo "║   Intel WiFi / 声卡 固件诊断修复工具      ║"
    echo "║   fix-firmware.sh v1.0                   ║"
    echo "╚══════════════════════════════════════════╝"
    echo -e "${NC}"

    # 解析参数
    while [ $# -gt 0 ]; do
        case "$1" in
            --dry-run) DRY_RUN=true ;;
            --wifi)    FIX_AUDIO=false ;;
            --audio)   FIX_WIFI=false ;;
            -h|--help)
                echo "用法: sudo $0 [选项]"
                echo ""
                echo "选项:"
                echo "  --dry-run   仅诊断,不修复"
                echo "  --wifi      仅检查/修复 WiFi"
                echo "  --audio     仅检查/修复声卡"
                echo "  -h, --help  显示帮助"
                exit 0
                ;;
            *) log_error "未知参数: $1"; exit 1 ;;
        esac
        shift
    done

    check_root

    if [ "$DRY_RUN" = true ]; then
        echo -e "${YELLOW}[DRY RUN 模式] 仅诊断,不做任何修改${NC}\n"
    fi

    # 系统信息
    echo -e "${BOLD}系统信息:${NC}"
    echo "  发行版: $(lsb_release -ds 2>/dev/null || echo 'Unknown')"
    echo "  内核:   $(uname -r)"
    echo "  架构:   $(uname -m)"
    echo ""

    # 确保 /lib/firmware 存在
    mkdir -p /lib/firmware/intel/sof-ipc4
    mkdir -p /lib/firmware/intel/sof-ace-tplg

    # 诊断
    [ "$FIX_WIFI" = true ]  && diagnose_wifi
    [ "$FIX_AUDIO" = true ] && diagnose_audio

    # 总结
    log_section "诊断总结"
    echo "  发现问题: $FOUND_ISSUES 个"
    if [ "$DRY_RUN" = false ]; then
        echo "  已修复:   $FIXED_ISSUES 个"
    fi

    if [ "$FOUND_ISSUES" -eq 0 ]; then
        log_ok "未发现固件问题,系统状态正常"
    elif [ "$FIXED_ISSUES" -eq "$FOUND_ISSUES" ]; then
        log_ok "所有问题已修复!"
        echo ""
        echo -e "${GREEN}${BOLD}建议重启系统以确保所有更改生效:${NC}"
        echo "  sudo reboot"
    else
        echo ""
        echo -e "${YELLOW}部分问题未能自动修复,请查看上述日志${NC}"
        echo ""
        echo "手动修复参考:"
        echo "  1. 安装更新的固件包:"
        echo "     sudo apt install linux-firmware firmware-sof-signed"
        echo "  2. 或从 Debian 仓库下载手动安装:"
        echo "     $DEBIAN_MIRROR"
        echo "  3. 或从 kernel.org 下载:"
        echo "     https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
    fi

    echo ""
}

main "$@"

修复日期:2026-07-07