第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层的数据结构,比如tSirMacAddrtSirBssDescription,和内核里的结构体往往不一样。你需要写一堆转换函数。我建议你把这些转换函数统一放在一个文件里,别散落在各处,否则后期维护会想哭。

4.4 数据路径(TX/RX)流程

数据路径,说白了就是数据包怎么从网口进来,又怎么发出去。在高通平台上,TX/RX的路径大致是这样的:

TX路径

  1. 上层协议栈调用ndo_start_xmit()
  2. 进入mac80211的ieee80211_subif_start_xmit()
  3. 经过TX调度,调用驱动的tx()回调
  4. 驱动把数据打包成HAL层的tx_desc
  5. 通过PCIe/SDIO等总线发给固件
  6. 固件最终通过天线发出去

我在项目中遇到过一个问题:TX吞吐量上不去。后来发现是驱动里每次发送都申请了一次DMA缓冲区,开销太大。改成预分配缓冲池后,吞吐量直接翻倍。

RX路径

  1. 固件收到数据,通过中断通知驱动
  2. 驱动在中断下半部(tasklet或workqueue)中读取数据
  3. 解析RX描述符,提取帧信息
  4. 调用ieee80211_rx()把数据交给mac80211
  5. 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的很多设计思路其实是相通的。