【解决Miracast出现组形成失败问题】

问题描述

现象

在 Miracast 连接过程中,p2p0 接口在 P2P 组形成阶段被意外断开,导致连接失败并出现 GROUP FORMATION FAILURE 错误。

关键日志

复制代码
[SME_AUTH_FREQ_CHECK] p2p0: num_multichan_concurrent=1, current_freq=2422, target_freq=5745, calling wpas_p2p_handle_frequency_conflicts
[FREQ_CONFLICT] wpas_p2p_handle_frequency_conflicts: entry, wpa_s=p2p0, freq=5745, ssid=0x823d2c00 (id=2)
wpas_p2p_handle_frequency_conflicts 8154
wpas_p2p_handle_frequency_conflicts 8172
[NL80211_DEAUTH] p2p0: nl80211_deauthenticate, bssid=0x822bf9c8M, reason=3, local_state_change=0
[CFG_DEAUTH] p2p%d: cfg80211 deauth entry, bssid=0x822bf9c8M, reason=3, local_state_change=0
[DEAUTH_ENTER] p2p%d: deauth request, bssid=0x822637d8M, reason=3, p2p=0, local_state_change=0
p2p%d: deauthenticating from 0x822637d8M by local choice (reason=3) [p2p=0]

调试过程

阶段一:问题定位

  1. 添加控制接口调试信息

    • 位置:ctrl_iface_udp.c, ctrl_iface.c
    • 目的:确认 DISCONNECT 命令是否正确路由到对应接口
    • 发现:p2p0 断开发生在 wlan0 收到 DISCONNECT 命令之前
  2. 添加 wpa_supplicant 层调试信息

    • 位置:wpa_supplicant.c
    • 函数:wpa_supplicant_deauthenticate, wpas_request_disconnection
    • 目的:追踪断开调用的来源
    • 发现:p2p0 断开由 wpa_supplicant_disable_network 触发
  3. 添加驱动层调试信息

    • 位置:mlme.c, cfg.c, sta.c
    • 目的:追踪驱动层的断开路径
    • 发现:断开请求来自 cfg80211 层,local_state_change=0 表示外部触发

阶段二:调用路径追踪

  1. 追踪 wpa_supplicant_deauthenticate 的所有调用点

    • wpa_supplicant.c 中所有调用 wpa_supplicant_deauthenticate 的位置添加调试信息
    • 发现:wpas_p2p_handle_frequency_conflicts 是触发源
  2. 分析频率冲突处理逻辑

    • 位置:p2p_supplicant.c - wpas_p2p_handle_frequency_conflicts
    • 发现:函数在 wpa_supplicant_associatesme_send_authentication 中被调用

阶段三:根本原因分析

调用时机

频率冲突检查在以下两个函数中被调用:

  1. wpa_supplicant_associate (wpa_supplicant.c:2692)

    • 调用条件:num_multichan_concurrent < 2 且检测到频率冲突
    • 用于非 SME 模式的驱动
  2. sme_send_authentication (sme.c:608)

    • 调用条件:同上
    • 用于支持 SME 模式的驱动
Bug 分析

wpas_p2p_handle_frequency_conflicts 函数中(p2p_supplicant.c:8171-8175):

c 复制代码
} else {
    /* P2P connection has priority, disable the STA network */
    wpa_supplicant_disable_network(wpa_s->global->ifaces,  // ❌ Bug: 应该使用 wpa_s
                                   ssid);
    wpa_msg(wpa_s->global->ifaces, MSG_INFO,              // ❌ Bug: 应该使用 wpa_s
            WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
    os_memset(wpa_s->global->ifaces->pending_bssid, 0,    // ❌ Bug: 应该使用 wpa_s
              ETH_ALEN);
    // ...
}

问题

  • wpa_s 是正在尝试连接的接口(可能是 p2p0
  • wpa_s->global->ifaces 是全局接口列表中的第一个接口(通常是 wlan0
  • 当 P2P 连接有优先级时,代码错误地禁用了第一个接口的网络,而不是正在尝试连接的接口的网络
  • 这导致 p2p0 在尝试切换频率时被错误地断开
触发场景
  1. p2p0 当前在频率 2422 (2.4GHz) 上
  2. p2p0 尝试切换到频率 5745 (5GHz)
  3. 检测到频率冲突(current_freq=2422, target_freq=5745)
  4. wpas_is_p2p_prioritized(iface) 返回 true(P2P 有优先级)
  5. 进入 else 分支,错误地禁用 wpa_s->global->ifaces(第一个接口)的网络
  6. 导致 p2p0 被断开

解决方案

方案选择

经过分析,提供了两个方案:

  1. 方案一:修复 bug(推荐但被拒绝)

    • wpa_s->global->ifaces 改为 wpa_s
    • 优点:保留频率冲突检查的保护机制
    • 缺点:需要修改核心逻辑
  2. 方案二:注释掉频率冲突检查(已采用)

    • 使用 #if 0 注释掉检查代码
    • 优点:快速解决问题,避免错误的处理逻辑
    • 缺点:失去频率冲突保护,可能在某些硬件上出现问题

实施修改

修改文件 1: wpa_supplicant.c
c 复制代码
#ifdef CONFIG_P2P
    /*
     * If multi-channel concurrency is not supported, check for any
     * frequency conflict. In case of any frequency conflict, remove the
     * least prioritized connection.
     */
    /* Disabled frequency conflict check to avoid incorrect interface handling
     * in wpas_p2p_handle_frequency_conflicts which causes p2p0 disconnection
     * when switching frequencies (e.g., from 2422 to 5745).
     */
#if 0
    if (wpa_s->num_multichan_concurrent < 2) {
        int freq, num;
        num = get_shared_radio_freqs(wpa_s, &freq, 1);
        if (num > 0 && freq > 0 && freq != params.freq.freq) {
            wpa_printf(MSG_DEBUG,
                       "Assoc conflicting freq found (%d != %d)",
                       freq, params.freq.freq);
            if (wpas_p2p_handle_frequency_conflicts(
                    wpa_s, params.freq.freq, ssid) < 0) {
                wpas_connect_work_done(wpa_s);
                return;
            }
        }
    }
#endif
#endif /* CONFIG_P2P */
修改文件 2: sme.c
c 复制代码
#ifdef CONFIG_P2P
    /*
     * If multi-channel concurrency is not supported, check for any
     * frequency conflict. In case of any frequency conflict, remove the
     * least prioritized connection.
     */
    /* Disabled frequency conflict check to avoid incorrect interface handling
     * in wpas_p2p_handle_frequency_conflicts which causes p2p0 disconnection
     * when switching frequencies (e.g., from 2422 to 5745).
     */
#if 0
    if (wpa_s->num_multichan_concurrent < 2) {
        int freq, num;
        num = get_shared_radio_freqs(wpa_s, &freq, 1);
        if (num > 0 && freq > 0 && freq != params.freq) {
            wpa_printf(MSG_DEBUG,
                       "Conflicting frequency found (%d != %d)",
                       freq, params.freq);
            if (wpas_p2p_handle_frequency_conflicts(wpa_s,
                                                    params.freq,
                                                    ssid) < 0) {
                wpas_connection_failed(wpa_s, bss->bssid);
                wpa_supplicant_mark_disassoc(wpa_s);
                wpabuf_free(resp);
                wpas_connect_work_done(wpa_s);
                return;
            }
        }
    }
#endif
#endif /* CONFIG_P2P */

调试工具和技巧

添加的调试信息位置

  1. 控制接口层

    • ctrl_iface_udp.c: UDP 控制接口接收命令
    • ctrl_iface.c: 控制接口命令处理
  2. wpa_supplicant 核心层

    • wpa_supplicant.c: 断开和关联处理
    • sme.c: SME 模式认证处理
  3. 驱动接口层

    • driver_nl80211.c: nl80211 驱动接口
    • nl80211.c (lnxporting): 内核 netlink 接口
    • cfg.c, mlme.c, sta.c: 驱动核心逻辑

调试信息格式

使用统一的调试标记:

  • [CTRL_IFACE_RX]: 控制接口接收
  • [CTRL_IFACE_PROCESS]: 控制接口处理
  • [WPAS_REQUEST_DISCONNECTION]: wpa_supplicant 断开请求
  • [WPAS_DEAUTH]: wpa_supplicant 去认证
  • [WPAS_DRV_DEAUTH]: 驱动层去认证
  • [NL80211_DEAUTH]: nl80211 去认证
  • [CFG_DEAUTH]: cfg80211 去认证
  • [DEAUTH_ENTER]: 驱动去认证入口
  • [SME_AUTH_FREQ_CHECK]: SME 认证频率检查
  • [ASSOC_FREQ_CHECK]: 关联频率检查
  • [FREQ_CONFLICT]: 频率冲突处理

技术要点

关键概念

  1. 多通道并发能力 (num_multichan_concurrent)

    • 表示硬件可以同时使用的不同频率数量
    • < 2 表示不支持多通道并发(只能使用一个频率)
    • >= 2 表示支持多通道并发
  2. 频率冲突检查

    • 目的:在单通道模式下,避免多个接口使用不同频率
    • 机制:检测到冲突时,根据优先级决定保留哪个连接
  3. 接口优先级

    • 通过 wpas_is_p2p_prioritized() 判断
    • 基于 wpa_s->global->conc_pref 配置
    • WPA_CONC_PREF_P2P: P2P 有优先级
    • WPA_CONC_PREF_STA: STA 有优先级

调用链

复制代码
p2p0 尝试切换频率 (2422 -> 5745)
  ↓
sme_send_authentication / wpa_supplicant_associate
  ↓
检测到频率冲突 (num_multichan_concurrent < 2)
  ↓
wpas_p2p_handle_frequency_conflicts
  ↓
wpas_is_p2p_prioritized(iface) 返回 true
  ↓
进入 else 分支
  ↓
错误地禁用 wpa_s->global->ifaces 的网络 (应该是 wpa_s)
  ↓
p2p0 被断开 ❌

测试验证

测试场景

  1. Miracast 连接

    • 启动 Miracast 服务
    • 等待 P2P 设备发现
    • 接受邀请并形成 P2P 组
    • 验证 p2p0 不再在频率切换时断开
  2. 频率切换

    • 从 2.4GHz 切换到 5GHz
    • 验证连接保持稳定

预期结果

  • p2p0 可以在不同频率间切换而不被断开
  • ✅ Miracast P2P 组形成成功
  • ✅ 不再出现 GROUP FORMATION FAILURE 错误

后续建议

长期解决方案

  1. 修复 wpas_p2p_handle_frequency_conflicts 中的 bug

    c 复制代码
    // 将 wpa_s->global->ifaces 改为 wpa_s
    wpa_supplicant_disable_network(wpa_s, ssid);
    wpa_msg(wpa_s, MSG_INFO, ...);
    os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
  2. 验证硬件多通道并发能力

    • 如果硬件实际支持多通道并发,考虑将 num_multichan_concurrent 设置为 2 或更大值
    • 这样可以完全避免频率冲突检查
  3. 完善测试覆盖

    • 添加频率切换场景的自动化测试
    • 覆盖不同优先级配置下的行为

相关文件

修改的文件

  • wpa_supplicant/wpa_supplicant.c
  • wpa_supplicant/sme.c

添加调试信息的文件

  • wpa_supplicant/ctrl_iface_udp.c
  • wpa_supplicant/ctrl_iface.c
  • wpa_supplicant/wpa_supplicant.c
  • src/drivers/driver_nl80211.c
  • net/wireless/nl80211.c (lnxporting)
  • hal_apollo/mac80211/cfg.c
  • hal_apollo/mac80211/mlme.c
  • hal_apollo/sta.c

核心问题文件

  • wpa_supplicant/p2p_supplicant.c - wpas_p2p_handle_frequency_conflicts()

总结

本次调试通过系统性地添加调试信息,从应用层到驱动层完整追踪了 p2p0 断开的调用路径,最终定位到 wpas_p2p_handle_frequency_conflicts 函数中的接口引用错误。通过注释掉频率冲突检查代码,临时解决了问题,但长期来看应该修复函数中的 bug 以恢复频率冲突保护机制。


相关推荐
demo007x9 小时前
在国内也能使用 Claude cli给自己提效,附实操方法
前端·后端·程序员
开心猴爷9 小时前
iOS App的tcp、udp数据包抓取在实际开发中的使用方式
后端
JavaGuru_LiuYu9 小时前
Spring Boot 整合 SSE(Server-Sent Events)
java·spring boot·后端·sse
xuejianxinokok9 小时前
如何在 Rust 中以惯用方式使用全局变量
后端·rust
爬山算法9 小时前
Hibernate(26)什么是Hibernate的透明持久化?
java·后端·hibernate
彭于晏Yan9 小时前
Springboot实现数据脱敏
java·spring boot·后端
alonewolf_999 小时前
Spring IOC容器扩展点全景:深入探索与实践演练
java·后端·spring
super_lzb10 小时前
springboot打war包时将外部配置文件打入到war包内
java·spring boot·后端·maven
天远云服10 小时前
Go语言高并发实战:集成天远手机号码归属地核验API打造高性能风控中台
大数据·开发语言·后端·golang
钱多多_qdd10 小时前
springboot注解(二)
java·spring boot·后端