libusb-win32 内核驱动源码分析(第一篇):驱动框架、PnP 与电源管理
1. 驱动概述
src/driver/ 目录包含 libusb-win32 的内核模式驱动程序 libusb0.sys 的完整实现。该驱动是一个 WDM(Windows Driver Model)或 KMDF(内核模式驱动框架)风格的驱动程序,主要职责包括:
- 作为 USB 设备的功能驱动或过滤驱动,与用户态 DLL 交互。
- 处理 PnP(即插即用)和电源管理 IRP。
- 接收来自用户态的 IOCTL 请求,并将其转换为 USB 请求块(URB)提交给 USB 主机控制器驱动(USBD)。
- 管理设备配置、接口声明、端点管道等 USB 资源。
- 提供设备接口(Device Interface)供用户态程序通过 SetupAPI 发现设备。
驱动采用 单二进制双模式 设计:通过注册表项 SurpriseRemovalOK 判断是作为功能驱动(非过滤)还是过滤驱动(Upper/Lower Filter)。
2. 入口函数 DriverEntry(libusb_driver.c)
驱动入口按照标准 WDM 驱动实现:
- 填充 DRIVER_OBJECT 的 MajorFunction 数组,将 所有 IRP 主功能码 指向统一的派发函数 dispatch。
- 设置 DriverExtension->AddDevice 为 add_device。
- 设置 DriverUnload 为 unload。
- 检测操作系统版本:通过 RtlGetVersion 获取版本信息,若为 Windows 8 及以上(版本号 > 6.1),则使用 NonPagedPoolNx 作为内存池类型,否则使用 NonPagedPool。这保证了驱动能通过 Windows 8 及更高版本的驱动验证测试(要求非可执行分页池)。
3. 设备添加 AddDevice(libusb_driver.c)
add_device 是驱动为每个物理设备创建功能设备对象(FDO)并附加到设备栈的入口。
3.1 硬件过滤
- 读取硬件 ID(reg_get_hardware_id)和兼容 ID。
- 仅处理 USB 设备(包含 usb\\ 且包含 vid_ 和 pid_),跳过 USB 集线器(含 hub 或 class_09)。
- 检查已附加的设备驱动,若存在 picopp 或 libusb0 则跳过(避免重复绑定)。
- 检查是否存在 WDF 驱动(如 WinUSB、WUDF),若存在则设置 has_wdf = TRUE,后续将禁止该设备成为电源策略拥有者,防止蓝屏。
3.2 创建设备对象与符号链接
- 尝试创建设备名 \\Device\\libusb0-xxxx(循环 1 到 LIBUSB_MAX_NUMBER_OF_DEVICES 找到未使用的编号)。
- 创建符号链接 \\DosDevices\\libusb0-xxxx 供用户态 CreateFile 打开。
- 设备扩展大小固定为 sizeof(libusb_device_t)。
3.3 初始化设备扩展(libusb_device_t)
- 保存物理设备对象(physical_device_object)、设备编号、设备 ID。
- 设置初始电源状态 PowerDeviceD0 和 PowerSystemWorking。
- 调用 reg_get_properties 从注册表读取关键配置:
- SurpriseRemovalOK:若为 DWORD 1,则驱动作为功能驱动(is_filter = FALSE),否则作为过滤驱动(is_filter = TRUE)。
- InitialConfigValue:指定初始配置值(默认 SET_CONFIG_ACTIVE_CONFIG,即自动选择活动配置)。
- DeviceInterfaceGUIDs:用户指定的设备接口 GUID,若不存在则使用默认 GUID(Libusb0DeviceGuid 或 Libusb0FilterGuid)。
- 若检测到 has_wdf,设置 disallow_power_control = TRUE,禁止该驱动成为电源策略拥有者。
- 调用 clear_pipe_info 清空端点缓存。
- 初始化移除锁(remove_lock_initialize)。
- 若设备接口启用,调用 IoRegisterDeviceInterface 注册设备接口,并在设备接口注册表中写入 LUsb0 值(设备编号),便于用户态快速打开。
3.4 附着到设备栈
- 调用 IoAttachDeviceToDeviceStack 将新创建的设备对象附着到物理设备对象之上。
- 若成功,则 dev->next_stack_device 指向下层设备。
- 根据 is_filter 决定 target_device:
- 过滤驱动:target_device = physical_device_object(直接向 PDO 发送 URB)。
- 功能驱动:target_device = next_stack_device(向栈中的下一设备发送)。
- 若支持 LIBUSB_ENABLE_CONTRACT_VERSION_602(Win8+),调用 USBD_CreateHandle 和 USBD_QueryUsbCapability 检测设备速度(SuperSpeed/HighSpeed/FullSpeed)。
- 复制下层设备的标志(DO_BUFFERED_IO、DO_DIRECT_IO、DO_POWER_PAGABLE),确保兼容。
- 最后清除 DO_DEVICE_INITIALIZING 标志,设备可被打开。
4. 设备扩展结构 libusb_device_t(libusb_driver.h)
该结构是每个设备实例的上下文,包含丰富的成员:
| self | 本设备对象 |
| physical_device_object | 物理设备对象(PDO) |
| next_stack_device | 栈中的下层设备 |
| target_device | 实际发送 URB 的目标设备 |
| handle(USBD_HANDLE) | USBD 句柄(Win8+) |
| remove_lock | 移除锁,保护并发 PnP 操作 |
| is_filter / is_started | 驱动模式标志和设备启动状态 |
| surprise_removal_ok | 是否支持意外移除 |
| id | 设备编号(用于符号链接) |
| device_descriptor | 缓存的设备描述符 |
| config | 当前配置信息(句柄、值、索引、描述符缓存、接口数组) |
| power_state | 当前电源状态(系统/设备) |
| device_power_states | 支持的各系统状态对应的设备状态 |
| initial_config_value | 初始配置值 |
| device_id | 硬件 ID 字符串 |
| disallow_power_control | 是否禁止成为电源策略拥有者 |
| device_interface_guid / device_interface_name | 设备接口 GUID 和名称 |
| control_read/write_timeout | 控制传输超时(可由用户态设置) |
| speed | USB 速度(Low/Full/High/Super) |
| pending_sequence / pending_busy | 用于保证端点传输顺序的原子变量 |
5. IRP 分发 dispatch(dispatch.c)
所有 IRP 首先进入 dispatch 函数,根据主功能码和当前设备状态分流:
- IRP_MJ_PNP → dispatch_pnp
- IRP_MJ_POWER → dispatch_power(若没有下层设备,直接返回 STATUS_INVALID_DEVICE_STATE)
- 对于其他 IRP,检查是否属于本驱动(accept_irp):
- IRP_MJ_DEVICE_CONTROL:若设备已启动,调用 dispatch_ioctl;否则返回错误。
- IRP_MJ_CREATE:若设备已启动,若电源状态非 D0 且允许电源控制,则调用 power_set_device_state 上电,然后返回成功。
- IRP_MJ_CLOSE:释放该文件对象绑定的所有接口(release_all_interfaces)。
- IRP_MJ_CLEANUP:直接完成。
- 其他 → 返回 STATUS_NOT_SUPPORTED。
- 若 IRP 不属于本驱动(即发往下层设备),则调用 pass_irp_down 向下传递。
accept_irp 判断依据:若 OriginalFileObject->DeviceObject == dev->self,或(非过滤模式且设备接口启用且 DeviceObject == physical_device_object)则接受。
6. PnP 处理(pnp.c)
dispatch_pnp 处理各 PnP 子功能码:
-
IRP_MN_START_DEVICE:调用 PoSetPowerState 通知电源管理器设备已进入 D0;若设备接口启用,调用 IoSetDeviceInterfaceState(TRUE) 启用接口;最后将 IRP 向下传递,并设置完成例程 on_start_complete。在完成例程中,若非过滤模式且尚未启动,则根据 initial_config_value 调用 set_configuration 设置配置(若配置值为 SET_CONFIG_ACTIVE_CONFIG 则尝试设置为活动配置)。标记 is_started = TRUE。
-
IRP_MN_REMOVE_DEVICE:禁用设备接口,设置 is_started = FALSE,调用 remove_lock_release_and_wait 等待所有未决请求完成,关闭 USBD 句柄,向下传递 IRP,最后删除符号链接、释放设备接口名称、释放缓存的配置描述符、分离设备对象并删除本设备对象。
-
IRP_MN_SURPRISE_REMOVAL:禁用设备接口,释放接口名称,清除配置缓存,标记 is_started = FALSE。
-
IRP_MN_STOP_DEVICE:仅标记 is_started = FALSE,不执行特殊操作,直接向下传递。
-
IRP_MN_DEVICE_USAGE_NOTIFICATION:根据下层设备的 DO_POWER_PAGABLE 标志设置本设备对象标志,向下传递并设置完成例程,在完成后恢复标志。
-
IRP_MN_QUERY_CAPABILITIES:若非过滤模式,将 Capabilities->SurpriseRemovalOK 设置为 dev->surprise_removal_ok;向下传递,在完成例程中保存 Capabilities->DeviceState 到 dev->device_power_states。
其他 PnP IRP 直接向下传递。
7. 电源管理(power.c)
电源管理处理 IRP_MJ_POWER,特别是 IRP_MN_SET_POWER。
- 若设备为过滤驱动或 disallow_power_control 为真,则设置完成例程 on_filter_power_state_complete,该例程仅记录电源状态变化并调用 PoSetPowerState 同步设备状态。
- 若为功能驱动且允许电源控制,则设置完成例程 on_power_state_complete,该例程在系统电源状态变化时,根据 dev->device_power_states 映射表调用 power_set_device_state 更改设备状态(非阻塞)。
- 对于设备电源状态变化,若新状态低于当前状态(如进入 D3),在发送 IRP 前先调用 PoSetPowerState 通知电源管理器。
- power_set_device_state 函数通过 PoRequestPowerIrp 发出设备电源设置 IRP,可选择阻塞等待完成。
该设计允许过滤驱动不干预电源策略,而功能驱动可完全控制设备电源。
8. 设备接口与注册表键值
驱动在 add_device 阶段从注册表(设备硬件键)读取若干值:
- SurpriseRemovalOK:决定是否为功能驱动。
- InitialConfigValue:决定启动后自动设置的配置。
- DeviceInterfaceGUIDs:用户可指定设备接口 GUID,否则使用默认值(过滤驱动用 Libusb0FilterGuid,功能驱动用 Libusb0DeviceGuid)。
驱动在启动/移除时调用 IoSetDeviceInterfaceState 启用/禁用接口,使用户态程序可通过 SetupAPI 发现设备。
9. 移除锁机制
为防止设备在 PnP 移除过程中仍有 I/O 请求正在处理,驱动实现了自定义移除锁(libusb_remove_lock_t):
- usage_count:原子计数,表示当前活跃的 I/O 请求数。
- remove_pending:标记设备正在被移除。
- event:事件对象,用于等待所有请求完成。
所有 IOCTL 处理函数在开始处理前调用 remove_lock_acquire(递增计数),完成时调用 remove_lock_release(递减计数)。当收到 IRP_MN_REMOVE_DEVICE 时,调用 remove_lock_release_and_wait,将 remove_pending 设为 TRUE,释放两次(使计数可能减为 0),然后等待事件,直到所有请求释放。
10. 总结
第一篇文档涵盖了 libusb-win32 内核驱动的基础骨架:
- 入口与添加:驱动如何初始化、创建设备对象、读取注册表配置并附着到设备栈。
- IRP 分发:统一的派发函数根据 IRP 类型和驱动程序模式进行分发。
- PnP 处理:完整响应启动、停止、移除、查询能力等关键 PnP 事件,并自动配置设备。
- 电源管理:区分功能驱动与过滤驱动,实现不同的电源策略处理。
- 设备接口与移除锁:保障设备可被用户态发现,并安全处理并发移除。
后续文档将深入分析 IOCTL 处理、USB 传输、URB 提交、端点管理、描述符缓存 等核心功能。
网硕互联帮助中心


![第5章,[Win32 章节] :绘制填充区域-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/08/20260828170845-6a91c09d7e3b9-220x150.png)



评论前必须登录!
注册