ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux WiFi驱动开发进阶:mac80211框架与SDIO设备树实战解析

Linux WiFi驱动开发进阶:mac80211框架与SDIO设备树实战解析 1. 整体设计与思路拆解1.1 这篇内容讲什么先把这个话题的背景说清楚。Linux WiFi设备驱动开发是嵌入式Linux开发里综合性最强、踩坑最多、也最考验基本功的方向之一。为什么这么说因为一个完整的WiFi驱动往往同时涉及总线驱动USB/SDIO/PCIe、电源管理、中断处理、固件加载、网络协议栈对接、无线子系统cfg80211/mac80211等多层内容。从某种意义上讲把WiFi跑通了你对整个Linux驱动体系的认知基本就建立起来了。这篇内容适合几类人一是刚接触嵌入式Linux驱动开发、想找完整学习路径的工程师二是已经在做字符设备或I2C/SPI驱动、准备进阶到网络设备驱动的同学三是做系统裁剪和内核移植、经常被“WiFi连不上”“网卡识别不了”这类问题折磨的人。我会从驱动框架、设备树配置、核心实现、调试手段几个维度展开把我自己实际开发中积累的东西一次讲清楚。1.2 为什么WiFi驱动是“驱动开发的天花板级练手项目”我这些年带过不少新人发现一个规律凡是能把WiFi驱动完整调通的人看其他驱动基本都能很快上手。原因很简单WiFi驱动把Linux驱动开发的核心知识点几乎全覆盖了。先说接口层面。市面上主流的WiFi芯片接口无非就是USB、SDIO和PCIe三大类每一类的总线驱动模型都不一样。USB有usb_driver结构体和URB机制SDIO走mmc子系统内部还要处理SDIO功能号和中断注册PCIe则涉及BAR空间映射和MSI中断。说白了你换一种芯片平台总线层的代码基本得重新写。再说内核框架层面。WiFi驱动不像GPIO或者LED那样用个platform_driver注册完了就结束了。它必须对接内核的无线子系统也就是cfg80211和mac80211。cfg80211负责向用户空间提供nl80211接口也就是iw和wpa_supplicant跟你打交道的那一层mac80211是软MAC芯片的协议栈实现帮你处理了大部分802.11协议细节比如帧的解析、重传、速率选择等。如果你的芯片是硬MAC类型那工作方式又不一样SDIO的芯片很少见硬MAC类型大部分老芯片用的是USB接口接一个全MAC的方案。我在实际项目中经常遇到一种情况硬件工程师拿着一个芯片厂提供的BSP包里面有板级支持代码、固件、配置工具看起来大而全但要把它撸到自己的内核版本上经常发现内核API已经变了设备树节点也跟原厂默认的不一致编译错误连成一片。这时候如果对整个框架不熟很容易陷入“改一行编译一下再改一行再编译”的死循环非常痛苦。1.3 方案选型软MAC还是硬MACWiFi芯片的工作模式可以粗略分成硬MAC和软MAC两种这个选型直接决定了你的驱动开发量。硬MAC芯片的意思是芯片内部已经集成了完整的MAC层处理包括帧的封装解析、ACK应答、重传逻辑、速率自适应等。这种芯片的驱动实现相对轻量只需要把芯片的寄存器配置好、数据通路打通然后通过cfg80211_ops向上注册即可。很多USB WiFi芯片比如联发科的mt7601u、瑞昱的rtl8188eu或者市面上大量基于esp8089的模组都属于这类。驱动的核心工作通常是固件加载、数据包的收发以及各种控制命令的下发。软MAC芯片则不同MAC层的状态机要靠主机端的mac80211来完成芯片只负责物理层和最基本的数据收发。这类芯片的驱动更复杂因为你不仅要实现数据通路还要实现scan扫描、connect连接、disconnect断连、set_channel信道切换等一系列回调并且要配合mac80211维护连接状态。以我在SDIO接口项目上常用的芯片为例很多模组厂商的驱动都是基于mac80211框架写的这样芯片的硬件设计可以做得更简单成本也更低。选型建议很直接如果项目对成本和功耗不敏感、系统资源充裕优先考虑硬MAC方案开发周期短、问题少如果做低功耗、高集成度的产品比如电池供电的IoT设备软MAC方案往往更合适。开发难度大约是硬MAC的一倍以上但对整个无线协议栈的理解会上去一个台阶。2. 设备驱动框架与WiFi对接的核心原理2.1 字符设备驱动和网络设备驱动的区别很多初学者习惯先看字符设备驱动因为网上资料最多什么miscdevice、file_operations、read/write/ioctl套路固定。这个学习路径本身没问题但如果带着字符设备驱动的思路去看WiFi驱动就会一头雾水。字符设备的思路是“文件接口”用户态open一个设备节点然后read/write/ioctl和内核交互。但网络设备不这样工作它是数据包驱动的。用户态的应用程序不会去open一个网卡设备节点而是通过socket发包socket经过网络协议栈最终走到驱动的ndo_start_xmit回调把skbsocket buffer发送出去。收包方向则是异步的驱动在中断或软中断里收到数据把数据包封装成skb然后调用netif_rx把它送进协议栈。所以WiFi驱动的代码里你看到的不是file_operations结构体而是net_device_ops。这个结构体里有一堆ndo_开头的函数指针比如ndo_open、ndo_stop、ndo_start_xmit、ndo_set_mac_address等。再加上无线子系统要求实现的cfg80211_ops驱动代码里会有两套ops这是WiFi驱动的最大特点。我在团队里做新人培训时常打一个比方字符设备驱动像是在银行柜台办业务一个窗口服务一个客户双方有问有答网络设备驱动像是寄快递你把包裹丢进网点就不管了剩下的事由分拨中心协议栈来决定往哪送驱动只负责把包裹搬上车或者从车上卸下来。理解了这个模型后续看代码会顺畅很多。2.2 cfg80211和mac80211分工关系Linux无线子系统在2.6时代经历过一波大重构最终形成了现在的架构cfg80211是核心管理层mac80211是协议栈实现层。两者的边界划分简单来说是cfg80211管策略、mac80211管状态机。举个例子用户执行iw dev wlan0 scan时系统调用经netlink进入cfg80211层的nl80211处理函数cfg80211会调用驱动注册的cfg80211_ops里的scan回调。如果驱动是软MAC类型这个scan回调通常是mac80211实现的它会驱动芯片去监听各个信道上的Beacon帧和Probe Response帧收集结果后通过cfg80211_scan_done通知上层。硬MAC的驱动则是自己实现scan芯片固件内部完成扫描后回报结果。连接过程也类似。wpa_supplicant发起连接时会下发连接请求和凭据经过cfg80211的connect回调。软MAC的方案里mac80211会执行完整的认证、关联状态机驱动只需要把管理帧交给芯片发出去同时设置信道和BSSID即可。硬MAC的方案则简单很多驱动把SSID和密码传给固件固件内部完成协商最终驱动上报连接成功事件。理解这层分工对做调试很有帮助。如果WiFi能扫到热点但连不上大概率问题在关联阶段需要抓管理帧如果连上了但ping不通问题可能出在加密密钥协商或者数据通路上需要去查PTK/GTK的安装情况。2.3 设备树里WiFi节点该怎么配置早年间没有设备树WiFi芯片的硬件信息都写在板级文件里更改一次就要重新编译内核。现在主流平台基本都切到设备树了WiFi的配置逻辑也变成了“描述硬件”的思路把接口类型、中断引脚、供电时序、时钟频率等信息写在dts里驱动启动时通过device树匹配获得资源。拿SDIO接口的WiFi芯片举例一个典型的设备树节点类似下面这样sdhci1 { status okay; bus-width 4; non-removable; vmmc-supply reg_wifi_en; vqmmc-supply reg_wifi_io; pinctrl-names default; pinctrl-0 wifi_pins_default; wifi1 { compatible vendor,wifi-chip; reg 0x1; interrupt-parent gpio; interrupts GPIO_ACTIVE_HIGH; reset-gpios gpio 36 GPIO_ACTIVE_LOW; }; };这里有几个细节容易被新手忽略。一个是non-removable属性很多平台上不加这个属性内核会认为SDIO设备可以热插拔初始化时检测顺序就可能出问题。另一个是供电节点WiFi模组对上电时序有严格要求先给主电源再给IO电源这个顺序错了芯片可能无法枚举。如果手里的硬件总是出现设备找不到的情况优先查这两项。中断引脚也值得单独拎出来说。WiFi的唤醒中断一般建议接到SoC的GPIO上因为SDIO接口本身的中断线在很多平台上性能不好或者不可用使用独立的GPIO中断更可靠。注意中断触发方式要跟芯片手册对牢有的芯片是上升沿触发有的需要低电平触发配错了会表现为iw命令执行后无响应、扫描超时、连接经常掉线等。3. 完整实操从零实现一个基于mac80211的WiFi驱动核心框架3.1 搭建驱动骨架这一节我以SDIO接口的软MAC方案为例把驱动开发的完整流程走一遍。不会用具体芯片的寄存器细节因为每家不同重点说框架和套路底层的寄存器操作你自己去查芯片手册就行。这样你拿到任何一家芯片的SDK都能快速理解它在干什么。首先是驱动模块的入口。SDIO驱动需要注册一个sdio_driver结构体这与platform_driver、i2c_driver在结构上非常相似都是总线框架下的标准写法static const struct sdio_device_id wifi_sdio_ids[] { { SDIO_DEVICE(SDIO_VENDOR_ID_VENDOR, SDIO_DEVICE_ID_CHIP) }, { } }; MODULE_DEVICE_TABLE(sdio, wifi_sdio_ids); static struct sdio_driver wifi_sdio_driver { .name vendor_wifi, .id_table wifi_sdio_ids, .probe wifi_sdio_probe, .remove wifi_sdio_remove, }; module_sdio_driver(wifi_sdio_driver);SDIO_DEVICE宏传进去的是厂商ID和设备ID这两个值在芯片的SDIO规格书里有。probe函数在设备匹配成功后调用它负责初始化硬件、申请中断、分配网络设备、注册无线子系统。remove函数则做反向操作。模块入口只是一个起点你的网卡要真正工作还需要两样东西一个struct ieee80211_hw对象mac80211的硬件描述以及在这个对象内部嵌套的一个struct wiphy对象cfg80211的无线描述。初始化这个对象群组是probe阶段最重要的任务。3.2 初始化ieee80211_hw和wiphystruct ieee80211_hw是mac80211框架下驱动的核心结构体。它的分配使用ieee80211_alloc_hw函数需要传入两个参数驱动的私有数据大小和ops回调集。私有数据一般放芯片相关的状态比如锁、工作队列、寄存器映射指针、链路状态标志等。我用一个实际项目的初始化代码来演示static int wifi_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct ieee80211_hw *hw; struct wifi_priv *priv; int ret; hw ieee80211_alloc_hw(sizeof(struct wifi_priv), wifi_ops); if (!hw) { dev_err(func-dev, failed to alloc hw\n); return -ENOMEM; } priv hw-priv; priv-hw hw; priv-func func; sdio_set_drvdata(func, priv); /* 设置硬件能力标志 */ hw-flags | IEEE80211_HW_SIGNAL_DBM; hw-flags | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; /* 支持802.11n的20/40MHz带宽 */ hw-wiphy-band_2GHz wifi_band_2ghz; hw-wiphy-interface_modes BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); hw-wiphy-max_scan_ssids 4; hw-wiphy-max_scan_ie_len 512; /* 注册到mac80211 */ ret ieee80211_register_hw(hw); if (ret) { dev_err(func-dev, failed to register hw: %d\n, ret); goto err_free_hw; } // ... 中断注册、任务队列初始化等 return 0; err_free_hw: ieee80211_free_hw(hw); return ret; }wifi_ops在这里就是struct ieee80211_ops软MAC驱动的核心工作都在这一组回调函数里。最常见的几个start和stop对应网卡启动和停止config用于信道、带宽等参数配置add_interface和remove_interface用于虚拟接口的创建和删除configure_filter用于配置硬件收包过滤规则tx是发送数据帧的入口。有朋友可能会问怎么没有扫描和连接的回调答案是软MAC方案里mac80211协议栈替你做掉了管理帧的状态机驱动只需要提供hw_scan或者让mac80211使用软件扫描即驱动不实现hw_scan时mac80211会触发频率切换通过config回调把频道设置下去然后在每个信道上做被动监听。connect相关的事件上报则由驱动主动调用ieee80211_connection_loss等接口告知上层。3.3 数据收发路径怎么打通WiFi驱动的数据收发是这门手艺里最有含金量的部分。发送方向上协议栈发下来的skb最终通过wifi_ops.tx函数进入驱动。驱动要做的事情包括把skb内容按照芯片要求组装成硬件描述符格式标记队列号DMA映射如果使用DMA然后写芯片寄存器通知硬件取数据。完成后还要检查返回状态如果发送成功用ieee80211_tx_status通知mac80211如果失败要根据错误类型决定是否重传。接收方向稍微复杂一些。芯片收到数据帧后会触发中断驱动的中断处理函数需要把数据从芯片内部FIFO或DMA缓冲区搬出来组装成struct sk_buff然后调用ieee80211_rx把它交给mac80211。如果芯片支持聚合接收你还得维护一个重排序缓冲区因为802.11n的A-MPDU机制会乱序到达。很多低端SDIO芯片不硬件支持这个最终是mac80211用软件去处理乱序的你的驱动反而要少操一份心。以下是一个简化的接收处理函数框架static void wifi_sdio_irq_handler(struct sdio_func *func) { struct wifi_priv *priv sdio_get_drvdata(func); struct sk_buff *skb; u32 int_status; sdio_claim_host(priv-func); int_status sdio_readl(priv-func, REG_INT_STATUS, NULL); sdio_release_host(priv-func); if (int_status INT_RX_DATA) { while (1) { skb wifi_rx_get_packet(priv); /* 从芯片读取一个数据包 */ if (!skb) break; ieee80211_rx(priv-hw, skb); /* 交给mac80211 */ } } }中断处理有几个细节值得注意。第一是sdio_claim_host和sdio_release_host是必需的SDIO总线在同一时刻只允许一个线程访问claim/release就是完成互斥。很多同学在这里踩坑表现为数据一多就死锁或者内核崩溃。第二是中断处理尽量别做太重的工作数据包读取可以放在工作队列里执行不然中断上下文时间太长会影响系统实时性。第三是接收如果有多个包尽量在一个中断里全部读完避免持续中断导致cpu占用过高。3.4 编译、设备树与加载验证驱动写完之后编译方式有两种。一种是编进内核在Kconfig里加条目make menuconfig里选上对应配置项。另一种是编成模块make Mdrivers/net/wireless/vendor/wifi modules然后insmod或者拷贝到目标板modprobe。开发调试阶段我强烈推荐编成模块迭代速度快改一行代码重新编个ko文件就好不用整个内核重新打包。编译完加载之前先把设备树配置确认好。前面给了SDIO设备树的模板实际项目里还要检查SoC的SDIO控制器是否处于OKAY状态、引脚复用是否正确、供电节点是否被引用。上电后先不带驱动加载用cat /sys/bus/sdio/devices/*/device查看SDIO设备是否枚举成功如果能看到厂商ID芯片ID说明硬件通路没问题了再加载驱动模块。驱动加载正常的标志是# dmesg | tail vendor_wifi: probe success ieee80211 phy0: vendor_wifi: registered with mac80211而且ip link命令可以看到一个wlan0接口。很多项目卡在probe阶段之前SDIO枚举都通不过这种问题八成是硬件问题或者设备树配置问题别急着改驱动代码先把硬件通路查清楚。如果probe成功但接口起不来那就要去看cfg80211注册是否正常以及驱动有没有正确向mac80211注册。3.5 实际联调扫描、连接与数据传输驱动能加载、接口能起来只能算是万里长征走完了一半。接下来才是真正的WiFi联调阶段也是我这个项目中耗时最多的环节。扫描验证很简单先执行# ip link set wlan0 up # iw dev wlan0 scan | head -30正常情况下你能看到周围的热点信息包括BSSID、SSID、信号强度、加密方式。如果扫描结果为空先不要怀疑是附近没有WiFi热点而是去排查驱动扫描流程的问题比如信道上是不是漏扫了、芯片有没有正确进入监听模式。连接测试要用wpa_supplicant。一个最简配置ctrl_interface/var/run/wpa_supplicant network{ ssidMyTestAP psktestpassword key_mgmtWPA-PSK }启动wpa_supplicant后再启动dhcpcd或udhcpc获取IP# wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf # udhcpc -i wlan0ping测试对端时如果通了那WiFi基本链路就是好的。不过联调阶段常见的问题远不止这些我单独开一个章节把我在调试过程中遇到的高频故障和排查方法仔细展开这些经验在网上可不好找。4. 调试阶段的常见问题与排障思路4.1 固件加载失败软MAC芯片无一例外都需要向芯片内加载固件常见的失败现象是dmesg里有firmware load failed之类的提示或者芯片始终无法进入ready状态。排查顺序是固定的。第一步检查固件文件本身。/lib/firmware/目录下有没有对应文件文件md5值跟芯片厂商发布的是否一致。我曾经踩过一个坑从芯片SDK里拷贝的固件文件是带符号链接的拷到目标板的时候链接关系丢了导致加载加载了不完整的固件芯片初始化始终不成功。检查方法很简单先在开发板上确认文件真实性再传到目标板。第二步看加载时机。有些芯片要求在SDIO读取固件前切换块大小有些要求启用特定电源模式。固件加载的细节各家不同最靠谱的方法就是对照芯片手册的初始化流程图一步步检查驱动里的加载序列。这里不推荐靠猜因为固件加载失败的原因太多种多样电性能、时钟、寄存器初值都可能出问题。第三步看固件版本和内核版本兼容性。老固件配新内核或者反过来都可能出现奇怪的问题。最好是直接使用厂商SDK里配套的版本组合跑通了再逐步升级。4.2 能扫描到热点但连接不上这个问题的排查点比较多我按发生率排序来说。最常遇到的是加密参数不匹配。比如AP端配置的是WPA2-PSK/AES你wpa_supplicant里配了WPA-PSK/TKIP协商阶段就会失败。先确认两端参数一致再看supplicant日志通常会有明确的报错原因。第二常见的是关联阶段芯片没有正常发送关联请求帧。这个可以用抓包工具比如tcpdump或airodump-ng来看但我更推荐直接在你的驱动里加打印在add_interface、config、tx几个回调里打上日志看到底卡在哪一步。如果卡在扫描之后一直往前推进不了多半是set_bssid或者信道设置有遗漏。第三常见是固件内部的关联状态机异常。这种情况下日志看起来一切正常但就是连不上。我建议先把驱动里所有回调的错误返回值检查一遍很多驱动代码会吞掉错误导致上面链路层显示“正在连接”实际硬件早就罢工了。4.3 连接上了但ping不通能连上热点但内网互不相通这个问题出现的频率极高原因也千奇百怪。先查IP配置。DHCP有没有成功获取到的IP跟AP网段是否一致直接用静态IP配置试一下排除DHCP的问题# ip addr add 192.168.1.100/24 dev wlan0 # ip route add default via 192.168.1.1IP没问题再查ARP。ip neigh show看ARP表有没有对端条目。没有的话多半是收包路径有问题数据到了芯片但没送进协议栈。这时候就需要在中断处理函数里加计数统计确认是否有收包中断产生、ieee80211_rx是否被正确调用。再往下查就需要抓包了。tcpdump -i wlan0 -p能看到本机有没有发出ARP请求如果发出去了但收不到回应数据有可能被AP端丢弃。此时可以检查加密密钥是否安装成功特别是GTK。很多驱动在重新关联之后GTK安装流程有bug表现为短暂能通过一会儿就断了。4.4 连接不稳定频繁掉线最后聊聊掉线问题。这是WiFi驱动里最难缠的问题因为触发原因太多。我遇到的情况按概率排序是电源问题占大头尤其是SDIO接口的WiFi瞬时大电流会拉低电源电压导致芯片工作异常其次是中断触发方式配错导致唤醒信号丢失表现出来就是偶尔掉线再就是固件版本bug。排查电源问题需要在硬件上量测电压波动如果你手上没有示波器只能通过软手段验证把WiFi发射功率调低一点看掉线频率是否明显降低如果降低了那几乎可以肯定是供电不足。还有一种验证方法是用USB供电如果稳定性大幅改善同理。中断问题可以用一个土办法排查在驱动里定期读取芯片状态寄存器和中断状态寄存器看看掉线前后有没有异常标志。注意一定要加延时直接在主循环里查会干扰芯片正常工作。固件问题没什么好办法只能换版本交叉验证。我在项目里通常维护一个固件版本表记录每个版本在哪些平台上的表现和问题方便回溯。5. 调试工具与性能优化心得5.1 抓包和日志的合理使用如果你做WiFi驱动开发tcpdump、wireshark和htop这三件套必须熟练。无线帧的抓包稍微有点特殊建议用monitor模式把WiFi接口配置成监听模式然后tcpdump抓原始802.11帧这样连管理帧都能看到。# iw dev wlan0 interface add mon0 type monitor # ip link set mon0 up # tcpdump -i mon0 -w wifi.pcap在调试wpa_supplicant的握手过程中无线侧抓包是很有价值的。你可以对照抓包结果判断关联是否完成、四次握手进行到了哪一步、是否存在重传风暴等问题。内核日志控制在调试阶段非常有价值。我的习惯是新建一个#define WIFI_DBG开关驱动里所有关键路径都加上条件打印调试时打开发布时关掉。要注意在生产环境别用printk刷屏否则对系统实时性的影响很大。5.2 吞吐性能的优化方向WiFi能跑通只是第一步产品落地时还要过吞吐量、时延、稳定性这三座大山。吞吐量优化方面我分享几个实际改过的点位。第一个是DMA对齐。很多SDIO控制器要求数据buffer按4字节或8字节对齐mac80211送下来的skb如果不满足对齐要求就只能做一次复制性能损耗明显。我当初把一个接收驱动从“总是复制”改成“尽量零拷贝”之后吞吐从30Mbps直接上到了50Mbps以上。方法是在驱动里检查skb数据的对齐情况有需要时才拷贝否则直接提交给ieee80211_rx。第二个是中断合并。芯片每次收一个包就触发一次中断的开销很大尤其是小包场景。打开芯片的RX complete aggregation功能让芯片凑够一定数量或者超时后再通知主机能有效降低CPU占用。我测试过打开这个功能后CPU占用可以降一半左右。第三个是调整NAPI权重和网络队列的预算值。这两者在net_device的配置和NAPI的poll函数里都可以调节。如果卡顿出现在CPU繁忙时刻可以试着提高预算让每次poll处理更多数据包。5.3 电源管理和调度优化的经验嵌入式产品对功耗要求高WiFi的电源管理做不好整机续航直接崩掉。Linux上的无线子系统提供了丰富的电源管理接口从iw dev wlan0 set power_save on到驱动内部的动态PSMPower Save Mode切换都可以调。软MAC方案的电源管理实现比较灵活你可以决定让mac80211的PSM机制接管还是自己在驱动里做。我的经验是除非芯片有非常明确的硬件PSM机制否则优先用mac80211的PSM实现因为它能正确处理TIM和缓存帧。我自己改过最久的一个bug是动态PSM切换时没有正确缓存数据帧导致切换后立即丢包。此外还可以考虑在系统空闲时调低WiFi的工作频率或者在不需要数据时把接口down掉以此省电。但这些策略的取舍需要结合实际产品场景比如对低时延敏感的设备频繁切PSM反而会让用户觉得卡。6. 从驱动开发到方案落地的一些经验6.1 开发节奏和版本管理的建议一个WiFi驱动项目从拿到SDK到稳定量产按我个人的经验节奏大致可以用3-4-3原则来概括。前面三成时间花在环境搭建、硬件验证、SDK梳理上中间四成时间花在代码移植、设备树适配、基础通信打通以及前几轮调试上最后三成时间会花在各种边缘场景打磨上温升、弱信号、重负载、长时间稳定性、兼容性测试。整个项目期间版本管理千万不能马虎。我见过太多团队用“代码备份-最终版-最终版2”这种方式管理代码最后一出问题都不知道该回退到哪个版本。建议从第一天就用git并且每次可编译的改动都打tag。驱动的每个里程碑状态要能快速重现比如“能扫描”“能连接”“吞吐达标”“功耗达标”各打一个tag后面回归测试直接切tag验证效率翻倍。6.2 多平台移植的注意事项WiFi芯片本身不挑平台但用户往往需要它在不同的SoC上跑。每次换平台都需要重点核查以下几项设备树里的接口配置是否匹配新平台。不同SoC的SDIO控制器差异不小甚至同一个SoC在不同封装上引出的WiFi引脚都可能不同。这些都必须在硬件调试前确认清楚否则纯属浪费时间。中断号和GPIO的映射逻辑。不同平台的GPIO控制器索引方式不一样有时还需考虑IOMMU的映射关系。驱动里写死的地址或寄存器偏移要全部改成从设备树或平台数据结构获取不然代码几乎没法跨平台。时钟频率设置。WiFi芯片对SDIO时钟频率有上限要求不同平台能提供的最大时钟也不同稳妥的做法是在设备树里配置时钟频率。6.3 与硬件/射频工程师的协作经验WiFi驱动问题有些根因并不在软件而是射频前端参数、天线匹配、晶振频率漂移等硬件问题。当你排查驱动查了很久没头绪时不妨拉上硬件工程师量测一下芯片的供电、时钟信号、射频输出功率经常能发现问题出在硬件端。另外有个经验是如果吞吐量偏低而驱动代码看来看去没有明显问题可以优先怀疑射频校准数据没有烧录或者丢失。很多WiFi模组出厂时会在OTP里烧录射频校准数据驱动加载时会读取这些数据。如果数据丢了芯片工作频率会偏移表现就是通信距离变短、吞吐偏低、误码率升高。这时候优先检查模组型号、外部时钟频率、校准数据读取流程是不是正确。这属于驱动和射频的交叉领域很多纯做驱动的人不熟悉把它提出来是想给大家一个排查方向很多时候问题不在你反复检查的代码路径里而在你从未怀疑过的外围环境里。
返回列表