📢平时在处理客户问题时,经常需要分析出现问题时抓取的 usbmon log,这个 log 中有一个字段非常重要:URB Status word,这个字段就是 struct urb 结构体中的 status 成员变量。
通过这个 status 的值,可以分析出模块端和 HOST 端 usb 的一些状态,有利于定位到问题点。
一、URB 的处理流程
Linux 中 URB(USB Request Block)的处理流程:
- 创建 URB:首先,使用
usb_alloc_urb()函数创建一个 URB 结构体。 - 初始化 URB:根据不同的传输类型,使用相应的函数来初始化这个 URB:
- 中断传输:使用
usb_fill_int_urb() - 批量传输:使用
usb_fill_bulk_urb() - 控制传输:使用
usb_fill_control_urb() - 等时传输:需要手动初始化
- 中断传输:使用
- 提交 URB:使用
usb_submit_urb()函数将初始化好的 URB 提交给 USB 控制器。如果提交成功,函数返回 0;如果失败,则返回一个负数。当提交失败时,在usbmon log中的Event Type字段会显示为E(表示提交错误)。 - URB 的命运:提交后的 URB 会有三种可能的结果,这三种结果对应 URB 的
status:- 成功完成传输
- 传输过程中发生错误
- URB 从 USB 控制器中被“取消链接”(unlinked)
- 回调函数:当 URB 结束时,系统会调用之前设置的
complete回调函数。
这样讲应该更容易理解了吧!
二、URB 成功传输分析
当 URB 成功发送给设备,并且设备正确响应时,对于输出 URB 来说,这意味着数据已成功发送;对于输入 URB,则表示成功接收到了请求的数据。这时,urb->status 会被设置为 0,表示操作成功。
下面是一条 qmi 消息成功交互的过程:
c
ffff8800b7d91740 1791319201 S Ii:3:008:9 -115:256 8 < // 这里的-115(EINPROGRESS)仅仅表示URB被提交给usb控制器去处理了
ffff8800b7d90600 1791319354 S Co:3:008:0 s 21 00 0000 0004 000c 12 = 010b0000 00000001 27000000
ffff8800b7d90600 1791320572 C Co:3:008:0 0 12 > // 这里urb status为0,表示qmi request消息成功发送给模块了
ffff8800b7d91740 1791329515 C Ii:3:008:9 0:256 8 = a1010000 04000000 // 这里Ti的urb status为0,表明可以读取qmi的response信息了
ffff8800b7d915c0 1791329530 S Ci:3:008:0 s a1 01 0000 0004 1000 4096 <
ffff8800b7d91740 1791329570 S Ii:3:008:9 -115:256 8 <
ffff8800b7d915c0 1791331992 C Ci:3:008:0 0 19 = 01120080 00000101 27000700 02040000 000000 // 这里返回0,表示HOST端成功的读取了模块的qmi response1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
再看一下 AT 指令的交互过程:
c
ffff8800b1480f00 2819829981 S Bo:3:008:3 -115 1 = 61
ffff8800b1480f00 2819830240 C Bo:3:008:3 0 1 > // 成功向模块发送了 A
ffff8800b1480f00 2819926000 S Bo:3:008:3 -115 1 = 74
ffff8800b1480f00 2819926105 C Bo:3:008:3 0 1 > // 成功向模块发送了T
ffff8800b1480f00 2821094084 S Bo:3:008:3 -115 1 = 0d
ffff8800b1480f00 2821094267 C Bo:3:008:3 0 1 > // 成功向模块发送了换行
ffff8800b1481e00 2821094738 C Bi:3:008:4 0 6 = 0d0a4f4b 0d0a
ffff8800b1481e00 2821094752 S Bi:3:008:4 -115 4096 <
ffff8800b1481b00 2821097257 C Ii:3:008:5 0:256 10 = a1200000 02000200 0000 // 成功读取模块的AT Response ==> OK1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
三、URB 传输异常分析
下面是常见 USB 传输错误及其状态码的说明:
**-EINPROGRESS (-115)**:这个状态表示 URB(USB 请求块)已经被提交给 USB 控制器处理,但还没有完成。这不算是一个真正的错误。**-EPROTO (-71)**:当 USB 设备无法被正确识别或枚举时,可能会出现这个错误。这也可能是因为数据传输过程中发生了协议错误。**-EILSEQ (-84)**:这是一个比较少见的错误,具体细节可以查看 Linux 内核文档中的error-codes.txt文件获取更多信息。**-ECOMM (-70)**:对于从 USB 设备读取数据的情况(即 IN URB),如果数据读取速度超过了将其写入内存的速度,就会触发此错误。**-ENOSR (-63)**:在向 USB 设备发送数据的情况下(即 OUT URB),如果系统内存中准备的数据不够快,不能满足 USB 所需的数据速率,则会发生此错误。**-EPIPE (-32)**:这表示 USB 管道遇到了问题,可能是由于批量端点设置了停止条件而堵塞了。可以通过调用usb_clear_halt()函数来尝试解决问题。**-EOVERFLOW (-75)**:当你试图发送给某个端点的数据包大小超过了该端点允许的最大值时,会产生此错误。**-ENODEV (-19)**:如果 USB 设备已经断开连接,但是系统尚未检测到这一点,并且驱动程序仍在尝试与之通信,那么就会遇到这个错误。
注意:像 -EPROTO、-EILSEQ 和 -EOVERFLOW 这样的错误通常表明存在硬件问题,比如 USB 设备本身、其固件或者 USB 线缆有问题。
四、URB 从 usb 控制器中"unlinked"
当出现“unlinked”情况时,通常是在以下两种场景中:
- 主动取消 URB:在驱动程序中,如果调用了
usb_unlink_urb()或usb_kill_urb()来取消已经提交给 USB 控制器的 URB:- 使用
usb_unlink_urb()后,URB 的状态值会变为-104(表示连接被重置)。 - 使用
usb_kill_urb()后,URB 的状态值会变为-2(表示没有找到)。
- 使用
- 设备被移除:如果一个 URB 已经提交给了某个 USB 设备,但该设备被移除了(比如拔掉了设备),那么 URB 的状态值会变成
-108(表示设备关闭)。
简单来说,就是当 URB 被取消或者设备被移除时,URB 的状态值会发生变化。
