先说结论:
1、只要能枚举成功为bulk设备,那么就能在设备管理器里面,手动更新驱动程序,强制选择为WINUSB设备即可!不一定要识别为Winusb设备,Win10通用。
2、要识别为Win USB设备,需要枚举时增加一些描述符,windows才能自动安装驱动,而无需强制选择。有2个版本增加描述符方法,这里讲1.0版本,下面重点讲讲单片机MCU如何自动识别为WinUSB
A,定义描述符,具体看这里:使用微软系统描述符1.0制作免驱动自定义USB设备
/* 下面是WINUSB所需的描述符 https://blog.csdn.net/weixin_46753805/article/details/116200729
*
* 会索引0xEE处的OS字符串描述符,确认设备是否支持WCID,若回复内容为MSFT100则说明该设备支持WCID,后续才会继续获取兼容ID特征描述等;
获取兼容ID特征描述符,回复兼容信息为WinUSB设备;
获取兼容ID后,系统会根据兼容ID的情况请求属性描述符,对于WinUSB设备需要注册设备接口GUID;
*
*
*
*/
// 当系统正确并获取到此字符串前,在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\[VID+PID+BCD_RELEASE_NUMBER]的位置创建一个键值为osvc字节的REG_BINARY的2字节密钥。
// "MSFT100" : index : 0xEE : langId : 0x0000
const uint8_t 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\Enum\USB的位置存储该设备的兼容ID.如VID_04D8&PID_FA2E
// "WINUSB\0\0" : wIndex : 0x0004
const uint8_t WINUSB_ExtendedCompatId_Descritpor[ ] =
{
// WCID descriptor = 16byte
0x28, 0x00, 0x00, 0x00, // dwLength
0x00, 0x01, // bcdVersion
0x04, 0x00, // wIndex=4用于获取设备的兼容ID,在windows领域内使用index=5用于获取固件自己定义的设备interface guid
0x01, // bCount
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved[7]
// WCID function descriptor
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]
};
// 在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\Vidxxx&&Pidxxx\LUSBW1\Device Parameters
// L"DeviceInterfaceGUID" : wIndex = 0x0005
// L"{12345678-1234-1234-1234-123456789ABC}"(可以换自己的GUID)
const uint8_t WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[ ] =
{
// WCID property descriptor = 10 byte
0x8E, 0x00, 0x00, 0x00, // dwTotalSize = Header + All sections
0x00, 0x01, // bcdVersion
0x05, 0x00, // wIndex=5用于获取固件自己定义的设备interface guid.
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
};现在的问题就是怎么在单片机端,把这些描述符给他上报上去。
B、在获取字符串那里增加index = 0xee分支,把OS_StringDescritpor上报上去
C、在非标准setup那里把0xC0+VENDORCODE,以及0xC1+VENDORCODE 分别上报WINUSB_ExtendedCompatId_Descritpor以及
WINUSB_ExtendedProperty_InterfaceGUID_Descritpor
Windows在每个流程会最少请求2次,第一次先请求前面0x10个字节,然后知道要传0x28和0x8E,第二次才请求实际长的数据
D、如果包长大于端点0的64字节,那么第二个包要到token_in分支进行传输!否则主机获取不到0x8E个字节就会报xact error错误!
完整的传输截图如下:

难点在ep0分包传输,以及在正确的位置上报描述符!

E、要Windows每次发出0xC1请求,需要把这个Device Parameters节点删除,否则会认为有数据而不会发0xC1请求!0xC0会每次发,只要0xEE上报有数据的话。
