简介《高通WiFi驱动编程指南》是一份高通官方英文技术文档对应 Wireless LAN Driver Version 11.0 for Access Point面向接入点设备的驱动开发与调试人员帮助读者弄清驱动如何衔接操作系统与无线网卡并完成硬件初始化、射频通信与电源管理。文档涵盖 Split MAC 分层设计、160/8080MHz 与 8 链操作、802.11ax、Hy-Fi 与 LOWI 定位、Spectral 扫描以及 CLI 与诊断工具等内容并附有详细的版本修订记录逐条说明 QCA_Networking_2017.SPF.7.0 与 8.0 各版本新增和更新的章节便于按模块定位问题、排查吞吐与稳定性故障。整个资源包只有 1 个 PDF 文件约 87.21MB单个文件就是完整的编程指南可按目录快速检索查阅。已有 2948 人学习下载适合作为高通无线方案开发、驱动移植与性能调优的案头参考资料使用时需留意文中的保密要求与出口管制声明。1. 从一份接入点 Wi-Fi 驱动编程手册说起它省掉的不是阅读时间而是试错接入点AP端 Wi-Fi 驱动最容易出问题的地方往往不是代码本身而是参数由谁负责同一个参数在主机侧改了没反应多半是它早被固件接管了。这份接入点 Wi-Fi 驱动编程手册把驱动版本、特性开关、调试命令和固件行为整理成一张可以随时对照查询的表真正有用的地方在于卡住时能三分钟判断是主机侧、固件侧还是法规配置在限制你而不在于从头读到尾。它面向做 AP、无线扩展器、企业网关的固件与应用开发者尤其是已经能在板子上跑通 hostapd、但一碰到多 VAP、OFDMA 调度或频谱扫描就没法继续排查的人。新手可以顺着命令走一遍拿到基线熟手更该盯住手册里那些「仅对某种架构生效」的标注——那才是省掉几天反复试错的地方。2. 驱动分层与 cfg80211 接口把手册里的框图落到真实代码路径拿到这类文档第一件事不是翻目录找命令而是先确认你这块板子跑的是哪种架构。同一份手册里的章节有一半可能是给另一种部署形态写的按错那一半去调试方向一开始就偏了。2.1 先分清主机侧负责多少 802.11接入点驱动通常有两种部署形态手册会用两个词反复区分它们一种是主机侧承担完整 802.11 处理。聚合、块确认、速率控制、重传都在主机 CPU 上跑固件基本只做物理层和部分定时相关的事。优点是行为可见、可以打补丁、调试信息丰富缺点是吃主机算力高并发小包场景下 CPU 占用会明显上来。另一种是固件承担 802.11 处理。主机看到的是一个接近以太网的接口802.11 的时序敏感部分全在固件里主机只负责桥接、转发和下发配置。优点是省 CPU、时延稳定缺点是很多行为变成黑匣子出问题时只能靠固件日志和统计计数器间接推断。判断方法很直接不用猜# 看启动日志里驱动自己怎么描述工作模式 dmesg | grep -iE wlan|wifi|firmware|mode|offload | tail -n 40 # 看接口的类型与特性位以太网兼容模式通常不带 wireless 扩展 iw dev iw phy | head -n 40 # 看统计计数器里有没有 802.11 层的东西重传、聚合、块确认 ethtool -S wlan0 2/dev/null | grep -iE retry|ampdu|amsdu|ba_|mpdu如果ethtool -S里能直接读到 MPDU、AMPDU、块确认相关的计数说明 802.11 的关键状态至少有一部分暴露给了主机如果只有 tx/rx packet、dropped 这类以太网口径的计数基本可以判定为固件承担主要处理。这一步为什么要放在最前面因为速率控制、聚合深度、重传策略这些参数在两种形态下的配置入口完全不同。我在一块固件承担主要处理的板子上花了大半天找速率控制算法最后才发现那个算法根本不在主机侧——这种方向性错误看一次架构标注就能避免。2.2 框图上的每一层在系统里都有对应节点手册里的分层框图一般画四到五层总线层、固件层、上层 MAC、配置接口层、用户态工具。这五层在运行系统里都能找到落点只是名字不完全一样框图分层运行系统里的落点主要用途总线与 DMAlspci -vv、dmesg里的 ring 初始化日志判断带宽、队列数、是否降速固件/sys/kernel/debug/ieee80211/phy0/drv/下的版本与状态文件确认固件版本、assert 记录上层 MAC/sys/kernel/debug/ieee80211/phy0/下的 peer、队列、统计看每站状态、队列深度配置接口iw phy、iw dev、iw reg get读能力集、法规域、当前信道用户态hostapd / wpa_supplicant / 厂商工具下发配置、做验证注意/sys/kernel/debug/ieee80211/phyN/下面那个厂商目录的名字各家不同有的是芯片系列名有的是驱动模块名。别去背路径用一条命令列出来# 有些发行版默认不挂 debugfs先确保挂上 mount -t debugfs none /sys/kernel/debug 2/dev/null || true # 列出每个 phy 下暴露的调试节点厂商目录名各不相同 for p in /sys/kernel/debug/ieee80211/phy*; do echo $p ls $p done如果ieee80211目录本身就不存在多半是内核配置里没开调试文件系统支持或者驱动编译时把调试节点裁掉了。这种情况下手册里一大半查询命令都用不了得先解决内核配置否则后面所有验证都是空的。2.3 抓一份能力快照作为后续所有对比的基线调试无线问题最怕「改了很多东西忘了原来是什么样」。我的习惯是拿到板子第一件事先存一份快照后面换固件、换信道、换配置全部跟这份快照做 diff。#!/bin/bash # cap_snapshot.sh —— 抓一份可 diff 的能力与状态基线 OUTcap_$(date %Y%m%d_%H%M%S).txt { echo ### kernel; uname -a echo ### reg domain; iw reg get echo ### interfaces; iw dev echo ### phy caps; iw phy echo ### hw counters; ethtool -S wlan0 2/dev/null | head -n 80 echo ### driver nodes; ls /sys/kernel/debug/ieee80211/phy0/ 2/dev/null echo ### dmesg tail; dmesg | tail -n 60 } $OUT 21 echo baseline saved: $OUT脚本本身没有技术含量价值在于采集口径统一。iw phy的输出很长包含支持的频段、信道宽度、接口组合限制、加密套件、HE/EHT 能力位这些正是排查「为什么这个组合起不来」时最需要的信息。iw reg get单独存一份是因为法规域会随板子的出厂配置、固件里的法规数据库版本变化很多「明明配置了 160MHz 却只跑 80MHz」的问题根子在法规域而不是 hostapd。参数上有一个容易忽略的点phy0、wlan0这类名字在不同板子上会变双频双并发平台通常是phy0/phy1对应两个射频接口名可能是wlan0/wlan1也可能是wlan0/wlan2。写脚本时最好从iw dev的输出里解析接口名不要写死否则换一块板子就翻车。快照存下来之后还有一个用法升级固件前后各抓一次直接 diff。能力位的变化、计数器名字的变化、法规域的变化都会暴露出来比对着手册逐条核对方便得多。3. VAP 与多频段配置hostapd 参数怎么和驱动能力对上多 SSID 是 AP 场景最基础也最容易配错的部分。VAPVirtual AP在驱动里不是「一个网卡多开几个名字」这么简单每个 VAP 都要占独立的 beacon 缓冲、密钥表项、转发条目和队列资源账算不清就会遇到「配置看着没错但就是起不来」。3.1 VAP 的资源账先看接口组合限制驱动对接口组合的限制是硬约束写在能力集里。查法只有一个# 关注 valid interface combinations 这一段 iw phy | sed -n /valid interface combinations/,/^$/p输出形如「若干个 AP 类型接口 若干个 station 类型接口总通道数不超过 N」的描述。这段文字的含义是同时存在的接口类型和数量有限制而且可能还有「必须同信道」或「可以不同信道」的附加条件。多频段并发平台会明确写清每个射频能跑多少路。除了接口数量还有几项隐性资源要一起算资源项约束来源观测方式VAP 数量驱动能力集每射频通常 8 到 16 路iw phy的接口组合段密钥表项硬件加密表大小每射频数百条关联失败时看dmesg报错beacon 缓冲每 VAP 独立占用与时隙相关多 VAP 时观察 beacon 发送间隔抖动转发条目硬件转发表容量大表场景下看转发计数器是否打满队列深度每 VAP 每接入类别一条队列ethtool -S里的队列丢弃计数真正让配置失败的原因八成是对表中第二项和第五项。密钥表打满表现为「前面几个客户端能连后面陆续连不上」队列打满表现为「低优先级业务大量重传但信道并不忙」。这两个现象都不会在 hostapd 日志里报错只能从驱动统计里看出来。3.2 一份能跑起来的 hostapd 配置模板下面这份配置以 5GHz、80MHz、HE 为例注释里标了几个必须成对出现的字段# /etc/hostapd/hostapd.conf —— 主 BSS5GHz / 80MHz / 802.11ax interfacewlan0 drivernl80211 ssidlab-ap-5g hw_modea # a5GHzg2.4GHz双频要拆成两个 phy 各起一个实例 channel36 # 先用非 DFS 信道把 CAC 这个变量摘掉 ieee80211n1 ieee80211ac1 ieee80211ax1 # 带宽三件套宽度枚举 中心频点必须配对写错就静默降宽 vht_oper_chwidth1 # 0:20 1:80 2:160 3:8080 vht_oper_centr_freq_seg0_idx42 # 36 起 80MHz中心频点 36 6 he_oper_chwidth1 he_oper_centr_freq_seg0_idx42 beacon_int100 dtim_period2 max_num_sta64 wpa2 wpa_key_mgmtWPA-PSK SAE # 开 SAE 前先确认固件支持不支持会导致关联直接失败 wpa_passphraselabtest12345中心频点的算法值得单独记一下因为这是最常见的「配置没报错但带宽不对」的原因80MHz 的中心频点等于起始信道加 6160MHz 等于起始信道加 14。例如从 36 起 160MHz中心频点是 50中心频点写错时hostapd 不会报错驱动会静默按更低带宽起来客户端看到的也是低带宽很容易误判成「客户端不支持」。还有一处容易踩wpa_key_mgmt同时写WPA-PSK和SAE时行为取决于 hostapd 版本和驱动对过渡模式的支持。老版本可能只取第一个值结果是部分客户端关联不上。要验证是否真的双模生效看关联日志里客户端最终协商的 AKM不要看配置文件。3.3 用手工建 VAP 做最小验证怀疑是 hostapd 配置问题还是驱动能力问题时最快的隔离手段是绕开 hostapd直接用配置接口建一个 AP 类型接口# 先把托管接口停掉避免名字冲突 ip link set wlan0 down # 直接建一个 AP 类型接口验证驱动是否允许当前组合 iw phy phy0 interface add vap2 type __ap # 起来看看它有没有被驱动接受 ip link set vap2 up iw dev vap2 info # 被拒时这里会有具体原因通常是 -ENOTSUPP 或 -EBUSY dmesg | tail -n 20__ap那两个下划线不是笔误是配置接口工具对 AP 模式的固定写法。这条命令能成功说明驱动在接口层面是放行的问题就落在 hostapd 的参数上如果这条命令直接失败那再怎么调 hostapd 也没用得回去看接口组合限制或者驱动编译选项。失败时dmesg里的打印比命令返回值有用得多。返回码只告诉你「不允许」驱动打印会告诉你「因为当前已有 N 路 AP 接口超过上限 M」这类具体信息直接指向要删哪一路。4. 吞吐与调度OFDMA、MU-MIMO 与空口公平的参数范围到了这一层配置本身不难难的是顺序和验证。802.11ax 的多用户特性是一串有依赖关系的开关前面没开后面开了也不生效而且不会报错。4.1 多用户特性的开关顺序正确的顺序是先让基础 HE 能力协商成功再开下载多用户再开上传多用户最后调多用户 EDCA 参数。每一步都要用客户端实际协商结果验证不能只看 AP 侧配置。# 第一步确认客户端确实以 HE 关联并且带宽符合预期 iw dev wlan0 station dump | grep -iE he|eht|rx bitrate|tx bitrate # 第二步看每个站的 HE 能力位判断它支持哪些多用户特性 iw dev wlan0 station get sta_mac | grep -iE he_capa|mu|ofdma # 第三步看信道实际占用确认不是带宽被法规或干扰压下去了 iw dev wlan0 survey dump | grep -E frequency|busy|in usestation dump里的rx bitrate/tx bitrate是协商结果不是实测吞吐。很多「开了 802.11ax 但速率还是 802.11ac 水平」的情况实际是客户端根本没协商到 HE或者协商到了 HE 但带宽只有 40MHz。这两件事在station dump里能分开看出来前者看有没有 HE 标志后者看 MCS 和带宽字段。上行的多用户调度对客户端配合度要求更高如果客户端不支持开了也只是 AP 侧空转还会额外消耗调度开销。我一般会在同一个信道下用两种客户端各做一轮先确认下行有增益再决定要不要开上行。4.2 空口公平的权重怎么落到 VAP 上空口公平ATF解决的是「一个慢客户端拖垮整个射频」的问题。它的基本模型是每个 VAP 或每个站分配一个权重调度器按权重占比分配空口时间而不是按包数或字节数。各厂商暴露这个能力的方式不同有的走 hostapd 扩展字段有的走私有命令行工具有的只能通过配置文件在启动时下发。不管入口在哪验证方法是一样的——采样各 VAP 的实际发送量看比值是否接近配置权重。下面这段脚本用每站的发送字节和协商速率反推占用时间虽然只是估算但足够判断权重有没有生效#!/usr/bin/env python3 # airtime_share.py —— 按站估算空口时间占比验证 ATF 权重是否生效 import re, subprocess, sys DUMP_RE re.compile( rStation (\S).*?rx bytes:\s*(\d).*?tx bytes:\s*(\d).*?tx bitrate:\s*([\d.]), re.S, ) def parse(dev): out subprocess.run([iw, dev, dev, station, dump], capture_outputTrue, textTrue).stdout rows [] for m in DUMP_RE.finditer(out): mac, rx, tx, rate m.group(1), int(m.group(2)), int(m.group(3)), float(m.group(4)) # 用 tx 字节与协商速率估算发送占用时间(ms)速率取 0 时跳过避免除零 airtime_ms (tx * 8) / (rate * 1e6) * 1000 if rate 0 else 0.0 rows.append((mac, tx, rate, airtime_ms)) return rows def main(devwlan0): rows parse(dev) total sum(r[3] for r in rows) or 1.0 rows.sort(keylambda r: -r[3]) for mac, tx, rate, at in rows: print(f{mac} tx{tx:12} rate{rate:7.1f}Mbps fairtime≈{at:8.1f}ms share{at/total*100:5.1f}%) if __name__ __main__: main(sys.argv[1] if len(sys.argv) 1 else wlan0)这段代码的用途是给出相对份额不能当成精确测量。tx bytes只统计成功发出的字节重传部分不计入所以重传多的站会被低估估算出的占用时间也不含管理帧和信道竞争开销。要更准得用驱动暴露的每站空口时间计数器如果那个计数器存在的话。关键参数说明tx bitrate是当前协商速率会随速率自适应变化采样时最好多采几次取中位数单次采样波动很大。权重生效与否看的是比值趋势不是绝对值——把访客 VAP 权重设成办公 VAP 的四分之一实测份额落在 15% 到 30% 之间都算正常。4.3 吞吐验证要看的四个数单跑一个 iperf3 得出结论是不够的。至少要同时看四组数据才能判断吞吐瓶颈在哪一层# 1) 实测吞吐下行用 -R四线程压满 iperf3 -c 192.168.10.20 -t 30 -P 4 -R # 2) 协商速率与重传重传率高说明信道质量或干扰有问题 iw dev wlan0 station dump | grep -E tx bitrate|rx bitrate|tx retries|tx failed # 3) 信道忙闲busy 高但吞吐低通常是同频干扰或非 Wi-Fi 干扰 iw dev wlan0 survey dump | grep -E frequency|busy|noise # 4) 驱动层丢弃队列打满或内存不足会在这里体现 ethtool -S wlan0 2/dev/null | grep -iE drop|full|no_mem|err四个数要对得上协商速率高、重传低、信道不忙、驱动无丢弃吞吐还上不去才轮到怀疑主机转发路径。反过来只要有一项异常就先解决那一项别急着去调调度参数。速率控制策略在这里也会干扰判断做基线测试时我一般会临时固定速率把自适应这个变量摘掉测完再放开。5. 避坑与排查五个会让你查一整天的问题这一节里的每一条都是「配置看不出来、日志不报错、只能靠特定命令才能暴露」的类型。按现象、原因、解决三段对照遇到时直接对号入座。5.1 起不来与挂不住现象一加第三路 SSID 时 hostapd 起不来日志里只有一句接口创建失败。 原因是接口组合限制或资源不够不一定是数量超限也可能是「当前已有接口占用了不允许并存的类型」。解决办法是先iw phy看组合限制原文再核对当前所有接口的类型必要时把不需要的监控类接口先删掉逐个加回去定位到具体是哪一路冲突。现象二客户端连上后几十秒内掉线重连又正常反复循环。 原因是密钥表项或转发条目打满多见于客户端数量多、短时间内大量上下线的场景。解决办法是看dmesg里关联阶段的报错同时把单 VAP 的客户端上限max_num_sta调低做二分确认是资源上限问题后要么减少 VAP 数量要么缩短空闲客户端的老化时间。现象三固件异常后整个射频不工作iw dev里接口还在但完全无响应。 原因是固件 assert 后没有触发恢复流程或者恢复流程只重建了部分资源。解决办法是先抓dmesg里的 assert 记录和固件日志确认是偶发还是可复现如果平台支持单射频重建优先用重建而不是整驱动重载整驱动重载会把所有 VAP 一起打掉影响面大得多。5.2 跑得慢与跑不稳现象四配了 160MHz客户端也只显示支持 160MHz实际一直跑 80MHz。 原因通常有三个中心频点索引算错导致静默降宽信道落在 DFS 段且 CAC 未完成法规域里该信道不允许 160MHz。解决办法是依次排除——先核对中心频点公式换非 DFS 信道再试一次最后iw reg get看法规域和信道禁用位。这三步做完还不行才去怀疑客户端。现象五吞吐在 300Mbps 到 600Mbps 之间大幅波动重传率不高信道也不忙。 原因是速率自适应在反复试探或者省电机制在干扰也可能是后台扫描周期性打断业务。解决办法是做基线时固定速率并关闭客户端省电观察波动是否消失如果扫描是主因把后台扫描的间隔和驻留时间调大或者在做吞吐测试期间临时停掉。这类问题最费时间的地方在于现象看起来像玄学其实每次波动都能在统计计数器里找到对应的时间点。6. 频谱采样与抓包日志把「感觉有干扰」变成可复现的数据无线问题最难的地方是复现。同一个位置、同一个信道早上好好的下午就掉速口头描述永远是「感觉有干扰」。频谱采样就是给这种描述装一个记录仪驱动按周期把信道的 FFT 采样上报到用户态工具把它聚合成每段子信道的能量占比你就能看到干扰到底落在哪个 20MHz 上、是宽带噪声还是窄带突起。采样文件的具体头部格式各家不同一定要以手头这份驱动文档的字段定义为准不要照搬别人的解析脚本。下面这段只演示聚合逻辑头部按「8 字节 N 个 16 位有符号样本」处理实际用的时候把头部长度和样本类型改掉即可#!/usr/bin/env python3 # fft_occupancy.py —— 把连续 FFT 采样按 20MHz 子信道聚合输出能量占比 import struct, sys BIN_W 312500 # 每个 FFT bin 的带宽(Hz)按采样参数换算不同配置不一样 SUB_W 20_000_000 # 聚合粒度20MHz HEADER 8 # 每帧头部字节数必须按文档改 def frames(path, fft_bins): with open(path, rb) as f: while True: head f.read(HEADER) # 头部里一般带时间戳和 bin 数 if len(head) HEADER: return body f.read(fft_bins * 2) if len(body) fft_bins * 2: return yield struct.unpack(f{fft_bins}h, body) def main(path, fft_bins64): bins_per_sub max(1, round(SUB_W / BIN_W)) # 每 20MHz 覆盖多少个 bin acc, cnt {}, 0 for samples in frames(path, fft_bins): cnt 1 for i, v in enumerate(samples): seg i // bins_per_sub acc[seg] acc.get(seg, 0) abs(v) # 累加幅度用于比较相对强弱 if not cnt: print(no frames parsed, check HEADER/fft_bins) return total sum(acc.values()) or 1 for seg in sorted(acc): print(fseg{seg:02d} energy{acc[seg]/cnt:9.1f} share{acc[seg]/total*100:5.1f}%) if __name__ __main__: main(sys.argv[1], int(sys.argv[2]) if len(sys.argv) 2 else 64)三个参数决定结果是否可信BIN_W必须按采样时的带宽和 bin 数换算写错会让所有分段平移HEADER写错会直接解析失败或者读出乱码fft_bins要和采样配置一致不一致时会截断或错位。跑完先看帧数帧数太少比如不到几百帧说明采样时间不够占比没有统计意义。验证方法我习惯用两组数据交叉把频谱采样的分段占比和iw dev wlan0 survey dump里的busy时间做对照。如果某个 20MHz 分段能量占比超过 60%同时该信道的 busy 时间也长期高于 50%基本可以确定干扰源就在那一段把主信道挪开就能验证。反过来如果频谱显示很干净但 busy 一直很高那问题不在外部干扰而在自身的帧交互效率或者隐藏节点方向完全不同。血泪经验是频谱采样必须和业务流量解耦。一边跑满速一边采样采到的全是自己的信号看起来「干扰极强」其实只是自己。从那以后我每次做干扰排查都强制先停业务跑一轮空口采样当基线再叠加业务跑第二轮做对比这两轮数据的差值才是真正要看的东西。希望帮到你。本文还有配套的精品资源点击获取