2、WiFi协议栈架构:OSI模型与WiFi协议栈映射、Linux无线子系统架构、mac80211/cfg80211框架解析
做WiFi适配,第一件事不是看芯片手册,而是搞清楚协议栈长什么样。我见过太多人一上来就追着寄存器啃,结果连数据包在系统里怎么流转都没弄明白。今天咱们就把这个地基打牢。
2.1 OSI模型与WiFi协议栈的映射关系
OSI七层模型,教科书上都讲过。但实际WiFi协议栈只用了下面几层。说白了,WiFi就是物理层+数据链路层的无线化实现。
| OSI层 | WiFi对应实体 | 核心功能 |
|---|---|---|
| 应用层 | 用户程序 (浏览器/SSH等) | 业务数据生成 |
| 传输层 | TCP/UDP (内核协议栈) | 端到端可靠传输 |
| 网络层 | IP (内核协议栈) | 路由与寻址 |
| 数据链路层 | mac80211 + 驱动 | 帧封装、介质访问、重传 |
| 物理层 | 射频芯片 + 天线 | 调制解调、信号收发 |
注意看,数据链路层在WiFi里被拆成了两部分:MAC层管理(mac80211处理)和MAC层硬件相关(驱动处理)。这个拆分是Linux无线子系统的精髓。
关键认知:WiFi的MAC层远比以太网复杂。以太网只管发帧,WiFi还要管信道扫描、关联认证、省电管理、速率选择。这些逻辑如果全塞进驱动,代码会炸掉。所以Linux把公共逻辑抽出来,形成了mac80211框架。
2.2 Linux无线子系统整体架构
我个人习惯把Linux无线子系统看成三层:
- 上层:用户空间工具(iw, wpa_supplicant, hostapd)
- 中层:内核无线框架(cfg80211 + mac80211)
- 底层:硬件驱动(直接操作寄存器)
这三层通过标准的netlink接口和内核API通信。你想想看,如果每一家芯片厂商都自己搞一套用户空间接口,那wpa_supplicant得适配多少种驱动?
实战经验:我在调试某款Realtek芯片时,发现iw命令能扫描但连不上AP。排查到最后,是cfg80211和驱动之间的scan结果回调没实现完整。这种问题,不懂框架结构根本无从下手。
下面这张图,是我每次培训必画的。它把整个数据路径和管理路径都串起来了。
2.3 cfg80211框架详解
cfg80211是Linux无线子系统的策略层。它不关心具体怎么发帧,只关心能不能发、怎么发、发给谁。
它的核心职责包括:
- 监管合规:不同国家信道不同,cfg80211负责查表。我记得有一次客户把设备卖到日本,结果5GHz信道不能用,就是因为regulatory domain没设对。
- 扫描管理:用户发起的扫描请求,由cfg80211拆分成多次硬件扫描,再合并结果返回。
- 连接状态机:关联、认证、4次握手,这些状态迁移都在cfg80211里维护。
- netlink接口:用户空间的iw/wpa_supplicant通过nl80211与cfg80211通信。
避坑指南:我曾经在移植驱动时,发现cfg80211注册的wiphy设备总是创建失败。查了两天才发现是regulatory hint回调没实现,导致内核认为设备不支持任何信道。记住:cfg80211 ops结构体里的每个回调,能实现就实现,别偷懒。
2.4 mac80211框架详解
mac80211是真正的协议实现层。它处理802.11帧的封装、解封装、重传、聚合等逻辑。
我把它分成两个部分:
- 软件MAC (SoftMAC):大部分MAC逻辑由CPU处理,驱动只负责收发原始帧。这是目前主流方案。
- 硬件MAC (HardMAC):MAC逻辑全在固件里,驱动只做配置。比如一些USB网卡。
mac80211给驱动提供了两个关键结构体:
| 结构体 | 作用 | 驱动需要实现 |
|---|---|---|
ieee80211_ops |
驱动回调函数表 | tx、start/stop、config、sta_add/remove等 |
ieee80211_hw |
硬件能力描述 | 填充支持的频段、带宽、队列数等 |
举个实际例子,驱动注册时大概长这样:
static const struct ieee80211_ops my_wifi_ops = {
.tx = my_wifi_tx,
.start = my_wifi_start,
.stop = my_wifi_stop,
.add_interface = my_wifi_add_interface,
.remove_interface= my_wifi_remove_interface,
.config = my_wifi_config,
.bss_info_changed= my_wifi_bss_info_changed,
.sta_state = my_wifi_sta_state,
};
static int my_wifi_probe(struct platform_device *pdev)
{
struct ieee80211_hw *hw;
hw = ieee80211_alloc_hw(sizeof(struct my_wifi_priv),
&my_wifi_ops);
if (!hw)
return -ENOMEM;
// 设置硬件能力
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_AP);
hw->queues = 4;
hw->max_rates = 1;
// 注册到mac80211
return ieee80211_register_hw(hw);
}
个人经验:写驱动时,ieee80211_ops里的回调不要全部实现。比如set_key,如果你的硬件不支持硬件加密,可以返回-ENOTSUPP,mac80211会自动切到软件加密。但tx和start是必须的,否则内核会直接报错。
2.5 数据包在协议栈中的流转
理解了框架,咱们看看一个数据包从网卡到应用的完整路径。嗯,这里要注意,WiFi的数据路径和以太网不太一样。
- 接收方向:硬件收到802.11帧 → 驱动通过
ieee80211_rx()送入mac80211 → mac80211解帧头、做聚合重组 → 转成802.3帧 → 送入内核网络栈 → 最终到socket。 - 发送方向:应用数据 → 内核网络栈 → mac80211封装802.11头 → 驱动通过
ieee80211_tx_info获取控制信息 → 硬件发送。
这里有个坑:WiFi帧头和以太网帧头不一样。mac80211负责做桥接转换。如果你在驱动里直接操作skb,一定要搞清楚当前是802.11格式还是802.3格式。
核心要点:驱动开发者只需要关注ieee80211_rx()和ieee80211_tx()这两个接口。mac80211帮你处理了所有协议细节。但前提是,你得正确填充ieee80211_tx_info结构体,告诉mac80211你的硬件支持什么。
2.6 框架选择:SoftMAC vs HardMAC
最后聊一个实际选型问题。做产品时,到底选SoftMAC还是HardMAC?
| 对比项 | SoftMAC | HardMAC |
|---|---|---|
| CPU负载 | 高(协议处理在CPU) | 低(固件处理) |
| 灵活性 | 高(可修改协议行为) | 低(依赖固件) |
| 调试难度 | 中等(内核代码可跟踪) | 高(固件黑盒) |
| 典型芯片 | ath9k, iwlwifi | brcmfmac, mt76 USB |
| 适用场景 | 开发调试、定制协议 | 量产产品、低功耗设备 |
我个人偏好SoftMAC。虽然CPU负载高一点,但出了问题你能在mac80211里加printk,能单步调试。HardMAC一旦固件崩溃,你只能找芯片原厂要日志,那感觉,就像在黑夜里找一只黑猫。