Rockchip RK3399 - linux下抓取usb数据包

发布时间 2023-10-07 23:30:44作者: 大奥特曼打小怪兽

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板
eMMC :16GB
LPDDR3 :4GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot :2017.09
linux :4.19
----------------------------------------------------------------------------------------------------------------------------

注意:本节介绍的内容基于Rockchip RK3399 - linux-headers制作》中移植的运行环境:内核版本``4.19.193以及debian 11`根文件系统。

一、安装usbmon

usbmonusb monitor,是linux内置的usb抓包工具。usbmon本质是一个内核模块,模块的位置:/lib/modules/4.19.193/kernel/drivers/usb/mon/usbmon.ko

想要启用usbmon,必须挂载debugfs并加载usbmon模块。

1.1 挂载debugfs文件系统

在开发板debian执行如下命令,如果提示已经挂载,则下次抓包就无需运行该命令了,表示系统默认会挂载该文件系统。

root@SOM-RK3399v2:/# mount -t debugfs none /sys/kernel/debug
mount: /sys/kernel/debug: none_debugs already mounted or mount point busy.

如上所示, debian系统默认已经挂载了debugfs文件系统,无需再去手动挂载。

1.2 安装usbmon模块

需要注意的是:如果你已经将usbmon编译到内核中,就不需要安装了。

确认内核支持usbmon模块

root@SOM-RK3399v2:/# ls /sys/module/usbmon
ls: cannot access '/sys/module/usbmon': No such file or directory

如上所示,目前内核不支持usbmon模块,需要手动安装usbmon模块。

执行如下命令安装usbmon

root@SOM-RK3399v2:/# modprobe usbmon
bash: modprobe: command not found

提示未找到命令则是因为/usr/sbin(或者/sbin)默认没有加到PATH;通过修改profile文件:

root@SOM-RK3399v2:/# vim /etc/profile

找到设置·PATH·的行,添加:

export PATH=/usr/sbin:$PATH

要想马上生效还要运行 source /etc/profile不然只能在下次重进此用户时生效。

重新运行命令:

root@SOM-RK3399v2:/# modprobe usbmon
root@SOM-RK3399v2:/# ls /sys/module/usbmon
coresize  holders  initsize  initstate  notes  refcnt  sections  srcversion  taint  uevent

这里的原理是,usbmon是一个模块,使用modprobe安装该模块后,该模块内部调用debugfs相关的API,这样在 /sys/kernel/debug/usb目录下便形成了usbmon这个目录。

查看/sys/kernel/debug/usb/usbmon目录,

root@SOM-RK3399v2:/# ls  /sys/kernel/debug/usb/usbmon
0s  0u  1s  1t  1u  2s  2t  2u  3s  3t  3u  4s  4t  4u  5s  5t  5u  6s  6t  6u

发现该目录下有以下内容:0s、0u、1s、1t、1u、2s、2t、2u等,其中1代表bus1,2代表bus2,0代表所有USB总线。

1.2.1 开机自动加载模块

这里有一个问题,就是内核4.19.193版本启动的时候为啥没有自动去加载/lib/modules/4.19.193/目录下的驱动模块,如果我们想让系统能自动加载该目录下的驱动模块,我们应该怎么做?

(1) 使用命令depmod -a

depmod命令用于分析可载入模块的相依性,-a参数的作用是探测所有的模块,建立模块的依赖关系,更新/lib/modules/4.19.193/modules.dep文件;

(2) 将模块设置为自动加载

/etc/modules文件里面添加我们要加载的驱动名(一个驱动占一行);

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
usbmon
1.2.2 重启验证

上述操作完成后,重启系统,使用 lsmodcat /proc/modulesls /sys/module/usbmon命令查看驱动是否已经加载。

root@SOM-RK3399v2:/# lsmod
Module                  Size  Used by
algif_hash             20480  1
algif_skcipher         16384  1
af_alg                 24576  6 algif_hash,algif_skcipher
bnep                   24576  2
hci_uart               61440  1
btbcm                  16384  1 hci_uart
serdev                 20480  1 hci_uart
hid_logitech_hidpp     36864  0
crct10dif_ce           16384  0
l2tp_ppp               24576  0
l2tp_netlink           24576  1 l2tp_ppp
l2tp_core              28672  2 l2tp_ppp,l2tp_netlink
ip6_udp_tunnel         16384  1 l2tp_core
udp_tunnel             16384  1 l2tp_core
pppox                  16384  1 l2tp_ppp
joydev                 28672  0
bcmdhd               1695744  0
cfg80211              638976  1 bcmdhd
hid_logitech_dj        20480  0
uio_pdrv_genirq        16384  0
uio                    20480  1 uio_pdrv_genirq
binfmt_misc            20480  1
usbmon                 36864  0                       # 这里
ledtrig_netdev         16384  0
nfsd                  344064  1
ip_tables              28672  0
root@SOM-RK3399v2:/# ls /sys/module/usbmon
coresize  holders  initsize  initstate  notes  refcnt  sections  srcversion  taint  uevent

二、抓取USB数据

2.1 确usb设备挂在哪条总线

首先需要获取想要监测的设备所在的总线以及设备号。linux中查看USB设备列表以及USB设备详细信息的有多种方法:

2.1.1 内核日志

我们在开发板上随便找一个USB接口插入USB触摸屏,内核输出日志如下:

[ 1173.581227] logitech-hidpp-device 0003:046D:4052.0006: HID++ 4.5 device connected.
[ 1182.859503] usb 1-1.2: new full-speed USB device number 4 using ehci-platform
[ 1182.959039] usb 1-1.2: New USB device found, idVendor=1a86, idProduct=e5e3, bcdDevice= 0.00
[ 1182.959245] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1182.959281] usb 1-1.2: Product: USB2IIC_CTP_CONTROL
[ 1182.959343] usb 1-1.2: Manufacturer: wch.cn
[ 1182.977982] input: wch.cn USB2IIC_CTP_CONTROL as /devices/platform/fe3c0000.usb/usb1/1-1/1-1.2/1-1.2:1.0/0003:1A86:E5E3.0007/input/input13
[ 1183.034737] hid-generic 0003:1A86:E5E3.0007: input,hidraw4: USB HID v1.00 Device [wch.cn USB2IIC_CTP_CONTROL] on usb-fe3c0000.usb-1.2/input0
[ 1183.142034] input: wch.cn USB2IIC_CTP_CONTROL as /devices/platform/fe3c0000.usb/usb1/1-1/1-1.2/1-1.2:1.0/0003:1A86:E5E3.0007/input/input14
[ 1183.144619] hid-multitouch 0003:1A86:E5E3.0007: input,hidraw4: USB HID v1.00 Device [wch.cn USB2IIC_CTP_CONTROL] on usb-fe3c0000.usb-1.2/input0

从上面的输出信息可以看到PID=e5e3,VID=1a86USB总线编号为1,设备地址为4。

2.1.2 lsusb

我们可以通过lsusb命令查看USB设备信息:

root@SOM-RK3399v2:/# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 004: ID 1a86:e5e3 QinHeng Electronics USB2IIC_CTP_CONTROL    # 这里
Bus 001 Device 003: ID 1a2c:4d7e China Resource Semico Co., Ltd USB Keyboard
Bus 001 Device 002: ID 14cd:8601 Super Top 4-Port hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

USB总线1上,有1个root hub,1个4-Port hub、1个USB键盘、1个USB触摸屏。

2.1.3 查看设备文件
root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/devices
T:  Bus=01 Lev=02 Prnt=02 Port=01 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 0.01 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1a86 ProdID=e5e3 Rev= 0.00
S:  Manufacturer=wch.cn
S:  Product=USB2IIC_CTP_CONTROL
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 64mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
E:  Ad=82(I) Atr=03(Int.) MxPS=  64 Ivl=1ms

这里看到的信息实际上和内核日志输出的信息一样的。

2.2 监测USB总线上的数据

查看USB总线上的数据;

root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/usbmon/1u | grep "1:004"
ffffffc0c88dab00 1925763364 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925775578 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925787174 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925787301 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925800237 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925800380 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925811243 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925811395 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925821178 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925821328 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925822163 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925822309 S Ii:1:004:2 -115:1 52 <

其中的1:004中1表示USB总线编号,004时设备编号。

执行该命令的同时我们点击一下USB触摸屏,就可以查看USB总线上的数据传输

三、USB传输基础

USB数据抓取到了,但是放眼一看,密密麻麻的全是数字,它们代表什么含义呢?

在解读usbmon抓取的数据包的含义之前,我们需要了解一下与USB传输有关的基础知识,这样才能更好的理解数据包的各个字段所代表的含义。

USB总线上传输的数据是以包为基本单位的,但是不能随意的使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务(transaction)进行传输。

3.1 USB信息包的种类

USB传输由一个或多个事务组成,每个事务又进一步含有多个USB包(packets)。

USB包的种类,总体上分为四类:令牌包、数据包、握手包、特殊包;

3.1.1 令牌包

令牌包用来发起一次USB传输,因为USB是主从结构的拓扑结构,所有的数据传输都是由主机发起的,设备只能被动的响应,这就需要主机发送一个令牌包来通知哪个设备进行响应,如何响应。

令牌包有 4 种,分别为输出(OUT)、输入(IN)、建立(SETUP)和帧起始(SOF)。

3.1.2 数据包

数据包就是用来传输数据的,可以从主机到设备,也可以从设备到主机,方向由令牌包来指定。

3.1.3 握手包

握手包的发送者一般为数据接收者,用来表示一个传输是否被对方确认。在传输正常的情况下,主机/设备会发送一个表示传输正确的 ACK握手包。

3.1.4 特殊包

特殊包用在一些特殊的场合,这里就不介绍了。

3.2 USB事务

介绍了USB信息包的分类,下面就要着重介绍USB的事务及传输类型。前面已经说了,我们不能随意的使用USB包来传输数据,必须按照一定的关系把这些不同的包组织成事务才能传输数据。

那么事务是什么呢? 事务通常由三个包组成:令牌包、数据包和握手包。

        img

注意:usbmon只抓取事务中的数据包,不会抓取令牌包和握手包。

3.3 USB传输类型

USB协议规定了4种传输类型:批量传输、等时传输、中断传输和控制传输。其中;

  • 批量传输、等时传输、中断传输每传输一次数据都是一个事务;
  • 控制传输包括三个过程,建立过程和状态过程分别是一个事务,数据过程可能包含多个事务。
3.3.1 批量传输

批量传输用于传输大量数据。USB协议不保证这些数据传输可以在特定的时间内完成,但保证数据的准确性。如果总线上的带宽不足以发送整个批量包,则将数据拆分为多个包传输。批量传输数据可靠,但实时性较低。如USB硬盘、打印机等设备就采用的是批量传输方式;

img

3.3.2 等时传输

等时传输也可以传输大量数据,但数据的可靠性无法保证。采用等时传输的USB设备更加注重保持一个恒定的数据传输速度,对数据的可靠性要求不高。如USB摄像头就使用的是等时传输方式;

img

3.3.3 中断传输

USB主机请求USB设备传输数据时,中断传输以一个固定的速率传送少量的数据。中断端点的数据传输方式为中断传输,数据传输可靠,实时性高,这里的中断并不是USB设备产生中断,而是USB主机每隔一个固定的时间主动查询USB设备是否有数据要传输,以轮询的方式提高实时性。如USB鼠标采用的是中断传输;

img

3.3.4 控制传输

控制传输用于配置设备、获取设备信息、发送命令到设备、获取设备的状态。每个USB设备都有端点0的控制端点,当USB设备插入到USB主机拓扑网络中时,USB主机就通过端点0与USB设备通信,对USB设备进行配置,便于后续的数据传输。USB协议保证控制传输有足够的带宽。控制传输可靠,时间有保证,但传输的数据量不大。如USB设备的枚举过程就采用的是控制传输;

img

img

四、USB数据包分析

下面是我点击USB触摸屏通过usbmon 抓取到的数据:

root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/usbmon/1u | grep "1:004"
ffffffc0c88dab00 1925763364 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925775578 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925787174 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925787301 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925800237 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925800380 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925811243 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925811395 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925821178 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925821328 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925822163 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925822309 S Ii:1:004:2 -115:1 52 <

下面从左到右分析这些字段代表的含义:URB标签 时间戳 事件类型 地址 URB状态 数据包的长度 数据标签 数据流。

4.1 URB Tag - URB标签

该字段表示驱动中定义的struct urb结构体变量在内核空间的地址,可以使用该字段区分不同的URB数据包。

URB标签字段的长度和系统的位数相同,以64为系统为例,该字段的长度为8个字节。

4.2 Timestamp - 时间戳

该字段表示的是时间戳,单位是微秒。1微秒 = 10^(-6)秒。

该时间是由下面的mon_get_timestamp函数获取的,使用ktime_get_ts64获取的时间位与上了0xFFF,因此usbmon显示的秒数范围为0 ~ 4096s;

// drivers/usb/mon/mon_text.c
static inline unsigned int mon_get_timestamp(void)
{
        struct timespec64 now;
        unsigned int stamp;

        ktime_get_ts64(&now);  // 获取当前时间
        stamp = now.tv_sec & 0xFFF;  /* 2^32 = 4294967296. Limit to 4096s. */
        stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC;   //USEC_PER_SEC=1000000 NSEC_PER_USEC=1000
        return stamp;
}

4.3 Event Type - 事件类型

事件类型有三种:

  • S - submission,向USB Controller提交URB
  • C - callbackURB提交完成后的回调;
  • E - submission error,向usb controller提交URB发生错误;

4.4 Address word - 地址

这个字段包含4部分,各个部分之间使用分号隔开,这4部分分别是URB类型及传输方向、USB总线号、USB设备地址端点号。

Ii:1:004:2
|  |  |  |
|  |  |  |__________ 端口号
|  |  |_____________ USB设备地址  
|  |________________ USB总线号
|___________________ URB类型及传输方向

URB类型及传输方向:USB有四种传输方式,分别是控制传输、批量传输、等时传输和中断传输。USB数据的传输方向是以USB主机端为参考对象的,USB主机向USB设备发送数据那么传输方向就是OutputUSB主机读取USB设备的数据那么传输方向就是Input

    Ci Co   Control input and output
    Zi Zo   Isochronous input and output
    Ii Io   Interrupt input and output
    Bi Bo   Bulk input and output

USB总线号:该字段表示USB总线号,每个USB Controller都有一条对应的USB总线,使用USB总线号区分它们,USB设备可以挂接到某条总线上;

USB设备地址:该字段表示USB设备的地址,每一个USB设备经过枚举后在USB总线都有一个唯一的地址;

端点号:表明该次数据传输是Input/Output到设备的哪个端点。上图中该字段是2,就表示这次数据传输使用的是设备的端点2。

4.5 URB Status word - URB状态

这个字段有两种表示形式: 

  • s + 一串数字:
  • 一串以分号间隔的数字(或单个数字)构成的,这串数字包含下面几个部分:URB statusintervalstart frameerror count。特别注意一点,该字段不同于 地址字段,对于不同的传输方式这几部分是可选的,并非所有部分都是必须的。

下图是不同的传输方式包含的信息;

img

下面分析不同的传输方式所包含的信息:

(1) 批量传输:只包含URB status这个字段,它对应着struct urb结构体中的status成员变量,表示URB的状态。URB status仅仅对事件类型中的Callback有意义,对于Submission是无意义的,之所以这么做是为了统一格式,方便使用脚本分析usbmon的数数据;

(2) 中断传输:URB statusIntervalURB status见前面的分析,Interval表示该URB对端点轮询的间隔时间;

(3) 等时传输:URB statusIntervalstart frameerror count。等时传输包含了所有部分,start frameerror count 是等时传输所特有的字段;

(4) 控制传输:控制传输在提交时(S:submission)这个字段是s,这里的s后面紧跟的数据是控制传输的建立过程主机发送的数据包(Setup packet),可以参考前面控制传输的示意图。控制传输在回调时(C:callback),这个字段代表的是URB status

img

该字段从左到右的格式如下,括号中的数字表示该部分占用的字节大小:

bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)

每个字段的含义可以在USB2.0规范中找到,这部分与USB的标准请求等相关。

4.6 Data Length - 数据包的长度

对于S(Submission)Data Length字段是主机请求发送/读取的数据长度,但是设备并不一定能够接收/发送主机请求的数据长度。实际接收/发送的数据长度在C(Callback)中的Data Length字段。

ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
							   |__ 输入		    |____ 请求读取的长度							
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
                               |__ 输入           |_______ 实际读取的长度 

4.7 Data tag - 数据标签

数据标签有三种:

  • =:后面紧跟数据流;

  • > :表示这是一次Output数据传输;

  • <:表示这是一次Input数据传输;

4.8 Data words follow - 数据流

这个字段就是一个事务中的数据包,我们平时分析usbmon抓取的数据包,也主要是看这个字段。注意一点,这个字段实际显示的数据 <= Data Length的值。

4.8.1 1个触摸点

1个触摸点时,我们读取到的数据:

ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000

报文格式;

0 1 2 3 4 5 6 7
0x01 0x01 0x00 0x75 0x09 0x08 0x09 0x30
设备ID 第一个点触摸状态 手指ID X坐标低8位 X坐标高8位 Y坐标低8位 Y坐标高8位
设备ID固定值 被按下 手指ID为0x00 X坐标为0x0975 X坐标为0x0975 Y坐标为0x0908 Y坐标为0x0908
4.8.2 2个触摸点

2个触摸点时,我们读取到的数据:

ffffffc0c88dab00 2543284410 C Ii:1:004:2 0:1 52 = 0101003d 02d50530 000101e1 035d0330 00000000 00000000 00000000 00000000

报文格式;

0 1 2 3 4 5 6 7
0x01 0x01 0x00 0x3d 0x02 0xd5 0x05 0x30
设备ID 第一个点触摸状态 手指ID X坐标低8位 X坐标高8位 Y坐标低8位 Y坐标高8位
设备ID固定值 被按下 手指ID为0x00 X坐标为0x023d X坐标为0x023d Y坐标为0x05d5 Y坐标为0x05d5
8 9 10 11 12 13 14 15
0x00 0x01 0x01 0xe1 0x03 0x5d 0x03 0x30
第二个点触摸状态 手指ID X坐标低8位 X坐标高8位 Y坐标低8位 Y坐标高8位
被按下 手指ID为0x01 X坐标为0x03e1 X坐标为0x03e1 Y坐标为0x035d Y坐标为0x035d

参考文章

[1] LinuxUSB抓包工具UsbMon的使用和包数据格式解析

[2] 使用usbmon抓取usb总线上的数据

[3] 详解usbmon抓取的log各字段的含义

[4] debian11 modprobe:未找到命令

[5] Ubuntu开机自动加载驱动模块

[6] Documentation/usb/usbmon.txt

[7] USB鼠标HID描述符以及数据格式