WinUSB
+ -

简单几步,让自定义USB设备也能免驱动运行

2022-06-16 2021 1
原文转自:http://blog.xtoolbox.org/custom_usb_device_without_driver/

本文作者XTOOLBOX,本站得到了作者本人的转载授权。

更完整的说明见《使用微软系统描述符1.0制作免驱动自定义USB设备》和《使用微软系统描述符2.0制作免驱动自定义USB设备

做过USB设备开发的人,对USB中的自定义HID设备一定不陌生。很多时候为了通过USB接口与上位机进行通讯,都会采用自定义HID设备的方式。采用这种方式的通讯设备,优点是不需要写驱动程序,Windows上也有相应的API进行操作。这种方式的缺点是通讯速率比较慢,因为HID设备采用中断方式传输数据,对全速设备而言最快一秒钟只能传64K字节数据。而USB全速设备的理论带宽能达到1M字节每秒,连10%的性能都没有达到。

如果采用Bulk传输,则可以达到理论最大带宽,榨干USB总线的性能。但是采用Bulk传输的时候,设备要么做成串口这样的标准设备,免去驱动的编写,这样设备就不是自定义的,使用起来不如自定义设备那么方便。要么做成Bulk传输的自定义设备,但是这样就得写编写驱动程序,而驱动开发也是一个大坑。

那么能不能既能获得自定义的好处,又不进行驱动开发呢?答案是肯定的。

为了省掉自定义设备的驱动开发,微软操碎了心。在Win8或更高版本的系统中,微软集成了WinUSB的WCID设备。WinUSB是微软提供的一个USB设备的通用驱动程序,这个驱动早在XP SP2就开始提供了。使用这个驱动用户不需要编写内核层的驱动程序就能访问USB设备。WCID则是USB驱动一种新的匹配机制,在2012年左右引入的。通常USB设备都是通过VID和PID来进行匹配的,而使用了WCID之后,设备不通过VID和PID来匹配驱动,而是通过一个叫做兼容ID(Windows Compatible ID)的东西来匹配,这样就不用为每一个VID和PID不同的设备编写INF文件了。

需要说明的是,这里说的免驱动有两层含义:一层是不需要编写驱动程序,系统自带了驱动程序,只需写一个inf文件,如USB串口;另一层是不需要编写inf文件,系统会根据设备类型来安装驱动,这需要操作系统的支持。

对于WinUSB设备而言,在Win8之前不用编写驱动程序,但是需要编写inf文件,匹配设备。在Win8之后,如果设备支持WCID,连inf也不用编写。

下面将介绍如何在设备中增加对WCID的支持,让在能在Win8之后的系统上实在真正的即插即用。

首先得要有一个能用起来的自定义设备,在这个设备的设备描述符中,USB版本号设置为2.00,在这个设备的基础之上进行如下的修改:

第一步

响应ID为0xEE的字符描述符请求,字符描述的内容为:

{
0x12,                                         /* bLength */
USB_STRING_DESCRIPTOR_TYPE,                   /* bDescriptorType */
'M', 0x00,                                    /* wcChar0 */
'S', 0x00,                                    /* wcChar1 */
'F', 0x00,                                    /* wcChar2 */
'T', 0x00,                                    /* wcChar3 */
'1', 0x00,                                    /* wcChar4 */
'0', 0x00,                                    /* wcChar5 */
'0', 0x00,                                    /* wcChar6 */
0x17,                                         /* bVendorCode */
0x00,                                         /* bReserved */
}

这一步是为了让Windows将我们的设备识别为WCID设备,以便进行下一步操作

第二步

响应请求号为0x17并且index为4的厂商自定义请求,返回内容为:

{
    0x28, 0x00, 0x00, 0x00,                       /* dwLength */
    0x00, 0x01,                                   /* bcdVersion */
    0x04, 0x00,                                   /* wIndex */
    0x01,                                         /* bCount */
    0,0,0,0,0,0,0,                                /* Reserved */
    /* WCID Function  */
    0x00,                                         /* bFirstInterfaceNumber */
    0x01,                                         /* bReserved */
    /* CID */
    'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 
    /* sub CID */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0,0,0,0,0,0,                                  /* Reserved */
};

这一步是为了向Windows上报我们设备的WCID值,因为我们需要的是WinUSB的驱动,所以上报的内容就是WinUSB驱动的WCID:“WINUSB”。

第三步

响应请求号为0x17并且index为5的厂商自定义请求,返回内容为:

{
  ///////////////////////////////////////
  /// WCID property descriptor
  ///////////////////////////////////////
  0x8e, 0x00, 0x00, 0x00,                           /* dwLength */
  0x00, 0x01,                                       /* bcdVersion */
  0x05, 0x00,                                       /* wIndex */
  0x01, 0x00,                                       /* wCount */

  ///////////////////////////////////////
  /// registry propter descriptor
  ///////////////////////////////////////
  0x84, 0x00, 0x00, 0x00,                           /* dwSize */
  0x01, 0x00, 0x00, 0x00,                           /* dwPropertyDataType */
  0x28, 0x00,                                       /* wPropertyNameLength */
  /* DeviceInterfaceGUID */
  'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00,       /* wcName_20 */
  'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00,       /* wcName_20 */
  't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00,       /* wcName_20 */
  'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00,       /* wcName_20 */
  'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00,      /* wcName_20 */
  0x4e, 0x00, 0x00, 0x00,                           /* dwPropertyDataLength */
  /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */
  '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00,       /* wcData_39 */
  'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00,       /* wcData_39 */
  '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00,       /* wcData_39 */
  '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00,       /* wcData_39 */
  '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00,       /* wcData_39 */
  'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00,       /* wcData_39 */
  '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00,       /* wcData_39 */
  'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00,       /* wcData_39 */
  'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00,       /* wcData_39 */
  '6', 0x00, '}', 0x00, 0x00, 0x00,                 /* wcData_39 */
}

这一步是为了向Windows注册接口的GUID。

上述内容添加完成后,将你的设备插入电脑,会发现设备能够自动安装上WinUSB驱动程序,如下图所示:

15465888691

现在问题又来了,设备管理器中确实看到设备驱动已经安装,并且能正常使用,但是要怎么用呢。

微软为了让这个WinUSB能用起来,提供了对应的API。不过这个API用起来不是那么方便,libusb对WinUSB做了进一步的封装,提供了Windows和Linux上访问USB设备的统一接口。笔者对libusb按照Qt的风格又做了一次封装,封装成了QLibUsb。

QLibUsb用起来会更方便一些了。先用enumDevices枚举设备,枚举时可以指定VID和PID,也可以不指定。然后调用open打开设备。打开成功后通过readEndpoints得到设备所有的IN端点号,通过writeEndpoints得到设备所有的OUT端点号。调用write函数向指定的OUT端点写数据。当有IN端点收到数据时,会触发设备的epDataReady信号,在这个信号中对收到的数据进行处理。QLibUsb代码地址:https://github.com/xtoolbox/qtlua/tree/master/src/qlibusb

运行XToolbox.exe后,在USB View框中填入VID和PID后点击【Refresh】按钮搜索设备,在下拉框中选中需要测试的设备后点击Open打开设备。设备打开成功后会在下方显示输入端点和输出端点,选中一个输出端点,填入一些数据后,点击【send】按钮发送数据。当接收到数据时,会在对应的输入端点栏中显示。如下图:
154712936790

上图中USB设备测试工具下载地址:

如果要在Win8以前的系统中支持WinUSB设备,还是需要编写inf文件,生成inf文件的工具下载:

关于WCID免驱动设备的更多内容在这里

更多关于STM32上USB设备开发的资料也可以阅读《STM32 USB设备开发指南》,此书还在编写中。下载地址:Github镜像,21ic镜像

完整的免驱动自定义设备代码在code.tusb.org,也包括上述测试工具的源代码

HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936   

0 篇笔记 写笔记

USB WCID设备中特殊字符描述符
WCID全称”Windows Compatible ID,中文名为“Windows兼容ID”。 WCID设备是一种向Windows系统提供额外信息的USB设备,以便于自动安装驱动程序,并在某些情况下允许立即访问。USB设备驱动的匹配安装一般是以VID/PID进行驱动匹配的,但WCID设备却是根据C......
使用WinUSB读写USB设备
Windows为WinUSB设备提供了API,主要通过以下几个步骤访问设备。通过扩展描述符中的GUID查看接口的路径用接口的路径作为参数,调用CreateFile打开接口使用WinUsb_Initialize得到WinUSB句柄通过WinUsb_WritePipe和WinUsb_ReadPipe对......
使用微软系统描述符1.0制作免驱动自定义USB设备
本文作者XTOOLBOX,本站得到了作者本人的转载授权。本文介绍如何使用微软的操作系统描述符来实现自定义USB设备在Windows系统上的免驱动使用。前言在Linux上开发USB设备是不需要特别的驱动的,Linux内核的USB驱动会将USB设备的基本操作都暴露到应用层,由应用层来完成实际的业......
使用微软系统描述符2.0制作免驱动自定义USB设备
本文作者XTOOLBOX,本站得到了作者本人的转载授权。前言在《使用微软系统描述符1.0制作免驱动自定义USB设备》一文中,介绍了如何使用1.0版本的系统描述符来制作免驱动设备,这里将介绍如何使用2.0版本的系统描述符来制作免驱动设备。无论是1.0还是2.0,都是为了让系统给设备安装WinUS......
简单几步,让自定义USB设备也能免驱动运行
本文作者XTOOLBOX,本站得到了作者本人的转载授权。更完整的说明见《使用微软系统描述符1.0制作免驱动自定义USB设备》和《使用微软系统描述符2.0制作免驱动自定义USB设备》做过USB设备开发的人,对USB中的自定义HID设备一定不陌生。很多时候为了通过USB接口与上位机进行通讯,都会......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!