如何使用 WinUSB 与 USB 设备 (USBDevice) 通信

选择 USB 设备的驱动程序模型

https://github.com/libusbx/libusbx/wiki/Windows-Backend

  • WinUSB does not support Windows 2003 ( 32bit/64bit )
  • WinUSB does not support Windows XP 64 bit ( not officially support on 64bit Windows XP)
  • WinUSB cannot be used to send an actual reset command to an USB device.
    This is a limitation of WinUSB.
  • WinUSB does not support isochronous transfers.
  • WinUSB cannot be used to set a device configuration that is different from the first one.
    This is a limitation of KMDF USB I/O Target.
  • WinUSB does not support multiple concurrent applications
  • WinUSB does not support USB 1.0 or USB 1.1
    If the USB Device Descriptor’s bcdUSB field is equal to 0x0100 or 0x0110,
    the hub driver will skip the query for the MS OS Descriptor and
    move to the “Serial Number String Descriptor Query” state

https://github.com/pbatard/libwdi/wiki/WCID-Devices

What is WCID?

A WCID device, where WCID stands for "Windows Compatible ID", is an USB device that provides extra information to a Windows system, in order to facilitate automated driver installation and, in some circumstances, allow immediate access.

WCID allows a device to be used by a Windows application almost as soon as it is plugged in, as opposed to the the usual scenario where an USB device that is neither HID nor Mass Storage requires end-users to perform a manual driver installation. As such, WCID can bring the 'Plug-and-Play' functionality of HID and Mass Storage to any USB device (that sports a WCID aware firmware).

WCID is an extension of the WinUSB Device functionality, put forward by Microsoft during the Windows 8 Developer Preview. However, it uses capabilities (Microsoft OS Descriptors, or MODs) that have been part of Windows since Windows XP SP2.

As of May 2012, an automated WinUSB WCID driver is provided on all platforms starting with Windows Vista. 
On Windows 8, it is native to the system (i.e. there's no need to fetch the driver online) whereas,
for Vista and Windows 7, it will be obtained from the internet, through Windows Update.


Microsoft Winusb Device Driver 1.0.0.0

http://download.windowsupdate.com/msdownload/update/driver/drvs/2012/05/20484220_5f2718fc6d44c5ae61d4275d679bbf1ededf58e5.cab http://de.drivers.exdat.com/download/17651/20484220_5f2718fc6d44c5ae61d4275d679bbf1ededf58e5.cab The package provides the installation files for Microsoft WinUSB Device Driver version 1.0.0.0. In order to manually update your driver, follow the steps below (the next steps): 1. Go to Device Manager (right click on My Computer, choose Manage and then find Device Manager in the left panel)
2. Right click on the hardware device you wish to update
and choose Update Driver Software
3. Choose to select the location of the new driver manually
and browse to the folder where you downloaded the driver

制造 USB 设备的独立硬件供应商 (IHV) 必须经常为应用程序提供访问设备功能的途径。
在过去,这意味着使用 Windows 驱动程序模型 (WDM) 为设备实现一个功能驱动程序,
并将该驱动程序安装在设备栈中系统提供的协议驱动程序之上。
Windows 驱动程序基础 (WDF) 现在是 USB 驱动程序的首选模型。

它为 IHV 提供 3 个选项来提供访问 USB 设备的途径:

  • 使用 WDF 用户模式驱动程序框架 (UMDF) 实现用户模式驱动程序。

  • 使用 WDF 内核模式驱动程序框架 (KMDF) 实现内核模式驱动程序。

  • WinUsb.sys 作为设备的功能驱动程序安装,
    并提供一个使用 WinUSB API <WinUsb.dll> 访问设备的应用程序。

    WinUSB 在 Windows XP 上不支持 WinUSB 选择性暂停

Windows 8 USB 驱动程序堆栈体系结构

该图分别显示了 USB 2.0 和 USB 3.0 的 USB 驱动程序堆栈。
当设备附加到 xHCI 控制器时,Windows 会加载 USB 3.0 驱动程序堆栈。
USB 3.0 堆栈是 Windows 8 中的新功能。

当设备连接到 eHCI、oHCI 或 uHCI 控制器时,Windows 会加载 USB 2.0 驱动程序堆栈。
USB 2.0 驱动程序堆栈随 Windows XP Service Pack 1 (SP1) 及更高版本的 Windows 操作系统一起提供。

Windows 8 USB 驱动程序堆栈

对于自定义 USB 设备应该使用哪个设备安装程序类?

独立硬件供应商 IHV 必须使用与 USB 设备类型(而不是总线类型)相关的安装程序类
如果要开发一个 Microsoft 还没有为其提供现有类 GUID 的设备类型,可以定义一个新的设备安装程序类

Windows 8 中定义了一个名为 USBDevice 
( ClassGuid = {88BAE032-5A81-49f0-BC3D-A4FF138216D6} ) 的新安装程序类
如果您正在开发一个设备类型,请将您的设备与 <USBDevice> 而不是 <安装程序类 USB> 关联。 
USBDevice 类支持 Windows Vista 以及更高版本的操作系统。

安装程序类 USB ( ClassGuid = {36fc9e60-c465-11cf-8056-444553540000} )
仅为 USB 主控制器和 USB 集线器保留,并且不能用于其他设备类别。
不正确地使用此安装程序类可能导致设备驱动程序无法通过 Windows 徽标测试。 

WinUSB 架构和模块

Windows USB (WinUSB) 是 Microsoft 提供的 USB 设备的通用驱动程序。

WinUSB 体系结构由内核模式驱动程序 (Winusb.sys) 和用户模式动态链接库 (Winusb.dll) 组成。

  • Winusb.sys 是一种内核模式驱动程序,可在 USB 设备的内核模式设备堆栈的协议驱动程序之上
    作为筛选器驱动程序或功能驱动程序进行安装。
  • Winusb.dll 是一种公开 WinUSB 功能的用户模式 DLL。
    当 Winusb.sys 作为设备的功能驱动程序安装后,应用程序可以使用这些功能与其进行通信。

对于不要求自定义功能驱动程序的设备,Winusb.sys 可以在该设备的内核模式堆栈中作为功能驱动程序进行安装。
随后,用户模式流程通过使用一组设备 I/O 控制请求调用 WinUSB 功能 ( WinUsb_Xxx 函数 )来与 Winusb.sys 进行通信。

 

上图显示了实施三个设备接口类的示例 WinUSB 配置,其中每个类都有单个注册设备接口:

  • Winusb.sys 的实例 1 注册设备接口 A,设备接口 A 支持用户模式驱动程序 (Usboem.dll)。
  • Winusb.sys 的实例 2 注册设备接口 B,设备接口 B 支持通过使用系统服务 (SVCHOST)
    与 Winusb.dll 进行通信的扫描仪的用户模式驱动程序 (Usbscan.exe)。
  • Winusb.sys 的实例 3 注册设备接口 C,设备接口 C 支持固件更新实用程序 (Usbfw.exe)。

仅加载了 Winusb.sys 的一个实例。
PDO 可以表示非复合设备(例如,上图中的示例 1),
也可以表示复合设备上的接口或接口集合(例如,实例 2 和 3)。
对于 USB 无线移动通信设备类 (WMCDC) 设备,PDO 甚至可以表示多个接口集合。

所有用户模式应用程序都可以与 USB 堆栈进行通信,
方法是加载 WinUSB 动态链接库 (Winusb.dll) 并调用由此模块公开的 WinUSB 功能。
  

WinUSB 驱动程序 (Winusb.sys)

 

WinUSB 动态链接库 (Winusb.dll)

This section describes the following functions, exposed by Winusb.dll,
which user-mode client drivers and applications can use
to communicate with USB devices.
WinUSB functions require Windows XP or later.
You can use these functions in your C/C++ application
to communicate with your USB device.
Microsoft does not provide a managed API for WinUSB.

Routine                               Description

WinUsb_Initialize                     Creates a WinUSB handle for the device specified by a file handle.
WinUsb_Free                           Releases all of the resources that WinUsb_Initialize allocated.

WinUsb_ControlTransfer                Transmits control data over a default control endpoint.
WinUsb_ReadPipe                       Reads data from the specified pipe.
WinUsb_WritePipe                      Writes data to a pipe.

WinUsb_GetOverlappedResult            Retrieves the results of an overlapped operation on the specified file.

WinUsb_ResetPipe                      Resets the data toggle and clears the stall condition on a pipe.
WinUsb_AbortPipe                      Aborts all of the pending transfers for a pipe.
WinUsb_FlushPipe                      Discards any data that is cached in a pipe.

WinUsb_QueryPipe                      Retrieves information about a pipe that is associated with an interface.
WinUsb_QueryDeviceInformation         Retrieves information about the physical device 
                                      that is associated with a WinUSB handle.

WinUsb_GetDescriptor                  Gets the requested descriptor.
WinUsb_GetAssociatedInterface         Retrieves a handle for an associated interface.

WinUsb_QueryInterfaceSettings         Retrieves the interface descriptor for 
                                      the specified alternate interface settings for a particular interface handle.
WinUsb_GetCurrentAlternateSetting     Gets the current alternate interface setting for an interface.
WinUsb_SetCurrentAlternateSetting     Sets the alternate setting of an interface.

WinUsb_GetPowerPolicy                 Gets the power policy for a device.
WinUsb_SetPowerPolicy                 Sets the power policy for a device.

WinUsb_GetPipePolicy                  Gets the policy for a specific pipe (endpoint).
WinUsb_SetPipePolicy                  Sets the policy for a specific pipe (endpoint).
View Code
/*++

Copyright (c) 2002 Microsoft Corporation

Module Name:

        wusb.h

Abstract:

        Public interface to winusb.dll

Environment:

        Kernel Mode Only

Notes:

        THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
        KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
        PURPOSE.

        Copyright (c) 2001 Microsoft Corporation.  All Rights Reserved.


Revision History:

        11/19/2002 : created

Authors:

--*/

#ifndef __WUSB_H__
#define __WUSB_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <windows.h>
#include <winusbio.h>


typedef PVOID WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;

   
#pragma pack(1)

typedef struct _WINUSB_SETUP_PACKET {
    UCHAR   RequestType;
    UCHAR   Request;
    USHORT  Value;
    USHORT  Index;
    USHORT  Length;
} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;

#pragma pack()



BOOL __stdcall
WinUsb_Initialize(
    IN HANDLE DeviceHandle,
    OUT PWINUSB_INTERFACE_HANDLE InterfaceHandle
    );


BOOL __stdcall
WinUsb_Free(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle
    );


BOOL __stdcall
WinUsb_GetAssociatedInterface(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN UCHAR AssociatedInterfaceIndex,
    OUT PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle
    );



BOOL __stdcall
WinUsb_GetDescriptor(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR DescriptorType,
    IN  UCHAR Index,
    IN  USHORT LanguageID,
    OUT PUCHAR Buffer,
    IN  ULONG BufferLength,
    OUT PULONG LengthTransferred
    );

BOOL __stdcall
WinUsb_QueryInterfaceSettings(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN UCHAR AlternateInterfaceNumber,
    OUT PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor
    );

BOOL __stdcall
WinUsb_QueryDeviceInformation(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN ULONG InformationType,
    IN OUT PULONG BufferLength,
    OUT PVOID Buffer
    );

BOOL __stdcall
WinUsb_SetCurrentAlternateSetting(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR SettingNumber
    );

BOOL __stdcall
WinUsb_GetCurrentAlternateSetting(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    OUT PUCHAR SettingNumber
    );


BOOL __stdcall
WinUsb_QueryPipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR AlternateInterfaceNumber,
    IN  UCHAR PipeIndex,
    OUT PWINUSB_PIPE_INFORMATION PipeInformation
    );


BOOL __stdcall
WinUsb_SetPipePolicy(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID,
    IN  ULONG PolicyType,
    IN  ULONG ValueLength,
    IN  PVOID Value
    );

BOOL __stdcall
WinUsb_GetPipePolicy(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN UCHAR PipeID,
    IN ULONG PolicyType,
    IN OUT PULONG ValueLength,
    OUT PVOID Value
    );

BOOL __stdcall
WinUsb_ReadPipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID,
    IN  PUCHAR Buffer,
    IN  ULONG BufferLength,
    OUT PULONG LengthTransferred,
    IN  LPOVERLAPPED Overlapped
    );

BOOL __stdcall
WinUsb_WritePipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID,
    IN  PUCHAR Buffer,
    IN  ULONG BufferLength,
    OUT PULONG LengthTransferred,
    IN  LPOVERLAPPED Overlapped    
    );

BOOL __stdcall
WinUsb_ControlTransfer(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  WINUSB_SETUP_PACKET SetupPacket,
    IN  PUCHAR Buffer,
    IN  ULONG BufferLength,
    OUT PULONG LengthTransferred,
    IN  LPOVERLAPPED Overlapped    
    );

BOOL __stdcall
WinUsb_ResetPipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID
    );

BOOL __stdcall
WinUsb_AbortPipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID
    );

BOOL __stdcall
WinUsb_FlushPipe(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  UCHAR PipeID
    );

BOOL __stdcall
WinUsb_SetPowerPolicy(
    IN  WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN  ULONG PolicyType,
    IN  ULONG ValueLength,
    IN  PVOID Value
    );

BOOL __stdcall
WinUsb_GetPowerPolicy(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN ULONG PolicyType,
    IN OUT PULONG ValueLength,
    OUT PVOID Value
    );

BOOL __stdcall
WinUsb_GetOverlappedResult(
    IN WINUSB_INTERFACE_HANDLE InterfaceHandle,
    IN LPOVERLAPPED lpOverlapped,
    OUT LPDWORD lpNumberOfBytesTransferred,
    BOOL bWait
    );
    

PUSB_INTERFACE_DESCRIPTOR __stdcall
WinUsb_ParseConfigurationDescriptor(
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    IN PVOID StartPosition,
    IN LONG InterfaceNumber,
    IN LONG AlternateSetting,
    IN LONG InterfaceClass,
    IN LONG InterfaceSubClass,
    IN LONG InterfaceProtocol
    );

PUSB_COMMON_DESCRIPTOR __stdcall
WinUsb_ParseDescriptors(
    IN PVOID    DescriptorBuffer,
    IN ULONG    TotalLength,
    IN PVOID    StartPosition,
    IN LONG     DescriptorType
    );


#ifdef __cplusplus
}
#endif

                   
#endif //__WUSB_H__

在 Windows 8 中,Microsoft 为 WinUSB 提供的信息 (INF) 文件 (Winusb.inf) 

包含 USB\MS_COMP_WINUSB(作为设备标识符字符串)。
这可为 MS 操作系统描述符中拥有匹配的 WinUSB 兼容 ID 的那些设备
自动加载 Winusb.sys 作为功能驱动程序。此类设备称为 WinUSB 设备。
硬件制造商不需要为其 WinUSB 设备分发 INF 文件,从而使最终用户的驱动程序安装过程更为简单。

如果你是开发设备的 OEM 或独立硬件供应商 (IHV),想要将 Winusb.sys 用作功能驱动程序,
并且想要自动加载驱动程序而不必提供自定义 INF,本主题中的信息对你适用。

什么是 WinUSB 设备

WinUSB 设备是一种通用串行总线 (USB) 设备,其 固件 定义了某些 Microsoft 操作系统 (OS) 特征描述符,
这些描述符将兼容 ID 报告为 "WINUSB"。

WinUSB 设备的用途是让 Windows 将 Winusb.sys 作为设备的功能驱动程序载入,而无需自定义 INF 文件。
对于 WinUSB 设备,你无须为设备分发 INF 文件,对最终用户而言,这大大简化了驱动程序安装过程。
相反,如果你需要提供自定义 INF,则不应将设备定义为 WinUSB 设备和在 INF 中指定设备的硬件 ID。

Microsoft 提供了 Winusb.inf,其中包含将 Winusb.sys 作为 USB 设备的设备驱动程序安装所需的信息。

在 Windows 8 之前,要将 Winusb.sys 作为功能驱动程序加载,你需要提供自定义 INF。
自定义 INF 指定设备特定的硬件 ID,同时包括内置 Winusb.inf 的部分。
这些部分是实例化服务、复制内置二进制文件以及注册设备接口 GUID(应用程序查找设备和与设备通讯必须使用 GUID)所必需的。
有关编写自定义 INF 的信息,请参阅 WinUSB (Winusb.sys) 安装。

在 Windows 8 中,内置 Winusb.inf 文件经过更新后可让 Windows 自动将 INF 与 WinUSB 设备匹配。

使用内置 Winusb.inf 安装 WinUSB 设备

在 Windows 8 中,内置 Winusb.inf 文件已更新。
INF 包括引用称为 "USB\MS_COMP_WINUSB" 的兼容 ID 的安装部分。

[Generic.Section.NTamd64]

%USB\MS_COMP_WINUSB.DeviceDesc%=WINUSB,USB\MS_COMP_WINUSB

更新的 INF 还包括称为 "USBDevice" 的新安装程序类。
"USBDevice" 安装程序类可供这些 Microsoft 未提供内置驱动程序的设备使用。
通常,此类设备不属于定义良好的 USB 类(如音频、Bluetooth 等),并且需要自定义驱动程序。
如果你的设备是 WinUSB 设备,则该设备很可能不属于 USB 类。
因此,你的设备必须安装在 "USBDevice" 安装程序类下。更新的 Winusb.inf 便于实现该要求。
注意,"USBDevice" 类不仅限于 WinUSB。
如果你有设备的自定义驱动程序,则可在自定义 INF 中使用 "USBDevice" 安装程序类。

在设备枚举过程中,USB 驱动程序堆栈会从设备读取兼容 ID。
如果兼容 ID 是 "WINUSB",Windows 会将其用作设备标识符,
并在更新的内置 Winusb.inf 中查找匹配,然后将 Winusb.sys 作为设备的功能驱动程序加载。

对于 Windows 8 之前的 Windows 版本,更新的 Winusb.inf 可通过 Windows 更新提供。
如果你的计算机配置为自动获取驱动程序更新,WinUSB 驱动程序将使用新的 INF 程序包进行安装,无需任何用户干预。

For versions of Windows earlier than Windows 8, the updated Winusb.inf is available through Windows Update.
If your computer is configured to get driver update automatically,
WinUSB driver will get installed without any user intervention by using the new INF package.

About using the USBDevice class:  
Do not use the "USB" setup class for unclassified devices.

That class is reserved for installing controllers, hubs, and composite devices.
Misusing the "USB" class can lead to significant reliability and performance issues.
For unclassified devices, use "USBDevice".
In Windows
8, to use "USBDevice" device class, simply add this to your INF.
[Version] Class
=USBDevice ClassGuid={88BAE032-5A81-49f0-BC3D-A4FF138216D6}
In Device Manager you will see a
new node USB Universal Serial Bus devices
and your device appears under that node.
In Windows
7, in addition to the preceding lines, you need to create these registry settings in the INF:

[Version]
Class=USBDevice
ClassGuid={88BAE032-5A81-49f0-BC3D-A4FF138216D6}
;---------- Add Registry Section ----------
[USBDeviceClassReg]
HKR,,,,"Universal Serial Bus devices"
HKR,,NoInstallClass,,1
HKR,,SilentInstall,,1
HKR,,IconPath,%REG_MULTI_SZ%," %systemroot%\system32\setupapi.dll,-20"

In Device Manager, you will see your device appear under USB Universal Serial Bus devices.
However, the device
class description is derived from the registry setting specified in your INF.
--Eliyas Yakub, Microsoft Windows USB Core Team

; winusbcompat.inf
; winusbcompat.cat
; Copyright (c) Microsoft Corporation. All rights reserved.
[Version] Signature="$Windows NT$" Class=USB Device ClassGuid={88BAE032-5A81-49f0-BC3D-A4FF138216D6} Provider=%WinPhone% CatalogFile=winusbcompat.cat DriverVer=05/11/2012,1.0 ; =============== Class section needed for downlevel OS =============== [ClassInstall32] AddReg = ClassInstall_AddReg [ClassInstall_AddReg] HKR,,,0,%ClassName% HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20" HKR,,NoInstallClass,,1 HKR,,BootCritical,,0 HKR,,Configurable,,1 ; ======================= End of Class section ======================== [DestinationDirs] DefaultDestDir = 12 [Manufacturer] %Generic.Mfg%=Generic.Section,NTx86,NTamd64,NTx86.6.0,NTamd64.6.0 ; =========================== Generic ================================= [Generic.Section.NTx86] ; Windows 2000 and XP %NULL.DeviceDesc%=NULL,USB\MS_COMP_WINUSB [Generic.Section.NTx86.6.0] ; Vista and later %USB\MS_COMP_WINUSB.DeviceDesc%=WinUsbDriver,USB\MS_COMP_WINUSB [Generic.Section.NTamd64] ; Windows 2000 and XP %NULL.DeviceDesc%=NULL,USB\MS_COMP_WINUSB [Generic.Section.NTamd64.6.0] ; Vista and later %USB\MS_COMP_WINUSB.DeviceDesc%=WinUsbDriver,USB\MS_COMP_WINUSB ; ======================= WinUsbDriver Sections ======================= [WinUsbDriver.NT] Include = Winusb.inf Needs = WINUSB.NT [WinUsbDriver.NT.Services] Include = Winusb.inf Needs = WINUSB.NT.Services ; =========================== NULL Sections =========================== [DummyReg] [NULL.NT] AddReg = DummyReg [NULL.NT.Services] AddService = , %NULL_SERVICE_INSTALL% ; ========================== Strings Section ========================== [Strings] ;Not localizable WinPhone="Windows Phone" REG_MULTI_SZ = 0x00010000 NULL_SERVICE_INSTALL = 0x00000002 ;Localizable Generic.Mfg="WinUsb Device" USB\MS_COMP_WINUSB.DeviceDesc="WinUsb Device" NULL.DeviceDesc="WinUsb Device (NULL)" ClassName = "Universal Serial Bus devices"

如何更改 WinUSB 设备的设备说明

对于 WinUSB 设备,设备管理器会显示“WinUsb 设备”作为设备说明。
该字符串是从 Winusb.inf 派生出来的。如果有多个 WinUSB 设备,所有设备都会获得相同的设备说明。

为了分别标识和区分设备管理器中的设备,Windows 8 在设备类中提供了一个新的属性,
指示系统优先使用设备报告的说明(在设备的 iProduct 字符串描述符中),而不是 INF 中的说明。
Windows 8 中定义的 "USBDevice" 类设置此属性。换句话说,
如果设备在 "USBDevice" 类下安装,系统会查询设备的设备说明,
并将设备管理器字符串设置为通过查询检索到的任何字符串。
在这种情况下,INF 中提供的设备说明将被忽略。 

新类属性在 Windows 的较早版本中不受支持。在较早版本的 Windows 上,若要自定义设备说明,
你必须编写自己的自定义 INF。( 不能自动加载 Winusb.sys 作为功能驱动程序 )

如何配置 WinUSB 设备

USB 设备在固件中存储设备、设备接口和端点的标准描述符。 
独立硬件供应商 (IHV) 还可以存储特定于类和供应商的描述符。 
但是,这些描述符可以包含的信息类型有限。 

通常,IHV 必须使用 Windows Update 或 CD 之类的介质向他们的用户
提供各种特定于设备的信息,比如图片、按钮、自定义驱动程序等等。

为了帮助 IHV 解决此问题,Microsoft 定义了 Microsoft 操作系统描述符。
IHV 可以使用这些描述符在固件中存储如今通常单独提供给客户的大部分信息。
支持 Microsoft 操作系统描述符的 Windows 版本使用控制请求检索信息,
使用它安装和配置设备,无需任何用户交互

Microsoft OS 描述符是什么?

Microsoft OS 描述符为 IHV 提供了一种方法,可以在设备固件中
将各种特定于供应商的信息存储为 OS 功能描述符,而不是与设备分开提供给用户。
可感知 Microsoft OS 描述符的 Windows 或 Windows 应用程序的版本
可以使用控制请求从设备中检索该描述符,而无需其他的用户交互。
尤其是,当初次插入设备时,Windows 可以自动检索该描述符,
并使用该信息给用户提供更完美的“即插即用”体验。

Windows 支持什么类型的 OS 功能描述符?
任何作为功能描述符存储的信息必须符合 Microsoft 已定义的标准格式之一。
未经 Microsoft 许可,不能定义或实现其他功能描述符。
Microsoft 已经定义了以下功能描述符。

Genre  -- 0x0000
此描述符是为 Windows 的未来版本设计的,现在还没有规范。

Extended Compat ID   -- 0x0004
Windows 使用类和子类代码帮助找到适合于 USB 设备的默认驱动程序。
但是,USB 设备工作组必须分配这些代码。这意味着实现了新功能的设备常常没有合适的类和子类代码,
所以 Windows 无法使用该代码选择默认驱动程序。IHV 可以通过将该信息作为扩展的兼容 ID OS
功能描述符存储在固件中来避免此问题。
然后 Windows 可以在设备插入时检索此信息,并使用它帮助确定加载哪个默认驱动程序。

Extended Properties  -- 0x0005 
目前,可以在两个层次上声明 USB 设备的属性:类层次和 devnode 层次。
该扩展属性 OS 功能描述符允许供应商将其他属性(比如帮助页面、URL 和图标)存储在设备固件中。

哪些 Windows 版本支持 Microsoft OS 描述符?

描述符                        Windows 版本
OS 字符串描述符                Windows XP SP1 及更高版本
扩展兼容 ID OS 功能描述符       Windows XP SP2 及更高版本
扩展属性 OS 功能描述符          Windows XP SP2 及更高版本
Genre OS 功能描述符           可能受未来的 Windows 版本支持

若要将 USB 设备标识为 WinUSB 设备,设备固件必须具有这些 Microsoft OS 描述符。

1) 支持 OS 字符串描述符

为了让 USB 驱动程序堆栈了解设备支持扩展的特征描述符,
设备必须定义存储在字符串索引 0xEE 处的 OS 字符串描述符
在枚举过程中,驱动程序堆栈查询字符串描述符。
如果存在描述符,驱动程序堆栈会假定设备包含一个或多个
OS 特征描述符和检索这些特征描述符所需要的数据。

检索的字符串描述符具有 bMS_VendorCode 字段值。
该值表示 USB 驱动程序堆栈必须用来检索扩展特征描述符的供应商代码。


#define bMS_VendorCode              ( 0x01 )
// "MSFT100" : index : 0xEE : langId : 0x0000
const U8 OS_StringDescritpor[ ] =
{ 0x12,  0x03,  'M',  0,  'S',  0,  'F',  0,  'T',  0,  '1',  0,  '0',  0,  '0',  0,  bMS_VendorCode,  0 };
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\<VVVVPPPPRRRR>

osvc
: 01 01

Indicates whether the operating system queried the device for Microsoft-Defined USB Descriptors.
If the previously-attempted OS descriptor query was successful, the value contains the vendor code
from the OS string descriptor.

  • 0x0000: The device did not provide a valid response to the Microsoft OS string descriptor request.

  • 0x01xx: The device provided a valid response to the Microsoft OS string descriptor request,
    where xx is the bMS_VendorCode contained in the response.

为什么必须将 OS 字符串描述符存储在索引 0xEE 上?
一台支持 Microsoft OS 描述符的设备必须包含一个 OS 字符串描述符,
存储在字符串索引 0xEE 位置。
该 OS 字符串描述符是一个标准的 USB 字符串描述符,具有以下功能:
1) 其存在表明该设备包含一个或多个 OS 功能描述符。
2) 它包含检索相关的 OS 功能描述符所需的数据。
3) 它包含一个签名字段,该字段可将 OS 字符串描述符和其他 IHV 可能选择存储在 0xEE 的字符串别开来。
4) 它包含一个允许未来修改 Microsoft OS 描述符的版本号。

如果在 0xEE 没有字符串描述符,或者该索引位置的字符串描述符
不是有效的 OS 字符串描述符,则 Windows 假设该设备不包含任何 OS 功能描述符

2) 设置兼容 ID 特征描述符

匹配内置 Winusb.inf 和加载 WinUSB 驱动程序模块所需要的扩展兼容 ID OS 特征描述符

扩展兼容 ID OS 特征描述符包含紧跟一个或多个功能部分的标题部分,具体取决于是否是复合设备。
标题部分指定整个描述符的长度、功能部分的数量以及版本号。

对于非复合设备,标题后紧跟一个仅与设备的接口关联的功能部分。
该部分的 compatibleID 字段必须指定 "WINUSB" 作为字段值。

复合设备有多个功能部分。
每个功能部分的 compatibleID 字段必须指定 "WINUSB"

// "WINUSB\0\0" : wIndex : 0x0004
const U8 WINUSB_ExtendedCompatId_Descritpor[ ] =
{
  0x28, 0x00, 0x00, 0x00,                         // dwLength
  0x00, 0x01,                                     // bcdVersion
  0x04, 0x00,                                     // wIndex
  0x01,                                           // bCount
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,       // Reserved[7]
  0x00,                                           // bFirstInterfaceNumber
  0x01,                                           // RESERVED ( 0x01 )
  'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,       // compactiableID[8]
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subCompactiableID[8]
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00              // Reserved[6]
};

3) 注册设备接口 GUID

注册设备接口 GUID 所需要的扩展属性 OS 特征描述符。
需要使用 GUID 从应用程序或服务查找设备、配置设备以及执行 I/O 操作。
在以前版本的 Windows 中,设备接口 GUID 注册通过自定义 INF 完成。
从 Windows 8 开始,你的设备应使用扩展属性 OS 特征描述符报告接口 GUID。

扩展属性 OS 特征描述符包含紧跟一个或多个自定义属性部分的标题部分。
标题部分描述整个扩展属性描述符,包含其总长度、版本号以及自定义属性部分的数量。
若要注册设备接口 GUID,使用 GUID 生成唯一的设备接口 GUID, 请添加将 

bPropertyName 字段设置为 "DeviceInterfaceGUID" 且将 
wPropertyNameLength 设置为 40 字节的自定义属性部分。
bPropertyData 字段设置为该 GUID,如 "{8FE6D4D7-49DD-41E7-9486-49AFC6BFE475}"。

注意,GUID 指定为 Unicode 字符串,且字符串长度为 78 字节(包括 Null 终止符)。
在设备枚举过程中,USB 驱动程序堆栈随后从扩展属性 OS 特征描述符检索 
DeviceInterfaceGUID 值,并在设备硬件注册表项中注册设备。
应用程序通过使用 
SetupDiXxx API 检索该值。

// L"DeviceInterfaceGUID" : wIndex = 0x0005
// L"{12345678-1234-1234-1234-123456789ABC}"
const U8 WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[ ] =
{
  0x8E, 0x00, 0x00, 0x00,     // dwTotalSize = Header + All sections
  0x00, 0x01,                 // bcdVersion
  0x05, 0x00,                 // wIndex
  0x01, 0x00,                 // wCount
  
  0x84, 0x00, 0x00, 0x00,     // dwSize -- this section
  
  0x01, 0x00, 0x00, 0x00,     // dwPropertyDataType
  
  0x28, 0x00,                 // wPropertyNameLength
  
  'D', 0x00, 'e', 0x00,       // bProperytName : WCHAR : L"DeviceInterfaceGUID"
  'v', 0x00, 'i', 0x00,       // bProperytName : WCHAR
  'c', 0x00, 'e', 0x00,       // bProperytName : WCHAR
  'I', 0x00, 'n', 0x00,       // bProperytName : WCHAR
  't', 0x00, 'e', 0x00,       // bProperytName : WCHAR
  'r', 0x00, 'f', 0x00,       // bProperytName : WCHAR
  'a', 0x00, 'c', 0x00,       // bProperytName : WCHAR
  'e', 0x00, 'G', 0x00,       // bProperytName : WCHAR
  'U', 0x00, 'I', 0x00,       // bProperytName : WCHAR
  'D', 0x00, 0x00, 0x00,      // bProperytName : WCHAR
  
  0x4E, 0x00, 0x00, 0x00,     // dwPropertyDataLength : 78 Bytes = 0x0000004E
  
  '{', 0x00, '1', 0x00,       // bPropertyData : WCHAR : L"{12345678-1234-1234-1234-123456789ABC}"
  '2', 0x00, '3', 0x00,       // bPropertyData
  '4', 0x00, '5', 0x00,       // bPropertyData
  '6', 0x00, '7', 0x00,       // bPropertyData
  '8', 0x00, '-', 0x00,       // bPropertyData
  '1', 0x00, '2', 0x00,       // bPropertyData
  '3', 0x00, '4', 0x00,       // bPropertyData
  '-', 0x00, '1', 0x00,       // bPropertyData
  '3', 0x00, '4', 0x00,       // bPropertyData
  '4', 0x00, '-', 0x00,       // bPropertyData
  '1', 0x00, '2', 0x00,       // bPropertyData
  '3', 0x00, '4', 0x00,       // bPropertyData
  '-', 0x00, '1', 0x00,       // bPropertyData
  '2', 0x00, '3', 0x00,       // bPropertyData
  '4', 0x00, '5', 0x00,       // bPropertyData
  '6', 0x00, '7', 0x00,       // bPropertyData
  '8', 0x00, '9', 0x00,       // bPropertyData
  'A', 0x00, 'B', 0x00,       // bPropertyData
  'C', 0x00, '}', 0x00,       // bPropertyData
  0x00, 0x00                  // bPropertyData
};

在枚举过程中,USB 驱动程序堆栈会读取扩展属性特征描述符并在该项下创建注册表项。 

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB
\<Device Identifier>\<Instance Identifier>\Device Parameters

DeviceInterfaceGUID : {12345678-1234-1344-1234-123456789ABC}

启用或禁用 WinUSB 电源管理功能

在 Windows 8 之前,要配置 WinUSB 的电源管理功能,
你必须在自定义 INF 的 HW.AddReg 部分中编写注册表项值。
在 Windows 8 中,你可以在设备中指定电源设置。
你可以通过扩展属性 OS 特征描述符报告值,从而为该设备启用或禁用 WinUSB 中的功能。
可配置的功能有两个:选择性挂起和系统唤醒。

选择性挂起允许设备在闲置时进入低能耗状态。
系统唤醒是指在系统处于低能耗状态时,设备唤醒系统的功能。

属性名描述
DeviceIdleEnabled 此值设置为 1 表示设备在闲置时可关闭(选择性挂起)。
DefaultIdleState 此值设置为 1 表示设备在闲置时可挂起(默认情况下)。
DefaultIdleTimeout 此值设置为 5000 毫秒表示确定设备处于闲置状态所需等待的毫秒时长。
UserSetDeviceIdleEnabled 此值设置为 1 允许用户控制设备启用或禁用 USB 选择性挂起的功能。设备“电源管理”属性页面上的复选框“允许计算机关闭此设备以节约电源”,用户可选中或取消选中该复选框以启用或禁用 USB 选择性挂起。
SystemWakeEnabled

此值设置为 1 允许用户控制设备从低能耗状态唤醒系统的功能。如果启用,“允许此设备唤醒计算机”复选框将显示在设备电源管理属性页面中。用户可选中或取消选中此复选框以启用或禁用 USB 系统唤醒。

例如,要在设备上启用选择性挂起,请添加一个自定义属性部分,将 

bPropertyName 字段设置为 Unicode 字符串,并将 "DeviceIdleEnabled" 和 
wPropertyNameLength 设置为 36 字节。 将 
bPropertyData 字段设置为 "0x00000001"。这些属性值会存储为 little-endian 32 位整数。

在枚举过程中,USB 驱动程序堆栈会读取扩展属性特征描述符并在该项下创建注册表项。 

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB
\<Device Identifier>\<Instance Identifier>\Device Parameters

DeviceIdleEnabled : 0x00000001

 

成功枚举 USB Device

枚举过程

80 06 00 01 00 00 40 00 : GET_DESCRIPTOR - device
12 01 00 02 ff ff ff 40 ff ff ff ff 00 01 01 02 03 01
                        -----------          |  | serial number
00 05 01 00 00 00 00 00 : SET_ADDRESS        | product string 
      ** 
80 06 00 01 00 00 12 00 : GET_DESCRIPTOR - device
12 01 00 02 ff ff ff 40 ff ff ff ff 00 01 01 02 03 01

80 06 00 02 00 00 ff 00 : GET_DESCRIPTOR  - configuration
09 02 20 00 01 01 00 80 fa
09 04 00 00 02 ff ff ff ff
07 05 81 02 40 00 00
07 05 02 02 40 00 00

80 06 ee 03 00 00 12 00 : GET_DESCRIPTOR - string : OS
      *****
12 03 4d 00 53 00 46 00 54 00 31 00 30 00 30 00 01 00 : "MSFT100"
                                                ** bMS_VendorCode

80 06 03 03 09 04 ff 00 : GET_DESCRIPTOR - string : serial number
16 03 30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00 : "0123456789"

c0 01 00 00 04 00 10 00 : CompactID - Header
28 00 00 00 00 01 04 00 01 00 00 00 00 00 00 00

c0 01 00 00 04 00 28 00 : CompactID - Header + Function
28 00 00 00 00 01 04 00 01 00 00 00 00 00 00 00 00 01 57 49 4e 55 53 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                                      ****"WINUSB"*****       ***********************

80 06 00 03 00 00 ff 00 : GET_DESCRIPTOR - string : language ID
04 03 09 04

80 06 02 03 09 04 ff 00 : GET_DESCRIPTOR - string : product
16 03 55 00 53 00 42 00 20 00 44 00 65 00 76 00 69 00 63 00 65 00 : "USB Device"

80 06 00 06 00 00 0a 00 : GET_DESCRIPTOR - device_qualifier
stall

c1 01 00 00 05 00 0a 00 : Property - Header
8e 00 00 00 00 01 05 00 01 00

c1 01 00 00 05 00 8e 00 : Property - Header + Data : L"DeviceInterfaceGUID"
8e 00 00 00 00 01 05 00 01 00
84 00 00 00
01 00 00 00
28 00
44 00 65 00 76 00 69 00 63 00 65 00 49 00 6e 00 74 00 65 00 72 00 66 00 61 00 63 00 65 00 47 00 55 00 49 00 44 00 00 00

4e 00 00 00 : L"{12345678-1234-1234-1234-123456789ABC"
7b 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00
2d 00 31 00 32 00 33 00 34 00
2d 00 31 00 33 00 34 00 34 00
2d 00 31 00 32 00 33 00 34 00 2d 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00 41 00 42 00 43 00
7d 00 00 00

80 06 00 01 00 00 12 00 : GET_DESCRIPTOR - device
12 01 00 02 ff ff ff 40 ff ff ff ff 00 01 01 02 03 01

80 06 00 02 00 00 09 00 : GET_DESCRIPTOR  - configuration only
09 02 20 00 01 01 00 80 fa

80 06 00 02 00 00 20 00 : GET_DESCRIPTOR  - configuration full
09 02 20 00 01 01 00 80 fa 09 04 00 00 02 ff ff ff ff 07 05 81 02 40 00 00 07 05 02 02 40 00 00

80 00 00 00 00 00 02 00 : GET_STATUS - device
01 00 : Self Power

00 09 01 00 00 00 00 00 : SET_CONFIGURATION
      **

自定义 Winusb.inf 安装 WinUSB.sys 作为设备的功能驱动程序

若要使用 WinUSB 作为设备的功能驱动程序,请创建一个驱动程序包。该驱动程序包必须包含下列文件:

  • WinUSB 辅助安装程序 (Winusbcoinstaller.dll)
  • KMDF 辅助安装程序 (WdfcoinstallerXXX.dll)
  • 用来安装 Winusb.sys 作为设备的功能驱动程序的 .inf 文件。
  • 签名的数据包目录文件。 在 x64 版本的 Windows Vista 上安装 WinUSB 时需要此文件。

A. 编写 WinUSB 安装的自定义 INF

作为驱动程序包的一部分,你需要提供 .inf 文件,该文件将安装 Winusb.sys 作为 USB 设备的功能驱动程序。

下面的示例 .inf 文件西那是的针对大多数 USB 设备的 WinUSB 安装,但进行了一些修改,
例如将相应部分名称中的 USB_Install 更改为相应的DDInstall 值。
你还应根据需要更改版本、制造商和型号部分。例如,提供相应的制造商名称、已签名目录文件的名称、
正确的设备类以及设备的供应商标识 (VID) 和产品标识 (PID)。

还应通知安装程序类已设置为 "USBDevice"。
供应商可以对不属于其他类以及不是 USB 主控制器或集线器的设备使用 "USBDevice" 安装程序类
如果要安装 WinUSB 作为 USB 复合设备中任一功能的功能驱动程序,必须在 INF 中提供中与该功能关联的硬件 ID。
可以从“设备管理器”的 devnode 属性中获取该功能的硬件 ID。硬件 ID 字符串格式为 "USB\VID_vvvv&PID_pppp"。

下面的 INF 安装 WinUSB 作为基于 x64 系统上 OSR USB FX2 主板的功能驱动程序。

;
;
; Installs WinUsb
;

[Version]
Signature = "$Windows NT$"
Class     = USBDevice
ClassGUID = {88BAE032-5A81-49f0-BC3D-A4FF138216D6}
Provider  = %ManufacturerName%
CatalogFile = WinUSBInstallation.cat
DriverVer=09/04/2012,13.54.20.543

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%ManufacturerName% = Standard,NTamd64

[Standard.NTamd64]
%DeviceName% =USB_Install, USB\VID_0547&PID_1002

; ========== Class definition ===========

[ClassInstall32]
AddReg = ClassInstall_AddReg

[ClassInstall_AddReg]
HKR,,,,%ClassName%
HKR,,NoInstallClass,,1
HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20"
HKR,,LowerLogoVersion,,5.2

; =================== Installation ===================

[USB_Install]
Include = winusb.inf
Needs   = WINUSB.NT

[USB_Install.Services]
Include =winusb.inf
Needs   = WINUSB.NT.Services

[USB_Install.HW]
AddReg=Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{9f543223-cede-4fa3-b376-a25ce9a30e74}"

; [DestinationDirs]
; If your INF needs to copy files, you must not use the DefaultDestDir directive here.  
; You must explicitly reference all file-list-section names in this section.

; =================== Strings ===================

[Strings]
ManufacturerName=""
ClassName="Universal Serial Bus devices"
DeviceName="Fx2 Learning Kit Device"
REG_MULTI_SZ = 0x00010000

除了特定于设备的值和以下列表中记录的若干问题,你可以使用这些部分和指令为任何 USB 设备安装 WinUSB。
这些列表项介绍了前面的 .inf 文件中的 Includes 和 Directives

  • USB_Install
    安装 WinUSB 需要 USB_Install 部分中的 Include 和 Needs 指令。不应修改这些指令。
  • USB_Install.ServicesUSB_Install.Services 部分中的 
    Include 指令包含系统提供的用于 WinUSB 的 .inf (WinUSB.inf)。
    如果该 .inf 文件在目标系统中尚未准备就绪,将由 WinUSB 辅助安装程序进行安装。
    Needs 指令可指定 WinUSB.inf 中包含安装 Winusb.sys 作为设备的功能驱动程序所需的信息的部分。不应修改这些指令。

    注意  
    由于 Windows XP 不提供 WinUSB.inf,因此需要通过辅助安装程序将该文件复制到 Windows XP 系统,
    或为 Windows XP 提供单独修改的部分。

  • USB_Install.HW:此部分是 .inf 文件的密钥,可为你的设备指定设备接口全局唯一标识符。
    AddReg 指令在标准注册表值中设置指定的接口 GUID。
    当 Winusb.sys 作为设备的功能驱动程序加载之后,它读取注册表项 DeviceInterfaceGUIDs
    的值并使用指定的 GUID 来表示设备接口。你应将此示例中的 GUID 替换为专门为设备指定的 GUID。
    如果设备的协议更改,请创建新的设备接口 GUID。
    注意  
    用户模式软件必须调用 SetupDiGetClassDevs 才能枚举与在 DeviceInterfaceGUIDs
    项下指定的任一设备接口类关联的注册设备接口。
    SetupDiGetClassDevs 返回用户模式软件稍后必须传递给 WinUsb_Initialize 
    例程的设备的设备句柄,才能获得设备接口的 WinUSB 句柄。

下面的 INF 安装 WinUSB 作为基于 x64 系统上 OSR USB FX2 主板的功能驱动程序。
该示例显示包含 WDF 辅助安装程序的 INF。

;
;
; Installs WinUsb
;

[Version]
Signature = "$Windows NT$"
Class     = USBDevice
ClassGUID = {88BAE032-5A81-49f0-BC3D-A4FF138216D6}
Provider  = %ManufacturerName%
CatalogFile = WinUSBInstallation.cat
DriverVer=09/04/2012,13.54.20.543

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%ManufacturerName% = Standard,NTamd64

[Standard.NTamd64]
%DeviceName% =USB_Install, USB\VID_0547&PID_1002

; ========== Class definition ===========

[ClassInstall32]
AddReg = ClassInstall_AddReg

[ClassInstall_AddReg]
HKR,,,,%ClassName%
HKR,,NoInstallClass,,1
HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20"
HKR,,LowerLogoVersion,,5.2

; =================== Installation ===================

[USB_Install]
Include = winusb.inf
Needs   = WINUSB.NT

[USB_Install.Services]
Include =winusb.inf
Needs   = WINUSB.NT.Services

[USB_Install.HW]
AddReg=Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{9f543223-cede-4fa3-b376-a25ce9a30e74}"

[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles

[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01011.dll,WdfCoInstaller","WinUsbCoInstaller2.dll"

[CoInstallers_CopyFiles]
WinUsbCoInstaller2.dll
WdfCoInstaller01011.dll

[DestinationDirs]
; If your INF needs to copy files, you must not use the DefaultDestDir directive here.  
CoInstallers_CopyFiles=11
; ================= Source Media Section =====================

[SourceDisksNames]
1 = %DiskName%

[SourceDisksFiles]
WinUsbCoInstaller2.dll=1
WdfCoInstaller01011.dll=1


; =================== Strings ===================

[Strings]
ManufacturerName=""
ClassName="Universal Serial Bus devices"
DeviceName="Fx2 Learning Kit Device"
REG_MULTI_SZ = 0x00010000
  • USB_Install.CoInstallers
    此部分(包含引用的 AddReg 和 CopyFiles 部分)包含安装 WinUSB 和 KMDF 辅助安装程序所需的数据和说明,
    并将它们与设备关联。大多数 USB 设备可以使用这些部分和指令,不用进行任何修改。
  • 基于 x86 和 x64 版本的 Windows 有单独的辅助安装程序。

    注意  
    每个辅助安装程序都有免费的检查版本。
    使用免费版本在免费 Windows 版本(包括所有的零售版本)上安装 WinUSB。
    使用检查版本(带有“_chk”后缀)在检查版本的 Windows 上安装 WinUSB。

Winusb.sys 在每次加载时都会注册一个设备接口,
该设备接口具有在注册表中的 DeviceInterfaceGUIDs 项下面指定的设备接口类。

HKR,,DeviceInterfaceGUIDs, 0x10000,"{D696BFEB-1734-417d-8A04-86D01071C512}"

注意  
如果将可再发行版本 WinUSB 数据包用于 Windows XP 或 Windows Server 2003,
请确保未在卸载程序包里卸载 WinUSB。其他 USB 设备可能正在使用 WinUSB,
因此其二进制文件必须保留在共享文件夹中。

B. 安装 Winusb.sys 设备的功能驱动程序

  1. 安装 Windows 驱动程序工具包。

  2. 在连有 USB 的计算机上创建驱动程序包文件夹。例如,c:\UsbDevice。
  3. 将 WinUSB 辅助安装程序(WinusbcoinstallerX.dll)从 WinDDK\BuildNumber\redist\winusb 
    文件夹复制到驱动程序包文件夹。

    如有必要,WinUSB 辅助安装程序 (Winusbcoinstaller.dll) 在目标系统中安装 WinUSB。
    WDK 根据系统架构的不同(基于 x86、基于 x64 和基于 Itanium 的系统)包含三个版本的辅助安装程序。
    它们共同的名字是 WinusbcoinstallerX.dll,位于WinDDK\BuildNumber\redist\winusb 文件夹中相应的子目录下。

  4. 将 KMDF 辅助安装程序 (WdfcoinstallerXXX.dll) 从 WinDDK\BuildNumber\redist\wdf
    文件夹复制到驱动程序包文件夹中。

    如有必要,KMDF 辅助安装程序 (WdfcoinstallerXXX.dll) 在目标系统中安装正确版本的 KMDF。
    WinUSB 辅助安装程序的版本必须与 KMDF 辅助安装程序的版本匹配,
    因为基于 KMDF 的客户端驱动程序(如 Winusb.sys)要求在系统中正确安装对应版本的 KMDF 架构。
    例如,Winusbcoinstaller2.dll 要求 KMDF 版本为 1.9,该辅助安装程序由 Wdfcoinstaller01009.dll 安装。
    x86 和 x64 版本的 WdfcoinstallerXXX.dll 包含在 WinDDK\BuildNumber\redist\wdf 文件夹下的 WDK 中。
    下表显示要用于目标系统的 WinUSB 辅助安装程序及其关联的 KMDF 辅助安装程序

    WinUSB 辅助安装程序KMDF 库版本KMDF 辅助安装程序
    Winusbcoinstaller.dll 要求 KMDF 1.5 或更高版本

    Wdfcoinstaller01005.dll

    Wdfcoinstaller01007.dll

    Wdfcoinstaller01009.dll

    Winusbcoinstaller2.dll 要求 KMDF 1.9 或更高版本 Wdfcoinstaller01009.dll
    Winusbcoinstaller2.dll 要求 KMDF 1.11 或更高版本 WdfCoInstaller01011.dll
  5. 编写 .inf 文件,该文件安装 Winusb.sys 作为 USB 设备的功能驱动程序。
  6. 为数据包创建签名的目录文件 在 x64 版本的 Windows 上安装 WinUSB 时需要此文件。
  7. 将 USB 设备连接到计算机。
  8. 打开“设备管理器”安装驱动程序。
    根据“更新驱动程序软件”向导的说明执行操作,并选择手动安装。
    你需要提供驱动程序包文件夹的位置才能完成安装。

如何通过 WinUSB 功能访问 USB 设备 < WinUsb.dll >

与构建设备 I/O 控制请求来执行标准 USB 操作(如配置设备、发送控制请求以及与设备互传数据)不同,应用程序调用等效的 WinUSB 功能。

Winusb.dll 使用应用程序提供的数据构建适当的设备 I/O 控制请求,然后将请求发送到 Winusb.sys 进行处理。
为与 USB 堆栈通信,WinUSB 功能使用与应用程序的请求对应的适当 IOCTL 调用 DeviceIoControl
当请求完成时,WinUSB 功能将 Winusb.sys 返回的任何信息(例如来自读请求的数据)再回传到调用进程。
如果调用 DeviceIoControl 成功,它将返回非零值。如果调用失败或者挂起(不立即处理),DeviceIoControl 将返回零值。
如果出现错误,应用程序可以调用 GetLastError 以获得更详细的错误消息。

使用 WinUSB 功能与设备通信比实现驱动程序更简单。不过,请注意以下限制:

  • WinUSB 功能一次仅允许一个应用程序与设备通信。如果需要多个应用程序同时与设备通信,则必须实现一个函数驱动程序。
  • WinUSB 功能不支持在同步终结点之间传输流数据。同步传输需要一个内核模式的函数驱动程序。
  • WinUSB 功能不支持已经提供内核模式支持的设备。此类设备的示例包括调制解调器和网络适配器,二者分别由电话服务 API (TAPI) 和 NDIS 支持。
  • 对于多功能设备,可以使用设备的 INF 文件分别为每个 USB 函数指定内置内核模式驱动程序或 Winusb.sys。
    不过,对于某个特定函数,只能指定其中一个选项,而不能同时指定两个。

可以将 Winusb.sys 安装为 USB 复合设备中函数的函数驱动程序。
这需要在 INF 中进行一些硬件 ID 修改。

Winusb.sys 也是 UMDF 函数驱动程序与关联的设备之间链接的关键部分。
Winusb.sys 作为高层筛选器驱动程序安装在设备的内核模式堆栈中。
应用程序通过与设备的 UMDF 函数驱动程序通信来发送读、写或设备 I/O 控制请求。
驱动程序与框架交互,后者将请求传递到 Winusb.sys。
Winusb.sys 随后处理该请求并将其传递到协议驱动程序,最后传递到设备。
任何响应都通过反向路径返回。
Winusb.sys 还作为设备堆栈的即插即用和电源所有者 (PPO)。

注意  
WinUSB 功能需要 Windows XP 或更高版本。
你可以在 C/C++ 应用程序中使用这些函数与你的 USB 设备通信。
Microsoft 不支持为 WinUSB 提供托管 API。

创建设备的文件句柄

要访问 USB 设备,需要设备的有效文件句柄。可以通过调用 CreateFile 创建此句柄。
此函数向 Winusb.sys 请求一个应用程序可用来与设备通信的设备句柄。
CreateFile 需要请求句柄的设备的设备路径。
要获取设备路径,你需要用于安装 Winusb.sys 的 INF 文件中指定的设备接口 GUID。
通过使用 SetupAPI 例程,你可以枚举指定设备接口类中的所有设备并检索设备的设备路径。

使用以下步骤可创建 USB 设备的文件句柄。

  1. 调用 SetupDiGetClassDevs 获取“设备信息集”的句柄,“设备信息集”是一个阵列,
    包含有关与指定的设备接口类匹配的所有安装的设备的信息。
    阵列中名为“设备接口”的每个元素对应于一个已经安装并向系统注册的设备。
    设备接口类通过传递在 INF 文件中定义的设备接口 GUID 进行标识。该函数将 HDEVINFO 句柄返回到设备信息集。

  2. 调用 SetupDiEnumDeviceInterfaces 枚举设备信息集中的设备接口并获取有关设备接口的信息。

    此调用需要以下各项:

    • 已初始化的调用程序分配的 SP_DEVICE_INTERFACE_DATA 结构,其 cbSize 成员设置为结构的大小。
    • 步骤 1 中的 HDEVINFO 句柄。
    • 在 INF 文件中定义的接口 GUID。

    SetupDiEnumDeviceInterfaces 查找设备接口的指定索引的设备信息集阵列,
    并使用有关该接口的基本数据填充初始化SP_DEVICE_INTERFACE_DATA 结构。

    注意  
    要枚举设备信息集中的所有设备接口,请在循环中调用 SetupDiEnumDeviceInterfaces
    直到函数返回 FALSE 并且失败错误代码为 ERROR_NO_MORE_ITEMS。
    通过调用 GetLastError 可以检索 ERROR_NO_MORE_ITEMS 错误代码。
    每迭代一次,将递增一次成员索引。

    或者,你可以调用 SetupDiEnumDeviceInfo,该函数可以枚举设备信息集并返回调用方分配的 
    SP_DEVINFO_DATA 结构中索引指定的有关设备接口元素的信息。
    然后可以将引用传递到 
    SetupDiEnumDeviceInterfaces 函数的 DeviceInfoData 参数中的此结构。

  3. 调用 SetupDiGetDeviceInterfaceDetail 获取设备接口的详细信息。
    信息在 SP_DEVICE_INTERFACE_DETAIL_DATA 结构中返回。
    由于 SP_DEVICE_INTERFACE_DETAIL_DATA 结构的大小各不相同,
    你需要调用 SetupDiGetDeviceInterfaceDetail 两次。
    第一次调用获取要为 SP_DEVICE_INTERFACE_DETAIL_DATA 结构分配的缓冲区大小。
    第二次调用使用有关接口的详细信息填充分配的缓冲区。调用 CreateFile 创建设备的文件句柄。在此步骤中,你需要一个 Null 结尾的字符串,
    1. 调用 SetupDiGetDeviceInterfaceDetail 并将 DeviceInterfaceDetailData 参数设置为 NULL
      此函数在requiredlength 参数中返回正确的缓冲区大小。此调用失败并显示 ERROR_INSUFFICIENT_BUFFER 错误代码。
      此错误代码是预期行为。
    2. 基于在 requiredlength 参数中检索的正确缓冲区大小为 SP_DEVICE_INTERFACE_DETAIL_DATA 结构分配内存。
    3. 再次调用 SetupDiGetDeviceInterfaceDetail 并将其作为引用传递到 DeviceInterfaceDetailData 参数中的初始化结构。
      在该函数返回时,该结构将填充有关接口的详细信息。
      设备路径在 SP_DEVICE_INTERFACE_DETAIL_DATA 结构的 DevicePath成员中。


  4. 其中包含在SP_DEVICE_INTERFACE_DETAIL_DATA 结构的 DevicePath 成员中接收的设备路径。
    将设备路径传递到 CreateFile 以获取设备的文件句柄。
    确保设置了 FILE_FLAG_OVERLAPPED 标志,因为 WinUSB 信赖于此设置。

下面的示例代码创建一个支持对设备进行同步读写访问的文件句柄。

View Code
BOOL GetDeviceHandle( GUID guidDeviceInterface, PHANDLE hDeviceHandle )
{
  if ( guidDeviceInterface == GUID_NULL )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;
  HDEVINFO hDeviceInfo;
  SP_DEVINFO_DATA DeviceInfoData;

  SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
  PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL;

  ULONG requiredLength = 0;

  LPTSTR lpDevicePath = NULL;

  DWORD index = 0;

  // Get information about all the installed devices for the specified
  // device interface class.
  hDeviceInfo = SetupDiGetClassDevs( &guidDeviceInterface, NULL, NULL,
    DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );

  if ( hDeviceInfo == INVALID_HANDLE_VALUE )
  {
    // ERROR 
    printf( "Error SetupDiGetClassDevs: %d.\n", GetLastError( ) );
    goto done;
  }

  //Enumerate all the device interfaces in the device information set.
  DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

  for ( index = 0; SetupDiEnumDeviceInfo( hDeviceInfo, index, &DeviceInfoData );
      index++ )
  {
    //Reset for this iteration
    if ( lpDevicePath )
    {
      LocalFree( lpDevicePath );
    }
    if ( pInterfaceDetailData )
    {
      LocalFree( pInterfaceDetailData );
    }

    deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

    //Get information about the device interface.
    bResult = SetupDiEnumDeviceInterfaces( hDeviceInfo, &DeviceInfoData,
      &guidDeviceInterface, 0, &deviceInterfaceData );

    // Check if last item
    if ( GetLastError( ) == ERROR_NO_MORE_ITEMS )
    {
      break;
    }

    //Check for some other error
    if ( !bResult )
    {
      printf( "Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError( ) );
      goto done;
    }

    //Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA
    //which we need to allocate, so we have to call this function twice.
    //First to get the size so that we know how much to allocate
    //Second, the actual call with the allocated buffer

    bResult = SetupDiGetDeviceInterfaceDetail( hDeviceInfo,
      &deviceInterfaceData, NULL, 0, &requiredLength, NULL );

    //Check for some other error
    if ( !bResult )
    {
      if ( ( ERROR_INSUFFICIENT_BUFFER == GetLastError( ) )
        && ( requiredLength > 0 ) )
      {
        //we got the size, allocate buffer
        pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(
          LPTR, requiredLength );

        if ( !pInterfaceDetailData )
        {
          // ERROR 
          printf( "Error allocating memory for the device detail buffer.\n" );
          goto done;
        }
      }
      else
      {
        printf( "Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError( ) );
        goto done;
      }
    }

    //get the interface detailed data
    pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

    //Now call it with the correct size and allocated buffer
    bResult = SetupDiGetDeviceInterfaceDetail( hDeviceInfo,
      &deviceInterfaceData, pInterfaceDetailData, requiredLength, NULL,
      &DeviceInfoData );

    //Check for some other error
    if ( !bResult )
    {
      printf( "Error SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError( ) );
      goto done;
    }

    //copy device path

    size_t nLength = wcslen( pInterfaceDetailData->DevicePath ) + 1;
    lpDevicePath = (TCHAR *) LocalAlloc( LPTR, nLength * sizeof(TCHAR) );
    StringCchCopy( lpDevicePath, nLength, pInterfaceDetailData->DevicePath );
    lpDevicePath[ nLength - 1 ] = 0;

    printf( "Device path:  %s\n", lpDevicePath );

  }

  if ( !lpDevicePath )
  {
    //Error.
    printf( "Error %d.", GetLastError( ) );
    goto done;
  }

  //Open the device
  *hDeviceHandle = CreateFile( lpDevicePath, GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED, NULL );

  if ( *hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    //Error.
    printf( "Error %d.", GetLastError( ) );
    goto done;
  }

  done: LocalFree( lpDevicePath );
  LocalFree( pInterfaceDetailData );
  bResult = SetupDiDestroyDeviceInfoList( hDeviceInfo );

  return bResult;
}

获取设备的 WinUSB 接口句柄

接下来,使用设备的文件句柄创建设备上第一个(默认)接口的 WinUSB 接口句柄。
WinUSB 例程使用此句柄标识目标设备而非文件句柄。要获取 WinUSB 接口句柄,
请通过传递在创建设备的文件句柄中创建的文件句柄调用 WinUsb_Initialize
使用后续调用中收到的句柄从设备中获取信息,然后将 I/O 请求发送到该设备。

以下示例代码使用上一步中创建的文件句柄初始化 WinUSB,并检索设备接口的 WinUSB 接口句柄的指针。

BOOL GetWinUSBHandle( HANDLE hDeviceHandle,
  PWINUSB_INTERFACE_HANDLE phWinUSBHandle )
{
  if ( hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    return FALSE;
  }

  BOOL bResult = WinUsb_Initialize( hDeviceHandle, phWinUSBHandle );
  if ( !bResult )
  {
    //Error.
    printf( "WinUsb_Initialize Error %d.", GetLastError( ) );
    return FALSE;
  }

  return bResult;
}

查询设备以获取 USB 描述符

接下来,查询设备以获取特定于 USB 的信息,如设备速度、接口描述符、相关终结点及其管道。
此过程类似于 USB 设备驱动程序使用的过程。不过,应用程序通过调用 WinUSB 功能
而非 WDF 框架库或任何 Windows WDM USB 客户端支持例程完成设备查询。

下表显示了你可以调用以获取特定于 USB 的信息的 WinUSB 功能:

  • 设备描述符

    调用 WinUsb_QueryDeviceInformation 从设备的设备描述符请求信息。
    要获取设备的速度,可在 InformationType 参数中设置 DEVICE_SPEED (0x01)。
    此函数返回 LowSpeed (0x01) 或 HighSpeed (0x03)。

  • 接口描述符

    调用 WinUsb_QueryInterfaceSettings 并传递设备的接口句柄,以获得对应的接口描述符。
    WinUSB 接口句柄对应于第一个接口。一些 USB 设备(如 OSR Fx2 设备)仅支持一个没有任何备用设置的接口。
    因此,对于这些设备,AlternateSettingNumber 参数设置为零,并且仅调用一次该函数。
    WinUsb_QueryInterfaceSettings 使用有关接口的信息填充调用方分配的 USB_INTERFACE_DESCRIPTOR 
    结构(在 
    UsbAltInterfaceDescriptor 参数中传入)。
    例如,接口中的终结点数在 
    USB_INTERFACE_DESCRIPTOR 的 bNumEndpoints成员中设置。

    对于支持多个接口的设备,通过在 AssociatedInterfaceIndex 参数中指定备用设置调用 
    WinUsb_GetAssociatedInterface 以获取关联接口的接口句柄。

  • 终结点

    调用 WinUsb_QueryPipe 获取有关每个接口每个终结点的信息。
    WinUsb_QueryPipe 使用有关指定终结点的管道填充调用方分配的 WINUSB_PIPE_INFORMATION 结构。
    终结点的管道由从零开始的索引标识,并且必须小于在上次调用
    WinUsb_QueryInterfaceSettings 
    中检索的接口描述符的 
    bNumEndpoints 成员中的值。
    OSR Fx2 设备有一个具有三个终结点的接口。对于此设备,
    函数的 
    AlternateInterfaceNumber 参数设置为 0,并且 PipeIndex 参数的值从 0 到 2 变化。

    若要确定管道类型,请检查 WINUSB_PIPE_INFORMATION 结构的 PipeInfo 成员。
    此成员设置为 USBD_PIPE_TYPE 枚举值之一:

    UsbdPipeTypeControl、UsbdPipeTypeIsochronous、UsbdPipeTypeBulk 或 UsbdPipeTypeInterrupt。

    OSR USB FX2 设备支持中断管道、批量传入管道和批量传出管道,
    因此 PipeInfo 设置为 UsbdPipeTypeInterrupt 或 UsbdPipeTypeBulk。

    UsbdPipeTypeBulk 值标识批量管道,但不提供管道的方向。
    方向信息在管道地址的高位编码,该地址存储在 WINUSB_PIPE_INFORMATION结构的 PipeId 成员中。
    确定管道方向的最简单方法是将 PipeId 值从 Usb100.h 传递到下列宏之一:

    • 如果方向是传入,则 USB_ENDPOINT_DIRECTION_IN (PipeId) 宏返回 TRUE
    • 如果方向是传出,则 USB_ENDPOINT_DIRECTION_OUT(PipeId) 宏返回 TRUE

    应用程序使用 PipeId 值在对 WinUsb_ReadPipe 等 WinUSB 功能的调用中标识哪个管道用于数据传输
    因此该示例存储所有三个 PipeId 值以供日后使用。

以下示例代码获取由 WinUSB 接口句柄指定的设备的速度。

View Code
BOOL GetUSBDeviceSpeed( WINUSB_INTERFACE_HANDLE hDeviceHandle,
  UCHAR* pDeviceSpeed )
{
  if ( !pDeviceSpeed || hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  ULONG length = sizeof(UCHAR);

  bResult = WinUsb_QueryDeviceInformation( hDeviceHandle, DEVICE_SPEED, &length,
    pDeviceSpeed );
  if ( !bResult )
  {
    printf( "Error getting device speed: %d.\n", GetLastError( ) );
    goto done;
  }

  if ( *pDeviceSpeed == LowSpeed )
  {
    printf( "Device speed: %d (Low speed).\n", *pDeviceSpeed );
    goto done;
  }
  if ( *pDeviceSpeed == FullSpeed )
  {
    printf( "Device speed: %d (Full speed).\n", *pDeviceSpeed );
    goto done;
  }
  if ( *pDeviceSpeed == HighSpeed )
  {
    printf( "Device speed: %d (High speed).\n", *pDeviceSpeed );
    goto done;
  }

  done: return bResult;
}

以下示例代码查询由 WinUSB 接口句柄指定的 USB 设备的各种描述符。
此示例函数检索支持的终结点类型及其管道标识符。
该示例存储所有三个 PipeId 值以供日后使用。

View Code
struct PIPE_ID
{
  UCHAR PipeInId;
  UCHAR PipeOutId;
};

BOOL QueryDeviceEndpoints( WINUSB_INTERFACE_HANDLE hDeviceHandle,
  PIPE_ID* pipeid )
{
  if ( hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
  ZeroMemory( &InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR) );

  WINUSB_PIPE_INFORMATION Pipe;
  ZeroMemory( &Pipe, sizeof(WINUSB_PIPE_INFORMATION) );

  bResult = WinUsb_QueryInterfaceSettings( hDeviceHandle, 0,
    &InterfaceDescriptor );

  if ( bResult )
  {
    for ( int index = 0; index < InterfaceDescriptor.bNumEndpoints; index++ )
    {
      bResult = WinUsb_QueryPipe( hDeviceHandle, 0, index, &Pipe );

      if ( bResult )
      {
        if ( Pipe.PipeType == UsbdPipeTypeControl )
        {
          printf( "Endpoint index: %d Pipe type: Control Pipe ID: %d.\n", index,
            Pipe.PipeType, Pipe.PipeId );
        }
        if ( Pipe.PipeType == UsbdPipeTypeIsochronous )
        {
          printf( "Endpoint index: %d Pipe type: Isochronous Pipe ID: %d.\n",
            index, Pipe.PipeType, Pipe.PipeId );
        }
        if ( Pipe.PipeType == UsbdPipeTypeBulk )
        {
          if ( USB_ENDPOINT_DIRECTION_IN( Pipe.PipeId ) )
          {
            printf( "Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index,
              Pipe.PipeType, Pipe.PipeId );
            pipeid->PipeInId = Pipe.PipeId;
          }
          if ( USB_ENDPOINT_DIRECTION_OUT( Pipe.PipeId ) )
          {
            printf( "Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index,
              Pipe.PipeType, Pipe.PipeId );
            pipeid->PipeOutId = Pipe.PipeId;
          }

        }
        if ( Pipe.PipeType == UsbdPipeTypeInterrupt )
        {
          printf( "Endpoint index: %d Pipe type: Interrupt Pipe ID: %d.\n",
            index, Pipe.PipeType, Pipe.PipeId );
        }
      }
      else
      {
        continue;
      }
    }
  }

  done: return bResult;
}

向默认终结点发送控制传输

接下来,通过向默认终结点发送控制请求来与设备通信。
除了与接口关联的终结点之外,所有 USB 设备都有一个默认终结点。
默认终结点的主要目的是为主机提供可用于配置设备的信息。
不过,设备还可以将默认终结点用于特定于设备的目的。

控制命令包含一个 8 字节的设置数据包,其中包括用于指定特定请求的请求代码和一个可选数据缓冲区。
请求代码和缓冲区格式由供应商定义。在本例中,应用程序将数据发送到设备来控制灯条。
设置灯条的代码是 0xD8,为简便起见将其定义为 SET_BARGRAPH_DISPLAY。
对于此请求,设备需要一个 1 字节的数据缓冲区,以通过设置适当的位指定哪些元素应亮起。

应用程序可以通过用户界面 (UI) 执行此设置,例如通过提供一组由八个复选框构成的控件来指定应亮起灯条的哪些元素。
指定的元素对应于缓冲区中适当的位。 为避开 UI 代码,本节中的示例代码设置位时,采用了使备用灯亮起的设置。

使用以下步骤发送控制请求。

  1. 分配一个 1 字节的数据缓冲区,并将数据加载到缓冲区,该缓冲区通过设置适当的位指定应亮起的元素。
  2. 在调用方分配的 WINUSB_SETUP_PACKET 结构中构建一个安装包。
    如下所示初始化成员以表示请求类型和数据:调用 WinUsb_ControlTransfer 以通过传递设备的 WinUSB 接口句柄、安装包和数据缓冲区,
    • RequestType 成员指定请求方向。
      它设置为 0,指示主机到设备数据传输。
      对于设备到主机传输,将 RequestType 设置为 1。
    • Request 成员设置为此请求的供应商定义的代码 0xD8。
      为简便起见将其定义为 SET_BARGRAPH_DISPLAY。
    • Length 成员设置为数据缓冲区的大小。
    • 此请求不需要 Index 和 Value 成员,
      因此它们设置为零。

  3. 将请求传输到默认终结点。此函数在LengthTransferred 参数中接收传输到设备的字节数。

以下代码示例将控制请求发送到指定的 USB 设备来控制灯条上的灯。

BOOL SendDatatoDefaultEndpoint( WINUSB_INTERFACE_HANDLE hDeviceHandle )
{
  if ( hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR bars = 0;

  WINUSB_SETUP_PACKET SetupPacket;
  ZeroMemory( &SetupPacket, sizeof(WINUSB_SETUP_PACKET) );
  ULONG cbSent = 0;

  //Set bits to light alternate bars
  for ( short i = 0; i < 7; i += 2 )
  {
    bars += 1 << i;
  }

  //Create the setup packet
  SetupPacket.RequestType = 0;
  SetupPacket.Request = 0xD8;
  SetupPacket.Value = 0;
  SetupPacket.Index = 0;
  SetupPacket.Length = sizeof(UCHAR);

  bResult = WinUsb_ControlTransfer( hDeviceHandle, SetupPacket, &bars,
    sizeof(UCHAR), &cbSent, 0 );
  if ( !bResult )
  {
    goto done;
  }

  printf( "Data sent: %d \nActual data transferred: %d.\n", sizeof( bars ),
    cbSent );

  done: return bResult;

}

发送 I/O 请求

接下来,将数据发送到设备的批量传入和批量传出终结点,这些终结点可分别用于读取请求和写入请求。
在 OSR USB FX2 设备上,这两个终结点配置为用于环回,因此该设备可将数据从批量传入终结点移动到批量传出终结点。
它不更改数据的值或添加任何新数据。对于环回配置,读取请求将读取由最近的写入请求发送的数据。
WinUSB 提供以下函数用于发送读取请求和写入请求:

  • WinUsb_WritePipe
  • WinUsb_ReadPipe

Ff540174.wedge(zh-cn,VS.85).gif发送写入请求

  1. 分配一个缓冲区,并使用希望写入设备的数据填充该缓冲区。
    如果应用程序没有将 RAW_IO 设置为管道的策略类型,则对缓冲区大小没有限制。
    WinUSB 根据需要将缓冲区拆分为适当大小的区块。
    如果设置了 RAW_IO,则缓冲区的大小受 WinUSB 支持的最大缓冲区大小的限制。
  2. 调用 WinUsb_WritePipe 将缓冲区写入设备。
    传递设备的 WinUSB 接口句柄、批量传出管道的管道标识符和缓冲区。
    此函数返回在 bytesWritten 参数中实际写入设备的字节数。 
    Overlapped 参数设置为 NULL 以请求同步操作。
    要执行异步写请求,请将 
    Overlapped 设置为指向 OVERLAPPED 结构的指针。

包含零长度数据的写入请求向下转发到 USB 堆栈。
如果传输长度大于最大传输长度,WinUSB 会将请求拆分为具有最大传输长度的较小请求,并连续提交它们。

以下代码示例分配一个字符串,并将其发送到设备的批量传出终结点。

BOOL WriteToBulkEndpoint( WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID,
  ULONG* pcbWritten )
{
  if ( hDeviceHandle == INVALID_HANDLE_VALUE || !pID || !pcbWritten )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR szBuffer[ ] = "Hello World";
  ULONG cbSize = strlen( szBuffer );
  ULONG cbSent = 0;

  bResult = WinUsb_WritePipe( hDeviceHandle, *pID, szBuffer, cbSize, &cbSent,
    0 );
  if ( !bResult )
  {
    goto done;
  }

  printf( "Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID,
    szBuffer, cbSent );
  *pcbWritten = cbSent;

  done: return bResult;

}

发送读取请求

调用 WinUsb_ReadPipe 从设备的批量传入终结点读取数据。
传递设备的 WinUSB 接口句柄、批量传入终结点的管道标识符和适当大小的空缓冲区。
当该函数返回时,缓冲区包含从设备读取的数据。读取的字节数返回函数的 bytesRead 参数。
对于读取请求,缓冲区必须是最大包大小的倍数。

零长度读请求将立即成功完成,并且不向下发送到堆栈。
如果传输长度大于最大传输长度,WinUSB 会将请求拆分为具有最大传输长度的较小请求,
并连续提交它们。如果传输长度不是终结点的 MaxPacketSize 的倍数,
WinUSB 会将传输大小增加到 MaxPacketSize 的下一个倍数。
如果设备返回的数据比请求的多,WinUSB 会保存超过的数据。
如果数据从以前的读请求中保留,WinUSB 会将其复制到下一个读请求的开头,并根据需要完成请求。

以下代码示例从设备的批量传入终结点读取数据。

BOOL ReadFromBulkEndpoint( WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID,
  ULONG cbSize )
{
  if ( hDeviceHandle == INVALID_HANDLE_VALUE )
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR* szBuffer = (UCHAR*) LocalAlloc( LPTR, sizeof(UCHAR) * cbSize );

  ULONG cbRead = 0;

  bResult = WinUsb_ReadPipe( hDeviceHandle, *pID, szBuffer, cbSize, &cbRead,
    0 );
  if ( !bResult )
  {
    goto done;
  }

  printf( "Read from pipe %d: %s \nActual data read: %d.\n", *pID, szBuffer,
    cbRead );

  done: LocalFree( szBuffer );
  return bResult;

}

释放设备句柄

在完成对设备的所有必要的调用之后,释放设备的文件句柄和 WinUSB 接口句柄。 为此,调用以下函数:

  • CloseHandle 释放由 CreateFile 创建的句柄,如本演练的创建设备的文件句柄一节中所述。
  • WinUsb_Free 释放由 WinUsb_Initialize 返回的设备的 WinUSB 接口句柄。

实现主函数

以下代码示例显示了控制台应用程序的主函数。

int _tmain( int argc, _TCHAR* argv[ ] )
{

  GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; //in the INF file

  BOOL bResult = TRUE;

  PIPE_ID PipeID;

  HANDLE hDeviceHandle = INVALID_HANDLE_VALUE;
  WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE;

  UCHAR DeviceSpeed;
  ULONG cbSize = 0;

  bResult = GetDeviceHandle( guidDeviceInterface, &hDeviceHandle );
  if ( !bResult )
  {
    goto done;
  }

  bResult = GetWinUSBHandle( hDeviceHandle, &hWinUSBHandle );
  if ( !bResult )
  {
    goto done;
  }

  bResult = GetUSBDeviceSpeed( hWinUSBHandle, &DeviceSpeed );
  if ( !bResult )
  {
    goto done;
  }

  bResult = QueryDeviceEndpoints( hWinUSBHandle, &PipeID );
  if ( !bResult )
  {
    goto done;
  }

  bResult = SendDatatoDefaultEndpoint( hWinUSBHandle );
  if ( !bResult )
  {
    goto done;
  }

  bResult = WriteToBulkEndpoint( hWinUSBHandle, &PipeID.PipeOutId, &cbSize );
  if ( !bResult )
  {
    goto done;
  }

  bResult = ReadFromBulkEndpoint( hWinUSBHandle, &PipeID.PipeInId, cbSize );
  if ( !bResult )
  {
    goto done;
  }

  system( "PAUSE" );

  done: CloseHandle( hDeviceHandle );
  WinUsb_Free( hWinUSBHandle );

  return 0;
}

用于管道策略修改的 WinUSB 功能

为了使应用程序获取并设置终结点管道的默认策略参数,Winusb.dll 公开 WinUsb_GetPipePolicy
功能来检索管道的默认策略。WinUsb_SetPipePolicy 功能允许应用程序将策略参数设置为新值。
使用 WinUSB,你可以通过将策略应用到终结点的管道来修改其默认行为。
你可以使用这些策略来配置 WinUSB,使你的设备与其功能以最佳方式匹配。
下表提供了 WinUSB 支持的管道策略的列表。
注意
下表中介绍的策略仅对指定的终结点有效。
设置其他终结点上的策略不会影响 WinUSB 对读取或写入请求的行为。

0x01 SHORT_PACKET_TERMINATE < FALSE > 批量 (OUT)/中断 (OUT)
发送一个零长度数据包的写入请求,其中缓冲区是终结点支持的最大数据包大小的倍数。
如果设备要求通过零长度的数据包终止向外传输。启用(策略参数值为 TRUE 或非零),
每个写入请求(为终结点支持的最大数据包大小的倍数)后都会跟一个零长度的数据包。
在将所有请求的数据发送到主控制器后,WinUSB 会发送带有零长度数据包的写入请求,
然后完成由 WinUsb_WritePipe 创建的请求。

0x02 AUTO_CLEAR_STALL < FALSE > 批量 (IN)/中断 (IN)
你不希望失败的传输将终结点保持在停止状态, 在不终止数据流的情况下自动清除停止的管道。
只有当 RAW_IO 处于禁用状态而且有多个针对终结点的挂起读取请求时,此策略才会有用。

如果启用(策略参数值为 TRUE 或非零),停止状态将自动清除。
当读取请求失败,主控制器返回除 STATUS_CANCELLED 或 STATUS_DEVICE_NOT_CONNECTED 以外的状态时,
WinUSB 在完成失败的请求之前将重置该管道。
重置管道将在不中断数据流的情况下清除停止状态。只要设备持续进行新传输,
数据就不断流向终结点。新的传输可以包含出现停止状态时位于队列中的请求。
启用此策略不会对性能产生严重影响。此策略参数不影响控制管道。

如果禁用(策略参数值为 FALSE 或零),在调用方通过调用 WinUsb_ResetPipe
手动重置终结点的管道之前,在停止传输后到达终结点的所有传输都会失败。

0x03 PIPE_TRANSFER_TIMEOUT 控制:5 秒 批量 (IN)/批量 (OUT)
在取消请求之前,等待超时间隔。 其他:0 秒 中断 (IN)/中断 (OUT)
你希望到达终结点的传输在指定的时间内完成。

如果设置为零(默认值),传输将不会超时,原因是主控制器不会取消传输。
在这种情况下,传输将一直处于等待状态,直到被手动取消或传输过程正常完成。

如果设置为非零值(超时间隔),主控制器将在接收到传输请求后开始计时。
当计时器超出设定的时间间隔,请求就会取消。计时器管理会对性能产生较小的影响。

注意 在 WinUSB 队列中等候的请求不会超时。
在 Windows Vista 中,对于所有的传输(不包含启用了 RAW_IO 的传输),WinUSB 会将请求包含在队列中,
直到目标终结点上所有之前的传输都完成。主控制器在计算超时间隔时不考虑排队时间。

启用 RAW_IO 后,WinUSB 不会将请求包含在队列中。相反,它会直接将请求传递给 USB 堆栈,
无论 USB 堆栈是否正在忙于处理之前的请求。如果 USB 堆栈忙碌,它可以延迟处理新的请求。
请注意,这样会导致超时。

0x04 IGNORE_SHORT_PACKETS < FALSE > 批量 (IN)/中断 (IN)
在接收短数据包或读取一定数量的字节时,完成读取请求。如果文件大小未知,请求将在短数据包时中止。
RAW_IO 已禁用,并且你不希望短数据包完成读取请求。

如果启用(策略参数值为 TRUE 或非零),主控制器在收到短数据包后不会立即完成读取操作。
相反,仅当下列情况出现时它才会完成操作:1) 出现错误。2) 请求被取消。3) 所有请求的字节都已收到。

如果禁用(策略参数值为 FALSE 或零),主控制器在读取请求的字节数量或收到短数据包后才完成读取操作。

0x05 ALLOW_PARTIAL_READS < TRUE > 批量 (IN)/中断 (IN)
允许来自设备的读取请求,该设备返回的数据多于调用方请求的数据。
设备发送的数据多于请求的数据。当请求缓冲区的大小是最大终结点数据包大小的倍数时,会出现这种情况。
当应用程序希望读取少量字节以确定要读取的总字节数量时,可以使用此策略。

如果禁用(策略参数值为 FALSE 或零)并且设备返回除请求数据之外的多余数据,WinUSB 将完成请求并报错。

如果启用(策略参数值为 TRUE 或非零)并且设备返回除请求数据之外的多余数据,
WinUSB 可以将来自读取请求的多余数据添加到下一个请求的开头 ( AUTO_FLUSH == TRUE )
或放弃这些多余的数据。( AUTO_FLUSH == FALSE )
如果启用,WinUSB 将立即成功完成零字节的读取请求,并且不会向堆栈发送请求。

0x06 AUTO_FLUSH < FALSE > 批量 (IN)/中断 (IN)
保存来自读取请求的多余数据,并将其添加到下一个读取请求或放弃多余的数据。
启用 ALLOW_PARTIAL _READS 策略。设备可以发送除请求数据之外的多余数据,
你的应用程序不需要任何额外的数据。当请求缓冲区的大小是最大终结点数据包大小的倍数时,会出现这种情况。

当 ALLOW_PARTIAL_READS 启用时,AUTO_FLUSH 将定义 WinUSB 的行为。
如果禁用 ALLOW_PARTIAL_READS,WinUSB 将忽略 AUTO_FLUSH 值。
WinUSB 可以放弃剩余的数据,也可以将其发送到调用方的下一个读取请求。
如果启用(策略参数值为 TRUE 或非零),WinUSB 将放弃多余的字节并且不显示任何错误代码。
如果禁用(策略参数值为 FALSE 或零),WinUSB 将保存多余的字节,
将其发送到该调用方下一个读取请求的开头,然后将数据发送给下一个读取操作的调用方。

ALLOW_PARTIAL_READS AUTO_FLUSH
禁用                 无关     设备返回除请求数据之外的多余数据,WinUSB 将完成请求并报错。
启用                 禁用     设备返回除请求数据之外的多余数据,WinUSB 将放弃这些多余的数据。
启用                 启用     将来自读取请求的多余数据添加到下一个请求的开头

0x07 RAW_IO < FALSE > 批量 (IN)/中断 (IN)
跳过队列和错误处理以提高多个读取请求的执行速度。
性能优先,同时应用程序向同一终结点提交同步读取请求。
RAW_IO 对调用方在 WinUsb_ReadPipe 中传递的缓冲区有一些限制:
缓冲区的长度必须是最大终结点数据包大小的倍数。
长度必须小于或等于 WinUsb_GetPipePolicy 检索的 MAXIMUM_TRANSFER_SIZE 的值。

如果启用,传输将跳过队列和错误处理,以提高多个读取请求的性能。
WinUSB 按如下所示处理读取请求:
1) 不是最大终结点数据包大小倍数的请求失败。
2) 大于 WinUSB 支持的最大传输大小的请求失败。
3) 格式标准的请求将立即发送到要在主控制器中计划的 USB 核心堆栈。

启用此设置能够明显提高多个读取请求的性能,
方法是减少一个传输的最后一个数据包和下一个传输的第一个数据包之间的延迟。

0x08 MAXIMUM_TRANSFER_SIZE 批量 (IN)/批量 (OUT)/中断 (IN)/中断 (OUT)
获取 WinUSB 支持的最大大小的 USB 传输。
这是一个只读策略,可以通过调用 WinUsb_GetPipePolicy 来检索。

0x09 RESET_PIPE_ON_RESUME < FALSE > 批量 (IN)/批量 (OUT)/中断 (IN)/中断 (OUT)
在重新开始挂起之后、接受新请求之前,重置终结点的管道。
如果启用,设备不会在挂起之后保留其数据切换状态。
从挂起状态恢复时,WinUSB 会重置终结点,然后才允许调用方向该终结点发送新请求。

http://sourceforge.net/projects/winusb-delphi

unit WinUsbDll;

{ Quick wrapper for WinUSB.DLL by Panu-Kristian Poiksalo }

{ WARNING: Mostly untested - Only initialize, readpipe and writepipe work ok }
{ License: LGPL. Assume that it doesn't work and don't blame me for any errors. }
{ Most data types were manually converted from strange types to Pascal types.
  Much of it was jus a quick hack. Those types that were not known or interesting
  to me were guessed so don't rely on any structured type to work properly.
  This is very early and very untested code. Only use it as basis for bad ideas. }

{ P.S. Instead of using winusb.dll, I think someone should make a delphi
  implementation for the WinUSB COM interface. }

{ Version: 0.01 Date: 18.11(November).2007 }

interface

uses
{$IFDEF WIN32}
  Windows, Dialogs;
{$ELSE}
  Wintypes, WinProcs, Dialogs;
{$ENDIF}

type
  HANDLE = THandle;
  PWINUSB_INTERFACE_HANDLE = ^THandle;
  WINUSB_INTERFACE_HANDLE = HANDLE;

  PVOID = Pointer;
  LPOVERLAPPED = Pointer;
  LONG = Cardinal;

  { #pragma pack(1) }

  // not tested so don't use
type
  _WINUSB_SETUP_PACKET = record
    RequestType_UNTESTED_DO_NOT_USE : Byte;
    Request_UNTESTED_DO_NOT_USE : Byte;
    Value_UNTESTED_DO_NOT_USE : Word;
    Index_UNTESTED_DO_NOT_USE : Word;
    Length_UNTESTED_DO_NOT_USE : Word;
  end { _WINUSB_SETUP_PACKET };

  WINUSB_SETUP_PACKET = _WINUSB_SETUP_PACKET;
  PWINUSB_SETUP_PACKET = ^_WINUSB_SETUP_PACKET;

type
  DESCRIPTOR = record // Generic descriptor
    Length : Byte;
    DescriptorType : Byte;
    Data : Array [ 0 .. 1000 ] of Byte; // will crash if descriptor is longer.
  end;

  { #pragma pack() }
type
  USBD_PIPE_TYPE = ( UsbdPipeTypeControl, UsbdPipeTypeIsochronous,
    UsbdPipeTypeBulk, UsbdPipeTypeInterrupt );

type
  _WINUSB_PIPE_INFORMATION = record
    PipeType : Cardinal;
    PipeId : Word; // strange, I know, but seems to fit the data
    MaximumPacketSize : Word;
    Interval : Word; // (?)
  end { _WINUSB_PIPE_INFORMATION };

type
  WINUSB_PIPE_INFORMATION = _WINUSB_PIPE_INFORMATION;
  PWINUSB_PIPE_INFORMATION = ^_WINUSB_PIPE_INFORMATION;
  USB_INTERFACE_DESCRIPTOR = DESCRIPTOR;
  PUSB_INTERFACE_DESCRIPTOR = ^USB_INTERFACE_DESCRIPTOR;
  USB_CONFIGURATION_DESCRIPTOR = DESCRIPTOR;
  PUSB_CONFIGURATION_DESCRIPTOR = ^USB_CONFIGURATION_DESCRIPTOR;

type
  BufferArray = Array of Byte;
  PBufferArray = ^BufferArray;

var
  WinUsb_Initialize : function( DeviceHandle : HANDLE;
    var InterfaceHandle : WINUSB_INTERFACE_HANDLE ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_Free_NotYetTested : function( InterfaceHandle
    : WINUSB_INTERFACE_HANDLE ) : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetAssociatedInterface_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    AssociatedInterfaceIndex : Byte;
    AssociatedInterfaceHandle : PWINUSB_INTERFACE_HANDLE ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetDescriptor : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    DescriptorType : Byte; Index : Byte; LanguageID : Word; Buffer : PUCHAR;
    BufferLength : Cardinal; var LengthTransferred : Cardinal ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_QueryInterfaceSettings_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    AlternateInterfaceNumber : Byte;
    UsbAltInterfaceDescriptor : PUSB_INTERFACE_DESCRIPTOR ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_QueryDeviceInformation_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    InformationType : Cardinal; BufferLength : PULONG; Buffer : PVOID )
    : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_SetCurrentAlternateSetting_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    SettingNumber : Byte ) : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetCurrentAlternateSetting_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    SettingNumber : PUCHAR ) : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_QueryPipe : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    AlternateInterfaceNumber : Byte; PipeIndex : Byte;
    PipeInformation : PWINUSB_PIPE_INFORMATION ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_SetPipePolicy_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE; PipeId : Byte;
    PolicyType : Cardinal; ValueLength : Cardinal; Value : PVOID ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetPipePolicy_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE; PipeId : Byte;
    PolicyType : Cardinal; ValueLength : PULONG; Value : PVOID ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_ReadPipe : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    PipeId : Byte; Buffer : PBufferArray; BufferLength : Cardinal;
    var LengthTransferred : Cardinal; Overlapped : LPOVERLAPPED ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_WritePipe : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    PipeId : Byte; Buffer : PBufferArray; BufferLength : Cardinal;
    var LengthTransferred : Cardinal; Overlapped : LPOVERLAPPED ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_ControlTransfer_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    SetupPacket : WINUSB_SETUP_PACKET; Buffer : PUCHAR; BufferLength : Cardinal;
    LengthTransferred : PULONG; Overlapped : LPOVERLAPPED ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_ResetPipe : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    PipeId : Byte ) : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_AbortPipe_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE; PipeId : Byte )
    : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_FlushPipe_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE; PipeId : Byte )
    : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_SetPowerPolicy_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    PolicyType : Cardinal; ValueLength : Cardinal; Value : PVOID ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetPowerPolicy_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    PolicyType : Cardinal; ValueLength : PULONG; Value : PVOID ) : Bool cdecl
  {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_GetOverlappedResult_NotYetTested
    : function( InterfaceHandle : WINUSB_INTERFACE_HANDLE;
    LPOVERLAPPED : LPOVERLAPPED; lpNumberOfBytesTransferred : LPDWORD;
    bWait : Bool ) : Bool cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_ParseConfigurationDescriptor_NotYetTested
    : function( ConfigurationDescriptor : PUSB_CONFIGURATION_DESCRIPTOR;
    StartPosition : PVOID; InterfaceNumber : LONG; AlternateSetting : LONG;
    InterfaceClass : LONG; InterfaceSubClass : LONG; InterfaceProtocol : LONG )
    : USB_INTERFACE_DESCRIPTOR cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  WinUsb_ParseDescriptors_NotYetTested : function( DescriptorBuffer : PVOID;
    TotalLength : Cardinal; StartPosition : PVOID; DescriptorType : LONG )
    : DESCRIPTOR cdecl {$IFDEF WIN32} stdcall {$ENDIF};

var
  DLLLoaded : Boolean { is DLL (dynamically) loaded already? }
{$IFDEF WIN32} = False; {$ENDIF}

implementation

var
  SaveExit : Pointer;
  DLLHandle : THandle;
{$IFNDEF MSDOS}
  ErrorMode : Integer;
{$ENDIF}

procedure NewExit; far;
begin
  ExitProc := SaveExit;
  FreeLibrary( DLLHandle )
end { NewExit };

procedure LoadDLL;
begin
  if DLLLoaded then
    Exit;
{$IFNDEF MSDOS}
  ErrorMode := SetErrorMode( $8000 { SEM_NoOpenFileErrorBox } );
{$ENDIF}
  DLLHandle := LoadLibrary( 'WINUSB.DLL' );
  if DLLHandle >= 32 then
  begin
    DLLLoaded := True;
    SaveExit := ExitProc;
    ExitProc := @NewExit;
    @WinUsb_Initialize := GetProcAddress( DLLHandle, 'WinUsb_Initialize' );
{$IFDEF WIN32}
    Assert( @WinUsb_Initialize <> nil );
{$ENDIF}
    @WinUsb_Free_NotYetTested := GetProcAddress( DLLHandle, 'WinUsb_Free' );
{$IFDEF WIN32}
    Assert( @WinUsb_Free_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetAssociatedInterface_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_GetAssociatedInterface' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetAssociatedInterface_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetDescriptor := GetProcAddress( DLLHandle,
      'WinUsb_GetDescriptor' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetDescriptor <> nil );
{$ENDIF}
    @WinUsb_QueryInterfaceSettings_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_QueryInterfaceSettings' );
{$IFDEF WIN32}
    Assert( @WinUsb_QueryInterfaceSettings_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_QueryDeviceInformation_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_QueryDeviceInformation' );
{$IFDEF WIN32}
    Assert( @WinUsb_QueryDeviceInformation_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_SetCurrentAlternateSetting_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_SetCurrentAlternateSetting' );
{$IFDEF WIN32}
    Assert( @WinUsb_SetCurrentAlternateSetting_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetCurrentAlternateSetting_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_GetCurrentAlternateSetting' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetCurrentAlternateSetting_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_QueryPipe := GetProcAddress( DLLHandle, 'WinUsb_QueryPipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_QueryPipe <> nil );
{$ENDIF}
    @WinUsb_SetPipePolicy_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_SetPipePolicy' );
{$IFDEF WIN32}
    Assert( @WinUsb_SetPipePolicy_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetPipePolicy_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_GetPipePolicy' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetPipePolicy_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_ReadPipe := GetProcAddress( DLLHandle, 'WinUsb_ReadPipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_ReadPipe <> nil );
{$ENDIF}
    @WinUsb_WritePipe := GetProcAddress( DLLHandle, 'WinUsb_WritePipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_WritePipe <> nil );
{$ENDIF}
    @WinUsb_ControlTransfer_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_ControlTransfer' );
{$IFDEF WIN32}
    Assert( @WinUsb_ControlTransfer_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_ResetPipe := GetProcAddress( DLLHandle, 'WinUsb_ResetPipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_ResetPipe <> nil );
{$ENDIF}
    @WinUsb_AbortPipe_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_AbortPipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_AbortPipe_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_FlushPipe_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_FlushPipe' );
{$IFDEF WIN32}
    Assert( @WinUsb_FlushPipe_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_SetPowerPolicy_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_SetPowerPolicy' );
{$IFDEF WIN32}
    Assert( @WinUsb_SetPowerPolicy_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetPowerPolicy_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_GetPowerPolicy' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetPowerPolicy_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_GetOverlappedResult_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_GetOverlappedResult' );
{$IFDEF WIN32}
    Assert( @WinUsb_GetOverlappedResult_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_ParseConfigurationDescriptor_NotYetTested :=
      GetProcAddress( DLLHandle, 'WinUsb_ParseConfigurationDescriptor' );
{$IFDEF WIN32}
    Assert( @WinUsb_ParseConfigurationDescriptor_NotYetTested <> nil );
{$ENDIF}
    @WinUsb_ParseDescriptors_NotYetTested := GetProcAddress( DLLHandle,
      'WinUsb_ParseDescriptors' );
{$IFDEF WIN32}
    Assert( @WinUsb_ParseDescriptors_NotYetTested <> nil );
{$ENDIF}
  end
  else
  begin
    DLLLoaded := False;
    ShowMessage( 'Error: WINUSB.DLL could not be loaded !!' );
    { Error: WINUSB.DLL could not be loaded !! }
  end;
{$IFNDEF MSDOS}
  SetErrorMode( ErrorMode )
{$ENDIF}
end { LoadDLL };

begin
  LoadDLL;

end.
 

原文地址:https://www.cnblogs.com/shangdawei/p/3026394.html