文章目录[隐藏]
1 概述
1.1 WCID 简介
WCID全称为“WINDOWS Compatible ID”,是一种可提供额外的信息,在Windows系统中,为了便于驱动程序自动安装,并在某些情况下,允许即时访问的USB设备。
WCID允许Windows应用程序几乎在插入后立即使用设备,这既不是HID也不是大容量存储的USB设备需要最终用户执行手动驱动程序安装。因此,WCID可以做到像HID和大容量存储USB设备那样“即插即用”。WCID是WinUSB设备功能的扩展,由Microsoft在Windows8开发人员预览版期间提出,WinUSB自Windows XP SP2以来一直是Windows一部分的功能。
WinUSB设备是一种通用串行总线(USB)设备,其固件定义了某些Microsoft操作系统(OS)特征描述符,这些描述符将兼容ID报告为"WINUSB"。WinUSB设备的用途是让Windows将Winusb.sys作为设备的功能驱动程序载入,而无需自定义INF文件。对于WinUSB设备,你无须为设备分发INF文件,这大大简化了驱动程序安装过程。截至2012年5月,从Windows Vista开始的所有平台上都提供了自动 WinUSB WCID 驱动程序。在 Windows 8 或更高版本地系统上已经集成了该驱动程序,这意味着Windows 8 或更高版本地系统上不需要安装驱动程序,系统自动会匹配WinUSB驱动,而对于Vista、Windows7、XP系统,可以通过一些第三方软件(如Zadig)来安装或生成WCID版本的驱动程序,同样只需要安装一次WCID版本的驱动软件,以后所有WCID设备都可以使用该驱动,间接实现了免驱。
在WCID之前,只有标准类别的USB设备,如HID(USB键盘、鼠标、游戏杆等)或大容量存储(U盘、基于闪存的存储、媒体播放器等)不需要安装驱动程序,因为Windows会在设备第一次插入时自动处理。而其他设备通常需要自己手动提供和安装驱动程序,在WIN8以后厂家自己提供的驱动程序还可能受到微软的驱动程序签名限制,造成不必要的麻烦。
原文参考:https://github.com/pbatard/libwdi/wiki/WCID-Devices
1.2 WinUSB限制条件
WinUSB有许多限制条件,一般来说注意以下几点即可:
1) WinUSB不支持Windows 2003 ( 32bit/64bit );
2)WinUSB不支持Windows XP 64位(可以通过第三方驱动支持);
3) USB设备版本必需是2.0或2.0以上,WinUSB不支持USB1.0和1.1。
原文参考:https://www.cnblogs.com/shangdawei/archive/2013/04/17/3026394.html
2 WinUSB设备枚举关键信息
设备枚举为WinUSB的三个关键信息:
1)OS字符串描述符;
2)扩展兼容ID OS功能描述符;
3)扩展属性OS功能描述符;
2.1 支持OS字符串描述
当有USB设备插入,系统会向设备索取设备描述符,根据设备描述符中的bcdUSB版本号判断是否检索0xEE位置的OS设备字串描述符,如果bcdUSB版本号大于等于2.0,则系统会检索0xEE位置的OS设备字串描述符,支持WinUSB的OS描述如下:
如果0xEE处的OS描述符不存在或者描述格式不正确,系统将设置osvc值为0x0000,表示该USB设备不是WCID设备。如果OS描述正确,系统将osvc设置为0x01##。
2.2 设置兼容ID特征描述符
在获取到osvc为有效值的前提下,系统将进一步获取兼容ID特征描述符,索引号为0x04,兼容ID特征描述符包含紧跟一个或多个功能部分的标题部分,具体取决于是否是复合设备。标题部分指定整个描述符的长度、功能部分的数量以及版本号。对于非复合设备,标题后紧跟一个仅与设备的接口关联的功能部分。该部分的compatibleID字段必须指定"WINUSB"作为字段值。复合设备有多个功能部分。每个功能部分的compatibleID字段必须指定"WINUSB"。兼容ID特征描述符格式如下:
2.3 扩展属性描述符
最后是添加扩展属性描述符,注册设备接口GUID,包含紧跟一个或多个自定义属性部分的标题部分,索引号为0x05。标题部分描述整个扩展属性描述符,包含其总长度、版本号以及自定义属性部分的数量。WinUSB应用程序正是通过GUID来检索对应的USB设备,并进行访问,详细定义如下表所示:
3 USB枚举过程
通过Keil调试可以获取到USB枚举的全部信息,如下图所示。枚举过程中共有40条交互信息:
在Excel中统计如表格所示,第一列表示方向:1为Windows往设备发,0为设备回复信息。
- 当Windows检测到有USB设备插入,首先会向设备索取设备描述符长度,设备正确回复后Windows分配USB设备地址,并以之前收到的实际长度去获取完整的设备描述符;
- 获取USB设备信息,包含基本配置、设备接口、端点等信息;
- Windows判断第一步获取的bcdUSB版本号如果大于等2.0,则还会索引0xEE处的OS字符串描述符,确认设备是否支持WCID,若回复内容为MSFT100则说明该设备支持WCID,后续才会继续获取兼容ID特征描述等;
- 获取兼容ID特征描述符,回复兼容信息为WinUSB设备;
- 获取兼容ID后,系统会根据兼容ID的情况请求属性描述符,对于WinUSB设备需要注册设备接口GUID;
- 后续其他信息获取…
4 实例应用
4.1 生成USB 工程
本实例采用STM32F405RGT6 + USBS3300 实现USB HS 高速WinUSB设备,使用STM32CubeMX生成基础工程,CubeMX版本号为6.3,STM32F4 package版本为1.26.2,电脑系统为WIN10。
备注:USB FS同样按如下使用方法,需要注意的是USB FS只能用外部时钟,且配置时需要注意外部时钟频率!!
//---------------------我是广告-----------------------
详细配置及参考工程请移步淘宝店:https://item.taobao.com/item.htm?spm=a230r.1.14.11.18cc4864SdV6xL&id=660992604144&ns=1&abbucket=20
//---------------------我是广告-----------------------
1)打开CubeMX选择MCU型号STM32F405RGT6:
2)按如下顺序在Connectivity中选中USB_TG_HS,然后设置External Phy为Device_Only,并且将NVIC的三个中断都勾选上:
3)按如下步骤在Middleware选项中选择USB_DEVICE,右侧HS IP选择Communication Device Class(Virtual Port Com),然后在Device Descriptor中设置相关属性:此处VID PID分别设置为0x1234、0xABCD(可自行更改,点击输框右侧小齿轮将格式改为16进制显示);设置PRODUCT_STRING,些处以GUOXUAN为例(可自行更改),这个字符串就是最终显示在设备管理器中的名字;设置CONFIGURATION_STRING 和 INTERFACE_STRING,固定为WINUSB_Config 和 WINUSB Interface。
4)点击下一页Clock Configuration 直接点确定会自动配置好时钟:
5)点击下一页配置工程信息,适当将堆栈容量改大,避免一些意外问题,设置好后点击右上角生成工程:
6)生成好后直接编译工程,可以编译成功:
将程序烧入单片机后连接电脑,该USB设备被识别成了串口,并且没有显示出产品字符串,因为设备描述符没有修改,设备被枚举成了CDC串行通信类:
4.2 修改工程文件
(1) 增加兼容ID描述符和扩展属性描述符定义,其中扩展描述符中的GUID定义可根据实际需求更改,本文以{12345678-ABCD-1234-ABCD-FEDCBA987654}为例:
//-----------ADD GUOXUAN----------------
#define USB_LEN_OS_FEATURE_DESC 0x28
__ALIGN_BEGIN uint8_t USBD_WINUSB_OSFeatureDesc[USB_LEN_OS_FEATURE_DESC] __ALIGN_END =
{
0x28, 0, 0, 0, // length
0, 1, // bcd version 1.0
4, 0, // windex: extended compat ID descritor
1, // no of function
0, 0, 0, 0, 0, 0, 0, // reserve 7 bytes
// function
0, // interface no
0, // reserved
'W', 'I', 'N', 'U', 'S', 'B', 0, 0, // first ID
0, 0, 0, 0, 0, 0, 0, 0, // second ID
0, 0, 0, 0, 0, 0 // reserved 6 bytes
};
#define USB_LEN_OS_PROPERTY_DESC 0x8E
__ALIGN_BEGIN uint8_t USBD_WINUSB_OSPropertyDesc[USB_LEN_OS_PROPERTY_DESC] __ALIGN_END =
{
0x8E, 0, 0, 0, // length 246 byte
0x00, 0x01, // BCD version 1.0
0x05, 0x00, // Extended Property Descriptor Index(5)
0x01, 0x00, // number of section (1)
//; property section
0x84, 0x00, 0x00, 0x00, // size of property section
0x1, 0, 0, 0, //; property data type (1)
0x28, 0, //; property name length (42)
'D', 0,
'e', 0,
'v', 0,
'i', 0,
'c', 0,
'e', 0,
'I', 0,
'n', 0,
't', 0,
'e', 0,
'r', 0,
'f', 0,
'a', 0,
'c', 0,
'e', 0,
'G', 0,
'U', 0,
'I', 0,
'D', 0,
0, 0,
// change by yourself
0x4E, 0, 0, 0, // ; property data length
'{', 0,
'1', 0,
'2', 0,
'3', 0,
'4', 0,
'5', 0,
'6', 0,
'7', 0,
'8', 0,
'-', 0,
'A', 0,
'B', 0,
'C', 0,
'D', 0,
'-', 0,
'1', 0,
'2', 0,
'3', 0,
'4', 0,
'-', 0,
'A', 0,
'B', 0,
'C', 0,
'D', 0,
'-', 0,
'F', 0,
'E', 0,
'D', 0,
'C', 0,
'B', 0,
'A', 0,
'9', 0,
'8', 0,
'7', 0,
'6', 0,
'5', 0,
'4', 0,
'}', 0,
0, 0,
};
//------------------------------
(2)增加OS字符串描述,固定为“MSFT100”,VENDOR_CODE可自行更改;增加这三个描述的接口指针函数:
//-----------ADD GUOXUAN----------------
const uint8_t USBD_OS_STRING[8] = {
'M',
'S',
'F',
'T',
'1',
'0',
'0',
0xA0, //USB_REQ_MS_VENDOR_CODE,
};
uint8_t *USBD_WinUSBOSStrDescriptor(uint16_t *length)
{
USBD_GetString((uint8_t *)USBD_OS_STRING, USBD_StrDesc, length);
return USBD_StrDesc;
}
uint8_t *USBD_WinUSBOSFeatureDescriptor(uint16_t *length)
{
*length = USB_LEN_OS_FEATURE_DESC;
return USBD_WINUSB_OSFeatureDesc;
}
uint8_t *USBD_WinUSBOSPropertyDescriptor(uint16_t *length)
{
*length = USB_LEN_OS_PROPERTY_DESC;
return USBD_WINUSB_OSPropertyDesc;
}
//------------------------------------
(3) 响应设备描述请求中的的的的USB_REQ_TYPE_VENDOR请求:
case USB_REQ_TYPE_VENDOR:
USBD_WinUSBGetDescriptor( pdev, req ); //-----CHANGE GUOXUAN-------
break;
(4) 响应接口请求USBD_StdItfReq中的case USBD_STATE_CONFIGURED:
if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES)
{
ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req);
if ((req->wLength == 0U) && (ret == USBD_OK))
{
(void)USBD_CtlSendStatus(pdev);
}
}
//-----------------ADD GUOXUAN-------------
if ( req->bmRequest == 0xC1 )
{
USBD_WinUSBGetDescriptor( pdev, req );
break;
}
//----------------------------------------
else
{
USBD_CtlError(pdev, req);
}
break;
(5) 响应0xEE请求:
//---------------ADD GUOXUAN----------
case 0xEE: // OS String
pbuf = (uint8_t *) pdev->pClass->GetWinUSBOSDescriptor(&len);
break;
//------------------------------------
(6) 修改CDC配置描述信息:
//---------------CHANGE GUOXUAN---------------
/* USB CDC device Configuration Descriptor */
#define WINUSB_CONFIG_DESC_SIZE 32
__ALIGN_BEGIN uint8_t USBD_CDC_CfgHSDesc[WINUSB_CONFIG_DESC_SIZE] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
WINUSB_CONFIG_DESC_SIZE, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface for WINUSB */
0x01, /* bConfigurationValue: Configuration value */
USBD_IDX_CONFIG_STR, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*---------------------------------------------------------------------------*/
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x00, /* bInterfaceNumber: Number of Interface, zero based index of this interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0xff, /* bInterfaceClass: vendor */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/* Endpoint IN Descriptor */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
};
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[WINUSB_CONFIG_DESC_SIZE] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
WINUSB_CONFIG_DESC_SIZE, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface for Game IO */
0x01, /* bConfigurationValue: Configuration value */
USBD_IDX_CONFIG_STR, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*---------------------------------------------------------------------------*/
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x00, /* bInterfaceNumber: Number of Interface, zero based index of this interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0xff, /* bInterfaceClass: vendor */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/* Endpoint IN Descriptor */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
};
//------------------------------------------
//---------------------我是广告-----------------------
详细配置及参考工程请移步淘宝店:https://item.taobao.com/item.htm?spm=a230r.1.14.11.18cc4864SdV6xL&id=660992604144&ns=1&abbucket=20
//---------------------我是广告-----------------------
编译下载及问题处理
修改完上述4个文件后,编译工程并下载,下载完后设备最好断一次电,重新插入USB后,在设备管理器中可以看到已经成功枚举出了设备名为GUOXUAN的通用串行总线设备:
但凡事都有意外,当下载好后重新插入USB也可能出现以下情况(不限于以下这两种!!),只要设备名上有叹号,说明明枚举不成功,此时除了检查代码有无错误以外还可以尝试删除注册表中已有的VID PID信息,因为CubeMX生成好工程时测试连接过一次,枚举为了串口,注测表中已记录这个设备的信息,但现在设备描述符又指定为了WinUSB设备,所以造成了枚举失败。
删除注册表中USB设备信息的方法:
1、下载PStools工具包 https://technet.microsoft.com/en-us/sysinternals/bb897553.aspx,解压到C:\pstools
2、以管理员身份运行CMD,输入命令:cd C:\pstools ,切换到pstools目录,再输入命令:psexec.exe -i -d -s regedit.exe
3、进入HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\删除相关设备(以本文为例,VID PID分别为 1234 ABCD)
进入HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\UsbFlags\删除相关设备(以本文为例,VID PID分别为 1234 ABCD)
4、OK,此时再插入该Device,即被当做全新设备插入,装载驱动。
原文参考:
https://www.codenong.com/41644081/
https://blog.csdn.net/lxc1014/article/details/45170107?utm_source=app&app_version=4.17.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
4.3 与上位机通信
在本测试中上位机发送一个数据包到USB设备,数据包的前两个字节设置为数据长度,设备接收到该数据包后根据数据长度返回一包数据。
4.3.1 修改数据接收和发送函数
修改usbd_cdc_if.c文件中数据接收函数,首先添加定义:
//-----------------ADD GUOXUAN-----------------------
extern volatile int8_t usb_rxne;
extern uint8_t usb_rx[2048];
//---------------------------------------------------
然后修改数据接收处理:
//-------------ADD GUOXUAN--------------------
memcpy(usb_rx, Buf, *Len);
usb_rxne = SET;
//--------------------------------------------
在main.c中添加数据发送函数:
首先引用头文件:
//---------------ADD GUOXUAN--------------------------
#include "usbd_cdc_if.h"
//----------------------------------------------------
添加定义:
//--------------ADD GUOXUAN-----------------------
uint8_t usb_rx[2048];
uint8_t usb_tx[81920];
volatile int8_t usb_rxne = RESET;
extern USBD_HandleTypeDef hUsbDeviceHS;
//-----------------------------------------------
添加接收函数:
//------------ADD GUOXUAN-------------------
void WinUSB_Receive_HS()
{
uint16_t i = 0;
if (usb_rxne != SET) return;
i = usb_rx[0] + (usb_rx[1]<<8);
CDC_Transmit_HS(usb_tx, i);
usb_rxne = RESET;
}
//------------------------------------------
最后在main里面的while(1)里调用:
while (1)
{
/* USER CODE END WHILE */
WinUSB_Receive_HS();
/* USER CODE BEGIN 3 */
}
4.3.2 与上位机通信测试
上位机采用C#编写,使用开源库WinUSBNet(XP使用LibUsbDotNet)进行访问,上位机发送一个包,设备回复一个包;上行和下行的包大小自由设置(本测试主要测上行速率,下发的包大小置固定为64,若下发包大小超过512,设备USB接收函数需要做相应处理,因为单次传输最大包长为512,大于512将会被分包接收);上行包的大小可改变,以测试不同包长时的速率;点击一次send发送1000个包,并统计速率(本次测试没有对接收和发送的数据作任何处理,实际应用情况下,加上数据处理后通信速率稍低于测试值)。WIN8、WIN7、XP均是在虚拟机运行测试的,通信速率可能会受到一些影响。
WIN10上测试结果如下:
WIN8上测试结果如下:
WIN7和XP上默认不带WinUSB驱动,设备插到电脑后,设备管理器可以看到设备枚举失败,但用zadig软件可以识别到该设备为WICD设备,可以安装WinUSB驱动程序,省去了手动写inf文件,也可以用zadig软件将驱动程序导出,通过右键更新驱动程序的方式安装。
WIN7上测试结果如下:
XP上测试结果如下(XP使用LibUsbDotNet,默认情况下接收buffer只能到4095):
5 常见问题
1)没有USB测试仪无法抓包分析
用Keil进行抓包,全新的USB枚举过程数据通信大概有40条左右,最大长度不超过150字节,所以定义一个数组usbLog[50][200],在USB设备请求通信入口 USBD_StdDevReq()函数处记录上位机下发的信息,在USB设备回复数据USBD_CtlSendData()函数处记录回复给上位机的信息,即可抓取到全部通信内容。第3章节USB枚举过程中信息正是用此方法获取的。
2)抓包分析抓不到0xEE命令
调试过程中,可能第一次并未完全修改正确,插入电脑后枚举失败,但是电脑已经记录下了该设备PID VID,当下次枚举的时候,该PID VID已经存在并且记录了该设备不是WinUSB设备,所示不会发送0xEE命令,解决办法是更换PID VID 或者4.2.5所述的方法删除注册表中相应的PID VID即可。
3)完全按上述流程修改后还是无法枚举成功
同第2条。
4)下载正确程序后不枚举或枚举失败
因为USB HS使用的是外部PHY,重新烧写单片机程序可能不会让USB重新枚举,通常断电一次即可枚举成功。
5)传输过程中有丢包现象
检查包长设置是否合理,缓冲区buffer是否足够。
版权声明:本文为CSDN博主「bingwueyi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bingwueyi/article/details/121622001
暂无评论