联发科 MT7601U 是一款极具代表性的 802.11n 2.4GHz 单芯片 USB 无线网卡,广泛应用于树莓派外设、小米随身 Wi-Fi、TP-Link TL-WN727N 以及各种嵌入式 IoT 设备中。
在 Linux 驱动演进史中,MT7601U 驱动经历了一次教科书级别的重构:从厂商原版(MediaTek / Ralink Vendor Driver)近 10 万行庞杂非标的代码,到开发者 Jakub Kicinski(kuba-moo,现 Linux 内核 Network 子系统 Maintainer) 与 Felix Fietkau(OpenWrt 核心成员) 基于 Linux 官方 mac80211 基础设施重写,并于 Linux 4.2 成功合入内核主线(drivers/net/wireless/mediatek/mt7601u)。
本文深入分析该项目的代码结构,拆解厂商原版与 Kuba 主线重构版本的架构差异与设计精华。
[!NOTE] 关联笔记:关于 Wi-Fi 协议栈 802.11 通信 6 阶段、Firmware
.bin加载机制与 Rust 驱动原理,请参阅 Wi-Fi 网络协议栈与 Rust Wi-Fi Driver 实战;关于通用 Linux 驱动设计模式,请参阅 Linux 驱动开发与设计模式。
硬件背景与芯片特性
MT7601U 芯片的架构包含以下核心特征:
- 主控与微控制器(MCU):芯片内部集成了基于 32 位 Andes (AND) RISC 架构的微控制器,负责硬件级 MAC 帧处理、电源管理与基带校准。
- 总线接口:USB 2.0 接口,暴露 EP0 控制端点、Bulk OUT 数据发送端点、Bulk IN 数据接收端点及 Interrupt 中断通知端点。
- EEPROM 存储:芯片内置 512 字节 EEPROM(或 eFuse 熔丝区),保存 MAC 地址、信道目标功率表(Target Power Table)与晶振频偏参数。
阶段一:厂商原版驱动(Vendor Driver)架构拆解
厂商原版驱动基于 Ralink/MediaTek 早期统一的无线驱动开发框架(RTMP Framework)移植而来。
目录分层结构
src/
├── chips/ # MT7601 芯片特定寄存器配置 (mt7601.c, rtmp_chip.c)
├── common/ # 协议与数据处理核心 (76 个文件: cmm_data.c, cmm_wpa.c, rtusb_bulk.c 等)
├── mcu/ # Andes MCU 固件加载与命令下发 (rtmp_and.c, rtmp_M51.c)
├── mac/ # MAC 层寄存器配置
├── phy/ # BBP & RF 射频校准
├── rate_ctrl/ # 厂商私有查表速率控制算法 (alg_legacy.c, RaCfg.c)
├── sta/ & ap/ # Station 客户端模式与 SoftAP 热点模式实现
└── os/linux/ # Linux OS 抽象层 (usb_main_dev.c, rt_linux.c, cfg80211.c)
核心实现特性
- 自建私有 802.11 MLME 状态机:
厂商驱动并没有充分利用 Linux 内核内置的
mac80211框架,而是在src/common/mlme.c中自己从零编写了一套庞大的 802.11 状态机(扫描、认证、关联、握手)。 - 私有用户态交互接口:
虽然
os/linux/cfg80211.c提供了部分cfg80211桩代码,但其底层严重依赖私有的Wireless Extensions(ioctliwpriv/iwconfig) 命令进行配置。 - 全局巨型结构体
PRTMP_ADAPTER(pAd): 驱动中几乎每一个函数都需要传递一个包含了数千个字段的全局上下文指针pAd,且包含了大量条件编译宏开关(如#ifdef CONFIG_STA_SUPPORT)。
历史局限性
- 代码膨胀:代码总量接近 10 万行,存在大量重复与陈旧的硬编码逻辑。
- 内核兼容性差:强烈绑定特定的旧版内核(如 Linux 2.6 / 3.x),随着内核 API 的演进(如
net_device与 API 修改),在新内核上编译频繁报错。
阶段二:Kuba 主线重构版本(Upstream kuba-moo)深度解构
为解决厂商驱动无法合入主线、新内核无法编译的问题,Jakub Kicinski 与 Felix Fietkau 完全基于 Linux 官方 mac80211 基础设施重新编写了 mt7601u 驱动。
统一分层架构
Linux 用户态 (wpa_supplicant / NetworkManager / iw)
│
│ nl80211 / socket
▼
Linux 内核 mac80211 协议栈
(速率控制 Minstrel-HT, 802.11 状态机 MLME)
│
│ ieee80211_ops 回调 (start/stop/config)
▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ kuba-moo mt7601u 主线驱动 (mt7601u.ko) │
├─────────────────────────────────────────┬───────────────────────────────────────────────┤
│ 核心控制与系统注册 (`main.c`, `init.c`) │ 注册 ieee80211_hw 接口,实现 mac80211 ops 回调 │
├─────────────────────────────────────────┼───────────────────────────────────────────────┤
│ 数据传输与 URB DMA 引擎 (`dma.c`, `tx.c`) │ 零拷贝分配 dma_buf/URB,封装 TxWI 头并批量提交 │
├─────────────────────────────────────────┼───────────────────────────────────────────────┤
│ USB 传输与总线控制 (`usb.c`) │ 统一管理 EP0 vendor_request,批量读写 MAC/BBP │
├─────────────────────────────────────────┼───────────────────────────────────────────────┤
│ MCU 固件交互与控制 (`mcu.c`) │ 内核 `request_firmware` 加载及 MCU 通信消息队列 │
├─────────────────────────────────────────┼───────────────────────────────────────────────┤
│ MAC / PHY / EEPROM 射频模块 │ EEPROM 解包 (`eeprom.c`),动态 BBP/RF 频偏校准 │
└─────────────────────────────────────────┴───────────────────────────────────────────────┘
│
│ USB EP0 / EP Bulk IN / OUT
▼
MT7601U 硬件无线网卡
核心模块实现细节
1. 系统注册与 mac80211 回调 (main.c, init.c)
重构后的驱动彻底抛弃了私有 MLME 状态机,而是直接向 Linux 无线子系统注册 struct ieee80211_ops 接口契约:
// main.c: 注册内核标准 ieee80211_ops 回调
static int mt7601u_start(struct ieee80211_hw *hw)
{
struct mt7601u_dev *dev = hw->priv;
mutex_lock(&dev->mutex);
ret = mt7601u_mac_start(dev); // 启动 MAC 与硬件 RX 接收
if (ret)
goto out;
// 调度定期物理校准定时任务
ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, MT_CALIBRATE_INTERVAL);
ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, MT_CALIBRATE_INTERVAL);
out:
mutex_unlock(&dev->mutex);
return ret;
}
// 将 802.11 速率控制完全交给内核标准的 Minstrel-HT 算法
// 链路状态通知、SSID 扫描与 802.11 状态转换全部由 mac80211 接管
2. USB 控制传输与防热拔出抽象 (usb.c)
针对 USB 通信,驱动封装了统一的 mt7601u_rr() (Read Register) 和 mt7601u_wr() (Write Register) API,使用 MT_VEND_MULTI_READ / MT_VEND_MULTI_WRITE 指令集通过 EP0 进行高效批量读写:
// usb.c: 安全的 Vendor Request 封装与重试机制
int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
const u8 direction, const u16 val, const u16 offset,
void *buf, const size_t buflen)
{
struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) {
ret = usb_control_msg(usb_dev, pipe, req, req_type, val, offset, buf, buflen, MT_VEND_REQ_TOUT_MS);
if (ret == -ENODEV)
set_bit(MT7601U_STATE_REMOVED, &dev->state); // 标记热拔出
if (ret >= 0 || ret == -ENODEV)
return ret;
msleep(5);
}
return ret;
}
3. 零拷贝 DMA 内存分配与数据流 (dma.c, tx.c)
驱动使用 struct mt7601u_dma_buf 管理 URB 与 DMA 地址,结合 usb_alloc_coherent() 实现零拷贝传输:
// dma.c: 申请 DMA 一致性内存并绑定 URB
bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_size_t len, struct mt7601u_dma_buf *buf)
{
struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
buf->urb = usb_alloc_urb(0, GFP_KERNEL);
buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma);
return !buf->urb || !buf->buf;
}
在 tx.c 中,驱动将内核传入的 sk_buff 数据包加上 4 字节的无线指示标头 TxWI(包含速率索引、加密 Key ID 与 PID 序列号),并使用 URB_NO_TRANSFER_DMA_MAP 标志直接提交给 xHCI 发送,大幅降低 CPU 开销。
4. 固件载入与 Andes MCU 异步通信 (mcu.c)
驱动使用标准的 request_firmware() API 动态载入 /lib/firmware/mt7601u.bin,并实现了 mt7601u_mcu_send_msg() 命令队列:
// mcu.c: 向 Andes 32 位 MCU 发送指令并等待 completion 响应
int mt7601u_mcu_send_msg(struct mt7601u_dev *dev, int cmd, const void *data, int len, bool wait_resp)
{
// 构造含有 CmdID 的 MCU 数据报文,提交 Bulk OUT URB 并使用 completion 机制等待响应
}
5. 动态校准与温度补偿 (phy.c, eeprom.c)
eeprom.c解析 512 字节 EEPROM 映像,提取 MAC 地址、LNA 增益、目标功率表与晶振频偏。phy.c通过内核延迟工作队列cal_work定期触发 DPD 功率补偿与温度频偏修正,保证无线收发信号稳定。
厂商原版 vs Kuba 主线重构全方位对比
| 对比维度 | 厂商原版驱动 (Vendor Driver) | Kuba 主线重构驱动 (Upstream kuba-moo) |
|---|---|---|
| 代码规模 | 约 70+ 个 C 文件,接近 10 万行代码 | 仅 20 多个 C 文件,~2 万行代码(精简 80%) |
| 802.11 协议栈 | 自建私有 MLME 状态机,代码极度冗余 | 纯正接入 Linux mac80211 官方基础设施 |
| 速率控制算法 | 私有查表硬编码速率切换 | 直接使用内核标准 Minstrel-HT 动态速率算法 |
| 用户态工具支持 | 依赖私有 iwpriv 与 Wext 命令 | 完全支持现代 nl80211 / iw / NetworkManager |
| 内存与 DMA 管理 | 复杂私有分配,易引发内存泄漏 | 使用 usb_alloc_coherent 实现零拷贝 DMA 映射 |
| 内核长久维护 | 强烈绑定旧内核(2.6/3.x),新内核编译报错 | Linux 4.2+ 原生自带,随 Linux 主线持续维护 |
总结
Kuba Kozak 对 MT7601U 驱动的重写,是嵌入式 Linux 驱动开发的经典范例。它表明:写好一个 Linux 驱动,绝不是机械地移植厂商的私有堆砌代码,而是深入理解 Linux 内核的标准子系统(如 mac80211 / VFS / USB),用最精炼、符合内核规范的架构去复用系统机制。