
做Linux驱动开发尤其是嵌入式方向的几乎没人能绕开WiFi。很多人以为WiFi驱动就是“把芯片的vendor SDK塞进内核编一下就行”真等到板子上的WiFi模块不工作、网速拉胯、连上就断的时候才发现自己对这个“黑盒子”其实一无所知。这篇文章不聊什么“破解WiFi密码”之类的歪门邪道那是另一个灰色领域跟驱动开发没有半毛钱关系。我们只关心一件正事在Linux系统里让一颗WiFi芯片正常工作、稳定收发数据并且在出问题时能快速定位是哪里坏了。这个内容覆盖的范围很广从接口类型选型、内核框架选择到具体probe函数的编写、设备树节点配置、固件加载再到吞吐量调优和日志排查全部串起来看你才能真正理解一套WiFi驱动在系统里扮演什么角色。不管你是刚入门驱动开发的新手还是在做方案集成、系统裁剪的老手这篇文章都值得花点时间从头到尾过一遍。因为WiFi驱动和普通的字符设备驱动、I2C设备驱动完全不是一回事它的数据路径更长、状态机更多、中断和底半部机制更复杂坑也就特别多。1. 整体思路与方案选型1.1 先分清你手里是哪种WiFi芯片做WiFi驱动开发的第一步永远不是打开代码编辑器而是先搞明白你的WiFi芯片到底是接在哪个总线上、走哪种主控接口。这一点直接决定了后续的驱动框架和整个开发路径一旦选错方向后面全白干。市面上常见的WiFi芯片接口大致有三种SDIO、USB和PCIe。SDIO接口在路由器、IPC网络摄像头、智能音箱这类嵌入式设备里最普遍特点是数据吞吐稳定、管脚少很多SoC都内置了SDIO控制器接一颗SDIO WiFi模块是性价比很高的方案。USB接口的WiFi模块则是开发板玩家和桌面Linux用户接触最多的比如那些免驱USB网卡插上就能用的背后其实是一个完整的USB设备驱动在跑。PCIe接口一般出现在笔记本无线网卡和高性能网卡上吞吐量最大但驱动复杂度也最高而且通常会涉及电源管理和热拔插的细节。接口类型典型场景驱动复杂度吞吐能力调试难度SDIO路由器、IPC、IoT中高中等偏上较难协议栈多USB开发板、桌面USB网卡中中等中等PCIe笔记本、高性能无线网卡高高较高这里有个非常常见的坑新人拿到一块USB WiFi模块就照着网上某篇讲SDIO WiFi驱动的文章去改代码结果怎么编译都过不了或者加载了模块但network interface死活不出现。原因很简单——SDIO驱动要注册的是sdio_driverUSB驱动要注册的是usb_driver两者挂在完全不同的子系统上init和exit流程也毫无共通之处。所以第一步一定是确认硬件接口别想当然。1.2 驱动框架怎么选mac80211还是cfg80211确定了硬件接口之后紧接着要面对的是软件层面的框架选择。Linux内核里WiFi驱动涉及两个核心框架cfg80211和mac80211。很多初学者分不清它俩的关系我打个比方如果把WiFi芯片比作一个“快递分拣中心”那么cfg80211就是整个行业的管理委员会负责制定规则、发放执照、对接运输公司内核网络栈和用户态工具mac80211则是标准的作业流程手册告诉你分拣、装车、卸货应该按什么顺序来而你写的驱动就是具体到这个分拣中心里的员工按照手册干活但搬运和分拣的细节得自己实现。具体到代码层面cfg80211是内核与用户态nl80211通信的桥梁用户用iw命令扫描、连接、配置AP底层走的都是nl80211最终会调用到cfg80211提供的接口。mac80211则实现了IEEE 802.11 MAC层的通用逻辑比如管理帧处理、加密、重传等它定义了一套ieee80211_ops结构体厂商驱动只需要把硬件相关的操作填进去就行。如果你的芯片是“软MAC”SoftMAC类型驱动就基于mac80211来写这也是绝大多数嵌入式WiFi芯片的常态如果芯片是“全MAC”FullMAC类型MAC层功能全在固件里完成那驱动就不需要mac80211直接利用cfg80211提供的API收发数据即可。我当时第一次做RTL8723DS的时候就纠结了很久到底要不要用mac80211。后来被老同事一句话点醒了内核源码里drivers/net/wireless/realtek/rtl8xxxu就是这类芯片的参考它选择了mac80211框架那我也跟着走。选框架的原则其实很简单——优先参考内核里已合入的同系列驱动厂商SDK可以看但别盲从因为厂商代码经常基于很老的内核版本写的拿到新版内核上编译可能一堆错误。2. 核心细节解析与实操要点2.1 数据流用户态的包如何跑到天线驱动开发绕不开数据流WiFi驱动的数据流比以太网驱动长得多我建议你先把这条链路在脑子里画出来后续所有调试思路都是沿着这条链路展开的。发送方向用户进程调用socket发送数据数据进入内核协议栈经过TCP/IP协议栈处理之后最终到达网络设备层。网络设备层会调用驱动注册的ndo_start_xmit函数这个函数拿着内核交给你的sk_buff把IP报文封装成802.11格式的帧然后交给芯片固件固件再通过DMA或其它方式和无线射频硬件交互最后从天线发出去。接收方向反过来芯片收到无线帧产生中断驱动在中断处理程序里做最小化处理然后通过NAPI机制调度收包过程把收到的数据从32位/64位的DMA缓冲区搬到内存里组成sk_buff剥掉802.11头部封装成以太网帧格式最终调用netif_receive_skb提交给内核协议栈。这里有一个重点你写驱动时看到的sk_buff在802.11层和网络层之间是有格式转换的。这就是为什么经常有人抓包发现“怎么多了个雷迪头”其实就是mac80211框架把来自上层的普通以太网帧加上802.11头部转换成了无线帧格式。理解了这个转换过程排查“连上热点但ping不通网关”这类问题时你就能快速判断是驱动封装出错还是上层路由没配好。2.2 关键结构体与注册流程WiFi驱动里必须认识的结构体不少但真正核心的就那么几个net_device、net_device_ops、cfg80211的wiphy、mac80211的ieee80211_hw和ieee80211_ops以及你在probe时注册使用的实际驱动结构体。拿基于mac80211的SDIO WiFi驱动为例整个注册流程大致是在probe函数里调用ieee80211_alloc_hw分配ieee80211_hw结构体并设置硬件能力标识比如支持的频段、带宽、加密方式等。用SET_IEEE80211_DEV和SET_IEEE80211_PERM_ADDR把设备指针和MAC地址关联到hw上。调用wiphy_read_of_fwnode或手动配置设备树属性把天线增益、功率限制等信息读进来。调用ieee80211_register_hw正式注册到mac80211框架。之后框架会自动创建一个wlan0网络接口驱动只需要在网络接口层提供net_device_ops的回调比如ndo_open、ndo_stop、ndo_start_xmit。static int wifi_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct ieee80211_hw *hw; struct my_wifi_priv *priv; int ret; /* 1. 分配mac80211硬件对象 */ hw ieee80211_alloc_hw(sizeof(*priv), my_wifi_ops); if (!hw) return -ENOMEM; priv hw-priv; priv-hw hw; priv-func func; sdio_set_drvdata(func, priv); /* 2. 初始化SDIO通信和中断 */ ret sdio_enable_func(func); if (ret) goto err_free_hw; ret sdio_claim_irq(func, wifi_sdio_irq); if (ret) goto err_disable_func; /* 3. 加载固件到芯片 */ ret wifi_load_firmware(priv); if (ret) goto err_release_irq; /* 4. 注册到mac80211 */ ret ieee80211_register_hw(hw); if (ret) goto err_release_irq; return 0; err_release_irq: sdio_release_irq(func); err_disable_func: sdio_disable_func(func); err_free_hw: ieee80211_free_hw(hw); return ret; }这个流程看起来简单实际坑很多。最典型的错误是分配hw时传入的硬件ops没填全或者填了错误的回调函数。mac80211框架在注册时会检查ops里的关键函数比如tx、start、stop、config等少了任何一个就注册失败。另一个高频错误是sdio_claim_irq之后忘了在probe失败路径上释放导致模块卸载后再次插拔系统直接崩溃。2.3 别忘了固件芯片自己不干活很多WiFi芯片内部没有ROM里的完整固件上电之后只是一堆“半成品”硬件。你需要从文件系统里读出一段二进制固件通过SDIO/USB/PCIe接口写到芯片的SRAM里芯片才能正常工作。这就是为什么Linux内核里有个叫linux-firmware的仓库里面放了大量网卡固件文件。驱动加载固件时一般调用request_firmware接口。这个函数会从/lib/firmware目录下找文件或者通过设备树中指定的firmware-name属性去找。找到后驱动把固件内容组织成芯片能识别的格式通过接口下发。固件加载失败的表现非常典型dmesg里能看到firmware request的报错驱动probe也可能直接失败因为很多芯片固件没加载的话连最基本的功能寄存器都读不到值。所以调试WiFi驱动遇到驱动初始化失败第一件事先去确认固件文件有没有放在正确的位置文件权限对不对。我见过太多人翻代码找半天bug最后发现只是固件拷贝丢了。3. 实操过程与核心环节实现3.1 内核配置与编译环境拿到一颗WiFi芯片准备写驱动的第一个实操动作是配置内核。内核配置这一步卡住了很多人因为在menuconfig里找WiFi驱动选项就不容易。以经典芯片RTL8723DS为例你在内核里要开启的选项大致包括Device Drivers --- Network device support --- Wireless LAN --- * Realtek rtl8xxxu support同时还需要确认这几个基础选项已经开启Networking support --- Wireless --- * cfg80211 - wireless configuration API * mac80211 - wireless stack support这里有个很隐蔽的坑如果你编内核时把cfg80211和mac80211都编成模块M而厂商驱动也编成模块加载顺序就必须谨慎。modprobe通常会根据依赖自动处理但如果你用insmod手动加载就很容易出现先加载厂商驱动、再加载mac80211结果驱动probe时报找不到mac80211符号的错误。所以新手阶段我建议把mac80211和cfg80211直接编进内核厂商驱动编成模块这样能少踩一半的动态加载坑。内核配置还涉及一个细节很多SoC的WiFi芯片是通过SDIO接口连接的但SDIO控制器本身也要在内核里开启比如CONFIG_MMC、CONFIG_MMC_SDHCI等。如果你的SDIO控制器驱动没编进内核WiFi驱动probe时根本看不到底层的SDIO设备接口列表里什么都没有。这个关联性很容易被忽略因为WiFi驱动本身的Kconfig文件并不依赖SDIO子系统的配置选项。3.2 编写probe函数与设备匹配当内核检测到SDIO设备插入时会调用你注册的sdio_driver的probe函数。SDIO驱动的注册代码一般这样写static const struct sdio_device_id wifi_sdio_ids[] { { SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, SDIO_DEVICE_ID_RTL8723DS) }, { } }; MODULE_DEVICE_TABLE(sdio, wifi_sdio_ids); static struct sdio_driver wifi_sdio_driver { .name rtl8723ds, .id_table wifi_sdio_ids, .probe wifi_probe, .remove wifi_remove, }; module_sdio_driver(wifi_sdio_driver);注意module_sdio_driver这个宏它自动生成了module_init和module_exit并且把所有初始化逻辑收敛在一个地方。有的厂商代码喜欢手写init函数里面还做了很多SDIO重新枚举的操作我见过最夸张的SDK里probe没执行完就把设备拔掉重新枚举了一遍美其名曰“解决初始化时序问题”。这种代码能跑但极度脆弱换个SoC就崩。匹配机制是驱动开发的基础你写驱动本质上就是在写“当设备出现时驱动怎么初始化它”的逻辑。SDIO的设备匹配靠vendor ID和device IDPCIe一样USB也一样。设备树里的compatible属性则是给platform总线设备用的WiFi芯片如果直接挂在平台总线上少见但也有就得靠compatible来匹配。理解了这个关系你就知道为什么有些驱动是“一插上就自动加载”有些必须手动insmod——前者通常是因为设备ID匹配成功后者要么是模块没进depmod库要么是设备树里信息对不上。3.3 从零串起WiFi连接流程驱动注册完成wlan0接口出现不过这只是万里长征第一步。接下来用户执行iw dev wlan0 scan扫描热点时驱动内部会发生什么扫描过程大致是用户态iw通过nl80211向内核发一个扫描命令cfg80211收到后转给mac80211mac80211调用你实现的ops-hw_scan或ops-scan回调驱动控制芯片切换到扫描模式依次在每个信道上发Probe Request再收集Probe Response。扫描结果通过cfg80211_scan_done上报最终iw能列出一堆BSSID。扫描之后是连接。连接过程更复杂但驱动视角只需要关注几个关键回调配置BSSID、配置信道、配置加密参数、配置速率等。mac80211会调用ops-config和ops-bss_info_changed你的驱动需要根据这些参数去设置芯片寄存器让芯片进入关联状态处理四次握手的EAPOL帧。这里的难点在于时序——芯片在关联状态下才会接收/发送EAPOL帧如果驱动的状态机切换太慢四次握手就可能超时表现为“能连上热点但马上又掉了或者密码明明正确却一直连不上”。实际操作中我在这个阶段最常用的调试手段是打开mac80211的动态调试信息echo 0xffff /sys/module/mac80211/parameters/debug echo 0xffff /sys/module/cfg80211/parameters/debug打开之后内核的printk会开始刷连接状态机和帧收发日志配合wpa_supplicant的-d参数基本能把连接过程中的每一步都看得清清楚楚。这个技巧比对着示波器猜快一百倍。4. 设备树、裁剪与性能调优4.1 设备树中如何描述WiFi芯片如果你是在嵌入式平台开发设备树Device Tree)是绕不开的一环。很多WiFi芯片挂在SDIO总线上看起来不需要写设备树节点——因为SDIO设备是动态枚举的不是传统platform设备。但实际情况是现代嵌入式平台对WiFi的描述基本都会写在设备树里用来配置电源GPIO、复位GPIO、中断触发方式、以及固件名等。一个典型的SDIO WiFi设备树节点长这样sdio0 { status okay; non-removable; bus-width 4; wifi1 { compatible realtek,rtl8723ds; reg 1; interrupt-parent gpio0; interrupts 19 IRQ_TYPE_LEVEL_LOW; firmware-name rtl8723ds/rtl8723ds.bin; power-gpios gpio0 20 GPIO_ACTIVE_HIGH; reset-gpios gpio0 21 GPIO_ACTIVE_LOW; }; };这里有几个关键点。reg 1表示这是SDIO function 1设备对应的就是WiFi功能。interrupts配置绝对不能错中断触发方式错了最典型的故障是“收包收不到但发包正常”或者“设备一有流量就死机”。power-gpios和reset-gpios是电源控制逻辑很多模块对复位时序极其敏感GPIO拉高拉低的时间都要精确我曾经遇到过一个模块reset引脚低电平必须保持至少10ms才能拉高芯片才能稳定启动否则固件加载阶段就失败而且故障概率只有30%左右特别难查。关于设备树里的compatible属性很多驱动工程师会混淆。SDIO设备驱动本身走的是vendor/device ID匹配设备树里写compatible只是给驱动提供配置入口真正的匹配还是靠SDIO VID/PID。所以你不要以为在设备树里写个compatible就能让驱动自动匹配上除非你的设备是platform设备。这个误解我见过太多次了。4.2 系统裁剪时别把WiFi裁没了做嵌入式Linux系统裁剪优化是日常操作。裁剪的目的通常是缩小内核体积、减少启动时间但一个很常见的结果是裁剪过后WiFi驱动挂了。为什么会挂我踩过的坑主要有两类。第一类是把cfg80211和mac80211裁掉了。有些裁剪思路按照“不需要的功能全砍”的原则看到Wireless相关选项觉得用不上就直接n掉结果WiFi模块永远起不来。裁剪之前先用make menuconfig搜索确认一下cfg80211和mac80211的依赖链是否完整。第二类坑是文件系统精简时把firmware目录裁了。BusyBox根文件系统本身就很小有人为了节约几个MB把/lib/firmware整个删了或者只保留一部分。WiFi驱动加载时request_firmware失败表现为dmesg里有firmware request failed但驱动模块又还在系统不崩溃只是没网络。这个故障特别迷惑人因为驱动模块明明已经加载成功了。正确的裁剪思路是在内核config里把不需要的驱动全关成n把和WiFi相关的选项编成模块或者编进内核然后在根文件系统里保留/lib/firmware目录只放用到的固件文件。这样既省空间又不影响WiFi功能。除此之外如果系统里没有udev/mdev机制SDIO总线不会自动触发事件你可能还要在启动脚本里手动modprobe对应的驱动模块这一步很多人会漏掉。4.3 吞吐量与功耗调优WiFi驱动开发做到最后性能调优一定是重头戏。最常见的性能指标就是吞吐量用iperf测试一发TCP包吞吐量上不去问题到底出在哪先看协商速率。用iw dev wlan0 link看一下当前的TX/RX速率如果速率很低说明物理层就没对齐可能是信道干扰、天线没接好也可能是功率配置偏低。再看信号强度RSSI低于-70dBm基本就很难跑满速率了这种情况不是驱动的问题是硬件布板或者天线设计的问题。如果协商速率正常但吞吐量依然跑不上去问题多半在驱动本身的收包路径上。SDIO WiFi驱动常见瓶颈是DMA缓冲区分配和NAPI收包处理。有些芯片在高速场景下如果驱动没有充分利用NAPI批量收包而是逐包中断处理那么CPU占用会飙升吞吐量直接腰斩。打开/proc/interrupts看看WiFi中断的频率如果一秒钟几万次那就说明收包路径有问题。功耗调优也是产品落地绕不开的环节。WiFi芯片一般都有省电模式STA模式下会周期性进入PS Mode芯片和AP协商好唤醒窗口。省电模式如果实现得不好最常见的问题是“连接正常但ping延迟忽高忽低甚至丢包”。遇到这种情况可以先通过iw命令关闭省电iw dev wlan0 set power_save off如果关掉省电后延迟稳定了那基本可以确定是驱动里PS唤醒流程有问题。SDIO接口下主机唤醒WiFi芯片一般靠一个OOB中断线实现设备树里那个interrupts配置的就是这根线。很多时候是唤醒中断线配置不对芯片睡着之后叫不醒。这个问题的排查非常烧脑我的经验是先用逻辑分析仪抓唤醒引脚的时序确认芯片有没有真正收到唤醒信号。5. 常见问题与排查技巧实录5.1 排查问题抓哪些log很多人在群里问“WiFi连不上怎么办”也不说设备型号也不贴日志。这种问题没人能帮得了你。WiFi驱动的问题排查日志信息的针对性和完整性是第一位的。抓日志的时候先把这几个命令的输出全部记录下来dmesg | grep -i -E wifi|wlan|rtl|cfg80211|mac80211|firmware iw dev iw dev wlan0 link iw reg get cat /proc/interruptsdmesg是最重要的第一手信息。驱动在probe、固件加载、扫描、连接等各个阶段都会打印日志尤其是出错时几乎都会留下错误码——但前提是你驱动代码里写了足够的调试打印。如果驱动本身是“哑巴”没有任何打印就需要自己加。我见过太多厂商SDK里全是空的调试宏关键路径上一句打印都没有调试起来全靠猜。除了内核日志用户态的wpa_supplicant日志也非常关键。用-nl80211 -ddd启动wpa_supplicant日志级别开到最大能看到扫描、认证、关联、四次握手每一步的交互。把所有日志按时间顺序对齐就能定位出是驱动没响应还是上层协议栈没收到事件。这套“内核日志用户态日志”联合排查的方法是我调试WiFi问题时最依赖的手段。5.2 驱动模块加载不生效怎么办驱动编译成模块后insmod进去看似加载成功但网络接口就是不出现。这种问题的排查思路要按顺序来。先查加载时有没有报错。很多SDIO驱动在probe阶段会读取芯片寄存器验证硬件如果SDIO读写失败会返回-ENODEV或-ETIMEDOUT但insmod本身不一定会失败因为insmod只保证module_init函数执行了而module_init可能只是注册了driver结构体真正的probe要等到设备匹配才触发。所以加载成功后要看dmesg里有没有probe相关的错误。再查设备到底有没有被内核发现。在/sys/bus/sdio/devices目录下看看有没有挂载的设备如果没有说明SDIO控制器和WiFi芯片之间的枚举就有问题可能是原理图连线、上电时序也可能是SDIO控制器驱动没配对。如果设备枚举成功但驱动没probe那就是VID/PID匹配不上或者驱动编译时没使能对应的ID表。最后如果是手动insmod记得检查依赖模块有没有先加载。cfg80211和mac80211如果没有先加载进内核厂商模块insmod时会出现unknown symbol的报错。5.3 连上热点却上不了网这个问题排在驱动问题排查里最让新手头疼wpa_supplicant已经连上热点iw dev wlan0 link显示已关联但ping网关不通。遇到这种情况先别慌问题很可能根本不在驱动。从链路层往上逐层排查。第一确认有没有拿到IP地址。很多连上但上不了网的情况其实是DHCP失败。执行ifconfig wlan0看有没有192.168.x.x如果没有抓一下DHCP过程的日志看看有没有收到DHCP Offer。第二确认网关能不能ping通。能ping通网关说明二层和三层都是通的问题出在外部网络或者DNS。第三检查DNS配置。有时候网卡驱动和数据面一切正常但resolv.conf里写了一个不可达的DNS服务器浏览器看着就是“上不了网”。第四确认路由表。如果系统里有多个网口路由表可能会把去往默认网关的流量走向有线网卡那WiFi即使连接正常也白搭。那什么时候才该怀疑驱动如果AP已经关联但二层数据帧一直不通也就是说ARP请求发出去没回应这时才需要回头看驱动。尤其是抓包发现只有发出的帧、完全没有收包大概率是RX路径出了问题。可以用ethtool -S wlan0看有没有RX error计数再用ifconfig wlan0看RX packets有没有持续增长。如果RX计数一直为零而你能看到TX明显增长那基本确定驱动收包链路有问题按之前提到的中断和NAPI方向去查。5.4 断连与功耗问题的排查最后一类高频问题就是“用着用着WiFi掉线”或者“一休眠再唤醒WiFi就没了”。这类问题在嵌入式设备上特别常见尤其是电池供电的设备。排查思路是先区分是从AP端断开还是设备端主动断开。抓hostapd或AP侧的日志看断开时候的状态。如果是设备端主动断的多半是驱动的心跳检测keep-alive超时或者省电模式下唤不醒。如果是AP端踢掉的可能是信号太弱、认证超时、或者被AP认为“客户端消失”。很多时候这类问题是由驱动里的电源管理逻辑引起的。嵌入式设备在系统休眠时WiFi芯片的供电会被切掉但驱动的suspend/resume回调没有正确实现“重新加载固件并重新连接”的流程。这时候就算系统唤醒WiFi芯片还是出厂状态wlan0虽然还在但已经完全失联了。处理这类问题的标准流程是先确认内核有没有执行驱动的suspend回调然后在resume回调里打点对比芯片上电后的寄存器状态再决定是否需要重新加载固件。这里有一个实用技巧很多SoC在休眠后会切断SDIO controller的电源导致WiFi芯片掉电。此时SDIO总线上的device看起来还在内核不重新枚举但芯片其实已经复位所以驱动的resume流程一定要做“完整的重新初始化”。我之前给某个IPC平台调这个问题花了两天时间最后发现只是resume流程里少了一次“重置SDIO控制器”的调用导致芯片同步乱了。另外断连问题也经常和干扰有关尤其是2.4GHz频段在复杂电磁环境下的表现非常不稳定。如果你的产品在实验室测试一切正常一到客户现场就频繁断连那就得多考虑射频干扰的因素而不是一味怀疑驱动状态机。这种时候信道切换和抗干扰策略可能需要通过cfg80211的配置接口来调整。做WiFi驱动开发这么些年我最大的体会是WiFi驱动问题没有银弹只有靠扎实的日志分析和逐层排查。以前我在内核对WiFi驱动的理解也停留在“能用就行”直到一次莫名其妙地吞吐量上不去我摸了一个多星期才定位到是DMA缓冲区对齐问题。从那以后我每次调试都把日志和状态寄存器当作最优先的工具而不是一上来就改代码。最后分享一个小技巧在SDIO WiFi驱动的probe流程里加一个简单的“寄存器回读自检”。芯片初始化完之后写一个寄存器再读回来对比是否一致。这个方法简单到离谱但能快速确认SDIO通信本身有没有问题而不是等数据收发时才发现通道故障。这个自检逻辑在量产阶段的产测代码里也能复用建议你保留。再扩展一下WiFi驱动后续还能做的事情其实很多LED状态控制、P2P、softAP重启、和蓝牙的共存优化、Mesh组网支持等等。学驱动不只是为了“让网卡能上网”而是通过驱动这个窗口去理解Linux内核的各种子系统是如何协同工作的。把一套WiFi驱动从头到尾吃透你对platform bus、SDIO子系统、网络协议栈、内核内存管理的理解都会上一个台阶。