wifit3芯片发现机制:pkgutil遍历与VID:PID轻量导入的零注册表设计
【免费下载链接】wifit3Wifite but USB-only & cross-platform.项目地址: https://gitcode.com/GitHub_Trending/wi/wifit3
wifit3 是一款纯 Python、跨平台的 USB 无线网卡审计工具,其芯片发现机制用pkgutil.iter_modules一次遍历chips/目录,只导入每个芯片包的"轻量名片"(SUPPORTED_IDS+import_driver),在内存中构建VID:PID → 驱动的索引表——全程不写注册表、不装系统驱动,插卡即识别。
为什么需要"零注册表"的芯片发现?
传统做法是让操作系统为网卡绑定内核驱动(Linux 的 DKMS 模块、Windows 的 NDIS/WinUSB),但 wifit3 的设计目标是"内置无线协议栈,绕开驱动版本地狱"(见 README.md 的 Why 章节)。因此它必须自己回答一个问题:
现在总线上插着的 USB 设备,是不是我支持的无线芯片?如果是,该用哪个驱动类?
答案藏在一个文件里:manager.py。它的模块 docstring 一句话概括了整套设计:
"The map is built by a
pkgutilwalk overchips/*that imports only each package's light__init__(SUPPORTED_IDS + import_driver), never the driver itself." —— manager.py#L1-L7
第一步:pkgutil 遍历 chips/ 目录
所有芯片驱动都放在src/wifit3/chips/下,每个芯片一个子包(如 chips/ar9271_v2/、chips/rtl8822bu_dkms/)。核心函数supported_ids()用pkgutil枚举这些子包:
for mod_info in pkgutil.iter_modules(chips_pkg.__path__, chips_pkg.__name__ + "."): if not mod_info.ispkg: continue ... mod = importlib.import_module(mod_info.name) for entry in getattr(mod, "SUPPORTED_IDS", None) or (): mapping[(entry.vid, entry.pid)] = Claim(entry, key, mod.import_driver)—— manager.py#L127-L152
三个关键设计点:
pkgutil.iter_modules自动发现:新增一个芯片包放进chips/,遍历自动把它纳入索引,无需修改任何中央清单文件。- 结果被
@functools.cache缓存:整个 VID:PID 映射表只在进程内构建一次,之后每次总线扫描都是 O(1) 字典查询。 - 冲突即断言:如果两个包声明了同一个 VID:PID,
assert slot not in mapping会立刻让问题暴露,而不是静默覆盖。
第二步:轻量导入——__init__.py只是"名片"
每个芯片包的__init__.py刻意做得极轻:只声明它认领的 VID:PID 目录(SUPPORTED_IDS)和一个import_driver()函数,绝不导入真正的 driver.py。以 AR9271 为例(ar9271_v2/init.py):
_IDS = ( (0x0CF3, 0x9271, "AR9271", None, AMBIGUOUS_AR9271), (0x0846, 0x9030, "AR9271", None, Netgear.N150), ... ) SUPPORTED_IDS = [DeviceID(vid, pid, chipset, vendor, product) for ...] def import_driver(): from .driver import AR9271V2Driver return AR9271V2Driver—— ar9271_v2/init.py#L9-L38
这就是"轻量导入"的含义:遍历 20+ 个芯片包时,每个包只付出几行数据 + 一次DeviceIDdataclass 构造的成本。真正动辄数千行、内含固件加载与寄存器序列的driver.py,直到某个设备实际匹配上才通过import_driver()被惰性导入(driver.py#L79-L83 定义的from_usb_device契约即在此时兑现)。
VID:PID 索引表里装了什么?
索引的值是一个Claim三元组(manager.py#L65-L69):
| 字段 | 含义 |
|---|---|
entry | DeviceID:vid/pid/chipset/品牌/型号,UI 展示用 |
key | 驱动族 key(如rtl8822bu),用于定位 setup 流程 |
import_driver | 惰性导入函数,匹配命中时才执行 |
品牌与型号的静态目录集中在 products.py 中,按厂商分 Enum 组织(如TPLink.ARCHER_T4U_PLUS),共覆盖数十个品牌数百张零售网卡。完整的硬件支持矩阵见 SUPPORTED-HARDWARE.md。
第三层:两类"撞号"的仲裁
真实世界里同一个 VID:PID 可能对应不同芯片,wifit3 用两个仲裁表处理:
🔀 DKMS 族(同一芯片、两个驱动包):如rtl8821au_dkms与rtl8821au(mainline 移植)都支持同一批 VID:PID。_DKMS_BY_DIR(manager.py#L100-L112)为每族指定默认赢家,可用环境变量(如WIFIT3_RTL8821=mainline)切换;遍历中落选包直接跳过。
🔀 共享 VID:PID(不同芯片、同一编号):最著名的是2357:0137——TP-Link Archer T4U 同一编号下有 MT7612U 与 RTL8822CU 两种方案。_VIDPID_FAMILIES(manager.py#L88-L117)在扫描时读取设备的实际 USB 描述符(端点0x85是否为 bulk-IN)来现场裁决:
def _resolve_2357_0137(dev): for intf in dev.get_active_configuration(): if any(ep.bEndpointAddress == 0x85 for ep in intf): return "mt76x2u" # bulk-IN 0x85 = Mediatek return "rtl8822cu"总线扫描:一次 O(1) 查表
识别流程最终在devices()(manager.py#L236-L256)中闭环:libusb 枚举总线上所有设备 → 用(idVendor, idProduct)直接查supported_ids()字典 → 命中即为受支持网卡。没有任何设备树解析、注册表读写或驱动安装动作——"零注册表"体现在整条链路里:
- 发现阶段只读
__init__.py级数据; - Windows 上未绑定 WinUSB 的卡也能通过 PnP 枚举被"预告"出来(manager.py#L259-L274),由 setup 层事后安装 WinUSB;
- 还有一条平行的
zerocd_ids()遍历(manager.py#L155-L169)收集"伪装成 U 盘"的 ZeroCD 网卡编号,供弹射手处理。
打包也能自检:supported_ids() 非空检查
因为依赖动态pkgutil遍历,PyInstaller 打出的单文件包必须把chips/下所有子包收集进去,否则应用能启动却"看不到任何网卡"。wifit3 在main.py#L36-L40 内置了打包自检:
"
supported_ids()is non-empty: the pkgutil chip-discovery walk only enumerates drivers PyInstaller actually collected, so an empty map means the bundle shipped with no drivers."
即启动自检会主动调用一次芯片发现,映射表为空则判定打包损坏——发现机制的"注册表"就是 Python 包结构本身,连自检都无需额外清单。
一图总览:三层轻量设计
| 层次 | 机制 | 成本 |
|---|---|---|
| 目录发现 | pkgutil.iter_modules遍历chips/* | 仅一次,带functools.cache |
| 名录读取 | 每包只导入轻量__init__(SUPPORTED_IDS) | 几行数据 + dataclass 构造 |
| 驱动加载 | import_driver()惰性导入driver.py | 仅匹配命中时 |
延伸阅读
- 设备层核心:manager.py
- 驱动契约:chips/driver.py
- 设备标识模型:models/device_id.py
- 硬件支持矩阵:docs/SUPPORTED-HARDWARE.md
- 移植新芯片的方法论:docs/porting/METHODOLOGY.md
💡一句话总结:wifit3 把"驱动注册"从操作系统层面搬进了 Python 包结构层面——pkgutil负责发现,__init__.py负责名录,import_driver()负责按需加载,三者合力实现了零注册表、零配置、插卡即识别的芯片发现机制。
【免费下载链接】wifit3Wifite but USB-only & cross-platform.项目地址: https://gitcode.com/GitHub_Trending/wi/wifit3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考