wudicgi 发表于 2020-8-25 21:49:31

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;
      // ...

zxq6 发表于 2020-8-25 21:51:58

对楼主这样的高手的敬仰有如滔滔江水连绵不绝。

wudicgi 发表于 2020-8-25 21:58:32

查找问题耗了不少时间,主要是一开始就关注为什么描述符修改的都一模一样了,为什么 CH552 这个设备就是获取不到名称
从硬件抓包结果看,CH552 只是因为处理速度慢,比 STM32F072 多了一些 NAK, 数据包的内容完全一样

另外如果有坛友比较熟悉 USB, 遇到过这种枚举后还发个 wLength = 258 的 GET_DESCRIPTOR 请求的情况,
希望能指教一下,实在是困惑

jingwaner 发表于 2020-8-25 22:06:35

WCH的例程写的质量比较一般,之前用CH554 HOST 驱动过USB printer,发现也有BUG,当然隔了几年记不得细节了。
建议楼主直接电话他们技术支持,服务还是不错的。也可以去他们技术论坛发帖咨询。
最后问下楼主你用什么设备抓的USB包?当初我用串口printf来debug host程序,那叫一个辛苦啊。。。

PCBBOY1991 发表于 2020-8-25 22:10:38

等待其他坛友解答,最近撸羊毛弄到一些…

wudicgi 发表于 2020-8-25 22:14:05

jingwaner 发表于 2020-8-25 22:06
WCH的例程写的质量比较一般,之前用CH554 HOST 驱动过USB printer,发现也有BUG,当然隔了几年记不得细节了 ...

确实,那个示例程序我开始先是把变量名、函数名和注释之类的都调整了
然后边理解边重构,不少地方写法有问题

WCH 的论坛里边我看回复都很慢,就不指望了,这种问题还是自己调试靠谱

抓包开始是用 Bus Hound, 后来怀疑系统层面抓的包不全,想看最底层通信的包,这样确定有没有差异最靠谱,
抓包工具的名字在软件标题栏有, USB Packet Viewer, 应该就是开源的 TeenyUSB 库的作者搞的

iamseer 发表于 2020-8-26 00:22:46

本帖最后由 iamseer 于 2020-8-26 00:28 编辑

看起来不是所有示例都有这个问题。
CDC,u盘示例都是16位长度
SetupLen = ((UINT16)UsbSetupBuf->wLengthH<<8) | (UsbSetupBuf->wLengthL);
但是其他例程的确实是8位。

官方论坛上我没有问过程序问题,但是硬件相关问题,基本第二个工作日都有答复。

iamseer 发表于 2020-8-26 00:33:34

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;
}

wxws 发表于 2020-8-26 07:41:25

看了下LZ的BLOG,玩得可真够杂的呀,牛

USB Packet Viewer,软件真是搞得不错

jingwaner 发表于 2020-8-26 08:41:18

iamseer 发表于 2020-8-26 00:33
CH552用逻辑分析仪抓包分析有时候也很方便,还可以让程序把变量打印出来,同步观察。我是用saleae logic ...

可以分享一下使用saleae logic解析USB包的效果吗?
好用的话,打算买一个。

chaplin1999 发表于 2020-8-26 08:53:15

USB Packet Viewer要配合他自己的硬件才能抓么?

iamseer 发表于 2020-8-27 12:37:08

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。

wudicgi 发表于 2020-8-27 21:05:11

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 中,数据包展开后是这样的:

wudicgi 发表于 2020-8-27 21:08:58

iamseer 发表于 2020-8-26 00:33
CH552用逻辑分析仪抓包分析有时候也很方便,还可以让程序把变量打印出来,同步观察。我是用saleae logic ...

这函数用着方便,不过不查的话 51 的汇编已经很多地方看不懂了
占用 1 个 IO 就行,也不用去初始化 UART

wudicgi 发表于 2020-8-27 21:11:59

chaplin1999 发表于 2020-8-26 08:53
USB Packet Viewer要配合他自己的硬件才能抓么?

软件和硬件都叫 USB Packet Viewer, 是得配合才能用
作者把软件的协议解析部分 lua 脚本开源了,但我看了下感觉一般人也改不动

我的 STM32 主控的设备用的是 TeenyUSB 这个开源的 USB 库,从它的官网上看到的 USB Packet Viewer 这个设备

huangqi412 发表于 2020-8-30 19:13:06

wudicgi 发表于 2020-8-25 22:14
确实,那个示例程序我开始先是把变量名、函数名和注释之类的都调整了
然后边理解边重构,不少地方写法有 ...

这是软件抓包还是硬件抓包

wudicgi 发表于 2020-8-31 21:51:47

huangqi412 发表于 2020-8-30 19:13
这是软件抓包还是硬件抓包

硬件抓包,可以抓 Full Speed 和 High Speed 的

huangqi412 发表于 2020-8-31 22:18:41

wudicgi 发表于 2020-8-31 21:51
硬件抓包,可以抓 Full Speed 和 High Speed 的

谢谢这个多少钱

desertsailor 发表于 2022-7-31 22:32:22

沁恒的例程有不少bug

dulala 发表于 2022-11-5 22:53:44

这个问题确实也困扰了我好几天。后来找官方,也没说出个所以然和原因,只是他们的例子就是那样子。让先让联系单片机的负责人,打通说了问题,说这个是USB的问题,让联系USB负责人,找了几次,让加个QQ,加了也只说这个也不知道为什么。他们的代码例子就那样子都是对的。我们都放弃方案,用STM
页: [1]
查看完整版本: CH552 官方 USB HID 示例程序中的一个因未完整处理 16 位长度值导致的 bug