Linux Wireless之WIFI扫描

Linux 下的 Wi-Fi 扫描流程横跨用户层、内核通用层与驱动层,核心路径是 应用 → nl80211 → cfg80211 → mac80211/驱动 → 硬件
下面从最新的内核源码(7.1.0-rc6)来看看整个过程。

一、整体架构总览

WiFi 扫描在 Linux 中分为以下几层,从上到下依次为:

┌─────────────────────────────────────────────────────────────────┐
│                    用户空间 (Userspace)                          │
│         wpa_supplicant / iw / NetworkManager                    │
│              通过 Netlink Socket 发送命令                        │
└───────────────────────────┬─────────────────────────────────────┘
                            │ NL80211_CMD_TRIGGER_SCAN
                            │ NL80211_CMD_GET_SCAN
                            │ NL80211_CMD_START_SCHED_SCAN
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│              nl80211 (Netlink 802.11 接口层)                     │
│              net/wireless/nl80211.c                             │
│   解析用户空间命令 → 构造扫描请求 → 调用 cfg80211                │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│              cfg80211 (无线配置子系统)                            │
│              net/wireless/scan.c, core.c                        │
│   扫描请求管理 / BSS 数据库 / 扫描结果过期 / 6GHz 拆分扫描       │
└───────────────────────────┬─────────────────────────────────────┘
                            │ rdev->ops->scan()
                            │ (或 mac80211: ieee80211_scan)
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│              mac80211 (软 MAC 层)                                │
│              net/mac80211/scan.c, cfg.c                         │
│   软件扫描状态机 / 硬件扫描调度 / 扫描结果收集                   │
└───────────────────────────┬─────────────────────────────────────┘
                            │ drv_hw_scan() / drv_sw_scan_start()
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│              硬件驱动 (Hardware Driver)                          │
│         iwlwifi / ath11k / mt76 / rtw89 / ...                   │
│   控制射频硬件发送 Probe Request / 接收 Beacon/Probe Response    │
└─────────────────────────────────────────────────────────────────┘

二、核心数据结构

2.1 扫描请求结构

┌──────────────────────────────────────────────────────────────┐
│              struct cfg80211_scan_request                     │
│              (include/net/cfg80211.h:2895)                    │
├──────────────────────────────────────────────────────────────┤
│  ssids[] / n_ssids          -- 要扫描的 SSID 列表             │
│  channels[] / n_channels    -- 要扫描的信道列表               │
│  ie / ie_len                -- 附加的 IE 信息                 │
│  flags                      -- 扫描标志 (随机化等)            │
│  mac_addr / mac_addr_mask   -- MAC 地址随机化                 │
│  bssid                      -- 指定 BSSID 扫描               │
│  duration                   -- 信道驻留时间                   │
│  rates[NL80211_NUM_BANDS]   -- 每个频段的速率掩码             │
│  wdev / wiphy               -- 关联的无线设备                 │
│  scan_6ghz                  -- 是否包含 6GHz 信道             │
│  scan_6ghz_params[]         -- 6GHz 扫描参数                  │
│  no_cck                     -- 不使用 CCK 速率                │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│              struct cfg80211_sched_scan_request               │
│              (include/net/cfg80211.h:3025)                    │
├──────────────────────────────────────────────────────────────┤
│  reqid                      -- 请求 ID                        │
│  ssids[] / n_ssids          -- 匹配的 SSID                    │
│  channels[] / n_channels    -- 扫描信道                       │
│  match_sets[] / n_match_sets-- RSSI 匹配集                    │
│  scan_plans[] / n_scan_plans-- 扫描计划 (间隔+迭代次数)       │
│  flags                      -- 标志                           │
│  mac_addr / mac_addr_mask   -- MAC 随机化                     │
│  min_rssi_thold              -- 最小 RSSI 阈值                │
│  delay                      -- 延迟报告                       │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│              struct cfg80211_bss                               │
│              (include/net/cfg80211.h:3174)                    │
├──────────────────────────────────────────────────────────────┤
│  bssid[ETH_ALEN]            -- BSS 标识                       │
│  channel                     -- 信道                          │
│  ies / beacon_ies / proberesp_ies -- IE 数据                  │
│  signal                     -- 信号强度 (mBm)                 │
│  beacon_interval            -- Beacon 间隔                    │
│  capability                 -- 能力字段                       │
│  ts_boottime                -- 引导时间戳                     │
│  hidden_beacon_bss          -- 隐藏 SSID 关联                 │
│  transmitted_bss            -- MBSSID 传输 BSS                │
│  nontrans_list              -- 非传输 BSS 列表                │
└──────────────────────────────────────────────────────────────┘

2.2 cfg80211 内部扫描状态

┌──────────────────────────────────────────────────────────────┐
│     struct cfg80211_registered_device (net/wireless/core.h)   │
├──────────────────────────────────────────────────────────────┤
│  scan_req          -- 当前活跃的扫描请求                      │
│  int_scan_req      -- 内部拆分扫描请求 (6GHz)                 │
│  scan_msg          -- 延迟的扫描完成消息                      │
│  scan_done_wk      -- 扫描完成工作队列项                      │
│  sched_scan_req_list -- 活跃的定时扫描请求链表                 │
│  sched_scan_res_wk -- 定时扫描结果工作队列项                  │
└──────────────────────────────────────────────────────────────┘

三、完整扫描流程

3.1 普通扫描 (Trigger Scan) 完整流程

用户空间 (wpa_supplicant / iw)
    │
    │ ① 发送 NL80211_CMD_TRIGGER_SCAN Netlink 消息
    │    携带: 频率列表、SSID列表、IE、扫描标志、MAC随机化参数
    ▼
┌───────────────────────────────────────────────────────────────┐
│  nl80211_trigger_scan()                     [nl80211.c:10805] │
│  ─────────────────────────────────────────────────────────── │
│  ② 解析 Netlink 属性:                                         │
│     - NL80211_ATTR_SCAN_FREQUENCIES (信道列表)                 │
│     - NL80211_ATTR_SCAN_SSIDS (SSID 列表)                     │
│     - NL80211_ATTR_IE (附加 IE)                               │
│     - NL80211_ATTR_SCAN_FLAGS (扫描标志)                      │
│     - NL80211_ATTR_MAC / NL80211_ATTR_MAC_MASK (MAC随机化)    │
│  ③ 分配 cfg80211_scan_request_int 结构                        │
│  ④ 填充 channels[], ssids[], ie 数据                          │
│  ⑤ rdev->scan_req = request                                   │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────────┐
│  cfg80211_scan()                            [scan.c:1069]     │
│  ─────────────────────────────────────────────────────────── │
│  ⑥ 检查 WIPHY_FLAG_SPLIT_SCAN_6GHZ 标志:                      │
│     ├─ 未设置: 直接调用 rdev_scan(rdev, request)               │
│     └─ 已设置 (6GHz 拆分扫描):                                 │
│        ├─ 统计非 6GHz 信道数量                                 │
│        ├─ 如果只有 6GHz 信道: cfg80211_scan_6ghz()             │
│        └─ 否则: 先扫描非 6GHz 信道, 完成后再扫描 6GHz         │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────────┐
│  rdev_scan()                                [rdev-ops.h:458]  │
│  ─────────────────────────────────────────────────────────── │
│  ⑦ 调用 rdev->ops->scan(rdev->wiphy, &request->req)          │
│     这是驱动注册的扫描回调函数                                  │
│     对于 mac80211 驱动: ieee80211_scan()                       │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────────┐
│  ⑧ 同时发送 NL80211_CMD_TRIGGER_SCAN 多播通知                  │
│     nl80211_send_scan_start()              [nl80211.c:20200]  │
│     → 多播到 NL80211_MCGRP_SCAN 组                             │
└───────────────────────────────────────────────────────────────┘

3.2 mac80211 层扫描处理

ieee80211_scan()                              [cfg.c:3289]
    │
    │ ⑨ 验证接口类型 (STATION/ADHOC/MESH/P2P_CLIENT/P2P_DEVICE)
    │    检查 AP 模式下是否允许扫描
    ▼
ieee80211_request_scan()                      [scan.c:1196]
    │
    ▼
__ieee80211_start_scan()                      [scan.c:731]
    │
    │ ⑩ 决定扫描方式:
    │
    ├─── 硬件扫描 (hw_scan) ───────────────────────────────────┐
    │    │                                                     │
    │    │  分配 hw_scan_req 结构                               │
    │    │  设置 SCAN_HW_SCANNING 标志                         │
    │    │  调用 ieee80211_prep_hw_scan() 准备信道列表和 IE    │
    │    │  调用 drv_hw_scan() → ops->hw_scan()                │
    │    │  驱动接管扫描, 完成后调用 ieee80211_scan_completed() │
    │    │                                                     │
    │    └─────────────────────────────────────────────────────┘
    │
    ├─── 单信道在网扫描 (on-channel) ──────────────────────────┐
    │    │                                                     │
    │    │  设置 SCAN_ONCHANNEL_SCANNING 标志                   │
    │    │  调用 drv_sw_scan_start() 通知驱动                   │
    │    │  发送 Probe Request 或等待 Beacon                    │
    │    │                                                     │
    │    └─────────────────────────────────────────────────────┘
    │
    └─── 软件扫描 (sw_scan) ──────────────────────────────────┐
         │                                                     │
         │  设置 SCAN_SW_SCANNING 标志                          │
         │  调用 ieee80211_start_sw_scan():                     │
         │    - drv_sw_scan_start() 通知驱动                    │
         │    - 停止 off-channel VIF                            │
         │    - 发送 NullFunc (PS bit)                          │
         │    - 配置过滤器                                      │
         │    - 启动 scan_work 状态机                           │
         │                                                     │
         └─────────────────────────────────────────────────────┘

3.3 软件扫描状态机

这是 mac80211 中最核心的扫描驱动逻辑:

                ieee80211_scan_work()  [scan.c:1095]
                          │
                          ▼
                ┌─────────────────┐
                │   SCAN_DECISION │ ◄──────────────────────┐
                │   [scan.c:923]  │                        │
                └────────┬────────┘                        │
                         │                                 │
          ┌──────────────┼──────────────┐                  │
          │              │              │                  │
          ▼              ▼              ▼                  │
所有信道完成      需要返回运营信道    继续扫描下一信道            │
     │              │              │                       │
     ▼              ▼              ▼                       │
扫描完成       ┌──────────┐  ┌───────────────┐             │
              │SCAN_     │  │ SCAN_SET_     │             │
              │SUSPEND   │  │ CHANNEL       │             │
              │[.1062]   │  │ [scan.c:990]  │             │
              └────┬─────┘  └───────┬───────┘             │
                   │                │                      │
                   ▼                ├──────┐               │
              返回运营信道            │      │               │
              (200ms)               │      ▼               │
                   │           被动信道  主动信道            │
                   │           (无IR)   (有IR)             │
                   ▼              │      │                │
              ┌──────────┐        │      ▼                │
              │SCAN_     │        │  ┌───────────────┐    │
              │RESUME    │        │  │ SCAN_SEND_    │    │
              │[.1077]   │        │  │ PROBE         │    │
              └────┬─────┘        │  │ [scan.c:689]  │    │
                   │              │  └───────┬───────┘    │
                   │              │          │            │
                   │              ▼          ▼            │
                   │          等待被动    发送 Probe        │
                   │          驻留时间    Request          │
                   │              │          │            │
                   └──────────────┴──────────┴────────────┘
                                  │
                                  ▼
                          ┌──────────────┐
                          │ 扫描完成      │
                          │ __ieee80211_  │
                          │ scan_completed│
                          └──────────────┘

各状态详细说明:

状态 函数 作用
SCAN_DECISION ieee80211_scan_state_decision() 决定下一步: 完成/暂停/切信道/发Probe
SCAN_SET_CHANNEL ieee80211_scan_state_set_channel() 切换到目标信道, 设置驻留时间
SCAN_SEND_PROBE ieee80211_scan_state_send_probe() 为每个 SSID 发送 Probe Request
SCAN_SUSPEND ieee80211_scan_state_suspend() 临时返回运营信道处理缓存帧 (200ms)
SCAN_RESUME ieee80211_scan_state_resume() 恢复扫描, 停止 VIF
SCAN_ABORT - 扫描被中止

3.4 扫描结果上报流程

┌───────────────────────────────────────────────────────────────┐
│  硬件接收到 Beacon / Probe Response 帧                          │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
ieee80211_scan_rx()                           [scan.c:277]
    │
    │ 过滤: 只处理 Probe Response / Beacon / S1G Beacon
    │ 验证信道匹配
    │ 检查 Probe Response 地址是否匹配扫描请求
    ▼
ieee80211_bss_info_update()                   [scan.c:168]
    │
    │ 填充 cfg80211_inform_bss 结构:
    │   - signal (信号强度)
    │   - channel (信道)
    │   - boottime_ns (引导时间)
    │   - parent_tsf / parent_bssid
    ▼
cfg80211_inform_bss_frame_data()              [scan.c:3246]
    │
    │ 解析管理帧: BSSID, TSF, Capability, Beacon Interval, IEs
    │ 判断帧类型 (Beacon / Probe Response)
    ▼
cfg80211_inform_bss_data()                    [scan.c:2253]
    │
    │ 验证信号强度、信道、6GHz 功率类型
    │ 创建 cfg80211_bss_ies 结构
    ▼
__cfg80211_bss_update()
    │
    │ 查找已有的 BSS 条目:
    │   - 找到: 更新 IE 和信号强度
    │   - 未找到: 插入新的 BSS 到哈希表和链表
    │
    │ 处理 Multi-BSSID (MBSSID) 非传输 BSS
    ▼
返回 cfg80211_bss 引用

3.5 扫描完成流程

┌───────────────────────────────────────────────────────────────┐
│  硬件扫描完成 / 软件扫描遍历完所有信道                              │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
ieee80211_scan_completed()                    [scan.c:540]
    │ (EXPORTED, 驱动调用)
    │
    │ 设置 SCAN_COMPLETED 标志 (如果中止还设置 SCAN_ABORTED)
    │ 排入 scan_work 工作队列
    ▼
ieee80211_scan_work() 检测到完成              [scan.c:1122]
    │
    ▼
__ieee80211_scan_completed()                  [scan.c:448]
    │
    │ ① 多频段硬件扫描: 启动下一频段扫描 (如果需要)
    │ ② 释放 hw_scan_req
    │ ③ 恢复功率等级和过滤器配置
    │ ④ 通知 MLME: ieee80211_mlme_notify_scan_completed()
    │    → 重启 STA 定时器
    │ ⑤ 通知 IBSS
    │ ⑥ 重新排队延迟的工作
    │ ⑦ 启动下一个 ROC (Remain On Channel)
    │
    ▼
cfg80211_scan_done()                          [scan.c:1183]
    │ (EXPORTED, mac80211 调用)
    │
    │ 存储 scan_info (aborted, tsf, bssid)
    │ 排入 scan_done_wk 工作队列
    ▼
__cfg80211_scan_done()                        [scan.c:1178]
    │
    ▼
___cfg80211_scan_done()                       [scan.c:1107]
    │
    │ ① 检查是否需要 6GHz 后续扫描
    │ ② 构建 Netlink 消息:
    │    - 正常完成: NL80211_CMD_NEW_SCAN_RESULTS
    │    - 被中止:   NL80211_CMD_SCAN_ABORTED
    │ ③ 处理 NL80211_SCAN_FLAG_FLUSH (清除过期 BSS)
    │ ④ 发送多播到 NL80211_MCGRP_SCAN
    ▼
用户空间收到扫描结果通知

3.6 扫描结果查询流程

用户空间 (wpa_supplicant / iw scan)
    │
    │ NL80211_CMD_GET_SCAN (dumpit)
    ▼
nl80211_dump_scan()                           [nl80211.c:12092]
    │
    │ 首次调用: cfg80211_bss_expire() 过期旧 BSS
    │
    │ 遍历 rdev->bss_list:
    │   └── nl80211_send_bss()               [nl80211.c:11933]
    │       │
    │       │ 构建 NL80211_CMD_NEW_SCAN_RESULTS 消息
    │       │ 包含 NL80211_ATTR_BSS 属性:
    │       │   - BSSID, TSF, IEs (Probe Response + Beacon)
    │       │   - Beacon Interval, Capability
    │       │   - Frequency, Signal (mBm)
    │       │   - Seen ms ago (上次看到时间)
    │       │   - Parent TSF, Parent BSSID
    │       │   - Chain Signal 等
    │       ▼
    │       返回给用户空间
    │
    │ 分页: 多次 dump 调用返回所有 BSS 条目
    ▼
用户空间获取完整扫描结果

3.7 定时扫描 (Scheduled Scan) 流程

用户空间
    │
    │ NL80211_CMD_START_SCHED_SCAN
    ▼
┌───────────────────────────────────────────────────────────────┐
│  nl80211_start_sched_scan()               [nl80211.c:11497]   │
│  ─────────────────────────────────────────────────────────── │
│  验证支持 (max_sched_scan_reqs, ops->sched_scan_start)        │
│  cfg80211_sched_scan_req_possible() 检查资源限制              │
│  解析: match_sets, scan_plans, channels, ssids               │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────────┐
│  rdev_sched_scan_start()                  [rdev-ops.h:888]    │
│  → rdev->ops->sched_scan_start()                              │
└───────────────────────────┬───────────────────────────────────┘
                            │
          ┌─────────────────┴─────────────────┐
          ▼                                   ▼
    mac80211 驱动                        直接 cfg80211 驱动
          │
          ▼
ieee80211_sched_scan_start()              [cfg.c:3351]
    │
    ▼
__ieee80211_request_sched_scan_start()    [scan.c:1331]
    │
    │ 构建扫描 IE、速率掩码
    │ 调用 drv_sched_scan_start() → ops->sched_scan_start()
    │ 存储 sched_scan_req 和 sched_scan_sdata
    ▼
    │
    ├─ cfg80211_add_sched_scan_req() 加入链表
    │
    ▼
┌───────────────────────────────────────────────────────────────┐
│  驱动定期执行扫描, 发现匹配网络时上报结果                      │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
ieee80211_sched_scan_results()              [scan.c:1431]
    │ (EXPORTED, 驱动调用)
    ▼
cfg80211_sched_scan_results()               [scan.c:1302]
    │
    │ 设置 report_results = true
    │ 排入 sched_scan_res_wk 工作队列
    ▼
cfg80211_sched_scan_results_wk()            [scan.c:1276]
    │
    │ 遍历 sched_scan_req_list 中 report_results=true 的请求
    │ 发送 NL80211_CMD_SCHED_SCAN_RESULTS 多播
    ▼
用户空间收到定时扫描结果

停止定时扫描:
    NL80211_CMD_STOP_SCHED_SCAN
    → nl80211_stop_sched_scan()           [nl80211.c:11550]
    → cfg80211_stop_sched_scan_req()      [scan.c:1340]
    → rdev_sched_scan_stop()
    → 发送 NL80211_CMD_SCHED_SCAN_STOPPED

3.8 6GHz 拆分扫描流程

┌───────────────────────────────────────────────────────────────┐
│  6GHz 频段扫描采用 "拆分扫描" 策略                             │
│  (WIPHY_FLAG_SPLIT_SCAN_6GHZ)                                 │
│                                                               │
│  原因: 6GHz 信道数量多, 与 2.4/5GHz 合并扫描耗时过长           │
└───────────────────────────┬───────────────────────────────────┘
                            │
                            ▼
第一阶段: 扫描 2.4GHz + 5GHz 信道
    │
    │ cfg80211_scan() 中拆分出非 6GHz 信道
    │ rdev_scan() 执行扫描
    │
    ▼
扫描完成回调 ___cfg80211_scan_done()
    │
    │ 检测到还有 6GHz 信道未扫描
    ▼
cfg80211_scan_6ghz()                          [scan.c:841]
    │
    │ 从 RNR (Reduced Neighbor Report) 元素中获取:
    │   - 协同 AP 的信道信息
    │   - Short SSID 匹配
    │ 构建 6GHz 专用扫描请求:
    │   - PSC (Preferred Scanning Channels) 信道
    │   - 协同 AP 关联的信道
    │   - unsolicited probe 信道
    │
    │ rdev_scan() 执行 6GHz 扫描
    ▼
第二阶段完成, 发送最终扫描结果

四、关键函数索引表

4.1 nl80211 层 (net/wireless/nl80211.c)

行号 函数 功能
10805 nl80211_trigger_scan() 解析用户空间扫描请求, 启动扫描
11054 nl80211_abort_scan() 中止正在进行的扫描
11497 nl80211_start_sched_scan() 启动定时扫描
11550 nl80211_stop_sched_scan() 停止定时扫描
12092 nl80211_dump_scan() 导出扫描结果到用户空间
11933 nl80211_send_bss() 发送单个 BSS 条目
20200 nl80211_send_scan_start() 多播扫描开始通知
20219 nl80211_build_scan_msg() 构建扫描完成/中止消息
20239 nl80211_send_scan_msg() 多播扫描完成通知

4.2 cfg80211 层 (net/wireless/scan.c)

行号 函数 功能
1069 cfg80211_scan() 扫描入口, 处理 6GHz 拆分
1107 ___cfg80211_scan_done() 扫描完成核心处理
1183 cfg80211_scan_done() 导出函数, 驱动调用通知扫描完成
841 cfg80211_scan_6ghz() 6GHz 拆分扫描处理
1302 cfg80211_sched_scan_results() 导出函数, 定时扫描结果通知
1340 cfg80211_stop_sched_scan_req() 停止定时扫描请求
2253 cfg80211_inform_single_bss_data() BSS 信息录入核心函数
3246 cfg80211_inform_bss_frame_data() 导出函数, 报告收到的 Beacon/Probe Response
468 __cfg80211_bss_expire() 过期旧的 BSS 条目

4.3 mac80211 层 (net/mac80211/scan.c)

行号 函数 功能
731 __ieee80211_start_scan() 扫描启动核心: 决定 hw/sw/onchannel
1095 ieee80211_scan_work() 软件扫描状态机主循环
923 ieee80211_scan_state_decision() 扫描决策状态
990 ieee80211_scan_state_set_channel() 设置信道状态
689 ieee80211_scan_state_send_probe() 发送 Probe Request 状态
448 __ieee80211_scan_completed() 扫描完成处理
540 ieee80211_scan_completed() 导出函数, 硬件扫描完成回调
277 ieee80211_scan_rx() 接收 Beacon/Probe Response
63 ieee80211_inform_bss() BSS 信息通知回调
1331 __ieee80211_request_sched_scan_start() 定时扫描启动

4.4 驱动接口

文件 回调 功能
include/net/cfg80211.h:5203 .scan cfg80211 驱动扫描回调
include/net/cfg80211.h:5205 .abort_scan cfg80211 驱动中止扫描回调
include/net/cfg80211.h:5306 .sched_scan_start cfg80211 定时扫描启动
include/net/cfg80211.h:5309 .sched_scan_stop cfg80211 定时扫描停止
include/net/mac80211.h:4749 .hw_scan mac80211 硬件扫描回调
include/net/mac80211.h:4751 .cancel_hw_scan mac80211 取消硬件扫描
include/net/mac80211.h:4753 .sched_scan_start mac80211 定时扫描启动
include/net/mac80211.h:4757 .sched_scan_stop mac80211 定时扫描停止

┌─────────────────────────────────────────────────────────────┐
│                    Netlink 命令                              │
├──────────────────────────┬──────────────────────────────────┤
│  NL80211_CMD_TRIGGER_SCAN    (1469)  触发扫描               │
│  NL80211_CMD_GET_SCAN        (1470)  获取扫描结果           │
│  NL80211_CMD_NEW_SCAN_RESULTS(1471)  扫描结果通知           │
│  NL80211_CMD_SCAN_ABORTED    (1472)  扫描中止通知           │
│  NL80211_CMD_ABORT_SCAN      (1604)  中止扫描命令           │
│  NL80211_CMD_START_SCHED_SCAN(1537)  启动定时扫描           │
│  NL80211_CMD_STOP_SCHED_SCAN (1538)  停止定时扫描           │
│  NL80211_CMD_SCHED_SCAN_RESULTS    (1539)  定时扫描结果     │
│  NL80211_CMD_SCHED_SCAN_STOPPED    (1540)  定时扫描停止     │
├──────────────────────────┴──────────────────────────────────┤
│                    多播组                                    │
├─────────────────────────────────────────────────────────────┤
│  NL80211_MCGRP_SCAN ("scan")  所有扫描事件发送到此组         │
└─────────────────────────────────────────────────────────────┘

六、扫描标志 (Scan Flags)

┌──────────────────────────────────────────────────────────────┐
│  NL80211_SCAN_FLAG_LOW_PRIORITY          低优先级扫描         │
│  NL80211_SCAN_FLAG_FLUSH                 刷新旧结果           │
│  NL80211_SCAN_FLAG_AP                    AP 模式扫描          │
│  NL80211_SCAN_FLAG_RANDOM_ADDR          使用随机 MAC 地址     │
│  NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME 最大信道驻留时间     │
│  NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP 接受广播探测响应  │
│  NL80211_SCAN_FLAG_OOC_PROBE_OUI        OOC 探测 OUI         │
│  NL80211_SCAN_FLAG_MIN_PREQ_CONTENT     最小探测请求内容      │
└──────────────────────────────────────────────────────────────┘

七、关键时序图

7.1 完整扫描时序 (从触发到结果上报)

时间轴 ──────────────────────────────────────────────────────────►

用户空间     │──NL80211_CMD_TRIGGER_SCAN──►│                    │
             │                              │                    │
nl80211      │  nl80211_trigger_scan()      │                    │
             │  cfg80211_scan()             │                    │
             │  rdev_scan()                 │                    │
             │  nl80211_send_scan_start()   │                    │
             │                              │                    │
mac80211     │  ieee80211_scan()            │                    │
             │  __ieee80211_start_scan()    │                    │
             │                              │                    │
             │  ┌─ SCAN_SET_CHANNEL ──┐    │                    │
             │  │  切换到信道 1       │    │                    │
             │  │  等待驻留时间       │    │                    │
             │  │  SCAN_SEND_PROBE    │    │                    │
             │  │  发送 Probe Request │    │                    │
             │  │  等待响应           │    │                    │
             │  │  SCAN_DECISION      │    │                    │
             │  └─────────────────────┘    │                    │
             │  ... 重复所有信道 ...        │                    │
             │                              │                    │
驱动         │  ── 收到 Beacon/Probe Resp ──►│                   │
             │     ieee80211_scan_rx()       │                   │
             │     cfg80211_inform_bss_*()   │                   │
             │                              │                    │
             │  扫描完成                     │                    │
             │  ieee80211_scan_completed()   │                    │
             │  cfg80211_scan_done()         │                    │
             │                              │                    │
nl80211      │  nl80211_build_scan_msg()    │                    │
             │  ──NL80211_CMD_NEW_SCAN_RESULTS──►│               │
             │                              │                    │
用户空间     │  ◄── 收到扫描结果通知 ────── │                    │
             │  NL80211_CMD_GET_SCAN        │                    │
             │  ◄── 获取完整 BSS 列表 ───── │                    │

八、源文件索引

文件路径 层级 职责
include/uapi/linux/nl80211.h UAPI Netlink 命令和属性定义
include/net/cfg80211.h cfg80211 数据结构、驱动操作、导出 API
include/net/mac80211.h mac80211 ieee80211_ops 定义
net/wireless/nl80211.c nl80211 Netlink 命令处理器
net/wireless/scan.c cfg80211 扫描核心: 请求管理、BSS 数据库、扫描完成
net/wireless/core.c cfg80211 初始化、清理、定时扫描停止
net/wireless/core.h cfg80211 内部结构定义
net/wireless/rdev-ops.h cfg80211 驱动调用包装器
net/mac80211/scan.c mac80211 扫描状态机、hw/sw 扫描、BSS 通知
net/mac80211/cfg.c mac80211 cfg80211 操作注册
net/mac80211/mlme.c mac80211 MLME 扫描完成通知
net/mac80211/driver-ops.h mac80211 驱动调用包装器

九、总结

Linux 内核 WiFi 扫描流程的核心设计特点:

  1. 分层架构: nl80211 → cfg80211 → mac80211 → 驱动, 每层职责清晰
  2. 异步模型: 扫描请求异步执行, 通过工作队列和 Netlink 多播通知结果
  3. 双模扫描: 支持硬件扫描 (hw_scan, 驱动实现) 和软件扫描 (sw_scan, mac80211 状态机)
  4. 6GHz 拆分: 6GHz 信道单独扫描, 利用 RNR 和 PSC 优化扫描效率
  5. BSS 数据库: cfg80211 维护统一的 BSS 哈希表, 支持过期清理和 MBSSID
  6. 定时扫描: 支持基于扫描计划 (interval + iterations) 的后台扫描
  7. MAC 随机化: 支持扫描时使用随机 MAC 地址保护隐私