CH552 官方 USB HID 示例程序中的一个因未完整处理 16 位长度值导致的 bug
本帖最后由 wudicgi 于 2020-8-25 21:51 编辑很多年没写 51 核单片机的程序了,都遗忘了 C51 程序中为了提高效率,经常使用 8 位长度数据类型所带来的弊端了。
最近在开发 BeatShow 设备的固件恢复功能时发现,使用 CH552 实现的 USB HID 设备总是获取不到设备名称,而 STM32F072 的就没问题。即使将所有 USB 描述符都改成一样的,问题也仍然存在。
调试 PC 端程序看到底层调用的 HidD_GetProductString() 函数执行结果是成功的,只是返回的字符串长度为 0, 所以就得从硬件下手了。
经过抓包,发现对于 CH552 设备,除了设备枚举过程中的正常 GET_DESCRIPTOR 请求,在 PC 端程序调用 HidD_GetProductString() 时系统又发送了额外的 GET_DESCRIPTOR 请求。而且这些请求的返回数据长度只有 2 字节,明显是不正常的。
CH552 设备枚举过程中正常的 GET_DESCRIPTOR 请求, wLength = 0x00FF = 255:
CH552 设备完成枚举后的额外 GET_DESCRIPTOR 请求, wLength = 0x0102 = 258:
STM32 设备在完成枚举后则没有这些额外的 GET_DESCRIPTOR 请求:
对于为什么系统只对 CH552 设备发送了这些额外的 GET_DESCRIPTOR 请求,经过一番搜索没有找到答案。但显然 CH552 的程序在这块的处理上有 bug。
CH552 实现的 bootloader 是以 WCH 官方的 USB HID 示例程序 CompatibilityHID.C 为基础,逐步重构和修改而来的。这个示例中有一段处理 SETUP 事物的代码是这样的:
case UIS_TOKEN_SETUP | 0: // SETUP 事务
len = USB_RX_LEN;
if (len == (sizeof(USB_SETUP_REQ)))
{
SetupLen = UsbSetupBuf->wLengthL;
len = 0; // 默认为成功并且上传 0 长度
SetupReq = UsbSetupBuf->bRequest;
// ...
可以看到只取了 wLength 的低字节部分 wLengthL 作为 SetupLen, 完全没有使用高字节部分 wLengthH。而另一个 VendorDefinedDev.C 示例程序中,就对高字节部分 wLengthH 做了检查:
case UIS_TOKEN_SETUP | 0: // endpoint 0# SETUP
len = USB_RX_LEN;
if (len == sizeof(USB_SETUP_REQ)) { // SETUP 包长度
SetupLen = UsbSetupBuf->wLengthL;
if (UsbSetupBuf->wLengthH || SetupLen > 0x7F) SetupLen = 0x7F; // 限制总长度
len = 0; // 默认为成功并且上传 0 长度
SetupReqCode = UsbSetupBuf->bRequest;
// ...
CompatibilityHID.C 示例程序中原本没有任何字符串描述符,也没有对 GET_DESCRIPTOR 请求的处理代码,相关的内容都是我后添加的。这可能是官方程序中这个 bug 存在了这么长时间的原因。
知道原因后就好修改了,如果程序要返回的数据没有超过 255 字节的,只要在 wLengthH 不为 0 时限制一下 SetupLen 的值就可以了:
case UIS_TOKEN_SETUP | 0: // SETUP 事务
len = USB_RX_LEN;
if (len == (sizeof(USB_SETUP_REQ)))
{
SetupLen = UsbSetupBuf->wLengthL;
if (UsbSetupBuf->wLengthH != 0)
{
SetupLen = 0xFF; // 限制总长度
}
len = 0; // 默认为成功并且上传 0 长度
SetupReq = UsbSetupBuf->bRequest;
// ...
对楼主这样的高手的敬仰有如滔滔江水连绵不绝。 查找问题耗了不少时间,主要是一开始就关注为什么描述符修改的都一模一样了,为什么 CH552 这个设备就是获取不到名称
从硬件抓包结果看,CH552 只是因为处理速度慢,比 STM32F072 多了一些 NAK, 数据包的内容完全一样
另外如果有坛友比较熟悉 USB, 遇到过这种枚举后还发个 wLength = 258 的 GET_DESCRIPTOR 请求的情况,
希望能指教一下,实在是困惑
WCH的例程写的质量比较一般,之前用CH554 HOST 驱动过USB printer,发现也有BUG,当然隔了几年记不得细节了。
建议楼主直接电话他们技术支持,服务还是不错的。也可以去他们技术论坛发帖咨询。
最后问下楼主你用什么设备抓的USB包?当初我用串口printf来debug host程序,那叫一个辛苦啊。。。 等待其他坛友解答,最近撸羊毛弄到一些… jingwaner 发表于 2020-8-25 22:06
WCH的例程写的质量比较一般,之前用CH554 HOST 驱动过USB printer,发现也有BUG,当然隔了几年记不得细节了 ...
确实,那个示例程序我开始先是把变量名、函数名和注释之类的都调整了
然后边理解边重构,不少地方写法有问题
WCH 的论坛里边我看回复都很慢,就不指望了,这种问题还是自己调试靠谱
抓包开始是用 Bus Hound, 后来怀疑系统层面抓的包不全,想看最底层通信的包,这样确定有没有差异最靠谱,
抓包工具的名字在软件标题栏有, USB Packet Viewer, 应该就是开源的 TeenyUSB 库的作者搞的
本帖最后由 iamseer 于 2020-8-26 00:28 编辑
看起来不是所有示例都有这个问题。
CDC,u盘示例都是16位长度
SetupLen = ((UINT16)UsbSetupBuf->wLengthH<<8) | (UsbSetupBuf->wLengthL);
但是其他例程的确实是8位。
官方论坛上我没有问过程序问题,但是硬件相关问题,基本第二个工作日都有答复。 wudicgi 发表于 2020-8-25 22:14
确实,那个示例程序我开始先是把变量名、函数名和注释之类的都调整了
然后边理解边重构,不少地方写法有 ...
CH552用逻辑分析仪抓包分析有时候也很方便,还可以让程序把变量打印出来,同步观察。我是用saleae logic抓包,还是很不错的。
分享我常用的一段调试代码。码率是主时钟的1/3。可以让逻辑分析仪直接挂Uart分析。
void sendCharDebug(char c) //8Mbps under 24M clk
{
uint8_t interruptOn = EA;
EA = 0;
//using P1.4
__asm__(//any branch will cause unpridictable timing
"mov a,dpl \n"//seems to be the parameter of func
"clr c \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"rrc a \n"
"mov _P1_4,c \n"
"setb c \n"
"mov _P1_4,c \n"
);
if (interruptOn) EA = 1;
//return charToSend;
} 看了下LZ的BLOG,玩得可真够杂的呀,牛
USB Packet Viewer,软件真是搞得不错 iamseer 发表于 2020-8-26 00:33
CH552用逻辑分析仪抓包分析有时候也很方便,还可以让程序把变量打印出来,同步观察。我是用saleae logic ...
可以分享一下使用saleae logic解析USB包的效果吗?
好用的话,打算买一个。 USB Packet Viewer要配合他自己的硬件才能抓么? jingwaner 发表于 2020-8-26 08:41
可以分享一下使用saleae logic解析USB包的效果吗?
好用的话,打算买一个。
最近没用saleae调usb 大概像这样
https://www.zzzconsulting.se/2020/04/25/sigrok-usb-dev.html
如果你想试试,可以随便先搞一个pulse view支持的逻辑分析仪,我猜CY7C68013A那种应该也可以。要是觉得不错再买saleae。 iamseer 发表于 2020-8-27 12:37
最近没用saleae调usb 大概像这样
https://www.zzzconsulting.se/2020/04/25/sigrok-usb-dev.html
这个软件的显示效果不错 {:3_41:}
我买这个 USB Packet Viewer 之前用金沙滩的逻辑分析仪抓过,和 Saleae Logic 的界面显示的差不多,
还是以波形为主,数据只是对波形加个注解,看起来比较费劲
而且这类逻辑分析仪都是先抓数据,结束后才能看到,不是随抓随更新
搞个便宜的 USB 抓包设备也不错,连接也简单,不用单接 D+, D-, GND 三根线了
USB Packet Viewer 中,数据包展开后是这样的:
iamseer 发表于 2020-8-26 00:33
CH552用逻辑分析仪抓包分析有时候也很方便,还可以让程序把变量打印出来,同步观察。我是用saleae logic ...
这函数用着方便,不过不查的话 51 的汇编已经很多地方看不懂了
占用 1 个 IO 就行,也不用去初始化 UART
chaplin1999 发表于 2020-8-26 08:53
USB Packet Viewer要配合他自己的硬件才能抓么?
软件和硬件都叫 USB Packet Viewer, 是得配合才能用
作者把软件的协议解析部分 lua 脚本开源了,但我看了下感觉一般人也改不动
我的 STM32 主控的设备用的是 TeenyUSB 这个开源的 USB 库,从它的官网上看到的 USB Packet Viewer 这个设备
wudicgi 发表于 2020-8-25 22:14
确实,那个示例程序我开始先是把变量名、函数名和注释之类的都调整了
然后边理解边重构,不少地方写法有 ...
这是软件抓包还是硬件抓包 huangqi412 发表于 2020-8-30 19:13
这是软件抓包还是硬件抓包
硬件抓包,可以抓 Full Speed 和 High Speed 的
wudicgi 发表于 2020-8-31 21:51
硬件抓包,可以抓 Full Speed 和 High Speed 的
谢谢这个多少钱 沁恒的例程有不少bug 这个问题确实也困扰了我好几天。后来找官方,也没说出个所以然和原因,只是他们的例子就是那样子。让先让联系单片机的负责人,打通说了问题,说这个是USB的问题,让联系USB负责人,找了几次,让加个QQ,加了也只说这个也不知道为什么。他们的代码例子就那样子都是对的。我们都放弃方案,用STM
页:
[1]