本节讨论使用DSF设备模拟器测试USB驱动程序的技术。
本节包括:
创建一个模拟USB 2.0 EHCI控制器
在模拟USB 2.0控制器中插入设备
模拟设备PNP枚举
用设备模拟器协调I/O
在计算机上重启模拟设备
使用设备仿真的测试策略
在将模拟设备插入模拟USB 2.0控制器之前,必须在目标系统上创建和枚举控制器。若要创建并枚举控制器,请运行DSF运行时在目标程序系统中安装的Softehcicfg.exe应用程序,该程序文件在\Program Files\dsf\softehci 文件夹中。
若要安装控制器,请打开命令窗口并运行 softehcicfg /install。这个命令将向DSF根枚举器驱动程序(Dsfroot.sys)发送一条消息,并告诉它枚举模拟EHCI控制器。您必须等待控制器枚举,然后才能将模拟设备插入其中。您可以查看是否通过检查设备管理器和查找“Microsoft USB 2.0 Host Controller Simulator”条目来枚举控制器,或者使用诸如微软 Windows Device Testing Framework (WDTF)之类的工具以编程方式枚举控制器。
注意,控制器的设备ID是"PCI\VEN_1414&DEV_0004&SUBSYS_00041414&REV_00"。这个ID是微软的一个被正式分配的PCI设备ID,它不应该与任何其他现有的PCI设备冲突。
您还可以通过使用Softehcicfg.exe意外地从目标系统中移除模拟的EHCI控制器。若要移除模拟的EHCI控制器,请打开命令提示符窗口并运行softehcicfg /remove。
您还可以枚举多个同时模拟的EHCI控制器,将实例参数添加到 softehcicfg /install。例如,您可以运行softehcicfg /install 0,然后运行softehcicfg /install 1。
控制器将由即插即用(PnP)管理器分配唯一的设备实例ID,这些ID以实例号结束,实例号表示为最多三个前导零。使用上面的示例,控制器可以被分配以下设备实例ID:
Instance 0:
PCI\VEN_1414&DEV_0004&SUBSYS_00041414&REV_00\1&1222FD94&9&0000
Instance 1:
PCI\VEN_1414&DEV_0004&SUBSYS_00041414&REV_00\1&1222FD94&9&0001
若要删除多个实例,请将实例参数追加到softehcicfg /remove。若要移除在较早示例中添加的两个控制器,请运行softehcicfg /remove 0,然后运行softehcicfg /remove 1。
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff538281%28v%3dvs.85%29
有两种方法将设备直接插入模拟USB 2.0控制器:
调用IDSF::HotPlug,传递设备的DSFDevice对象(IDSFDevice)。
调用SoftEHCIRootHubPort::HotPlug, 传递设备的SoftUSBDevice 对象(ISoftUSBDevice)。
通常,测试应用程序将把设备模拟器插入控制器,但是模拟器可以选择性地设计成将自己插入控制器。如果测试应用程序插入设备,则测试应用程序将需要访问其SoftUSBDevice 对象或其DSFDevice对象。每个对象都可以从另一个对象访问。考虑下面的VBScript代码示例。
Set DSFDevice = SoftUSBDevice.DSFDevice
const IID_ISoftUSBDevice = "{9AC61697-81AE-459A-8629-BF5D5A838519}"
Set SoftUSBDevice = DSFDevice.Object(IID_ISoftUSBDevice)
如果模拟设备被实现为COM对象,那么DSFDevice 对象或SoftUSBDevice 对象可以作为属性公开。例如, DSF USB Loopback Device Simulation通过LoopbackDevice::DSFDevice 属性公开DSF设备对象。
IDSF::HotPlug方法采用两个参数:总线的名称和模拟设备的DSFDevice对象。EHCI控制器模拟器的总线名称是“USB2.0”。HotPlug返回一个DSFBus 对象(IDSFBus),您可以稍后使用它来拔出设备。(例如,从\Program Files\dsf\USBLoopback文件夹中与DSF运行时一起安装的RunLoopbackSample.wsf loopback示例脚本中),考虑以下代码示例:
Dim LoopbackDev : Set LoopbackDev = WScript.CreateObject("SoftUSBLoopback.LoopbackDevice", "LoopbackEvent_")
Dim LoopbackDSFDev : Set LoopbackDSFDev = LoopbackDev.DSFDevice
Dim DSF : Set DSF = CreateObject("DSF.DSF")
Dim Bus : Set Bus = DSF.HotPlug(LoopbackDSFDev, "USB2.0")
... use the loopback device ...
Bus.UnPlug LoopbackDSFDev
为了使用SoftEHCIRootHubPort::HotPlug,你必须访问EHCI控制器模拟器。可以通过为控制器搜索IDSF::Devices 属性来访问此模拟器,如EnumSimulatedDevices例程中的以下示例脚本所示。
'/////////////////////////////////////////////////////////////////////////////
' Function EnumSimulatedDevices
'
' This function searches the collection of simulated devices
' referenced by DSF.Devices for a device that exposes an ancillary
' object from DSFDevice.Object with the specified GUID. If found it returns the
' DSFDevice object otherwise it returns Nothing.
'/////////////////////////////////////////////////////////////////////////////
const IID_IDSFBus = "{E927C266-5364-449E-AE52-D6A782AFDA9C}"
Dim CtrlrDev : Set CtrlrDev = EnumSimulatedDevices(IID_IDSFBus)
...
Private Function EnumSimulatedDevices(SearchObjectGUID)
Dim DevSought : Set DevSought = Nothing
Dim Dev : Set Dev = Nothing
Dim DSF : Set DSF = CreateObject("DSF.DSF")
Dim ObjSought : Set ObjSought = Nothing
For Each Dev in DSF.Devices
If Dev.HasObject(SearchObjectGUID) Then
Set ObjSought = Dev.Object(SearchObjectGUID)
If Not ObjSought Is Nothing Then
Set DevSought = Dev
Exit For
End If
End If
Next
Set EnumSimulatedDevices = DevSought
End Function
如果使用多个模拟EHCI控制器并且需要根据设备实例ID查找特定控制器,则可以修改前面的EnumSimulatedDevices示例函数,以通过检查循环中每个设备的InstanceID 属性来检查设备实例ID,如下面是代码示例。
For Each Dev in DSF.Devices
If Dev.HasObject(SearchObjectGUID) And (Dev.InstanceID = SearchID) Then
Set ObjSought = Dev.Object(SearchObjectGUID)
If Not ObjSought Is Nothing Then
Set DevSought = Dev
Exit For
End If
End If
Next
根集线器端口对象可从EHCI控制器的端口集合获得。下面的代码示例演示如何获取根集线器端口1的对象。
Dim CtrlrObj : Set CtrlrObj = CtrlrDev.Object(IID_EHCICtrlrObj)
Dim RootHubPorts : Set RootHubPorts = CtrlrObj.Ports
Dim RootHubPort1 : Set RootHubPort1 = RootHubPorts(1)
第一行从控制器的DSFDevice 对象中提取SoftEHCICtrlr 对象。第二行获取根集线器端口集合,第三行获取端口1的SoftEHCIRootHubPort 对象。
最后一步是使用设备模拟器的SoftUSBDevice 对象将设备模拟器插入根集线器端口。例如,考虑 loopback设备的以下代码示例。
const IID_ISoftUSBDevice = "{9AC61697-81AE-459A-8629-BF5D5A838519}"
RootHubPort1.HotPlug LoopbackDevice.DSFDevice.Object(IID_ISoftUSBDevice)
要删除设备,运行RootHubPort1.Unplug。
如果调用SoftEHCIRootHubPort::Unplug 或者 IDSFBus::UnPlug,目标系统将该调用视为意外移除,并且在功能上等同于从根集线器端口移除USB电缆而不首先在软件中启动移除(例如,通过使用安全移除硬件图标)。
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff542263(v%3dvs.85)
从目标系统的角度来看,模拟USB设备的枚举与真实USB设备的枚举实际上没有什么不同。您可以在设备管理器中看到设备,还可以通过使用工具(如微软 Windows Device Testing Framework (WDTF)或 SetupDiXxx API)以编程方式发现设备。
在Windows Vista的Windows Driver Kit(WDK)中包含的DSF版本(版本6000)中,在插入第二设备之前,必须等待枚举一个设备。类似地,当您拔出模拟设备时,在移除或插入附加设备之前,必须完成设备移除。
从WDK for Windows Server 2008(版本6001)中包含的DSF版本开始,可以使用外部集线器模拟器同时枚举多个设备。类似地,可以通过拔出父外部集线器模拟器来拔除多个设备。
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff542301(v%3dvs.85)
使用设备模拟器测试驱动程序通常需要使用设备启动I/O,同时在模拟器中运行操作以满足那些I/O请求。测试用例可以是肯定的(期望I/O成功完成)或否定的(期望I/O以特定错误完成)。
如果测试包括设备的多个实例,则必须将设备模拟器的实例与即插即用(PnP)设备实例匹配。目前,DSF中没有能够将模拟的USB设备与给定设备实例ID积极匹配的工具。
枚举设备的实例(例如,通过使用微软Windows Device Testing Framework [WDTF]),插入模拟设备,然后重新枚举以找到设备的其他实例。
通过使用RegisterDeviceNotification 来接收设备到达通知,并在插入模拟设备之后等待通知,从而处理WM_DEVICECHANGE消息或注册设备通知。有关WM_DEVICECHANGE 和RegisterDeviceNotification的更多信息,请参见MSDN Library 网站。
在您已经明确地标识了设备并且可以对其发起I/O之后,您必须确保设备模拟器相应地响应I/O请求。例如,考虑DSF USB Loopback Device Simulation,它具有一个具有一个批量IN端点和一个批量OUT端点的单个接口。Rwbulk.exe(您可以在\src\usb\bulkusb\exe文件夹中的WDK安装中找到)调用Microsoft Win32 API函数CreateFile 以打开bulkusb 驱动程序(在\src\usb\bulkusb\sys文件夹中)公开的设备接口,然后调用Win32 API函数ReadFile 和 WriteFile发送I/O请求。WriteFile 调用使控制器向大容量端点发送数据传输。loopback 设备通过将接收到的数据排队到批量IN端点,在CLoopbackDevice::OnWriteTransfer函数(在\src\Test\DSF\USB\SoftUSBLoopback文件夹中)中处理这个I/O事件。来自Rwbulk.exe的后续ReadFile调用导致控制器请求来自大容量IN端点的数据传输,然后大容量IN端点使用排队的数据。
您可以设计一个确定的肯定测试用例,以便loopback 设备向测试应用程序中触发一个事件,以指示收到批量OUT传输。测试应用程序可以调用WriteFile,等待确认收到批量OUT传输的事件,然后等待WriteFile调用完成。这个过程需要两个线程或使用异步重叠I/O,因为WriteFile调用将被阻塞,直到I/O完成。
您可以设计一个否定的测试用例,以便对loopback设备进行编程,以在下一个事务上从批量OUT端点返回事务错误。您可以通过向回送设备添加属性来完成这种情况(例如,LoopbackDevice.ErrOnNextBulkOUT = True)。
loopback设备可以在每次调用CLoopbackDevice::OnWriteTransfer开始时检查该属性,如果属性为真,则返回USB_ERR作为事务状态,并将属性重置为false。在设置此属性之后,测试应用程序可以调用WriteFile ,然后检查它是否返回相应的错误。
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff538279(v%3dvs.85)
模拟USB 2.0控制器在重启、关机、休眠和挂起操作之间被持久化,直到通过运行softehcicfg /remove被移除。
模拟的外部集线器和模拟的USB设备不会在DSF的当前版本中重新启动。
外部集线器和设备在关机、休眠、暂停时不会被删除,但是,在计算机重新启动时也不会出现。这种情况类似于在系统关闭或暂停USB电缆时物理删除连接到控制器的装置。
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff542262(v%3dvs.85)
设备模拟提供了新的机会,以提高您的驱动程序测试策略,是不可用的真实硬件。最明显的好处是您必须手动完成测试的自动化。总有一些测试用例必须手动运行,例如必须知道能够100%工作的通用用户集成场景和涉及无法模拟的潜在电气和时序问题的场景。
仿真提供了机会来达到代码路径和生成数据值,这在真实硬件不可能做到的。您应该彻底检查您的驱动程序和设备应用程序代码,以确定如何使用仿真覆盖这些情况。
仿真还允许在驱动程序开发过程中修改,这在真实硬件不可能做到的。在开发过程中,应考虑以下事项以加强过程:
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/windows/hardware/dsf/ff542527(v%3dvs.85)