瑞萨RZ/T2H平台USB识别故障的调试与代码修改
当使用瑞萨RZ/T2H平台时,用户有时会遇到无法识别USB DISK的错误报告。本文详细分析了USB通信流程、SCSI数据传输以及异常USB DISK的端点描述符的差异,最终提出了针对HOST端驱动层的修改方案。该问题常见于在RZ/T2H上使用了FSP 3.1及之前的版本。
Part 1 问题发现
1.1 软硬件环境
扫描下方二维码或复制链接到浏览器下载相关内容。
瑞萨RZ/T2 FSP3.1:下载路径
https://github.com/renesas/rzt-fsp/releases/tag/v3.1.0
硬件环境:RZ/T2H EVKIT
https://www.renesas.com/en/design-resources/boards-kits/rz-t2h-evkit
1.2 U盘不能被正确识别时出现的错误现象
本文以参考例程RZT2H_EVB_CR52_0_usb_hmsc裸跑程序,SRAM Debug模式为例,RZ/T2H作为USB Mass Storage Host工作。
RZT2H_EVB_CR52_0_usb_hmsc下载路径
https://www.renesas.com/en/document/scd/rzt2h-example-program-v300-deprecated?language=en&r=25567073
当USB DISK插入RZ/T2H EVB的USB口时,调用r_usb_hehci_main.c中的usb_hstd_ehci_interrupt_handler函数,会触发如下错误中断:
左右滑动查看完整内容

长按可保存查看大图
最终进入:
左右滑动查看完整内容
长按可保存查看大图
通过查看RZ/T2H的用户手册,可以看到出现了USBERRINT,即传输出错。具体描述可见下图:

点击可查看大图
将该USB DISK在Windows主机和Linux主机下测试,确认了该USB DISK均可被正常识别,排除了USB DISK自身异常的情况。同时,将RZ/T2H EVB运行在Linux环境,经测试,该USB DISK也可以在该环境下被正常识别,也排除了EVB的USB硬件接口问题的可能。
那么为何在Linux环境下的RZ/T2H EVB没有触发USBERRINT,而在裸跑例程却触发了异常?
我们怀疑该错误和底层驱动有关。因为USB规范本身的数据流程比较复杂,所以我们需要使用专业工具来分析USB通信数据流。
1.3 USB协议分析
我们使用USB协议分析器来对比测试。
通过USB协议分析结果看到,当USB Disk被正常识别时,其占用的Endpoint编号分别是1(代表IN)和2(代表OUT)。2个方向的数据传输分别使用不同的Endpoint。
长按保存查看大图
识别和通信初始化过程中,Host端发起BULK OUT操作,默认使用endpoint=2。

长按保存查看大图
接下来,我们来分析一下SCSI的数据流程。
首先看一下该USB DISK被识别到的基本信息:
Interface class为0x08->MSC,SubClass为0x06->SCSI transparent command set,protocol为0x50->Bulk-Only Transport

点击可查看大图
当RZ/T2H作为USB HOST识别到Class为0x08后,一个正常的USB DISK识别和初始读取工作流程应该如下步骤所示。
第一步:通过Bulk OUT(EP 2)向USB DISK发送CBW,CBW长度为31字节:55 53 42 43 01 00 00 00 24 00 00 00 80 00 06 12 00 00 00 ...
第二步:RZ/T2H在EP1上发起IN transaction token(见如下高亮截图):
左右滑动查看完整内容

长按保存查看大图
第三步:USB DISK在EP 1 DATA0上返回Inquiry的36字节数据00 80 00 01 1F 73 6D 69 53 4D 49 20 20 20 20 20 55 53 42 20 44 49 53 4B 20 20 20 20 20 20 20 20 31 31 30 30,代表了厂商、型号等信息。
第四步:RZ/T2H返回ACK表示成功接收。
左右滑动查看完整内容

长按保存查看大图
第五步:USB DISK再在EP1上返回13字节数据(如下图):55 53 42 53表示CSW的固定签名,ASCII码为“USBS”。如下所示解析:
01 00 00 00是Tag,与HOST发出的Tag一致;
00 00 00 00表示数据残留为0;
00 表示命令圆满完成。
左右滑动查看完整内容

长按保存查看大图
第六步:如下图,HOST发起读取USB DISK容量信息的数据帧及USB DISK返回的数据帧:
左右滑动查看完整内容

长按保存查看大图
第七步:HOST从0(00 00 00 00)开始读取1个扇区(512字节)(00 01),读到的全0:
左右滑动查看完整内容

长按保存查看大图
第八步:RZ/T2H再从2048(00 00 08 00)处读取1个扇区:
左右滑动查看完整内容

长按保存查看大图
而对于出问题的USB DISK,被识别为BULK IN和OUT,均占用EP_1,其数据过程为:
第一步:枚举阶段正常:
左右滑动查看完整内容

长按保存查看大图
第二步:但是到了SCSI过程,RZ/T2H EP_1上通过Bulk OUT向USB DISK发送CBW:
左右滑动查看完整内容

长按保存查看大图
发送完了这31个CBW后,RZ/T2H理应像先前描述的标准流程一样,在IN Bulk transfer里发起 IN transaction token(见正确情况下的第二步),但此时的RZ/T2H仍然继续往OUT package发送数据,只是DATA0变成了DATA1。
注
0x69 0x81 0x58是一个IN package token,二进制即0110 1001 1000 0001 0101 1000,解析时是小端顺序。

长按保存查看大图
USB PID高四位+低四位校验,低四位取反后,若与高四位相等,则发送正确。0x69=0110 1001,0110=~1000。
1000 000代表Device ID,小端即1;
1000代表Endpoint,小端即1;
10101是CRC5。
左右滑动查看完整内容

长按保存查看大图
从这里可以看出是RZ/T2H作为HOST时状态乱了。
1.4对比分析
通过对比,我们可以看到:
不能正常工作的USB DISK,其端点描述符是:endpoint bulk out number=1,endpoint bulk in number=1;
能正常工作的USB DISK,其端点描述符是:endpoint bulk out number=2,endpoint bulk in number=1。

长按保存查看大图
这样我们之前看到的log就非常清晰了,正常工作的USB DISK,RZ/T2H作为HOST均在EP 2上发送CBW,然后到EP 1上发起IN transaction的CBW等待USB DISK回复。
异常的情况,RZ/T2H在EP 1上发送CBW,且没有发起inquiry的CBW,而是继续在EP 1上以DATA1切换session的方式,继续往OUT BULK发送数据。
因此,根本原因是对于bulk in=0x81,bulk out=0x01,即EP编号均为1的USB DISK,RZ/T2H 无法通过EP编号去工作。
1.5问题定位及代码调试
在源代码中打开USB DEBUG宏,以准确找到上下文问题。在此处加打印:
左右滑动查看完整内容

长按保存查看大图
正常情况下,当发送完CBW后,打印得到的pipe_dir为128(0x80),即表示Host发起了request:让Device Address为1,endpoint为1的Device准备发数据:

长按保存查看大图
对于异常情况,打印得到的pipe_dir为0,即OUT。根据正常情况下的数据比对,此时应该是Host发起让Device向上发数据的请求,而不是让Host对Device说:Host还有数据继续往下发送,这就是上述数据流程错误的软件表现。
注
Pipe是USB Host与 USB Device之间建立的虚拟通道,其建立了Host与Device的endpoint的关系,即声明了Device的哪个endpoint作为pipe in,另外哪个endpoint作为了pipe out,之间有一一对应关系。

长按保存查看大图
在调用获取pipe_dir参数的API:pipe_dir=usb_hstd_get_pipe_dir(pipe_id)时,是通过pipe_id参数获取该Pipe的Dir参数,且pipe_id又与endpoint number是对应关系。所以有问题的USB Disk,得到结果:EP out,其pipe_id为1;EP in,其pipe_id也为1。
所以代码中获得的pipe_dir参数,均为OUT。
Part 2解决方法
2.1
在 ztfspsrc _usb_hmsc_driver.c中增加一个标志位g_ep_number_shared_flag,且用g_ep_number_shared_flag==TRUE来标志Bulk In EP==Bulk Out EP的异常情况,然后强制使用一个新的EP Number,在尽量少改代码的前提下支持异常情况。
在枚举阶段,如果发现In endpoint和Out endpoint的编号相同,就是用此标志位表示这是异常情况,需要在让Device发送数据时,共用pipe out的管道编号。
左右滑动查看完整内容

长按保存查看大图
2.2
在r_usb_hmsc_driver.c中,增加对IN/OUT EP编号相同的设备的处理:
USB Mass Storage Profile默认Bulk in的endpoint编号为1,Bulk out的endpoint编号为2。所以我们对于in和out相同编号的device,强行将Bulk out的编号修改成2,使得USB协议层调用管道编号时,取用管道编号为2的pipe。
增加代码如下:
左右滑动查看完整内容

长按保存查看大图
2.3
在文件r_usb_hmsc_driver.c的usb_hmsc_detach()函数中复位g_ep_number_shared_flag:
左右滑动查看完整内容

长按保存查看大图
2.4
在 ztfspsrc _usb_basicsrcdriver _usb_hdriver_rz.c中,声明引入外部变量:
左右滑动查看完整内容

长按保存查看大图
并根据其状态,强制修改相关参数:
左右滑动查看完整内容

长按保存查看大图
修改如上代码后,再插入不能工作的USB DISK,可以看到尽管Bulk out和Bulk in的端点号均为1,RZ/T2H可以正常工作了。
左右滑动查看完整内容

长按保存查看大图
通过本次的问题调查,我们学习了USB DISK的数据收发流程,遇到USB通信问题时的调查步骤和常用工具。理解到了当USB HOST试图通过枚举阶段的EP编号转化为管道编号,进而在该管道上进行收发。但是有些USB设备的IN EP和OUT EP编号可能相同,此时我们需要做代码调整,让发往OUT EP的管道采用正确的EP编号,避免用IN EP的编号。RZ/T2H通过FSP生成的代码易于修改和调试,并预先加入了可能会调试的代码点,用户只需要打开相应的宏即可打印,从而观察理解上下文并定位问题。
瑞萨RZ/T2H完整的软硬件设计规范、参考例程、工具等,请扫描下方二维码或复制链接到浏览器登录获取更多资料。
RZ/T2H
https://www.renesas.com/en/products/microcontrollers-microprocessors/rz-mpus/rzt2h-advanced-high-end-mpu-integrated-powerful-application-processing-and-high-precision-real-time
