云计算百科
云计算领域专业知识百科平台

windows 驱动实例分析系列: libusb驱动分析-驱动层源码篇(一)

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 提交、端点管理、描述符缓存 等核心功能。

赞(0)
未经允许不得转载:网硕互联帮助中心 » windows 驱动实例分析系列: libusb驱动分析-驱动层源码篇(一)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!