第4章:WiFi协议栈核心:cfg80211/mac80211框架适配
好,咱们直接进入正题。这一章讲的是WiFi协议栈里最核心的两个框架——cfg80211和mac80211。说实话,很多刚接触高通平台的工程师,一看到这两个名字就头大。我当年也一样,觉得这玩意儿太抽象了。但说白了,它们就是Linux内核里帮你管好WiFi设备的两套标准接口。
4.1 cfg80211:用户态与内核态的桥梁
cfg80211,全称是Configuration 802.11。它的任务很简单:把用户空间发下来的命令,比如「扫描一下周围WiFi」、「连上这个AP」,翻译成驱动能听懂的话。
我习惯把cfg80211理解成一个「调度中心」。它不直接操作硬件,但它定义了所有操作的回调函数。你在驱动里要做的,就是把这些回调填上。
核心数据结构:struct cfg80211_ops
static struct cfg80211_ops qca_wlan_cfg80211_ops = {
.add_virtual_intf = qca_add_vif,
.del_virtual_intf = qca_del_vif,
.change_virtual_intf = qca_change_vif,
.scan = qca_scan,
.connect = qca_connect,
.disconnect = qca_disconnect,
.start_ap = qca_start_ap,
.stop_ap = qca_stop_ap,
// ... 还有几十个回调
};
嗯,这里要注意:不是所有回调你都得实现。比如你的芯片不支持AP模式,那start_ap和stop_ap就可以留空。我在项目中遇到过有人把所有回调都填了个空函数,结果内核报了一堆警告。其实没必要,内核会自己判断。
4.2 mac80211:软件MAC层的核心
mac80211是干嘛的?它负责管理802.11协议中那些「软件能做」的部分。比如管理帧的生成、TX/RX的调度、加密解密等等。
你想想看,如果所有MAC层功能都扔给硬件,那芯片复杂度就上去了。所以高通的做法是:把一部分功能放在软件里,用mac80211来实现。这样既灵活,又降低了硬件成本。
我个人习惯,在移植时重点关注这几个回调:
- tx():发送数据帧的入口
- start() / stop():启动和停止硬件
- config():配置变更,比如信道、功率
- configure_filter():设置硬件过滤规则
避坑指南:我曾经在移植时忽略了configure_filter()的实现,结果导致AP模式下一直接不到Probe Request帧。查了两天才发现,原来是硬件过滤把管理帧全丢了。所以,这个回调千万别偷懒。
4.3 高通WLAN HAL层接口
到了HAL层,事情就变得更有趣了。高通把WLAN HAL设计成了一套统一的接口,用来隔离内核驱动和底层固件。说白了,HAL就是驱动和固件之间的「翻译官」。
高通WLAN HAL的核心是wlan_hdd_main.c这个文件。它负责把cfg80211/mac80211的调用,转换成HAL层的命令。比如:
// cfg80211回调
static int qca_connect(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_connect_params *sme)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
// 转换成HAL命令
hdd_connect(hdd_ctx, sme);
return 0;
}
这里有个关键点:HAL层的数据结构,比如tSirMacAddr、tSirBssDescription,和内核里的结构体往往不一样。你需要写一堆转换函数。我建议你把这些转换函数统一放在一个文件里,别散落在各处,否则后期维护会想哭。
4.4 数据路径(TX/RX)流程
数据路径,说白了就是数据包怎么从网口进来,又怎么发出去。在高通平台上,TX/RX的路径大致是这样的:
TX路径
- 上层协议栈调用
ndo_start_xmit() - 进入mac80211的
ieee80211_subif_start_xmit() - 经过TX调度,调用驱动的
tx()回调 - 驱动把数据打包成HAL层的
tx_desc - 通过PCIe/SDIO等总线发给固件
- 固件最终通过天线发出去
我在项目中遇到过一个问题:TX吞吐量上不去。后来发现是驱动里每次发送都申请了一次DMA缓冲区,开销太大。改成预分配缓冲池后,吞吐量直接翻倍。
RX路径
- 固件收到数据,通过中断通知驱动
- 驱动在中断下半部(tasklet或workqueue)中读取数据
- 解析RX描述符,提取帧信息
- 调用
ieee80211_rx()把数据交给mac80211 - mac80211处理后交给上层协议栈
注意:RX路径中,中断处理一定要快。我见过有人把整个RX处理都放在中断上下文里,结果系统响应变得极慢。正确的做法是:中断里只做最少的处理,比如把数据搬到一个队列里,然后唤醒一个内核线程去处理。
4.5 WMM与QoS配置
WMM(Wi-Fi Multimedia)说白了就是给不同种类的数据包分优先级。比如视频流要优先于网页浏览,语音要优先于文件下载。
在高通平台上,WMM的配置主要在HAL层。你需要设置好每个AC(Access Category)的参数:
| AC | 用途 | 优先级 | 典型CWmin | 典型CWmax |
|---|---|---|---|---|
| AC_VO | 语音 | 最高 | 3 | 7 |
| AC_VI | 视频 | 高 | 7 | 15 |
| AC_BE | 尽力而为 | 普通 | 15 | 63 |
| AC_BK | 背景 | 最低 | 15 | 1023 |
嗯,这里要注意:CWmin和CWmax的值不是随便填的。它们决定了退避算法的行为。CW越小,竞争到信道的机会越大。但如果你把AC_VO的CW设得太小,会导致其他AC几乎抢不到信道。
我曾经在一个项目中,客户反映视频通话时断时续。查了半天,发现是AC_VI的TXOP(传输机会)设得太短,导致视频帧经常被截断。把TXOP从0改成3008微秒后,问题就解决了。
配置建议:在调试WMM时,我习惯先用默认参数跑一遍,然后用iperf打流,观察每个AC的吞吐量和延迟。如果某个AC的表现不符合预期,再逐个调整参数。千万别一上来就大改,否则你根本不知道是哪个参数引起的。
小结
这一章的内容确实不少。cfg80211/mac80211框架、HAL层接口、数据路径、WMM配置,每一个都是移植工作中的硬骨头。但只要你理解了它们各自的分工,再结合实际的调试经验,慢慢就能上手。
下一章,我们会深入蓝牙协议栈的移植细节。到时候你会发现,蓝牙和高通WiFi的很多设计思路其实是相通的。