arm linux usb设备工作异常,需要跟踪监视 usb 总线上的数据包。可以使用 usbmon 工具。这需要在内核编译时选择支持 usbmon。可以编译到内核里面,也可以编译成模块单独使用。
1 内核编译支持
前提:需要debugfs文件系统支持
linux 内核版本: 4.19.94
执行 make menuconfig,选择 Device Drivers->USB Support,
选择其中的 USB Monitor,打 * 编译到内核中,或者 M 编译成 usbmon.ko 模块。
1.1 编译内核
即使是编译成 usbmon.ko 模块,也需要重新编译内核,以在内核中支持响应的库函数。
编译内核:
make -j8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage
编译模块:
make -j8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
编译完成后生成新的 zImage 内核程序和 usbmon.ko 模块文件。
2 如何使用
2.1 安装usbmon模块
如果是将usbmon编译到内核中,就不需要安装了。
首先用新生成的 zImage 刷板子,然后将 usbmon.ko 复制到板上某个位置,例如:/usr/share/mod/usbmon.ko
安装 usbmon:
insmod /usr/share/mod/usbmon.ko
2.2 使用
2.2.1 确定监视的 usb 设备
lsusb列出设备
root@am335x:~# lsusb
Bus 002 Device 005: ID ffff:5678
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 004: ID 2c7c:0125 Quectel Wireless Solutions Co., Ltd. EC25 LTE modem
Bus 002 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
这里,看到有2个USB控制器(2个总线),USB控制器1上只有一个root hub设备。USB控制器2上有1额root hub,一个USB2.0 Hub设备,两个USB设备004和005。要监视的设备是 EC20设备正是其中的004设备,对应的usb设备编号为:
Bus 002 Device 004 – 表示Bus总线2 上的设备 004。
查看总线编号:
root@am335x:~# ls /sys/kernel/debug/usb/usbmon/
0s 0u 1s 1t 1u 2s 2t 2u
其中,1u 和 2u 为分别为总线001和002的编号。
从USB总线002抓包的命令为:
root@am335x:~# cat /sys/kernel/debug/usb/usbmon/2u
ce70f900 192480265 S Bo:2:005:1 -115 31 = 55534243 2b0a0000 00000000 00000600 00000000 00000000 00000000 000000
ce70f900 192480406 C Bo:2:005:1 0 31 >
ce70f900 192480428 S Bi:2:005:2 -115 13 <
ce70f900 192480492 C Bi:2:005:2 0 13 = 55534253 2b0a0000 00000000 00
ce18bd80 192804017 S Bo:2:004:5 -115 64 = 45000040 5ae14000 40111112 0af84a4d ddb39bc1 9be00035 002c6dcb 07100100
ce18bd80 192804138 C Bo:2:004:5 0 64 >
ce0e6a80 192878056 C Bi:2:004:8 0 80 = 45000050 5ae14000 40111102 ddb39bc1 0af84a4d 00359be0 003cab18 07108180
ce0e6a80 192878087 S Bi:2:004:8 -115 1514 <
ce18bd80 192879465 S Bo:2:004:5 -115 64 = 45000040 5ae84000 4011110b 0af84a4d ddb39bc1 c0170035 002c3979 17100100
ce18bd80 192879561 C Bo:2:004:5 0 64 >
ce0e6980 192933978 C Bi:2:004:8 0 64 = 45000040 057a4000 39116d79 ddb39bc1 0af84a4d 0035c017 002cb8f8 17108180
看到抓包数据中有 004 和 005 设备的数据,数据量较大,只想抓 004 设备的话,可以过滤一下:
root@am335x:~# cat /sys/kernel/debug/usb/usbmon/2u | grep "2:004"
ce3c2400 291508414 C Bi:2:004:8 0 40 = 45040028 3ad74000 340634ee 276b5a57 0af84a4d 075bcdf8 e72e6908 64a5040c
ce3c2400 291508450 S Bi:2:004:8 -115 1514 <
ce18bd80 291510431 S Bo:2:004:5 -115 42 = 4500002a 7a674000 4006e95f 0af84a4d 276b5a57 cdf8075b 64a5040c e72e6909
ce18bd80 291510527 C Bo:2:004:5 0 42 >
ce18bd80 291510745 S Bo:2:004:5 -115 40 = 45000028 7a684000 4006e960 0af84a4d 276b5a57 cdf8075b 64a5040e e72e6909
ce18bd80 291510786 C Bo:2:004:5 0 40 >
ce18bd80 291554533 S Bo:2:004:5 -115 64 = 45000040 702f4000 4011fbc3 0af84a4d ddb39bc1 ee0b0035 002ca4ed 7dc20100
ce18bd80 291554655 C Bo:2:004:5 0 64 >
ce3c2380 291555837 C Bi:2:004:8 0 40 = 45040028 3c444000 34063381 276b5a57 0af84a4d 075bcdf8 e72e6909 00000000
ce3c2380 291555865 S Bi:2:004:8 -115 1514 <
ce3c2b00 291555926 C Bi:2:004:8 0 40 = 45040028 3c454000 34063380 276b5a57 0af84a4d 075bcdf8 e72e6909 00000000
ce3c2b00 291555935 S Bi:2:004:8 -115 1514 <
2.2.2 USB 数据包含义
数据格式,自左向右:
-
URB Tag: 用于识别URB,通常是16进制数字,表示内核中URB数据结构的地址,也可以是一个序列号,或者其他的唯一字符串。所谓URB,是指usb 请求块(usb request block),是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构。
-
Timestamp: 微秒表示的时间戳,一个十进制数字。时间戳精度取决于系统时钟。
-
Event Type: 事件类型,并非URB类型。S - 表示submission 提交;C - 表示 callback 回调;E - 表示 submission error 提交错误。
-
“Address” word 地址字(一个管道),由4个字段组成,用冒号: 分隔,包括: URB类型与方向:总线编号:设备地址:终端号码。URB类型与方向用2字节表示:
== == =============================
Ci Co 控制输入和输出
Zi Zo 同步输入和输出
Ii Io 中断输入和输出
Bi Bo 批量输入和输出
== == ============================= -
URB 状态字,或者是一个字母或者是接分号分隔的数字。URB状态,间隔,开始帧,和错误数。与"Address" word不同,所有字段都是可选的。间隔只对中断或同步URB才输出。开始帧只对同步URB输出。错误数只对同步回调事件输出。
状态字段是个十进制数字,有时会是负数,表示URB状态。此字段对于提交事件没有意义,但为了脚本解析仍然打印出来。当错误发生时,此字段为错误码。
在提交控制数据包的情况下,此字段包含设置标签而不是一组数字。很容易判断是否设置标签,因为它从不是数字。因此,如果脚本在这个状态字中找到一组数字,将继续读取数据长度(同步URB除外)。如果发现了其他东西,比如一个字母,会作为设置包读入,然后再读入数据长度或同步描述符。 -
设置包,如果有,则包含5个字:包括 bmRequestType,
bRequest、wValue、wIndex、wLength,如USB规范2.0所规定。
如果设置标记为“s”,则这些单词可以安全解码。否则,设置
数据包存在,但未捕获,字段包含填充符。 -
同步帧描述符和描述符本身的数量。如果同步传输事件具有一组描述符
,URB中的一个单词首先被打印,然后每个描述符打印一个单词,直到
总共5个。该单词由3个冒号分隔的十进制数字组成:状态、偏移量和长度。对于提交事件,输出初始长度。对于回调事件,输出实际长度。 -
数据长度。对于提交事件,是请求的长度。对于回调,是实际长度。
-
数据标签。即使长度不是零,usbmon不一定是捕获到了数据。只有当此标记为“=”时,数据字才会出现。
-
数据字,采用大端十六进制格式。注意它们是不是机器字,而是一个字节流,分割成字更容易阅读。因此,最后一个字可以包含一到四个字节。收集的数据长度有限,可以小于数据长度。在数据长度字中报告。在同步输入(Zi)的情况下当接收到的数据在缓冲区中稀疏时收集的数据可以大于数据长度值(因为数据长度仅统计接收的字节,而数据字包含整个传输缓冲器)。
例如:
获取port 状态的输入控制传输数据为:
d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
批量输出传输,以31字节批量包装发送SCSI命令0x28(READ_10)到设备地址为 5 的存储设备:
dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
dd65f0e8 4128379808 C Bo:1:005:2 0 31 >