分析基于 Linux 内核源码,主要文件:
net/mac80211/ieee80211_i.hnet/mac80211/tx.cnet/mac80211/rx.cnet/mac80211/scan.cnet/mac80211/rate.hnet/mac80211/rc80211_minstrel_ht.hnet/mac80211/sta_info.hnet/wireless/nl80211.cnet/wireless/core.h
Linux 无线网络子系统分为三个主要层次:用户空间通过 nl80211 Netlink 接口与内核的 cfg80211 层通信;cfg80211 是通用无线配置框架(上层);mac80211 是 IEEE 802.11 协议的软件实现层(中间层);最底层是具体的硬件驱动。
+------------------------------------------------------------------+
| 用户空间 |
| wpa_supplicant iw hostapd NetworkManager |
+------------------------------------------------------------------+
| NETLINK (Generic Netlink / nl80211 协议族)
+------------------------------------------------------------------+
| nl80211 接口层 |
| net/wireless/nl80211.c |
| static const struct genl_ops nl80211_ops[] (L18297) |
| static const struct genl_small_ops nl80211_small_ops[] (L18309)|
+------------------------------------------------------------------+
| cfg80211_ops 回调
+------------------------------------------------------------------+
| cfg80211 通用配置层 |
| net/wireless/core.h |
| struct cfg80211_registered_device (cfg80211_rdev) (L31) |
| struct cfg80211_internal_bss (L186) |
| struct wiphy (无线物理设备抽象) |
+------------------------------------------------------------------+
| ieee80211_ops 回调 (mac80211_config_ops)
+------------------------------------------------------------------+
| mac80211 中间协议层 |
| net/mac80211/ieee80211_i.h |
| struct ieee80211_local (全局状态) (L1409) |
| struct ieee80211_sub_if_data (虚拟接口 sdata) (L1119) |
| struct txq_info / ieee80211_txq (发送队列) (L957) |
+------------------------------------------------------------------+
| ieee80211_ops (驱动回调集合)
+------------------------------------------------------------------+
| 硬件驱动层 |
| ath9k / iwlwifi / mt76 / rtw88 等驱动 |
| struct ieee80211_hw (驱动可见的硬件抽象) |
+------------------------------------------------------------------+
| 物理层 PHY (射频/基带)
+------------------------------------------------------------------+
| 无线硬件 |
+------------------------------------------------------------------+
这种分层设计的好处在于:驱动开发者只需实现 ieee80211_ops 中的硬件相关操作(如 TX/RX DMA、信道切换等),而复杂的 802.11 协议状态机(关联、认证、分片、加密、速率控制等)由 mac80211 统一实现。
net/wireless/core.h 第 31 行定义了 cfg80211 中最核心的设备结构:
struct cfg80211_registered_device {
const struct cfg80211_ops *ops; // 操作回调集合(驱动/mac80211实现)
struct list_head list; // 全局 rdev 链表节点
// rfkill 射频开关支持
struct rfkill_ops rfkill_ops;
struct work_struct rfkill_block;
// 监管域(regdomain)管理
char country_ie_alpha2[2];
const struct ieee80211_regdomain *requested_regd;
enum environment_cap env;
int wiphy_idx; // wiphy 索引(系统唯一)
int devlist_generation, wdev_id; // 接口列表版本号
// BSS 数据库(扫描结果缓存)
spinlock_t bss_lock;
struct list_head bss_list;
struct rb_root bss_tree; // 按 BSSID+频率 索引的红黑树
u32 bss_generation;
struct cfg80211_scan_request_int *scan_req; // 当前扫描请求
// 扫描完成工作项
struct wiphy_work scan_done_wk;
/* 必须最后,wiphy_priv() 依赖偏移量计算 */
struct wiphy wiphy __aligned(NETDEV_ALIGN); // L126
};wiphy_to_rdev() 函数(L130)使用 container_of 从 wiphy 指针反向获取 cfg80211_registered_device:
static inline struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
{
BUG_ON(!wiphy);
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}BSS(基本服务集)是扫描结果的基本单元,net/wireless/core.h 第 186 行定义:
struct cfg80211_internal_bss {
struct list_head list; // 链接到 bss_list
struct list_head hidden_list; // 隐藏 SSID 相关链表
struct rb_node rbn; // 红黑树节点(用于快速查找)
unsigned long ts; // 时间戳
unsigned long refcount; // 引用计数
atomic_t hold; // 防止提前释放
u64 parent_tsf; // 父 BSS 的 TSF 时间戳
// ...
};BSS 数据库以红黑树组织(bss_tree),支持按频率和 BSSID 快速查找,同时用 bss_list 链表支持遍历。
net/mac80211/ieee80211_i.h 第 1409 行定义了 mac80211 层最重要的结构,它代表一个物理无线设备的完整状态:
struct ieee80211_local {
/* 驱动可见部分,必须在首位 */
struct ieee80211_hw hw; // L1413
/* AQL(Airtime Queue Limits)公平调度 */
struct fq fq; // Fair Queue
struct codel_vars *cvars; // CoDel 变量
struct codel_params cparams; // CoDel 参数
/* 每 AC 的 TXQ 调度锁和活跃队列列表 */
spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; // L1420
struct list_head active_txqs[IEEE80211_NUM_ACS];
u16 schedule_round[IEEE80211_NUM_ACS];
/* AQL 全局统计 */
u16 airtime_flags;
u32 aql_txq_limit_low[IEEE80211_NUM_ACS]; // L1428
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
atomic_t aql_total_pending_airtime;
const struct ieee80211_ops *ops; // 驱动操作集
/* 扫描状态 */
unsigned long scanning; // L1562,scan flags 位图
struct wiphy_delayed_work scan_work; // L1581
struct ieee80211_sub_if_data __rcu *scan_sdata; // 发起扫描的接口
/* STA 数据库(哈希表 + 链表) */
struct list_head sta_list; // L1535
struct rhltable sta_hash; // L1536
struct rhltable link_sta_hash; // L1537(MLO 链路 STA)
/* 速率控制算法 */
struct rate_control_ref *rate_ctrl; // L1550
/* 电源管理 */
struct ieee80211_sub_if_data *ps_sdata; // L1640,当前 PS 接口
struct wiphy_work dynamic_ps_enable_work; // L1641
struct wiphy_work dynamic_ps_disable_work; // L1642
struct timer_list dynamic_ps_timer; // L1643
int dynamic_ps_forced_timeout; // L1651
// ...
};每个 wlan0、wlan0-1 等接口对应一个 ieee80211_sub_if_data(简称 sdata),定义于 ieee80211_i.h 第 1119 行:
struct ieee80211_sub_if_data {
struct list_head list; // 链接到 local->interfaces
struct wireless_dev wdev; // cfg80211 无线设备(必须在前)
struct net_device *dev; // Linux 网络设备
struct ieee80211_local *local; // 指向物理设备状态
/* 加密 */
struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS]; // L1149
struct ieee80211_key __rcu *default_unicast_key;
/* 控制端口(EAPOL 路径) */
__be16 control_port_protocol; // L1154,通常为 0x888E (EAPOL)
bool control_port_no_encrypt; // L1155
bool control_port_over_nl80211; // L1157,EAPOL 走 nl80211 上报
/* QoS 映射 */
struct mac80211_qos_map __rcu *qos_map; // L1160
/* 工作队列项和帧队列 */
struct wiphy_work work; // L1162
struct sk_buff_head skb_queue; // L1163(RX 帧暂存队列)
struct sk_buff_head status_queue; // L1164(TX 状态帧队列)
/* 接口类型特定状态 */
union {
struct ieee80211_if_ap ap; // AP 模式
struct ieee80211_if_vlan vlan; // VLAN 模式
struct ieee80211_if_managed mgd; // STA managed 模式
struct ieee80211_if_ibss ibss; // IBSS/Ad-Hoc 模式
struct ieee80211_if_mesh mesh; // Mesh 模式
struct ieee80211_if_ocb ocb; // OCB 模式(V2X)
struct ieee80211_if_mntr mntr; // monitor 模式
struct ieee80211_if_nan nan; // NAN 模式
} u; // L1186~1195
/* MLO(多链路操作)支持 */
struct ieee80211_link_data deflink; // L1197,默认(单链路)link
struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; // L1198
/* 必须在最后:driver 通过 ieee80211_vif 访问 sdata */
struct ieee80211_vif vif; // L1218
};从 vif 反向获取 sdata 的方法(L1222):
static inline struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
{
return container_of(p, struct ieee80211_sub_if_data, vif);
}STA 关联状态机的核心,定义于 ieee80211_i.h 第 521 行:
struct ieee80211_if_managed {
struct timer_list timer; // 重传/超时定时器
struct timer_list conn_mon_timer; // 连接监控定时器(NULL帧探测)
struct timer_list bcn_mon_timer; // Beacon 监控定时器
/* 各种工作项 */
struct wiphy_work monitor_work; // 连接状态监控
struct wiphy_work beacon_connection_loss_work; // Beacon 丢失处理
/* 关联状态 */
unsigned long beacon_timeout;
unsigned long probe_timeout;
u8 connection_loss:1,
driver_disconnect:1,
reconnect:1,
associated:1; // L533~536
/* 认证/关联数据(进行中) */
struct ieee80211_mgd_auth_data *auth_data; // L538
struct ieee80211_mgd_assoc_data *assoc_data; // L539
bool powersave; // L543,用户请求省电
bool broken_ap; // L544,问题 AP,关闭 PS
/* U-APSD 配置 */
unsigned int uapsd_queues; // L565,已启用 U-APSD 的 AC
unsigned int uapsd_max_sp_len; // L572,最大 SP 长度
/* RSSI 阈值监控 */
int rssi_min_thold, rssi_max_thold; // L581
/* 能力协商缓存 */
struct ieee80211_ht_cap ht_capa; // L583
struct ieee80211_ht_cap ht_capa_mask; // L584
struct ieee80211_vht_cap vht_capa; // L585
// ...
};每个 STA 的每个 TID,以及每个虚拟接口,都有一个 TXQ(发送队列),定义于 ieee80211_i.h 第 957 行:
struct txq_info {
struct fq_tin tin; // Fair Queue TIN(流量整形入口)
struct codel_vars def_cvars; // 默认流的 CoDel 变量
struct codel_stats cstats; // CoDel 统计
u16 schedule_round; // 调度轮次(防止无限循环)
struct list_head schedule_order; // 链接到 local->active_txqs
struct sk_buff_head frags; // 解聚合后的分片暂存
unsigned long flags; // TXQ 标志(停止/AMPDU/AMSDU 等)
/* 驱动可见部分必须在最后 */
struct ieee80211_txq txq; // L970,暴露给驱动的结构
};ieee80211_txq 是 ieee80211_hw.h 中面向驱动的公开结构,包含 vif、sta、tid、ac 等字段,驱动通过 ieee80211_tx_dequeue() 从中取帧。
TXQ 初始化路径(tx.c L1505):
void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct txq_info *txqi, int tid)
{
fq_tin_init(&txqi->tin);
codel_vars_init(&txqi->def_cvars);
codel_stats_init(&txqi->cstats);
__skb_queue_head_init(&txqi->frags);
INIT_LIST_HEAD(&txqi->schedule_order);
txqi->txq.vif = &sdata->vif;
// 根据 tid 计算 AC(Access Category)
if (tid == IEEE80211_NUM_TIDS) {
txqi->txq.ac = IEEE80211_AC_VO; // 管理帧 MMPDU TXQ
} else {
txqi->txq.ac = ieee80211_ac_from_tid(tid);
}
// ...
}sta_info.h 中定义的 sta_info 是 mac80211 内部对一个无线站点(对端)的完整描述,其中包含大量重要子结构:
站点状态标志(sta_info.h L78):
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH, // 已认证
WLAN_STA_ASSOC, // 已关联
WLAN_STA_PS_STA, // 处于省电模式
WLAN_STA_AUTHORIZED, // 已授权(802.1X 通过)
WLAN_STA_MFP, // 管理帧保护
WLAN_STA_BLOCK_BA, // 禁止 BA 会话(挂起/恢复期间)
WLAN_STA_PS_DRIVER, // 驱动要求保持 PS 状态
WLAN_STA_PSPOLL, // 站点在 PS 时发送了 PS-Poll
WLAN_STA_UAPSD, // U-APSD 相关
WLAN_STA_RATE_CONTROL, // 速率控制已初始化
WLAN_STA_USES_ENCRYPTION, // 需要加密
// ...
};AMPDU 聚合 MLME 状态(sta_info.h L281):
struct sta_ampdu_mlme {
/* RX 方向 */
struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; // 每 TID 的 RX 重排序缓冲
unsigned long agg_session_valid[...]; // 有效 BA 会话位图
/* TX 方向 */
struct wiphy_work work; // BA 协商工作项
struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; // 每 TID 的 TX AMPDU 会话
u8 addba_req_num[IEEE80211_NUM_TIDS]; // 每 TID 的 ADDBA 发送次数
u8 dialog_token_allocator; // 对话令牌分配器
};Airtime 公平性信息(sta_info.h L138):
struct airtime_info {
u64 rx_airtime; // 已用 RX 空口时间(微秒)
u64 tx_airtime; // 已用 TX 空口时间
unsigned long last_active; // 最后活跃时间(jiffies)
s32 deficit; // 公平调度赤字
atomic_t aql_tx_pending; // 待发送帧估计空口时间
u32 aql_limit_low; // AQL 低水位
u32 aql_limit_high; // AQL 高水位
};nl80211 使用 Linux 的 Generic Netlink(GENL)框架,注册了名为 nl80211 的协议族。net/wireless/nl80211.c 第 40 行:
static struct genl_family nl80211_fam;nl80211 注册了多个多播组,用于内核向用户空间推送事件(nl80211.c L43~63):
enum nl80211_multicast_groups {
NL80211_MCGRP_CONFIG, // 配置变更(接口、密钥等)
NL80211_MCGRP_SCAN, // 扫描结果通知
NL80211_MCGRP_REGULATORY, // 监管域变更
NL80211_MCGRP_MLME, // MLME 事件(关联/解关联/漫游等)
NL80211_MCGRP_VENDOR, // 厂商自定义事件
NL80211_MCGRP_NAN, // NAN(邻居感知网络)事件
NL80211_MCGRP_TESTMODE // 测试模式
};所有 nl80211 命令通过 nl80211_ops[](L18297)和 nl80211_small_ops[](L18309)两张表分发。nl80211_small_ops 是大型命令表,部分典型命令:
NL80211_CMD_SET_WIPHY -> nl80211_set_wiphy 设置 wiphy 参数
NL80211_CMD_GET_INTERFACE -> nl80211_get_interface 获取接口信息
NL80211_CMD_NEW_INTERFACE -> nl80211_new_interface 创建新接口
NL80211_CMD_NEW_KEY -> nl80211_new_key 安装加密密钥
NL80211_CMD_START_AP -> nl80211_start_ap 启动 AP
NL80211_CMD_TRIGGER_SCAN -> nl80211_trigger_scan 触发扫描
NL80211_CMD_CONNECT -> nl80211_connect 发起连接
NL80211_CMD_ASSOCIATE -> nl80211_associate 发起关联
NL80211_CMD_AUTHENTICATE -> nl80211_authenticate 发起认证
用户空间通过 NL80211_ATTR_IFINDEX 或 NL80211_ATTR_WDEV 指定操作目标,__cfg80211_rdev_from_attrs()(nl80211.c L140)负责解析:
// nl80211.c L180~199
if (attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
netdev = __dev_get_by_index(netns, ifindex);
if (netdev) {
if (netdev->ieee80211_ptr)
tmp = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
// ...
}
}每个无线网络设备的 net_device->ieee80211_ptr 指向 struct wireless_dev,后者的 wiphy 字段指向物理设备,从而联系到 cfg80211_registered_device。
| 类型 | 说明 |
|---|---|
| 硬件扫描 (HW scan) | 驱动实现 hw_scan 回调,硬件自主完成 |
| 软件扫描 (SW scan) | mac80211 控制信道切换,发送 Probe Request |
| 被动扫描 (Passive) | 只监听 Beacon,不发 Probe Request |
| 主动扫描 (Active) | 发送 Probe Request,等待 Probe Response |
扫描延时常量(scan.c L28~30):
#define IEEE80211_PROBE_DELAY (HZ / 33) // ~30ms,发送 Probe 前等待
#define IEEE80211_CHANNEL_TIME (HZ / 33) // ~30ms,主动扫描单信道时间
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9) // ~111ms,被动扫描单信道时间ieee80211_scan_work(scan.c L1095)是核心扫描工作函数,在 local->scan_work delayed work 中执行。扫描状态机通过 local->next_scan_state 驱动(ieee80211_i.h L1398):
enum mac80211_scan_state {
SCAN_DECISION, // 决策:继续扫描还是返回工作信道
SCAN_SET_CHANNEL, // 切换到待扫描信道
SCAN_SEND_PROBE, // 发送 Probe Request(主动扫描)
SCAN_SUSPEND, // 暂停扫描,回到工作信道发数据
SCAN_RESUME, // 恢复扫描
SCAN_ABORT, // 中止扫描
};扫描状态机流程:
nl80211 TRIGGER_SCAN 命令
|
v
__ieee80211_start_scan() (scan.c L731)
|
+-- HW scan: drv_hw_scan() -> 驱动自主完成
| |
| v ieee80211_scan_completed() (L540)
|
+-- SW scan: ieee80211_start_sw_scan() (L557)
|
v
ieee80211_scan_work() (L1095) [delayed work]
|
v
+---> SCAN_DECISION
| |
| +-- 还有信道? -> SCAN_SET_CHANNEL
| | |
| | v 切换信道、等待
| |
| +-- no IR/radar? -> 被动扫描
| | 监听 Beacon -> SCAN_BEACON_WAIT
| | 收到 Beacon -> SCAN_BEACON_DONE -> SCAN_SEND_PROBE
| |
| v 主动扫描
| SCAN_SEND_PROBE
| |
| v ieee80211_send_scan_probe_req() (L660)
| | 发送 Probe Request(所有 SSID)
| |
+<-- SCAN_DECISION (等待 Probe Response)
|
所有信道完成
|
v
__ieee80211_scan_completed() (L448)
|
cfg80211_scan_done() -> nl80211 通知用户空间
scan.c L302~310 展示了这一关键逻辑:
if (test_and_clear_bit(SCAN_BEACON_WAIT, &local->scanning)) {
/*
* we were passive scanning because of radar/no-IR, but
* the beacon/proberesp rx gives us an opportunity to upgrade
* to active scan
*/
set_bit(SCAN_BEACON_DONE, &local->scanning);
wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
}在雷达频段(DFS)或禁止主动发射(no-IR)信道上,mac80211 先以被动模式等待 Beacon,一旦收到 Beacon(证明该信道已有 AP 使用),立即升级为主动扫描模式。
ieee80211_scan_rx() 函数(scan.c L277)处理收到的 Beacon 和 Probe Response:
void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
{
// 仅处理 Probe Response 和 Beacon
if (!ieee80211_is_probe_resp(mgmt->frame_control) &&
!ieee80211_is_beacon(mgmt->frame_control) && ...)
return;
// 验证 DA(Probe Response 需匹配本机地址)
// ...
// 更新 BSS 数据库
bss = ieee80211_bss_info_update(local, rx_status, mgmt, skb->len, channel);
// ...
}ieee80211_bss_info_update()(scan.c L167)调用 cfg80211_inform_bss_frame_data(),将 BSS 信息上报给 cfg80211 层缓存。
[未关联]
|
| NL80211_CMD_AUTHENTICATE / nl80211_authenticate
v
[认证中]
| 发送 Auth Request (Open System 或 SAE)
| 收到 Auth Response
v
[已认证]
|
| NL80211_CMD_ASSOCIATE / nl80211_associate
v
[关联中]
| 发送 Association Request (含 RSN IE、HT Caps 等)
| 收到 Association Response
v
[已关联] (ieee80211_if_managed.associated = 1)
|
| WPA2/3: 四次握手 (EAPOL)
v
[已授权] (WLAN_STA_AUTHORIZED 标志置位)
|
| 正常数据传输
v
[连接正常]
EAPOL(EAP Over LAN,EtherType 0x888E)是 WPA/WPA2/WPA3 四次握手的承载协议,在内核中有特殊处理路径:
控制端口(Control Port)机制:
ieee80211_sub_if_data 中的字段(ieee80211_i.h L1154~1157):
__be16 control_port_protocol; // 通常 = htons(ETH_P_PAE) = 0x888E
bool control_port_no_encrypt; // EAPOL 帧是否加密
bool control_port_no_preauth; // 是否跳过预认证
bool control_port_over_nl80211; // EAPOL 是否通过 nl80211 上报TX 路径中的 EAPOL 处理:
EAPOL 帧在关联完成之前必须能够发出(否则四次握手无法进行),ieee80211_tx_h_check_assoc()(tx.c L270)专门处理这种情况:在软件扫描期间只允许 Probe Request 和 null function 帧,但控制端口帧(EAPOL)有例外路径。
RX 路径中的 EAPOL 处理:
收到 EAPOL 帧时,mac80211 的 RX 路径会检查 control_port_over_nl80211 标志:
- 若设置:通过 nl80211 的
NL80211_CMD_CONTROL_PORT_FRAME上报给用户空间(wpa_supplicant) - 若未设置:作为普通 802.3 帧递交给网络栈,wpa_supplicant 通过 PF_PACKET socket 接收
四次握手(以 WPA2-PSK 为例):
STA (wpa_supplicant) AP (hostapd)
| |
| <--- EAPOL-Key (Message 1) | ANonce
| |
| PTK = PRF(PMK, ANonce, |
| SNonce, MACs) |
| |
| EAPOL-Key (Message 2) ---> | SNonce + MIC
| |
| <--- EAPOL-Key (Message 3) | GTK(加密) + MIC
| |
| EAPOL-Key (Message 4) ---> | 确认
| |
| [安装 PTK/GTK 密钥] |
| NL80211_CMD_NEW_KEY |
| 内核: ieee80211_key_alloc() |
四次握手完成后,wpa_supplicant 调用 nl80211 的 NL80211_CMD_NEW_KEY 将协商好的 PTK(Pairwise Transient Key)和 GTK(Group Temporal Key)安装到内核,此后数据帧由内核硬件加密引擎处理。
应用层数据 (TCP/UDP)
|
v (网络栈)
ieee80211_subif_start_xmit() [net_device->ndo_start_xmit]
|
v
ieee80211_xmit() (tx.c L2023)
|
v
ieee80211_tx() (tx.c L1934)
|
+-- ieee80211_tx_prepare() // 初始化 tx_data 结构
|
+-- invoke_tx_handlers_early() // 早期处理器链(加密、PS 缓冲等)
| |
| +-- ieee80211_tx_h_dynamic_ps() (L193) 动态省电
| +-- ieee80211_tx_h_check_assoc() (L270) 关联状态检查
| +-- ieee80211_tx_h_multicast_ps_buf() (L381) 组播 PS 缓冲
| +-- ieee80211_tx_h_encrypt() 加密处理
|
+-- ieee80211_queue_skb() // 入队到 TXQ(软件队列)
| |
| v ieee80211_txq_enqueue() (L1442)
| fq_tin_enqueue() -> CoDel/FQ 队列
|
+-- invoke_tx_handlers_late() // 晚期处理器链
|
+-- __ieee80211_tx() (L1746)
|
v
drv_tx() // 调用驱动的 .tx 回调
ieee80211_txq_enqueue()(tx.c L1442)将帧放入 Fair Queue:
static void ieee80211_txq_enqueue(struct ieee80211_local *local,
struct txq_info *txqi,
struct sk_buff *skb)
{
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;
u32 flow_idx;
ieee80211_set_skb_enqueue_time(skb); // 记录入队时间(CoDel 使用)
spin_lock_bh(&fq->lock);
/* 管理帧不走 CoDel,直接入 frags 队列 */
if (unlikely(txqi->txq.tid == IEEE80211_NUM_TIDS)) {
__skb_queue_tail(&txqi->frags, skb);
} else {
flow_idx = fq_flow_idx(fq, skb); // 按五元组哈希计算流索引
fq_tin_enqueue(fq, tin, flow_idx, skb, fq_skb_free_func);
}
spin_unlock_bh(&fq->lock);
}出队通过 fq_tin_dequeue_func()(tx.c L1400)实现 CoDel(Controlled Delay)算法:
static struct sk_buff *fq_tin_dequeue_func(struct fq *fq,
struct fq_tin *tin,
struct fq_flow *flow)
{
// ...
return codel_dequeue(txqi,
&flow->backlog,
cparams,
cvars,
cstats,
codel_skb_len_func,
codel_skb_time_func,
codel_drop_func, // 超时丢包
codel_dequeue_func);
}CoDel 通过测量帧在队列中的驻留时间(sojourn time)来判断拥塞,目标是将排队延迟控制在 5ms 以内。
AQL(Airtime Queue Limits)是 mac80211 实现 Airtime Fairness 的机制。ieee80211_local 中(ieee80211_i.h L1427~1432):
u16 airtime_flags; // 开关标志
u32 aql_txq_limit_low[IEEE80211_NUM_ACS]; // 每 AC 低水位限制
u32 aql_txq_limit_high[IEEE80211_NUM_ACS]; // 每 AC 高水位限制
u32 aql_threshold; // 总体阈值
atomic_t aql_total_pending_airtime; // 全局待发空口时间
atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS]; // 每 AC 待发空口时间airtime_info 结构(sta_info.h L138)跟踪每个站点的空口时间使用情况:
struct airtime_info {
u64 rx_airtime; // 已消耗 RX 空口时间
u64 tx_airtime; // 已消耗 TX 空口时间
unsigned long last_active; // 最后活跃时间
s32 deficit; // 公平调度赤字(负值表示欠账)
atomic_t aql_tx_pending; // 当前在 HW 队列中的估计空口时间
};调度活跃性检查(ieee80211_i.h L105~112):
static inline bool
ieee80211_sta_keep_active(struct sta_info *sta, u8 ac)
{
/* 如果站点在过去 100ms 内有活动,保持其在 active list */
return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
}ieee80211_tx_h_dynamic_ps()(tx.c L193)在每次 TX 时检查省电状态:
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
{
// U-APSD VO 队列:不唤醒省电(由触发帧机制处理)
if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) &&
(ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) &&
skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO)
return TX_CONTINUE;
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
// 停止所有队列(等待硬件退出省电)
ieee80211_stop_queues_by_reason(&local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_PS,
false);
// 触发动态 PS 禁用工作项
wiphy_work_queue(local->hw.wiphy,
&local->dynamic_ps_disable_work);
}
// 重置动态 PS 定时器
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
return TX_CONTINUE;
} 硬件中断 (DMA 完成)
|
v
驱动调用 ieee80211_rx_list() / ieee80211_rx_napi() (rx.c L5435/L5598)
|
v
ieee80211_rx_list()
| 验证: band/rate、suspend状态、started状态
|
v
ieee80211_rx_monitor() (L846) // 复制给 monitor 接口
|
v
ieee80211_prepare_rx_handoff()
|
v
ieee80211_rx_reorder_ampdu() (L1445) // AMPDU 重排序
|
v
ieee80211_rx_handlers() (L4195) // RX 处理器链
|
+-- ieee80211_rx_h_check_dup() 去重检查
+-- ieee80211_rx_h_decrypt() 解密
+-- ieee80211_rx_h_check_more_data() PS 相关
+-- ieee80211_rx_h_sta_process() STA 信息更新
+-- ieee80211_rx_h_defragment() 分片重组
+-- ieee80211_rx_h_michael_mic_verify() TKIP MIC 校验
+-- ieee80211_rx_h_amsdu() A-MSDU 解聚合
+-- ieee80211_rx_h_data() 数据帧递交
+-- ieee80211_rx_h_ctrl() 控制帧处理
+-- ieee80211_rx_h_mgmt() 管理帧处理
|
v
netif_receive_skb() / cfg80211 上报 / nl80211 事件
ieee80211_rx_data(ieee80211_i.h L221)贯穿 RX 处理链的上下文:
struct ieee80211_rx_data {
struct list_head *list; // 输出帧链表
struct sk_buff *skb; // 当前处理帧
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata; // 接收接口
struct ieee80211_link_data *link; // MLO 链路
struct sta_info *sta; // 发送站点
struct link_sta_info *link_sta; // MLO 链路 STA
struct ieee80211_key *key; // 使用的密钥
unsigned int flags; // IEEE80211_RX_AMSDU 等
int seqno_idx; // 序列号数组索引(0..16,16=non-QoS)
int security_idx; // 安全 PN 数组索引
int link_id; // MLO 链路 ID(-1 表示无效)
union {
struct {
u32 iv32;
u16 iv16;
} tkip;
struct {
u8 pn[IEEE80211_CCMP_PN_LEN];
} ccm_gcm;
}; // 解密相关(TKIP/CCMP 重放保护)
};ieee80211_rx_reorder_ampdu()(rx.c L1445)处理 A-MPDU 接收侧的乱序帧:
static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
struct sk_buff_head *frames)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
u8 tid = ieee80211_get_tid(hdr);
// 获取该 TID 的 RX BA 会话
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx) {
// 无 BA 会话,收到 BlockAck 策略帧则发 DelBA
if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
!test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
ieee80211_send_delba(...);
goto dont_reorder;
}
// 刷新会话超时定时器
if (tid_agg_rx->timeout)
tid_agg_rx->last_rx = jiffies;
// 分片帧不入重排序缓冲
if (sc & IEEE80211_SCTL_FRAG) {
ieee80211_queue_skb_to_iface(...);
return;
}
// 进入重排序缓冲区
if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb, frames))
return;
dont_reorder:
__skb_queue_tail(frames, skb);
}重排序缓冲区 tid_ampdu_rx 结构(sta_info.h L238):
struct tid_ampdu_rx {
struct sk_buff_head *reorder_buf; // 重排序缓冲数组(按序列号索引)
unsigned long *reorder_time; // 每槽入队时间(用于超时释放)
u16 head_seq_num; // 当前窗口头部序列号
u16 stored_mpdu_num; // 缓冲中的 MPDU 数量
u16 ssn; // Starting Sequence Number
u16 buf_size; // 重排序窗口大小(通常 64)
u16 timeout; // 超时值(TU)
struct timer_list reorder_timer; // 超时释放定时器
// ...
};AMPDU 重排序工作原理:
接收到 AMPDU 中的 MPDU(可能乱序到达):
序列号窗口: [head_seq_num, head_seq_num + buf_size)
[64][65][__][67][68][__][70]...
^ <- head_seq_num(等待 SN=66 到来)
若 SN=66 到来: 窗口向前推进,释放 64、65、66、67、68
若超时: reorder_timer 触发,强制释放窗口头部积压的帧
ieee80211_rx_monitor()(rx.c L846)将原始帧(含 radiotap 头)复制给所有 monitor 接口:
static struct sk_buff *
ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
struct ieee80211_rate *rate)
{
// 遍历所有 monitor 接口
list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
// 检查信道匹配
chandef = &sdata->vif.bss_conf.chanreq.oper;
if (chandef->chan &&
chandef->chan->center_freq != status->freq)
continue;
// 克隆并投递给 monitor 接口
skb = skb_clone(monskb, GFP_ATOMIC);
skb->dev = prev_sdata->dev;
netif_receive_skb(skb);
}
// 返回清理后的 skb(去掉 radiotap/FCS)
return ieee80211_clean_skb(origskb, present_fcs_len, rtap_space);
}minstrel_ht 是 mac80211 默认的自适应速率控制算法(Adaptive Rate Control,ARC),由 Felix Fietkau 开发。它通过统计每个速率的成功/失败次数,计算期望吞吐量,动态选择最优发送速率。
相关文件:net/mac80211/rc80211_minstrel_ht.h 和 rc80211_minstrel_ht.c。
minstrel_ht 将所有可用速率组织成"分组"(groups),每个分组对应一种 MCS/流数/带宽/GI 组合(rc80211_minstrel_ht.h L43~58):
#define MINSTREL_MAX_STREAMS 4 // 最多 4 空间流
#define MINSTREL_HT_STREAM_GROUPS 4 // HT: BW(20/40) x SGI(on/off)
#define MINSTREL_VHT_STREAM_GROUPS 6 // VHT: BW(20/40/80) x SGI(on/off)
#define MINSTREL_HT_GROUPS_NB (4 * 4) // 16 个 HT 分组
#define MINSTREL_VHT_GROUPS_NB (4 * 6) // 24 个 VHT 分组
#define MINSTREL_LEGACY_GROUPS_NB 2 // CCK + OFDM 遗留分组
#define MINSTREL_GROUPS_NB (16 + 24 + 2) // 共 42 个速率分组每个分组最多 10 个速率(MCS_GROUP_RATES = 10,对应 MCS0~MCS9)。
每个速率的统计(rc80211_minstrel_ht.h L110):
struct minstrel_rate_stats {
u16 attempts, last_attempts; // 当前/上次采样周期尝试次数
u16 success, last_success; // 当前/上次采样周期成功次数
u32 att_hist, succ_hist; // 历史总计数
u16 prob_avg; // 成功率移动平均(EWMA)
u16 prob_avg_1; // 辅助 EWMA
u8 retry_count; // 最大重传次数
u8 retry_count_rtscts; // 使用 RTS/CTS 时的重传次数
};每站点的 minstrel_ht 状态(rc80211_minstrel_ht.h L154):
struct minstrel_ht_sta {
struct ieee80211_sta *sta;
/* AMPDU 长度统计(用于计算聚合效率) */
unsigned int ampdu_len;
unsigned int ampdu_packets;
unsigned int avg_ampdu_len; // EWMA 平均 AMPDU 长度
/* 全局最优速率 */
u16 max_tp_rate[MAX_THR_RATES]; // 最高吞吐量速率(前 4 个)
u16 max_prob_rate; // 最高成功率速率(备用路径)
unsigned long last_stats_update; // 上次统计更新时间
/* 帧开销(微秒)*/
unsigned int overhead; // 普通帧开销
unsigned int overhead_rtscts; // RTS/CTS 帧开销
u16 sample_rate; // 当前采样速率
unsigned long sample_time; // 采样时间
/* 支持的 MCS 位图 */
u16 supported[MINSTREL_GROUPS_NB]; // L192,每分组支持的速率位图
/* 每分组统计数据 */
struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; // L195
};minstrel_ht 维护两类最优速率:
-
max_tp_rate(最高吞吐量):期望吞吐量 = 成功率 x 速率 x AMPDU 平均长度最高的速率,用于正常数据发送。
-
max_prob_rate(最高成功率):作为低成功率环境的保底选择,保证基本可达性。
期望吞吐量计算(rc80211_minstrel_ht.c 中的 minstrel_ht_get_tp_avg(),对应头文件 L199):
int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
int prob_avg);计算公式近似为:
tp_avg = prob_avg * bitrate * avg_ampdu_len / overhead
EWMA 参数(rc80211_minstrel_ht.h L20~21):
#define EWMA_LEVEL 96 // EWMA 权重系数(分母 EWMA_DIV=128)
#define EWMA_DIV 128采样周期(L73):
#define MINSTREL_SAMPLE_RATES 5 // 每次采样的速率数
#define MINSTREL_SAMPLE_INTERVAL (HZ / 50) // 20ms 采样间隔采样类型(L129~134):
enum minstrel_sample_type {
MINSTREL_SAMPLE_TYPE_INC, // 递增采样(相邻速率)
MINSTREL_SAMPLE_TYPE_JUMP, // 跳跃采样(随机高速率)
MINSTREL_SAMPLE_TYPE_SLOW, // 慢速采样(低成功率速率验证)
__MINSTREL_SAMPLE_TYPE_MAX
};速率控制算法通过 rate_control_ops 接口与 mac80211 交互(rate.h L25~37):
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_tx_rate_control *txrc);
void rate_control_tx_status(struct ieee80211_local *local,
struct ieee80211_tx_status *st);
void rate_control_rate_init(struct link_sta_info *link_sta);
void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_supported_band *sband,
struct link_sta_info *link_sta,
u32 changed);minstrel 注册(rate.h L98~108):
#ifdef CONFIG_MAC80211_RC_MINSTREL
int rc80211_minstrel_init(void);
void rc80211_minstrel_exit(void);
#endif| 机制 | 说明 | 适用场景 |
|---|---|---|
| PS-Poll | STA 发 PS-Poll 取回单帧 | 传统省电 |
| U-APSD | 触发帧触发 SP(服务周期),批量取回 | WMM 语音/视频 |
| Dynamic SMPS | 动态调整 MIMO 接收链数 | 减少 RF 功耗 |
当 STA 进入省电模式(设置 frame control 中的 Power Management 位),AP 会缓冲发往该 STA 的帧。AP 在 Beacon 的 TIM(Traffic Indication Map)中标记哪些 AID 有缓冲帧。STA 收到 Beacon 后若发现自己的 AID 在 TIM 中,则发送 PS-Poll 控制帧取回缓冲数据。
AP 侧缓冲管理(ieee80211_i.h L315~326):
struct ps_data {
/* TIM 位图(按 AID 索引) */
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]
__aligned(__alignof__(unsigned long));
struct sk_buff_head bc_buf; // 组播/广播缓冲队列
atomic_t num_sta_ps; // 处于 PS 的 STA 数量
int dtim_count; // DTIM 计数器
bool dtim_bc_mc; // DTIM 期间投递组播
};缓冲帧限制常量(ieee80211_i.h L46~52):
/* AP 模式下组播/广播最大缓冲帧数 */
#define AP_MAX_BC_BUFFER 128
/* 所有 STA 总缓冲帧数上限(每帧约 2KB,512帧 = 约 1MB) */
#define TOTAL_MAX_TX_BUFFER 512U-APSD 通过"触发帧"机制让 STA 控制服务周期:STA 发送一个 QoS Data 帧(作为触发帧),AP 收到后启动服务周期,将所有缓冲帧批量发送给 STA。
U-APSD 配置(ieee80211_i.h L78):
#define IEEE80211_DEFAULT_UAPSD_QUEUES 0 // 默认不启用 U-APSD注释(L60~77)解释了为何默认关闭 U-APSD:某些 AP(如 Netgear R7000)会静默重分类数据帧到不同 AC,导致非 U-APSD AC 的帧被 AP 缓冲,STA 永远收不到(如 ARP 响应)。
U-APSD 最大 SP 长度(ieee80211_i.h L80~81):
#define IEEE80211_DEFAULT_MAX_SP_LEN \
IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL // 每次服务周期不限制帧数动态省电在无数据发送时自动进入省电模式,有数据时立即唤醒。
控制流程(tx.c L193 的 ieee80211_tx_h_dynamic_ps()):
有 TX 帧到来
|
+-- 检查驱动是否支持 SUPPORTS_PS
+-- 检查是否在扫描(扫描期间不省电)
+-- 若硬件已在 PS 状态 (IEEE80211_CONF_PS)
| 停止所有 HW 队列
| 提交 dynamic_ps_disable_work(向驱动发 conf 关闭 PS)
|
+-- 重置动态 PS 定时器(dynamic_ps_timeout ms 后超时)
|
动态 PS 定时器超时
|
v
dynamic_ps_enable_work
|
v
向驱动提交 IEEE80211_CONF_PS 标志(进入省电)
动态 PS 相关字段(ieee80211_local,ieee80211_i.h L1641~1651):
struct wiphy_work dynamic_ps_enable_work; // 启用 PS 工作项
struct wiphy_work dynamic_ps_disable_work; // 禁用 PS 工作项
struct timer_list dynamic_ps_timer; // 定时器
int dynamic_ps_forced_timeout; // 用户通过 WEXT 强制设置的超时(ms)SMPS 通过减少活跃接收天线数来节省功耗。动态 SMPS 根据流量动态切换。
SMPS 模式管理(ieee80211_link_data_managed,ieee80211_i.h L1001~1003):
enum ieee80211_smps_mode req_smps, // 请求的 SMPS 模式
driver_smps_mode; // 实际驱动中的 SMPS 模式SMPS 重配工作项(L1025~1027):
struct wiphy_work request_smps_work; // 用户发起 SMPS 变更
struct wiphy_work recalc_smps; // 重新计算 SMPS(硬件重配)状态类型定义(ieee80211_i.h L1097):
enum ieee80211_smps_mode smps_mode; // link_data 中当前生效的 SMPS 模式mac80211 区分不同的连接模式以支持从 Legacy 到最新的 IEEE 802.11be(EHT/UHR)(ieee80211_i.h L390~407):
enum ieee80211_conn_mode {
IEEE80211_CONN_MODE_S1G, // IEEE 802.11ah(低功耗 IoT)
IEEE80211_CONN_MODE_LEGACY, // 802.11a/b/g
IEEE80211_CONN_MODE_HT, // 802.11n(HT)
IEEE80211_CONN_MODE_VHT, // 802.11ac(VHT)
IEEE80211_CONN_MODE_HE, // 802.11ax(HE/Wi-Fi 6)
IEEE80211_CONN_MODE_EHT, // 802.11be(EHT/Wi-Fi 7)
IEEE80211_CONN_MODE_UHR, // Ultra High Reliability
};
enum ieee80211_conn_bw_limit {
IEEE80211_CONN_BW_LIMIT_20, // 20 MHz
IEEE80211_CONN_BW_LIMIT_40, // 40 MHz
IEEE80211_CONN_BW_LIMIT_80, // 80 MHz
IEEE80211_CONN_BW_LIMIT_160, // 160 MHz(也包括 80+80)
IEEE80211_CONN_BW_LIMIT_320, // 320 MHz(EHT 新增)
};MAC 层帧发送队列的停止是多路由的,由 queue_stop_reason 枚举管理(ieee80211_i.h L1325~1339):
enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_DRIVER, // 驱动请求停止
IEEE80211_QUEUE_STOP_REASON_PS, // 省电模式
IEEE80211_QUEUE_STOP_REASON_CSA, // 信道切换通告
IEEE80211_QUEUE_STOP_REASON_AGGREGATION, // AMPDU 聚合建立
IEEE80211_QUEUE_STOP_REASON_SUSPEND, // 系统挂起
IEEE80211_QUEUE_STOP_REASON_SKB_ADD, // 内部帧入队
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, // 离信道操作(扫描等)
IEEE80211_QUEUE_STOP_REASON_FLUSH, // 队列刷新
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, // TDLS 拆链
IEEE80211_QUEUE_STOP_REASON_RESERVE_TID, // TID 预留
IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE, // 接口类型变更
};对应的 queue_stop_reasons 字段(ieee80211_local,ieee80211_i.h L1442~1443):
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; // 每队列停止原因位图
int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS]; // 引用计数多个停止原因可同时存在(以位图记录),只有当所有停止原因都清除后,队列才能恢复发送。
802.11be(Wi-Fi 7)引入了多链路操作(MLO),允许设备同时在多条链路(如 2.4GHz + 5GHz + 6GHz)上并发收发。mac80211 对此有专门支持:
多链路接口结构(ieee80211_sub_if_data,ieee80211_i.h L1197~1198):
struct ieee80211_link_data deflink; // 默认链路(单链路设备用此)
struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; // MLO 链路数组链路数据结构(ieee80211_link_data,ieee80211_i.h L1066):
struct ieee80211_link_data {
struct ieee80211_sub_if_data *sdata;
unsigned int link_id; // 链路 ID
/* 每链路加密密钥 */
struct ieee80211_key __rcu *gtk[...];
/* 每链路信道管理 */
struct ieee80211_chanctx *reserved_chanctx;
struct ieee80211_chan_req reserved;
/* 每链路 SMPS 状态 */
enum ieee80211_smps_mode smps_mode;
/* 每链路功率 */
int user_power_level; // dBm
int ap_power_level; // dBm
/* DFS 定时器 */
struct wiphy_hrtimer_work dfs_cac_timer_work;
/* 每链路 AC 参数 */
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct ieee80211_bss_conf *conf; // 链路的 BSS 配置
// ...
};扫描时对 MLO 的处理(scan.c L206~227):
if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
if (rx_status->link_valid) {
s8 link_id = rx_status->link_id;
link_conf = rcu_dereference(scan_sdata->vif.link_conf[link_id]);
}
} else {
link_conf = &scan_sdata->vif.bss_conf;
}用户进程 write(socket)
|
v [内核网络栈]
ip_output() / udp_sendmsg()
|
v
dev_queue_xmit()
|
v [TC 流量控制,如有]
ieee80211_subif_start_xmit() [net_device->ndo_start_xmit]
|
v
ieee80211_xmit() [tx.c:2023, 添加 802.11 头]
|
v
ieee80211_tx() [tx.c:1934]
|
+-- TX handlers (early):
| - 动态 PS 检查
| - 关联状态验证
| - 组播 PS 缓冲
| - 速率选择 (rate_control_get_rate)
|
+-- ieee80211_queue_skb() [入 TXQ]
| |
| v
| ieee80211_txq_enqueue() [tx.c:1442]
| |
| v
| fq_tin_enqueue() [Fair Queue / CoDel]
|
+-- TX handlers (late):
| - 加密 (ieee80211_tx_h_encrypt)
| - 帧头完成
|
+-- __ieee80211_tx() [tx.c:1746]
|
v
drv_tx() [调用驱动 .tx 回调]
|
v
[硬件 DMA 发送]
|
v [发送完成中断]
drv_tx_status() [速率控制反馈]
|
v
rate_control_tx_status() [minstrel 更新统计]
[硬件 DMA 完成中断]
|
v [驱动填写 ieee80211_rx_status]
ieee80211_rx_list() [rx.c:5435]
|
+-- 验证 band/rate
+-- 挂起/重配 检查
|
v
ieee80211_rx_monitor() [rx.c:846, 分发给 monitor]
|
v
ieee80211_rx_reorder_ampdu() [rx.c:1445, AMPDU 重排序]
|
v
ieee80211_rx_handlers() [rx.c:4195]
|
+-- 去重检查 (seqno)
+-- 解密 (AES-CCMP/GCMP/TKIP)
+-- PN 重放保护
+-- STA 信息更新 (RSSI/信号统计)
+-- 分片重组
+-- A-MSDU 解聚合
|
+-- 数据帧:
| ieee80211_rx_h_data() -> netif_receive_skb()
|
+-- 管理帧:
| ieee80211_rx_h_mgmt()
| |
| +-- Beacon: 更新 BSS/DTIM/TIM
| +-- Auth/Assoc/Disassoc: MLME 状态机
| +-- Action: Block Ack / TDLS / Spectrum Mgmt
|
+-- 控制帧:
PS-Poll -> 释放缓冲帧
mac80211 和 cfg80211 提供了丰富的 debugfs 接口,挂载于 /sys/kernel/debug/ieee80211/:
/sys/kernel/debug/ieee80211/
├── phy0/
│ ├── rc/
│ │ ├── name # 速率控制算法名称
│ │ └── fixed_rate_idx # 强制固定速率(调试用)
│ ├── stations/
│ │ └── <mac>/
│ │ ├── rc_stats # minstrel 速率统计
│ │ ├── ampdu_stats # AMPDU 聚合统计
│ │ └── airtime # Airtime 使用情况
│ ├── queues # 发送队列状态
│ ├── ps # 省电模式状态
│ └── scan # 当前扫描状态
速率控制 debugfs 注册(rate.h L67~86):
static inline void rate_control_add_debugfs(struct ieee80211_local *local)
{
debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
local->debugfs.rcdir = debugfsdir;
debugfs_create_file("name", 0400, debugfsdir,
local->rate_ctrl, &rcname_ops);
local->rate_ctrl->ops->add_debugfs(&local->hw, local->rate_ctrl->priv,
debugfsdir);
}| API | 文件与行号 | 用途 |
|---|---|---|
ieee80211_rx_list() |
rx.c:5435 | 驱动提交接收帧(批量) |
ieee80211_rx_napi() |
rx.c:5598 | 驱动提交接收帧(NAPI) |
ieee80211_rx_irqsafe() |
rx.c:5628 | 中断上下文提交接收帧 |
ieee80211_scan_completed() |
scan.c:540 | 驱动通告 HW 扫描完成 |
ieee80211_scan_work() |
scan.c:1095 | 软件扫描工作函数 |
ieee80211_txq_init() |
tx.c:1505 | 初始化 TXQ |
ieee80211_txq_enqueue() |
tx.c:1442 | 帧入队到 TXQ/FQ |
ieee80211_tx() |
tx.c:1934 | TX 主路径 |
rate_control_get_rate() |
rate.h:25 | 选择发送速率 |
rate_control_tx_status() |
rate.h:29 | 更新发送结果统计 |
cfg80211_scan_done() |
core.h | 通知用户空间扫描完成 |
ieee80211_sta_update_pending_airtime() |
sta_info.h:148 | 更新 AQL 空口时间 |
ieee80211_rx_reorder_ampdu() |
rx.c:1445 | AMPDU 重排序入口 |
ieee80211_scan_rx() |
scan.c:277 | 处理扫描收到的 Beacon/Probe Response |
ieee80211_bss_info_update() |
scan.c:167 | 更新 BSS 数据库 |
mac80211/cfg80211
|
+-- Regulatory(监管域)
| net/wireless/reg.c
| 管理信道允许功率、DFS 规则
|
+-- rfkill(射频开关)
| net/rfkill/
| 软/硬件射频开关集成(cfg80211_rdev.rfkill_ops)
|
+-- PM(电源管理)
| WoWLAN(Wake-on-Wireless-LAN)支持
| drivers/net/wireless/ 驱动层
|
+-- Netfilter/nftables
| 无线帧进入网络栈后走标准 netfilter 路径
|
+-- TC(流量控制)
| ieee80211_subif_start_xmit 之前经过 TC egress
|
+-- genetlink / netlink
| nl80211 基于 Generic Netlink (GENL) 框架
|
+-- crypto 子系统
| AES/CCMP/GCMP 加解密通过 kernel crypto API
|
+-- LED 子系统
TX/RX/assoc LED 触发(ieee80211_i.h L1591~1595)
tx_led / rx_led / assoc_led / radio_led
A-MPDU(Aggregated MAC Protocol Data Unit)是 IEEE 802.11n 引入的帧聚合机制,将多个 MPDU 合并在一个 PPDU 中发送,显著提升吞吐量(尤其是短帧场景)。
TX 聚合会话的建立由 ieee80211_start_tx_ba_session()(agg-tx.c L600)发起:
int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
u16 timeout)
{
// 前置条件检查
if (!pubsta->valid_links &&
!pubsta->deflink.ht_cap.ht_supported &&
!pubsta->deflink.vht_cap.vht_supported &&
!pubsta->deflink.he_cap.has_he &&
!pubsta->deflink.eht_cap.has_eht &&
!pubsta->deflink.s1g_cap.s1g)
return -EINVAL; // 对端不支持任何高吞吐率标准
if (!ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION) ||
ieee80211_hw_check(&local->hw, TX_AMPDU_SETUP_IN_HW))
return -EINVAL;
// 限速:超过最大重试次数后拒绝建立
if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES)
return -EBUSY;
// ...
}完整握手流程:
发起方 (Initiator / STA) 接收方 (Recipient / AP)
| |
| ieee80211_start_tx_ba_session() |
| drv_ampdu_action() |
| IEEE80211_AMPDU_TX_START |
| |
| --- ADDBA Request Frame -------> | action_code = WLAN_ACTION_ADDBA_REQ
| dialog_token | agg_size, timeout, start_seq_num
| capab: AMSDU|POLICY|TID|BUF |
| |
| [addba_resp_timer 启动,超时则停] |
| |
| <-- ADDBA Response Frame ------- | capab, status (0=Accept)
| |
| ieee80211_start_tx_ba_cb() |
| drv_ampdu_action() |
| IEEE80211_AMPDU_TX_OPERATIONAL |
| |
| [正式开始 A-MPDU 聚合发送] |
| HT_AGG_STATE_OPERATIONAL 置位 |
| |
| 定期发送 BlockAck Request (BAR) |
| 来同步接收方窗口 |
在建立聚合会话期间,对应 TID 的队列会被停止(防止乱序),由 ieee80211_agg_stop_txq() 和 ieee80211_agg_start_txq() 管理(agg-tx.c L181~225):
// 停止 TXQ:设置 IEEE80211_TXQ_STOP 标志
static void ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
{
struct ieee80211_txq *txq = sta->sta.txq[tid];
struct txq_info *txqi = to_txq_info(txq);
spin_lock_bh(&fq->lock);
set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
spin_unlock_bh(&fq->lock);
}
// 启动 TXQ:清除停止标志,若 enable=true 则额外设置 AMPDU 标志
static void ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
{
if (enable)
set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
else
clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
schedule_and_wake_txq(sta->sdata->local, txqi);
}当需要重置接收方序列号窗口时,发送 BAR(Block Ack Request)帧(agg-tx.c L103~131):
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
{
struct sk_buff *skb;
struct ieee80211_bar *bar;
u16 bar_control = 0;
bar = skb_put_zero(skb, sizeof(*bar));
bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
IEEE80211_STYPE_BACK_REQ);
bar_control |= IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
bar_control |= IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT);
bar->control = cpu_to_le16(bar_control);
bar->start_seq_num = cpu_to_le16(ssn);
// BAR 帧不加密,需要 TX 状态反馈
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_REQ_TX_STATUS;
}RX 侧的聚合由对端发起 ADDBA Request 触发,在 __ieee80211_stop_rx_ba_session()(agg-rx.c L58~115)中处理会话拆除:
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason, bool tx)
{
// 通知驱动停止 RX BA 会话
params.action = IEEE80211_AMPDU_RX_STOP;
if (drv_ampdu_action(local, sta->sdata, ¶ms))
sdata_info(...);
// 如果是本地发起的停止,向对端发 DelBA
if (initiator == WLAN_BACK_RECIPIENT && tx)
ieee80211_send_delba(sta->sdata, sta->sta.addr,
tid, WLAN_BACK_RECIPIENT, reason);
// 停止重排序定时器
timer_delete_sync(&tid_rx->session_timer);
spin_lock_bh(&tid_rx->reorder_lock);
tid_rx->removed = true;
spin_unlock_bh(&tid_rx->reorder_lock);
timer_delete_sync(&tid_rx->reorder_timer);
// RCU 延迟释放
call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
}A-MSDU(Aggregated MAC Service Data Unit)是另一种聚合方式,将多个 MSDU 封装在一个 MPDU 中,减少帧头开销。与 A-MPDU 不同,A-MSDU 在 MPDU 层面,不需要 BlockAck 协商。
A-MSDU 大小限制由 HE 6GHz 能力控制(he.c L42~56):
switch (le16_get_bits(he_6ghz_capa->capa,
IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
break;
default:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
}A-MPDU 与 A-MSDU 对比:
A-MSDU 结构(单个 MPDU 内):
+----802.11 Header----+----MSDU1----+----MSDU2----+----MSDU3----+
| QoS Data Frame | LLC/SNAP+data | LLC/SNAP+data | data |
+---------------------+-------------+-------------+-----------+
^ ^
A-MSDU 子帧1 A-MSDU 子帧N
A-MPDU 结构(单个 PPDU 内):
+--MPDU1--+--MPDU2--+--MPDU3--+--MPDU4--+
| MPDU1 | MPDU2 | MPDU3 | MPDU4 | <- 每个 MPDU 独立有 802.11 头
+DELIMITER+DELIMITER+DELIMITER+DELIMITER+ <- 4字节分隔符(EOF/CRC/padding)
A-MPDU Delimiter (4字节): EOF(1bit)|RSVD(1bit)|MPDU Length(12bit)|CRC(8bit)|EOF(8bit)
对比:
A-MSDU: 减少 802.11 帧头开销,但一个错误导致整个 MPDU 丢弃
A-MPDU: 每个 MPDU 独立 CRC,部分失败可选择性重传(需 BlockAck)
最优方案: A-MPDU of A-MSDU(在每个 MPDU 内再嵌套 A-MSDU)
mac80211 支持以下加密套件,定义于 include/linux/ieee80211.h:
| 套件 | 标识符 | 用途 | 密钥长度 |
|---|---|---|---|
| WEP-40 | WLAN_CIPHER_SUITE_WEP40 | 已废弃 | 40-bit |
| WEP-104 | WLAN_CIPHER_SUITE_WEP104 | 已废弃 | 104-bit |
| TKIP | WLAN_CIPHER_SUITE_TKIP | WPA | 128-bit |
| CCMP-128 | WLAN_CIPHER_SUITE_CCMP | WPA2/3 | 128-bit |
| CCMP-256 | WLAN_CIPHER_SUITE_CCMP_256 | WPA3 | 256-bit |
| GCMP-128 | WLAN_CIPHER_SUITE_GCMP | WPA3 6GHz | 128-bit |
| GCMP-256 | WLAN_CIPHER_SUITE_GCMP_256 | WPA3 6GHz | 256-bit |
| AES-CMAC | WLAN_CIPHER_SUITE_AES_CMAC | 管理帧保护 BIP | 128-bit |
| BIP-GMAC-128 | WLAN_CIPHER_SUITE_BIP_GMAC_128 | 管理帧保护 | 128-bit |
| BIP-GMAC-256 | WLAN_CIPHER_SUITE_BIP_GMAC_256 | 管理帧保护 | 256-bit |
密钥在 mac80211 中以 ieee80211_key 结构表示,包含硬件加速的判断逻辑(key.c L120~226)。
密钥的生命周期:
wpa_supplicant 四次握手完成
|
v NL80211_CMD_NEW_KEY
nl80211_new_key()
|
v
cfg80211_ops->add_key()
|
v
ieee80211_add_key() [cfg.c]
|
v
ieee80211_key_alloc() // 分配 ieee80211_key 结构
|
v
ieee80211_key_enable_hw_accel() [key.c:120]
|
+-- drv_set_key(SET_KEY) -> 驱动尝试硬件加速
|
+-- 若硬件不支持 -> 软件加密路径
| key->flags 不含 KEY_FLAG_UPLOADED_TO_HARDWARE
|
v
RCU 安装到 sdata->keys[] 或 sta->ptk[]
|
v
[数据传输阶段] TX: ieee80211_tx_h_encrypt() / RX: ieee80211_rx_h_decrypt()
|
v
NL80211_CMD_DEL_KEY
|
v
ieee80211_key_disable_hw_accel() [key.c:228]
|
v drv_set_key(DISABLE_KEY)
kfree_rcu(key)
ieee80211_key_enable_hw_accel()(key.c L120)实现了硬件/软件加密的仲裁:
static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
ret = drv_set_key(key->local, SET_KEY, sdata,
sta ? &sta->sta : NULL, &key->conf);
if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
// 硬件处理加密,无需软件 tailroom
if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC | ...)))
decrease_tailroom_need_count(sdata, 1);
return 0;
}
out_unsupported:
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
// ...
// 支持软件加密的算法:降级到软件
if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL))
return -EINVAL; // 驱动不允许降级
return 0; // 允许软件加密
default:
return -EINVAL; // 不支持该算法
}
}tailroom 计数(key.c L75~107)用于管理 SKB 尾部空间的分配:当有软件加密密钥时,TX 路径在分配 SKB 时需要额外预留 MIC/ICV 空间。
CCMP 是 WPA2 的核心加密算法,使用 AES 计数模式(CTR)加密 + CBC-MAC 认证(CCM 模式)。
加密流程(wpa.c L379~450 的 ccmp_encrypt_skb()):
CCMP 帧格式:
802.11 头 CCMP 头(8B) 加密载荷 MIC(8B)
[FC|Dur|Addr] [PN0~5|KeyID] [Encrypted MSDU] [MIC]
^IV/PN字段^ ^AES-CTR加密^ ^CBC-MAC^
CCMP Header 格式(wpa.c L402~412 的 ccmp_pn2hdr()):
static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
{
hdr[0] = pn[5]; // PN0
hdr[1] = pn[4]; // PN1
hdr[2] = 0; // Reserved
hdr[3] = 0x20 | (key_id << 6); // ExtIV=1, KeyID
hdr[4] = pn[3]; // PN2
hdr[5] = pn[2]; // PN3
hdr[6] = pn[1]; // PN4
hdr[7] = pn[0]; // PN5
}AAD 构建(wpa.c L315~377):
CCMP 的 AAD(Additional Authentication Data)包含 802.11 帧头的部分字段,用于防止帧头被篡改:
static u8 ccmp_gcmp_aad(struct sk_buff *skb, u8 *aad, bool spp_amsdu)
{
// AAD = FC(masked) | A1 | A2 | A3 | SC(seq masked) | [A4] | [QoS]
mask_fc = hdr->frame_control;
mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | // 屏蔽重传位
IEEE80211_FCTL_PM |
IEEE80211_FCTL_MOREDATA);
mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); // 设置 Protected
// 序列号控制:保留分片号,清除序列号
aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f; // 只保留 Frag#
aad[23] = 0;
// ...
}PN(Packet Number)防重放保护:每个密钥维护一个单调递增的 48-bit PN,接收方检查 PN 是否大于上次收到的 PN(ieee80211_rx_data.ccm_gcm.pn)。
TKIP(Temporal Key Integrity Protocol)是 WPA 的过渡加密方案,在 WEP 基础上添加了:
- Michael MIC(消息完整性码)
- IV 序列化(防重放)
- 每包密钥混合(TKIP Key Mixing)
TX MIC 添加(wpa.c L30~91 的 ieee80211_tx_h_michael_mic_add()):
ieee80211_tx_result
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
{
key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
michael_mic(key, hdr, data, data_len, mic); // 计算 8B MIC
// 追加到帧尾
mic = skb_put(skb, MICHAEL_MIC_LEN);
}RX MIC 校验(wpa.c L94~192 的 ieee80211_rx_h_michael_mic_verify()):
若 MIC 验证失败,调用 cfg80211_michael_mic_failure() 通知用户空间,触发 WPA 对策机制(60s 内再次失败则断开连接)。
TKIP 加密(wpa.c L194~245 的 tkip_encrypt_skb()):
static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
// 递增 PN(48-bit,分为 iv32:iv16 两部分)
pn = atomic64_inc_return(&key->conf.tx_pn);
pos = ieee80211_tkip_add_iv(pos, &key->conf, pn);
// 若是软件加密,追加 ICV(4字节 CRC32)
skb_put(skb, IEEE80211_TKIP_ICV_LEN);
return ieee80211_tkip_encrypt_data(&tx->local->wep_tx_ctx,
key, skb, pos, len);
}WPA3 支持 Extended Key ID,允许在不中断流量的情况下更换 PTK,避免重新关联。key.c L290~320 中的 ieee80211_pairwise_rekey() 处理密钥轮换:
static void ieee80211_pairwise_rekey(struct ieee80211_key *old,
struct ieee80211_key *new)
{
if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) {
// Extended Key ID 重新密钥
if (sta->ptk_idx != INVALID_PTK_KEYIDX &&
!ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) {
// 不支持 AMPDU Key Border 的驱动必须停止所有聚合会话
// 避免 A-MPDU 中混用不同 KeyID 的帧
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
__ieee80211_stop_tx_ba_session(sta, i,
AGG_STOP_LOCAL_REQUEST);
}
}
}mac80211 通过 ieee80211_chanctx 结构管理信道使用,支持多接口复用同一信道:
信道上下文分配(chan.c L830~904):
ieee80211_new_chanctx()
|
v ieee80211_alloc_chanctx() 分配并初始化 chanctx 结构
| ctx->conf.def = chanreq->oper // 设置信道定义
| ctx->conf.radar_enabled = false
| ctx->radar_detected = false
|
v ieee80211_add_chanctx()
| drv_add_chanctx(local, ctx) // 通知驱动
|
v list_add_rcu(&ctx->list, &local->chanctx_list)
信道上下文迭代器(chan.c L109~133):
// 遍历所有已分配(assigned)到某 chanctx 的接口+链路
#define for_each_chanctx_user_assigned(local, ctx, iter)
// 遍历所有预留(reserved)的
#define for_each_chanctx_user_reserved(local, ctx, iter)
// 遍历全部
#define for_each_chanctx_user_all(local, ctx, iter)ieee80211_chanreq_compatible()(chan.c L222~240)检查两个信道请求是否兼容(可共用一个 chanctx),基于 cfg80211_chandef_compatible() 的物理兼容性(40MHz 与 20MHz 子信道兼容等)。
DFS 是 5GHz 信道上的强制要求(IEEE 802.11h),用于检测雷达信号并避免干扰航空雷达。
DFS 状态机:
信道请求使用 5GHz DFS 信道(如 U-NII-2)
|
v
CAC(Channel Availability Check)开始
| ieee80211_link_data.dfs_cac_timer_work 启动
| cfg80211_cac_event(NL80211_RADAR_CAC_STARTED)
|
v [CAC 期间只接收,不发送任何帧]
| CAC 持续时间(通常 60 秒)
|
+-- 超时(无雷达)-> 信道可用
| cfg80211_cac_event(NL80211_RADAR_CAC_FINISHED)
| 正常使用该信道
|
+-- 检测到雷达 -> 立即停止使用
ieee80211_radar_detected() [util.c:3664]
|
v 标记所有使用该信道的 chanctx
ieee80211_radar_mark_chan_ctx_iterator()
|
v 调度雷达处理工作
wiphy_work_queue(&local->radar_detected_work)
|
v ieee80211_dfs_radar_detected_work() [util.c:3620]
| 取消所有 CAC
| cfg80211_radar_event() -> 通知用户空间
|
v wpa_supplicant/hostapd 切换到其他信道
雷达检测接口(util.c L3664~3676):
void ieee80211_radar_detected(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef)
{
trace_api_radar_detected(local);
// 原子地标记所有使用该信道的 chanctx
ieee80211_iter_chan_contexts_atomic(hw,
ieee80211_radar_mark_chan_ctx_iterator, chandef);
// 调度工作队列处理(不能在中断上下文中直接处理)
wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
}
EXPORT_SYMBOL(ieee80211_radar_detected);CAC 取消(util.c L3581~3618):
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
list_for_each_entry(sdata, &local->interfaces, list) {
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
// 取消 CAC 定时器
wiphy_hrtimer_work_cancel(local->hw.wiphy,
&link->dfs_cac_timer_work);
if (!sdata->wdev.links[link_id].cac_started)
continue;
// 释放信道,通知 CAC 被中止
ieee80211_link_release_channel(link);
cfg80211_cac_event(sdata->dev, &chandef,
NL80211_RADAR_CAC_ABORTED,
GFP_KERNEL, link_id);
}
}
}CSA(Channel Switch Announcement)允许 AP 提前通知所有关联的 STA 即将进行的信道切换,减少数据丢失。
队列停止原因中包含 IEEE80211_QUEUE_STOP_REASON_CSA,在 CSA 期间停止发送数据帧,等待所有 STA 完成切换。
信道切换参数在 ieee80211_bss_conf 中(ieee80211_i.h L690~700 附近):
bool csa_active; // CSA 进行中
bool color_change_active; // BSS 颜色变更进行中(HE)
u8 csa_count_offs; // Beacon 中 CSA Count 字段偏移802.11ax(High Efficiency,HE)相较于 802.11ac 主要带来以下增强:
802.11ax (Wi-Fi 6) 核心技术:
+----------------------------------------------------------+
| OFDMA (多用户正交频分多址) |
| DL: AP 同时向多个 STA 分配不同 RU 发送 |
| UL: 多个 STA 同时向 AP 发送(需要触发帧协调) |
+----------------------------------------------------------+
| MU-MIMO (多用户 MIMO) |
| DL MU-MIMO: AP 同时向最多 8 个 STA 发送 |
| UL MU-MIMO: 多 STA 同时上传(802.11ax 新增) |
+----------------------------------------------------------+
| BSS Coloring (BSS 着色) |
| 为每个 BSS 分配 1-63 的颜色值 |
| STA 可快速判断帧是否来自本 BSS,减少不必要的退避 |
+----------------------------------------------------------+
| TWT (Target Wake Time,目标唤醒时间) |
| STA 与 AP 协商精确唤醒时间,大幅节省 IoT 设备电量 |
+----------------------------------------------------------+
| 1024-QAM (额外 25% 吞吐量提升) |
| 更高效的 LDPC 编码 |
+----------------------------------------------------------+
| 空间复用(Spatial Reuse) |
| BSS 内外颜色区分,允许在检测到弱信号 OBSS 时仍发送 |
+----------------------------------------------------------+
OFDMA 将信道分割为若干个资源单元(RU),AP 通过触发帧(Trigger Frame)协调多用户同时传输。mac80211 中的 OFDMA 相关标志(debugfs_sta.c L762):
PFLAG(MAC, 3, OFDMA_RA, "OFDMA-RA"); // 对端支持 OFDMA 随机访问EHT(802.11be)进一步扩展了 OFDMA 能力(debugfs_sta.c L1164~1173):
PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_80MHZ, "NON-OFDMA-UL-MU-MIMO-80MHZ");
PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_160MHZ, "NON-OFDMA-UL-MU-MIMO-160MHZ");
PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_320MHZ, "NON-OFDMA-UL-MU-MIMO-320MHZ");
PFLAG(PHY, 8, RX_1024QAM_WIDER_BW_DL_OFDMA, "RX-1024QAM-WIDER-BW-DL-OFDMA");
PFLAG(PHY, 8, RX_4096QAM_WIDER_BW_DL_OFDMA, "RX-4096QAM-WIDER-BW-DL-OFDMA");mlme.c 中针对更宽带宽 OFDMA 的特殊处理(mlme.c L1024~1039):
// 驱动可声明 IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW 来忽略
// 仅用于更宽带宽 OFDMA 目的的信道(不作为工作信道对待)
if (sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW)
return false;BSS Coloring 是 802.11ax 引入的空间复用机制,通过在 HE Operation IE 中携带颜色值(1~63),使得 STA 可在物理层快速识别帧的归属 BSS,从而实现更激进的空间复用。
颜色冲突检测(rx.c L3452~3494):
static void
ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
{
// 1. 驱动未使能颜色特性 -> 跳过
if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
NL80211_EXT_FEATURE_BSS_COLOR))
return;
// 2. 硬件自行检测 -> 跳过软件检测
if (ieee80211_hw_check(&rx->local->hw, DETECTS_COLOR_COLLISION))
return;
// 3. CSA/颜色变更进行中 -> 跳过
if (bss_conf->csa_active || bss_conf->color_change_active ||
!bss_conf->he_bss_color.enabled)
return;
// 4. 解析收到的 Beacon 的 HE Operation IE
ie = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ...);
if (ie) {
he_oper = (void *)(ie->data + 1);
color = le32_get_bits(he_oper->he_oper_params,
IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
// 5. 颜色与本 BSS 相同 -> 报告碰撞
if (color == bss_conf->he_bss_color.color)
ieee80211_obss_color_collision_notify(&rx->sdata->vif,
BIT_ULL(color),
bss_conf->link_id);
}
}ieee80211_obss_color_collision_notify() 最终通知 cfg80211,再通过 nl80211 向 hostapd 上报,由 hostapd 决定是否进行颜色变更(Color Change Announcement)。
HE 引入了新的 MCS 表示方式,支持 MCS011(含 1024-QAM),并区分不同空间流数。MCS 交集计算(115 的 he.c L67ieee80211_he_mcs_intersection()):
static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
__le16 *he_own_tx, __le16 *he_peer_tx)
{
// 对每个 NSS(最多 8 流)取 own 和 peer 能力的交集
for (i = 0; i < 8; i++) {
own_rx = (le16_to_cpu(*he_own_rx) >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
peer_rx = (le16_to_cpu(*he_peer_rx) >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
// 取两者中较低的 MCS 支持等级
// IEEE80211_HE_MCS_NOT_SUPPORTED = 3 (最高,表示不支持)
// 0 = MCS 0-7, 1 = MCS 0-9, 2 = MCS 0-11
}
}HE 6GHz 频段能力(he.c L13~57)处理 6GHz 频段特有的 SMPS 模式和最大 MPDU 长度设置。
TWT 允许 STA 与 AP 协商精确的唤醒/睡眠时间表,特别适合 IoT 设备(如智能传感器)。
TWT 在 mac80211 中通过 ieee80211_hw 标志和 nl80211 命令支持,驱动需声明支持后方可使用。
rc80211_minstrel_ht.c L19~50 定义了每个速率分组的发送时间计算宏:
/* 平均 AMPDU 大小:16 个 MPDU */
#define AVG_AMPDU_SIZE 16
/* 平均包大小:1200 字节 */
#define AVG_PKT_SIZE 1200
/* 平均数据包的比特数(含 AMPDU) */
#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
/* 给定每符号比特数时,需要的符号数 */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
/* OFDM 符号时间:SGI = 3.6us,LGI = 4us */
#define MCS_SYMBOL_TIME(sgi, syms) \
(sgi ? \
((syms) * 18000 + 4000) / 5 : /* 3.6us/sym */ \
((syms) * 1000) << 2 /* 4us/sym */ \
)
/* 每包平均发送持续时间(纳秒) */
#define MCS_DURATION(streams, sgi, bps) \
(MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)/* HT 分组排序:HT40 > SGI > #streams */
#define GROUP_IDX(_streams, _sgi, _ht40) \
MINSTREL_HT_GROUP_0 + \
MINSTREL_MAX_STREAMS * 2 * _ht40 + \ // 0 或 8(HT40偏移)
MINSTREL_MAX_STREAMS * _sgi + \ // 0 或 4(SGI偏移)
_streams - 1 // 0~3(1~4流)HT 分组布局(16个组):
GROUP_IDX(1,0,0)=0 : 1流, LGI, HT20 MCS0-7
GROUP_IDX(2,0,0)=1 : 2流, LGI, HT20 MCS8-15
GROUP_IDX(3,0,0)=2 : 3流, LGI, HT20 MCS16-23
GROUP_IDX(4,0,0)=3 : 4流, LGI, HT20 MCS24-31
GROUP_IDX(1,1,0)=4 : 1流, SGI, HT20 MCS0-7
GROUP_IDX(2,1,0)=5 : 2流, SGI, HT20 ...
...
GROUP_IDX(1,0,1)=8 : 1流, LGI, HT40
...
GROUP_IDX(4,1,1)=15 : 4流, SGI, HT40 最高速率
minstrel_ht_set_best_tp_rates()(rc80211_minstrel_ht.c L548~568)维护前 4 名最高吞吐量速率:
遍历所有速率分组的所有速率:
计算 cur_tp_avg = minstrel_ht_get_tp_avg(mi, group, idx, prob_avg)
|
v 按 tp_avg 降序插入 mi->max_tp_rate[0..3] 排序列表
| (保留前 MAX_THR_RATES=4 名)
同时维护:
mi->max_prob_rate: 成功率 > 75% 中吞吐量最高的速率
mg->max_group_prob_rate: 每分组内成功率最高的速率
分组内最优成功率速率选择(rc80211_minstrel_ht.c L573~630 的 minstrel_ht_set_best_prob_rate()):
- 若成功率 > 75%:选吞吐量最高的
- 否则:选成功率最高的(更保守)
ieee80211_hw 是 mac80211 暴露给驱动的硬件抽象,定义于 include/net/mac80211.h:
struct ieee80211_hw {
struct ieee80211_conf conf; // 当前硬件配置(信道、功率、PS 等)
struct wiphy *wiphy; // 指向 wiphy
const char *rate_control_algorithm; // 覆盖默认速率控制算法
void *priv; // 驱动私有数据指针
unsigned long flags[...]; // ieee80211_hw_flags 位图
unsigned long extra_flags[...]; // 扩展标志
/* TX/RX 队列配置 */
u8 queues; // 驱动硬件队列数(通常 4 对应 4 个 AC)
u16 max_rates; // 每帧最多速率重试数
u8 max_rate_tries; // 每速率最多重试次数
u8 max_rx_aggregation_subframes; // RX AMPDU 最大子帧数
u8 max_tx_aggregation_subframes; // TX AMPDU 最大子帧数
/* SKB 空间需求 */
unsigned int extra_tx_headroom; // 驱动需要的额外帧头空间
unsigned int extra_beacon_tailroom; // Beacon 额外尾部空间
/* 网络设备统计 */
int vif_data_size; // 驱动 ieee80211_vif 私有数据大小
int sta_data_size; // 驱动 ieee80211_sta 私有数据大小
int chanctx_data_size; // 驱动信道上下文私有数据大小
netdev_features_t netdev_features; // 网络设备特性(TSO/GSO 等)
u8 uapsd_queues; // 支持 U-APSD 的队列掩码
u8 uapsd_max_sp_len; // 最大 SP 长度
u8 n_cipher_schemes; // 自定义加密方案数量
// ...
};重要硬件标志(ieee80211_hw_flags,include/net/mac80211.h):
IEEE80211_HW_HAS_RATE_CONTROL 驱动实现速率控制(不走 minstrel)
IEEE80211_HW_RX_INCLUDES_FCS RX 帧包含 FCS(驱动不去掉)
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING 主机缓冲 PS 组播帧
IEEE80211_HW_AMPDU_AGGREGATION 驱动/硬件支持 AMPDU 聚合
IEEE80211_HW_SUPPORTS_PS 硬件支持省电模式
IEEE80211_HW_SUPPORTS_DYNAMIC_PS 硬件支持动态省电
IEEE80211_HW_MFP_CAPABLE 硬件支持管理帧保护(802.11w)
IEEE80211_HW_REPORTS_TX_ACK_STATUS TX ACK 状态上报
IEEE80211_HW_SUPPORTS_REORDERING_BUFFER 硬件自行处理 AMPDU 重排序
IEEE80211_HW_WANT_MONITOR_VIF 驱动需要专用 monitor vif
IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT AMPDU 跨密钥边界支持(EKI)
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW AMPDU 建立由硬件处理
ieee80211_ops 是驱动向 mac80211 注册的操作表,分为以下几类:
基本 TX/RX:
struct ieee80211_ops {
// TX:驱动发送帧(软件队列模型用 wake_tx_queue 替代)
void (*tx)(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb);
// 唤醒 TXQ 调度(新式接口,替代 tx)
void (*wake_tx_queue)(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
// ...
};硬件配置:
int (*start)(struct ieee80211_hw *hw); // 开启硬件
void (*stop)(struct ieee80211_hw *hw); // 停止硬件
int (*config)(struct ieee80211_hw *hw, u32 changed); // 配置变更
void (*bss_info_changed)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
u64 changed); // BSS 配置变更接口管理:
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void (*remove_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int (*change_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum nl80211_iftype new_type, bool p2p);STA 管理:
int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd cmd,
struct ieee80211_sta *sta);加密密钥:
int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);扫描:
int (*hw_scan)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *req);
void (*cancel_hw_scan)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);AMPDU 聚合:
int (*ampdu_action)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params);
// params->action 可以是:
// IEEE80211_AMPDU_RX_START/STOP
// IEEE80211_AMPDU_TX_START/STOP/OPERATIONAL信道上下文(多信道支持):
int (*add_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx);
void (*remove_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx);
void (*change_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx,
u32 changed);
int (*assign_vif_chanctx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx);驱动模块加载
|
v 驱动调用 ieee80211_alloc_hw(sizeof_priv, &ops)
| 分配 ieee80211_hw + ieee80211_local 结构
| hw->priv 指向驱动私有数据
|
v 驱动填充 hw->queues, hw->flags, hw->wiphy->bands[] 等
|
v ieee80211_register_hw(hw)
| 注册 wiphy(cfg80211 层)
| 注册 net_device(每个接口)
| 初始化速率控制(rate_control_alloc)
|
v 驱动就绪,等待 ops->start() 调用
|
v 用户空间通过 nl80211 请求关联
| ops->start() -> 硬件上电
| ops->add_interface() -> 创建 VIF
| ops->config() -> 配置信道/功率
| ...
TIM(Traffic Indication Map)的作用:
TIM 是 Beacon 帧中的信息元素(IE),AP 用其告知处于省电模式的 STA 哪些 AID 有缓冲帧待取:
Beacon 帧结构(省电相关部分):
+--Fixed Params--+--SSID IE--+--Rates IE--+--TIM IE--+--HT IE--+...
^
TIM IE 格式:
+--DTIM Count--+--DTIM Period--+--Bitmap Ctrl--+--Virtual Bitmap--+
| 0~(period-1)| 1~255 | BC/MC flag + | AID 位图 |
| | | Bitmap Offset| (最多 251B) |
DTIM(Delivery Traffic Indication Map):
每隔 DTIM period 个 Beacon 周期,AP 发送 DTIM Beacon,此时发送缓冲的组播/广播帧。TIM IE 的 Bitmap Control 字节的最低位(bit0)表示是否有组播/广播缓冲帧。
内核侧 TIM 更新流程:
当有帧被缓冲给处于 PS 的 STA 时:
sta->ps_tx_buf[ac]队列缓冲帧ieee80211_set_tim(sta, true)在 TIM 位图中设置对应 AID- 下次 Beacon 发出时,驱动从
ps_data.tim位图读取并填充 TIM IE
组播/广播帧在 DTIM Beacon 之后立即发送。ps_data.dtim_bc_mc 字段控制此行为(ieee80211_i.h L315~326):
DTIM Beacon 发出
|
v 驱动调用 ieee80211_get_buffered_bc()
| 取出 ps_data.bc_buf 中的组播帧
|
v 逐帧发送(More Data 位置1,最后一帧置0)
|
v 处于 PS 的 STA 在 DTIM 期唤醒
收到帧后可重新进入省电
WMM 为不同 AC 提供差异化 PS 处理,触发 AC 的 QoS 帧同时作为 service period 触发器。
WMM AC 与 U-APSD 的对应关系:
AC_BK (Background) <-> UAPSD Queue 0 (低优先级后台流量)
AC_BE (Best Effort) <-> UAPSD Queue 1 (标准流量)
AC_VI (Video) <-> UAPSD Queue 2 (视频流量)
AC_VO (Voice) <-> UAPSD Queue 3 (语音流量,最高优先级)
IEEE80211_DEFAULT_UAPSD_QUEUES = 0 默认全部禁用,驱动可通过 hw->uapsd_queues 覆盖。
用户进程 write(socket)
|
v [内核网络栈]
ip_output() / udp_sendmsg()
|
v
dev_queue_xmit()
|
v [TC 流量控制,如有]
ieee80211_subif_start_xmit() [net_device->ndo_start_xmit]
|
v
ieee80211_xmit() [tx.c:2023, 添加 802.11 头]
|
v
ieee80211_tx() [tx.c:1934]
|
+-- TX handlers (early):
| - 动态 PS 检查
| - 关联状态验证
| - 组播 PS 缓冲
| - 速率选择 (rate_control_get_rate)
|
+-- ieee80211_queue_skb() [入 TXQ]
| |
| v
| ieee80211_txq_enqueue() [tx.c:1442]
| |
| v
| fq_tin_enqueue() [Fair Queue / CoDel]
|
+-- TX handlers (late):
| - 加密 (ieee80211_tx_h_encrypt)
| - 帧头完成
|
+-- __ieee80211_tx() [tx.c:1746]
|
v
drv_tx() [调用驱动 .tx 回调]
|
v
[硬件 DMA 发送]
|
v [发送完成中断]
drv_tx_status() [速率控制反馈]
|
v
rate_control_tx_status() [minstrel 更新统计]
[硬件 DMA 完成中断]
|
v [驱动填写 ieee80211_rx_status]
ieee80211_rx_list() [rx.c:5435]
|
+-- 验证 band/rate
+-- 挂起/重配 检查
|
v
ieee80211_rx_monitor() [rx.c:846, 分发给 monitor]
|
v
ieee80211_rx_reorder_ampdu() [rx.c:1445, AMPDU 重排序]
|
v
ieee80211_rx_handlers() [rx.c:4195]
|
+-- 去重检查 (seqno)
+-- 解密 (AES-CCMP/GCMP/TKIP)
+-- PN 重放保护
+-- STA 信息更新 (RSSI/信号统计)
+-- 分片重组
+-- A-MSDU 解聚合
|
+-- 数据帧:
| ieee80211_rx_h_data() -> netif_receive_skb()
|
+-- 管理帧:
| ieee80211_rx_h_mgmt()
| |
| +-- Beacon: 更新 BSS/DTIM/TIM
| +-- Auth/Assoc/Disassoc: MLME 状态机
| +-- Action: Block Ack / TDLS / Spectrum Mgmt
| +-- HE Beacon: BSS Coloring 冲突检测
|
+-- 控制帧:
PS-Poll -> 释放缓冲帧
/sys/kernel/debug/ieee80211/
├── phy0/
│ ├── rc/
│ │ ├── name # 速率控制算法名称(如 "minstrel_ht")
│ │ └── fixed_rate_idx # 强制固定速率(-1 表示自动)
│ ├── stations/
│ │ └── aa:bb:cc:dd:ee:ff/
│ │ ├── rc_stats # minstrel_ht 速率统计表
│ │ ├── rc_stats_csv # CSV 格式速率统计
│ │ ├── ampdu_stats # AMPDU 聚合会话统计
│ │ ├── airtime # Airtime 使用情况(TX/RX 微秒)
│ │ ├── aql # AQL 待发空口时间
│ │ ├── flags # STA 标志位(ASSOC/AUTH/PS 等)
│ │ ├── ht_capa # HT 能力
│ │ ├── vht_capa # VHT 能力
│ │ └── he_capa # HE 能力
│ ├── queues # 每队列停止原因和帧计数
│ ├── ps # 省电模式状态(动态 PS 超时等)
│ ├── scan # 当前扫描状态
│ ├── chanctx/ # 信道上下文列表
│ │ └── <n>/
│ │ ├── frequency # 中心频率
│ │ ├── width # 信道带宽
│ │ └── users # 使用此 chanctx 的接口数
│ └── hwflags # 硬件标志位
rc_stats 文件的典型输出格式(来自 rc80211_minstrel_ht_debugfs.c):
type rate throughput ewma prob retry last_attempts last_success
HT20/LGI MCS0 5.8 98.9% 2 1234 1219
HT20/LGI MCS1 11.4 99.0% 2 856 848
...
HT20/SGI MCS7 68.2 91.3% 4 322 294 *T
HT40/SGI MCS7 130.1 88.2% 4 189 167
...
OFDM 54.0Mbps 38.7 96.5% 3 56 54
CCK 11.0Mbps 8.8 99.9% 6 12 12 *P
字段说明:
*T:当前最高吞吐量速率(max_tp_rate[0])*P:当前最高成功率速率(max_prob_rate)ewma prob:EWMA 成功率(minstrel_rate_stats.prob_avg)
# 查看所有无线接口的速率控制算法
cat /sys/kernel/debug/ieee80211/phy0/rc/name
# 强制使用 MCS7 速率(调试用,-1 为自动)
echo 7 > /sys/kernel/debug/ieee80211/phy0/rc/fixed_rate_idx
# 查看关联 STA 的 AMPDU 统计
cat /sys/kernel/debug/ieee80211/phy0/stations/aa:bb:cc:dd:ee:ff/ampdu_stats
# 查看队列状态
cat /sys/kernel/debug/ieee80211/phy0/queues
# 通过 iw 查看 minstrel 统计(封装了 debugfs)
iw dev wlan0 station dump监管域(Regulatory Domain)控制每个国家/地区的信道使用限制,包括最大发射功率、DFS 要求、no-IR 信道等。
net/wireless/reg.c 监管域核心实现
|
+-- cfg80211_get_chan_state() 查询信道状态
| 返回: NL80211_CHAN_NO_HT / HT20 / HT40 / ...
|
+-- regulatory_hint() 驱动或用户上报监管域
| 触发监管域重新计算
|
+-- nl80211_set_reg() 用户空间直接设置监管域
|
+-- ieee80211_check_combinations() 检查信道组合是否符合规定
由 chan.c 中的信道管理调用
rfkill_block 工作项 (cfg80211_registered_device.rfkill_ops)
|
+-- 软 rfkill (写 /sys/class/rfkill/rfkillX/soft)
| cfg80211_rfkill_set_block()
| -> ieee80211_rfkill_poll() -> drv_rfkill_poll()
|
+-- 硬 rfkill (物理开关)
wiphy_rfkill_stop_all()
-> 停止所有接口、断开所有关联
加密算法通过内核 crypto API 实现:
// wpa.c 中 CCMP 加密调用 kernel crypto API
// aes_ccm.h 封装:
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm,
u8 *b_0, u8 *aad, u8 *data, size_t data_len,
u8 *mic, size_t mic_len);
// 密钥安装时分配 tfm(转换对象):
// key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data, key_len, mic_len)GCMP(AES-GCM 模式)通过 aes_gcm.h 类似实现,相比 CCMP 计算效率更高,是 WPA3 6GHz 的标准加密。
| API | 文件与行号 | 用途 |
|---|---|---|
ieee80211_rx_list() |
rx.c:5435 | 驱动提交接收帧(批量) |
ieee80211_rx_napi() |
rx.c:5598 | 驱动提交接收帧(NAPI) |
ieee80211_rx_irqsafe() |
rx.c:5628 | 中断上下文提交接收帧 |
ieee80211_scan_completed() |
scan.c:540 | 驱动通告 HW 扫描完成 |
ieee80211_scan_work() |
scan.c:1095 | 软件扫描工作函数 |
ieee80211_txq_init() |
tx.c:1505 | 初始化 TXQ |
ieee80211_txq_enqueue() |
tx.c:1442 | 帧入队到 TXQ/FQ |
ieee80211_tx() |
tx.c:1934 | TX 主路径 |
rate_control_get_rate() |
rate.h:25 | 选择发送速率 |
rate_control_tx_status() |
rate.h:29 | 更新发送结果统计 |
rate_control_rate_init() |
rate.c:31 | 初始化站点速率控制 |
cfg80211_scan_done() |
core.h | 通知用户空间扫描完成 |
ieee80211_sta_update_pending_airtime() |
sta_info.h:148 | 更新 AQL 空口时间 |
ieee80211_rx_reorder_ampdu() |
rx.c:1445 | AMPDU 重排序入口 |
ieee80211_scan_rx() |
scan.c:277 | 处理扫描收到的 Beacon/Probe Response |
ieee80211_bss_info_update() |
scan.c:167 | 更新 BSS 数据库 |
ieee80211_start_tx_ba_session() |
agg-tx.c:600 | 发起 TX AMPDU 会话 |
ieee80211_stop_tx_ba_session() |
agg-tx.c | 停止 TX AMPDU 会话 |
ieee80211_send_bar() |
agg-tx.c:103 | 发送 BlockAck Request |
__ieee80211_stop_rx_ba_session() |
agg-rx.c:58 | 停止 RX AMPDU 会话 |
ieee80211_key_enable_hw_accel() |
key.c:120 | 尝试硬件加速密钥 |
ieee80211_pairwise_rekey() |
key.c:290 | PTK 密钥轮换处理 |
ieee80211_tx_h_michael_mic_add() |
wpa.c:30 | TKIP TX MIC 添加 |
ieee80211_rx_h_michael_mic_verify() |
wpa.c:94 | TKIP RX MIC 校验 |
ieee80211_crypto_tkip_encrypt() |
wpa.c:249 | TKIP 加密 |
ieee80211_radar_detected() |
util.c:3664 | 驱动报告雷达检测 |
ieee80211_dfs_cac_cancel() |
util.c:3581 | 取消 CAC |
ieee80211_dfs_radar_detected_work() |
util.c:3620 | 雷达检测工作函数 |
ieee80211_rx_check_bss_color_collision() |
rx.c:3452 | BSS 颜色碰撞检测 |
ieee80211_he_mcs_intersection() |
he.c:67 | HE MCS 能力交集 |
ieee80211_new_chanctx() |
chan.c:879 | 创建新信道上下文 |
ieee80211_chanreq_compatible() |
chan.c:222 | 信道请求兼容性检查 |
ieee80211_agg_stop_txq() |
agg-tx.c:181 | 停止 TXQ(聚合建立期) |
ieee80211_agg_start_txq() |
agg-tx.c:202 | 启动 TXQ(聚合完成后) |
Linux mac80211/cfg80211 无线子系统是一个高度模块化的三层架构:
cfg80211 提供设备抽象和统一的用户接口(通过 nl80211 Netlink),维护 BSS 数据库(红黑树 + 链表)、监管域等全局状态,通过 cfg80211_ops 回调向上层框架导出能力。核心结构 cfg80211_registered_device 嵌入 struct wiphy 用于访问和反向查找。
mac80211 实现 802.11 协议的绝大部分逻辑:
- 扫描状态机(被动/主动扫描升级,scan.c 状态机)
- 关联/认证 MLME(ieee80211_if_managed 结构)
- TX 路径(FQ-CoDel 软件队列 + AQL airtime fairness)
- RX 路径(AMPDU 重排序窗口 + 解密 + 分片重组)
- A-MPDU/A-MSDU 聚合(agg-tx.c/agg-rx.c ADDBA/DELBA 握手 + 重排序缓冲)
- 速率控制(minstrel_ht EWMA 自适应,42 个速率分组,HT/VHT/Legacy)
- 加密(CCMP/GCMP AES 认证加密 + TKIP Michael MIC + 硬件加速仲裁)
- 电源管理(Dynamic PS + U-APSD + TIM/DTIM + 动态 SMPS)
- 信道管理(chanctx 信道上下文 + DFS CAC + 雷达检测工作流)
- Wi-Fi 6/7(HE 能力协商 + BSS Coloring + OFDMA + MLO 多链路操作)
驱动层 仅需实现硬件相关操作(DMA、PHY 配置、信道切换),通过 ieee80211_ops 接口与 mac80211 对接,通过 ieee80211_hw 标志声明硬件能力(AMPDU/PS/MFP/硬件加密等),这使 Linux 能以统一的质量支持数十种无线芯片(Intel iwlwifi、Qualcomm ath9k/ath10k/ath11k、MediaTek mt76、Realtek rtw88/rtw89 等),同时持续演进支持最新 Wi-Fi 7(802.11be)的 MLO 多链路操作和 EHT(Extremely High Throughput)特性。
由 Claude Code 分析生成